japan.internet.com
デベロッパー2010年1月8日 10:00
文字サイズ文字サイズ小文字サイズ中文字サイズ大

iPhoneアプリケーションでXML Webサービスを利用する

この記事のURLhttp://japan.internet.com/developer/20100108/26.html
著者:Wei-Meng Lee
海外internet.com発の記事

はじめに

 外部の世界との通信は、iPhoneアプリケーションを楽しく便利なものにする方法の1つです。これだけ多くの便利な機能を提供する無数のWebサービスが存在する今日では、特にそう言えます。しかしiPhoneでWebサービスを利用することは、なかなか根性が必要な作業です。他の開発ツール(Microsoft Visual Studioなど)と異なり、XcodeにはWebサービスの利用を容易にするツールが組み込まれていません。すべてを手作業で行い、Webサービスに送信する適切なXMLメッセージを作成し、返されたXMLの結果を解析する方法を知る必要があります。

 本稿では、iPhoneアプリケーション内からXML Webサービスと通信する方法を詳しく説明します。本稿のサンプルを通じて、各プロジェクトから他のWebサービスを利用するための基礎を学んでもらいたいと思います。

Webサービスを利用する

 Webサービスを利用するXcodeプロジェクトにとりかかる前に、実際のWebサービスを使って、どのような方法が存在するかを見てみましょう。筆者が気に入っている例は、.NETで作成したASMX XML Webサービスを使用するものです。ここでは具体的な例として、IPアドレスを入力するとそのIPアドレスが所属する国を返す、IPToCountryというWebサービスを使います。

 IPToCountryは、http://www.ecubicle.net/iptocountry.asmxにあります。SafariでこのURLをロードすると、このWebサービスが、図1に示す2つのWebメソッドを公開していることが分かります。

図1 IPToCountry Webサービス: このWebサービスは、2つのWebメソッドを公開する
図1 IPToCountry Webサービス: このWebサービスは、2つのWebメソッドを公開する
 例えばFindCountryAsXMLは、結果(国)をXML文字列として返します。図1の[FindCountryAsXML]のリンクをクリックすると、図2のページが表示されます。

図2 FindCountryAsXmlサービス: このWebサービスをブラウザインターフェースからテストすると、クライアントとサーバの間でXMLパケットを交換する様子が見てとれる
図2 FindCountryAsXmlサービス: このWebサービスをブラウザインターフェースからテストすると、クライアントとサーバの間でXMLパケットを交換する様子が見てとれる
 重要なのは、このページの「Test」セクションに続くセクションです。そこには、このWebサービスを利用するためのさまざまな方法が詳しく示されています。.NETの世界では、Webサービスへのアクセスはかなり単純明快な処理です。Visual Studioには、WSDLドキュメントをダウンロードしたときに、Webサービス用のWeb Proxyサービスオブジェクトを自動的に作成するツールが組み込まれているからです。iPhone開発では、これを手作業で行わなければなりません。従って、Webサービスの利用の根底にある仕組みを理解することが、非常に重要となります。

SOAP 1.1を使用する

 このWebサービスを利用するための方法の1つは、SOAP(Simple Object Access Protocol)を使用することです。SOAPを使用する場合は、次の例に示すように、HTTP POSTメソッドを用いてWebサービスにリクエストを送信します。

POST /iptocountry.asmx HTTP/1.1
Host: www.ecubicle.net
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://www.ecubicle.net/webservices/FindCountryAsXml"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/">
      <V4IPAddress>string</V4IPAddress>
    </FindCountryAsXml>
  </soap:Body>
</soap:Envelope>
 コードの中の太字で示した部分はプレースホルダーであり、実際の値に置き換える必要があります。このコードの重要なポイントを次にまとめておきます。

  • WebサービスのURLはhttp://www.ecubicle.net/iptocountry.asmx
  • SOAPAction属性のURLはhttp://www.ecubicle.net/webservices/FindCountryAsXml
  • リクエストのContent-Typetext/xml; charset=utf-8
  • HTTPメソッドはPOST
  • SOAPリクエストは次のとおり
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/">
      <V4IPAddress>string</V4IPAddress>
    </FindCountryAsXml>
  </soap:Body>
</soap:Envelope>
  • SOAPリクエストのContent-Lengthは、完全なSOAPリクエストに含まれる文字の総数
 Webサービスは、次の形式でレスポンスを返します。

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <FindCountryAsXmlResponse 
      xmlns="http://www.ecubicle.net/webservices/">
      <FindCountryAsXmlResult>xml result</FindCountryAsXmlResult>
    </FindCountryAsXmlResponse>
  </soap:Body>
</soap:Envelope>
 実際にリクエストを出した場合は、その結果(国)がXMLコードのブロックに囲まれて返されます(上の例において太字で示した部分)。これをXMLの結果から取り出す必要があります。

SOAP 1.2を使用する

 SOAP 1.2の使い方は、SOAP 1.1の使い方に非常によく似ています。リクエストの例を次に示します。

POST /iptocountry.asmx HTTP/1.1
Host: www.ecubicle.net
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/">
      <V4IPAddress>string</V4IPAddress>
    </FindCountryAsXml>
  </soap12:Body>
</soap12:Envelope>
 SOAP 1.2におけるSOAPレスポンスは、次のようになります。

HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <FindCountryAsXmlResponse 
       xmlns="http://www.ecubicle.net/webservices/">
      <FindCountryAsXmlResult>xml result</FindCountryAsXmlResult>
    </FindCountryAsXmlResponse>
  </soap12:Body>
</soap12:Envelope>

HTTP GETを使用する

 多くのWebサービスでは、何らかの理由でSOAPを使用したくない場合には、それよりも簡単なHTTP GETメソッドを使用できます。GETリクエストを送信するための形式を次に示します。

GET /iptocountry.asmx/FindCountryAsXml?V4IPAddress=string HTTP/1.1
Host: www.ecubicle.net
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
 ポイントを次に示します。

  • WebサービスのURLはhttp://www.ecubicle.net/iptocountry.asmx/FindCountryAsXml?V4IPAddress=string
  • リクエストのContent-Typetext/xml; charset=utf-8
  • リクエストのContent-Lengthは0(すべてがクエリ文字列によって送信されるため、何かを別途送信する必要がない)
  • HTTPメソッドはGET
 Webサービスは、次の形式でレスポンスを返します。

<?xml version="1.0"?>
xml result

HTTP POSTを使用する

 HTTP GETを使用する方法に加えて、HTTP POSTを使用する方法もあります。リクエストを送信する形式を次に示します。

POST /iptocountry.asmx/FindCountryAsXml HTTP/1.1
Host: www.ecubicle.net
Content-Type: application/x-www-form-urlencoded
Content-Length: length
V4IPAddress=string
 ポイントを次に示します。

  • WebサービスのURLはhttp://www.ecubicle.net/iptocountry.asmx/FindCountryAsXml
  • リクエストのContent-Typeapplication/x-www-form-urlencoded
  • リクエストのContent-LengthV4IPAddress=stringの長さ
  • HTTPメソッドはPOST
 Webサービスは、次の形式でレスポンスを返します。

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0"?>
xml result

iPhoneアプリケーションでWebサービスを利用する

 背景をすべて説明したところで、いよいよお待ちかねのiPhoneアプリケーションでWebサービスを利用するという作業にとりかかります。まず、SOAPを使用してWebサービスと通信する方法を説明します。

 Xcodeを用いて、ViewベースのApplicationプロジェクトを作成し、これに「WebServices」という名前を付けます。ファイル「WebServicesViewController.xib」をダブルクリックして、Interface Builderの中で開きます。[View]アイテムをダブルクリックし、次の各ビューを配置します。図3は、これらのビューを示したものです。

  • Label
  • Text Field
  • Round Rect Button
  • Activity Indicator
図3 Webサービスクライアントの構築: 各種ビューを配置した[View]ウィンドウ
図3 Webサービスクライアントの構築: 各種ビューを配置した[View]ウィンドウ
 Xcodeに戻り、ファイル「WebServicesViewController.h」を編集して、次のコードの中の太字で示したステートメントを追加します。

#import <UIKit/UIKit.h>
@interface WebServicesViewController : UIViewController {
    //---outlets---
    IBOutlet UITextField *ipAddress;
    IBOutlet UIActivityIndicatorView *activityIndicator;
    
    //---web service access---
    NSMutableData *webData;
    NSMutableString *soapResults;
    NSURLConnection *conn;
}
@property (nonatomic, retain) UITextField *ipAddress;
@property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
- (IBAction)buttonClicked:(id)sender;
@end
 ファイルを保存してInterface Builderに戻り、次の操作を行います。

  • Ctrlキーを押しながら[File's Owner]アイテムをクリックし、[Text Field]ビュー上にドラッグします。[ipAddress]を選択します。
  • Ctrlキーを押しながら[File's Owner]アイテムをクリックし、[Activity Indicator]ビュー上にドラッグします。[activityIndicator]を選択します。
  • Ctrlキーを押しながら[Rounded Rect Button]ビューをクリックし、[File's Owner]アイテム上にドラッグします。[buttonClicked:]アクションを選択します。
 この状態で[File's Owner]アイテムを右クリックすると、図4に示すような接続が表示されます。

図4 接続の完了: この図には、[File's Owner]アイテムのアウトレット接続とアクションが示されている
図4 接続の完了: この図には、[File's Owner]アイテムのアウトレット接続とアクションが示されている
 ファイル「WebServicesViewController.m」においてまず、次のようにText FieldおよびActivity Indicatorプロパティに対するsetterとgetterを作成します。

import "WebServicesViewController.h"
@implementation WebServicesViewController
@synthesize ipAddress;
@synthesize activityIndicator;
 次に、SOAPリクエストパケットを作成し、Webサービスへと送信するbuttonClicked:メソッド(リスト1参照)を定義します。

リスト1 ボタンクリック時のコード
- (IBAction)buttonClicked:(id)sender {

    NSString *soapMsg = 
        [NSString stringWithFormat:
            @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
             "<soap:Envelope 
               xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
               xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
               xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
             "<soap:Body>"
             "<FindCountryAsXml 
               xmlns=\"http://www.ecubicle.net/webservices/\">"
             "<V4IPAddress>%@</V4IPAddress>"
             "</FindCountryAsXml>"
             "</soap:Body>"
             "</soap:Envelope>",  
             ipAddress.text
        ];

    //---print it to the Debugger Console for verification---
    NSLog(soapMsg);
    
    NSURL *url = [NSURL URLWithString: 
      @"http://www.ecubicle.net/iptocountry.asmx"];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];

    //---set the headers---
    NSString *msgLength = [NSString stringWithFormat:@"%d", 
      [soapMsg length]];
      [req addValue:@"text/xml; charset=utf-8" 
      forHTTPHeaderField:@"Content-Type"];
    [req addValue:@"http://www.ecubicle.net/webservices/FindCountryAsXml" 
        forHTTPHeaderField:@"SOAPAction"];
    [req addValue:msgLength forHTTPHeaderField:@"Content-Length"];

    //---set the HTTP method and body---
    [req setHTTPMethod:@"POST"];
    [req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
    
    [activityIndicator startAnimating];
    
    conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn) {
        webData = [[NSMutableData data] retain];
    }    

}
 ここで少し、リスト1に追加したコードで何をしているのかを見ていきましょう。最初に、SOAPリクエストパケットを作成しています。

NSString *soapMsg = 
    [NSString stringWithFormat:
        @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
         "<soap:Envelope 
          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
          xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
          xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
         "<soap:Body>"
         "<FindCountryAsXml 
           xmlns=\"http://www.ecubicle.net/webservices/\">"
         "<V4IPAddress>%@</V4IPAddress>"
         "</FindCountryAsXml>"
         "</soap:Body>"
         "</soap:Envelope>",  ipAddress.text
    ];
 次に、NSMutableURLRequestオブジェクトとNSURLオブジェクトのインスタンスを使用して、URLをロードするためリクエストオブジェクトを作成しています。

NSURL *url = [NSURL URLWithString: 
  @"http://www.ecubicle.net/iptocountry.asmx"];
NSMutableURLRequest *req = 
  [NSMutableURLRequest requestWithURL:url];
 次に、Content-TypeSOAPActionContent-Lengthなどの各種ヘッダを、リクエストオブジェクトに設定しています。HTTPメソッドとHTTPボディも設定しています。

NSString *msgLength = 
  [NSString stringWithFormat:@"%d", [soapMsg length]];
[req addValue:@"text/xml; charset=utf-8"  
  forHTTPHeaderField:@"Content-Type"];
[req addValue:@"http://www.ecubicle.net/webservices/FindCountryAsXml" 
  forHTTPHeaderField:@"SOAPAction"];
[req addValue:msgLength 
  forHTTPHeaderField:@"Content-Length"];
[req setHTTPMethod:@"POST"];
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
 実際にWebサービスにリクエストを送信する前に、[Activity Indicator]ビューのアニメーションを起動しています。これにより、アプリケーションがWebサービスからのレスポンスを待っていることを示す、視覚的なフィードバックをユーザーに提供します。

[activityIndicator startAnimating];
 Webサービスとの接続を確立するには、先ほど作成したリクエストオブジェクトとともにNSURLConnectionクラスを使用します。

conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn) {
   webData = [[NSMutableData data] retain];
}    
 ここでNSURLConnectionオブジェクトは、Webサービスへとリクエストを送信し、Webサービスからのレスポンスの受信に伴い、さまざまなメソッド(これらについては後で定義します)を非同期に呼び出します。NSMutableDataクラスのdataメソッドは、バイトバッファのラッパーとなる、空のデータオブジェクトを返します。これを用いてWebサービスからのデータを受信します。

 Webサービスからのデータの受信が開始すると、次に示すconnection:didReceiveResponse:メソッドが呼び出されます。

-(void) connection:(NSURLConnection *) connection 
didReceiveResponse:(NSURLResponse *) response {
   [webData setLength: 0];
}
 上のコードでは、webDataの長さを0に初期化していることに注意してください。Webサービスから連続的にデータを受信する場合は、connection:didReceiveData:メソッドが繰り返し呼び出されます。このメソッドを用いて、受信したデータをwebDataオブジェクトに付加していきます。

-(void) connection:(NSURLConnection *) connection 
didReceiveData:(NSData *) data {
   [webData appendData:data];
}
 伝送中にエラーが生じると、次のconnection:didFailWithError:メソッドが呼び出されます。

-(void) connection:(NSURLConnection *) connection 
didFailWithError:(NSError *) error {
[webData release];
    [connection release];
}
 接続が終了し、レスポンスのダウンロードに成功すると、次のconnectionDidFinishLoading:メソッドが呼び出されます。

-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
    NSLog(@"DONE. Received Bytes: %d", [webData length]);
    NSString *theXML = [[NSString alloc] 
                        initWithBytes: [webData mutableBytes] 
                        length:[webData length] 
                        encoding:NSUTF8StringEncoding];
    //---shows the XML---
    NSLog(theXML);
    [theXML release];    
    [activityIndicator stopAnimating];    
    
    [connection release];
    [webData release];
}
 この例では、Webサービスから受信したXMLレスポンスを単に[Debugger Console]ウィンドウに表示し、[Activity Indicator]ビューのアニメーションを停止します。

 最後に、次のdeallocメソッドで、すべてのプロパティとオブジェクトを解放します。

- (void)dealloc {    
    [ipAddress release];
    [activityIndicator release];
    [xmlParser release];
    [soapResults release];
    [super dealloc];
}
 これで完成です。Command-Rキーを押して、アプリケーションをiPhone Simulatorでテストしてください。テキストフィールドに34.5.6.7というIPアドレスを入力し、[Find Country]ボタンをタップします。Xcodeにおいて、Shiftキーを押しながらCommand-Rキーを押して、[Debugger Console]ウィンドウを開くと、次のリクエストがWebサービスに送信されたことがわかります。

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <FindCountryAsXml xmlns="http://www.ecubicle.net/webservices/">
         <V4IPAddress>34.5.6.7</V4IPAddress>
      </FindCountryAsXml>
   </soap:Body>
</soap:Envelope>
 WebサービスのSOAPレスポンスには、「United States」という文字列が含まれています。

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope 
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Body>
      <FindCountryAsXmlResponse 
         xmlns="http://www.ecubicle.net/webservices/">
         <FindCountryAsXmlResult>
            <IPCountryService xmlns="">
               <Country>United States</Country>
            </IPCountryService>
         </FindCountryAsXmlResult>
      </FindCountryAsXmlResponse>
   </soap:Body>
</soap:Envelope>
 Webサービスからのレスポンスが返ってきたことから、Webサービスとの通信に成功したことが分かります。次の問題は、必要な結果を取り出すためにどのようにしてXMLを解析するかです。この例の場合、必要な結果は<Country>要素の中にあります。本稿の最後のセクションでは、XMLレスポンスを解析する方法を説明しますが、その前に、SOAPメソッドを用いてWebサービスと通信する代わりに、HTTP GETとHTTP POSTメソッドを使用したい場合にどうすればよいかを紹介します。

Webサービスに対してHTTP POSTを使用する

 HTTP POSTを使用してWebサービスと通信するために必要な作業は、次のようにbuttonClicked:メソッドを変更することだけです。

- (IBAction)buttonClicked:(id)sender {
    NSString *postString = 
        [NSString stringWithFormat:@"V4IPAddress=%@", 
        ipAddress.text];
    NSLog(postString);
    
    NSURL *url = [NSURL URLWithString: 
        @"http:// www.ecubicle.net/iptocountry.asmx/FindCountryAsXml"];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
    NSString *msgLength = 
      [NSString stringWithFormat:@"%d", [postString length]];
    
[req addValue:@"application/x-www-form-urlencoded" 
    forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
    [req setHTTPMethod:@"POST"];
    [req setHTTPBody: [postString 
       dataUsingEncoding:NSUTF8StringEncoding]];
    
    [activityIndicator startAnimating];
    
    conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn) {
        webData = [[NSMutableData data] retain];
    }    
}
 基本的には、SOAPメッセージを作成する必要がなくなり、V4IPAddress=34.5.6.7のような単一の文字列をWebサービスにPOSTするだけとなります。変更したアプリケーションを実行し、Debugger Consoleを見ると、次のようなレスポンスが返されているはずです。

<?xml version="1.0" encoding="utf-8"?>
<IPCountryService>
    <Country>United States</Country>
</IPCountryService>

Webサービスに対してHTTP GETを使用する

 HTTP GETを用いたWebサービスとの通信は、さらに容易です。すべてのリクエスト情報がクエリ文字列で引き渡されるからです。HTTP GETメソッドを用いてWebサービスにアクセスするように変更したbuttonClicked:メソッドを次に示します。

- (IBAction)buttonClicked:(id)sender {
NSString *queryString = 
       [NSString stringWithFormat:
          @"http://www.ecubicle.net/iptocountry.asmx/" + 
          "FindCountryAsXml?V4IPAddress=%@", 
          ipAddress.text];
NSURL *url = [NSURL URLWithString:queryString];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req addValue:@"text/xml; charset=utf-8" 
         forHTTPHeaderField:@"Content-Type"];
[req addValue:0 forHTTPHeaderField:@"Content-Length"];
[req setHTTPMethod:@"GET"];
[activityIndicator startAnimating];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn) {
webData = [[NSMutableData data] retain];
}
}
 HTTP POSTメソッドの場合と同様に、変更したアプリケーションを実行したときにDebugger Consoleに表示されるレスポンスは次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<IPCountryService>
    <Country>United States</Country>
</IPCountryService>

XMLレスポンスを解析する

 さて、Webサービスを呼び出すためのさまざまの方法を理解したところで、次にXMLレスポンスを解析する方法について説明します。iPhone SDKには、XMLファイルを解析するために使用できるNSXMLParserオブジェクトがあります。NSXMLParserクラスは、XMLドキュメント(または文字列)を逐次的に解析するSimple API for XML(SAX)を実装したものです。

 NSXMLParserオブジェクトは、XMLドキュメントを読み込み、その最初から最後までをスキャンします。ドキュメントの中で各種項目(要素、属性、コメントなど)を検出するたびに、要素の値の抽出など、適切なアクションが実行できるように、デリゲートに通知します。

 Webサービスからのレスポンスを解析するには、ファイル「WebServicesViewController.h」に次のステートメントを追加します。

#import <UIKit/UIKit.h>
@interface WebServicesViewController : UIViewController {
    //---outlets---
    IBOutlet UITextField *ipAddress;
    IBOutlet UIActivityIndicatorView *activityIndicator;
    
    //---web service access---
    NSMutableData *webData;
    NSMutableString *soapResults;
    NSURLConnection *conn;
    //---xml parsing---
    NSXMLParser *xmlParser;
    BOOL *elementFound;
}
@property (nonatomic, retain) UITextField *ipAddress;
@property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
- (IBAction)buttonClicked:(id)sender;
@end
 ファイル「WebServicesViewController.m」では、connectionDidFinishLoading:メソッドに、次の太字で示したステートメントを追加します。

-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
    NSLog(@"DONE. Received Bytes: %d", [webData length]);
    NSString *theXML = [[NSString alloc] 
                        initWithBytes: [webData mutableBytes] 
                        length:[webData length] 
                        encoding:NSUTF8StringEncoding];
    //---shows the XML---
    NSLog(theXML);
[theXML release];    
    [activityIndicator stopAnimating];    
    if (xmlParser)
    {
        [xmlParser release];
    }
    xmlParser = [[NSXMLParser alloc] initWithData: webData];
    [xmlParser setDelegate: self];
    [xmlParser setShouldResolveExternalEntities:YES];
    [xmlParser parse];
    
    [connection release];
[webData release];
}
 NSXMLParserクラスのインスタンスを作成し、Webサービスから返されたレスポンスによってそれを初期化します。パーサーは、XMLドキュメントの中で各種項目を検出するたびに、いくつかのメソッドを呼び出します。次はそれらのメソッドを定義します。

 最初に実装するメソッドは、parser:didStartElement:namespaceURI:qualifiedName:attributes:です。このメソッドは要素の開始タグを検出したときに呼び出されます。

//---when the start of an element is found---
-(void) parser:(NSXMLParser *) parser 
   didStartElement:(NSString *) elementName 
namespaceURI:(NSString *) namespaceURI 
qualifiedName:(NSString *) qName
attributes:(NSDictionary *) attributeDict {
    
    if( [elementName isEqualToString:@"Country"])
    {
        if (!soapResults)
        {
            soapResults = [[NSMutableString alloc] init];
        }
        elementFound = YES;
    }
}
 上のコードでは、現在のタグが<Country>であるかどうかを調べています。そうである場合、Boolean型の変数elementFoundYESを設定します。

 次に実装するメソッドはparser:foundCharacters:です。パーサーが要素のテキストを検出すると呼び出されます。

-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string
{
    if (elementFound)
    {
        [soapResults appendString: string];
    }
}
 ここで、最後に検出したタグが<Country>ならば、Country要素の値を取り出し、soapResultsに代入する処理を行います。

 最後は、パーサーが要素の終わりを検出した場合に呼び出すparser:didEndElement:namespaceURI:qualifiedName:メソッドです。

//---when the end of element is found---
-(void)parser:(NSXMLParser *)parser 
    didEndElement:(NSString *)elementName 
    namespaceURI:(NSString *)namespaceURI 
    qualifiedName:(NSString *)qName
{
    if ([elementName isEqualToString:@"Country"])
    {
        //---displays the country---
        NSLog(soapResults);        
        UIAlertView *alert = [[UIAlertView alloc] 
          initWithTitle:@"Country found!" 
                                                          
        message:soapResults 
        delegate:self  
        cancelButtonTitle:@"OK" 
        otherButtonTitles:nil];
        [alert show];
        [alert release];
        [soapResults setString:@""];
        elementFound = FALSE; 
    }
}
 ここでは、単に</Country>タグを探しています。これを検出すれば、Country要素の値が正しく抽出されたことになります。次に、UIAlertViewオブジェクトを用いてその値をプリントします。

 これで完成です。ここでCommand-Rキーを押して、このアプリケーションをiPhone Simulatorでテストできます。IPアドレスを入力し、[Find Country]ボタンをクリックします。図5に、その結果を示します。

図5 国名の検出: 完成したアプリケーションでは、XMLの結果を解析した後に国名を表示する
図5 国名の検出: 完成したアプリケーションでは、XMLの結果を解析した後に国名を表示する
 以上、実際のiPhoneアプリケーションを使って、SOAP、HTTP GET、HTTP POSTという3種類の方法でiPhoneアプリケーションからWebサービスを利用する例を紹介しました。今回の例では、Webサービスを呼び出し、XML形式のレスポンスからデータを抽出する方法を示しました。Webサービスの利用(およびXMLの解析)方法を習得すれば、Webの世界にあふれているさまざまなデータをiPhoneアプリケーションで利用できるようになります。

著者紹介

Wei-Meng Lee(Wei-Meng Lee)
Microsoft MVP受賞者。Microsoft社の最新テクノロジー実地研修を専門とするDeveloper Learning Solutions社を創設。.NETとワイヤレステクノロジーの開発者、指導者として知られる。
国際的なカンファレンスでたびたび講演し、.NET、XML、ワイヤレステクノロジーに関する著書、共著書多数。.NETからMac OS Xに至るまで広範な分野について執筆している。著書に『.NET Compact Framework Pocket Guide』 (Oreilly&Associates Inc、2005年5月)、『ASP.NET 2.0: A Developer's Notebook』 (Oreilly&Associates Inc、2005年6月)、『Programming Sudoku』 (Apress刊、2006年3月)など。ブログ「Wei-Meng Lee's Blog」を開設している。
japan.internet.comのウエブサイトの内容は全て、国際法、日本国内法の定める著作権法並びに商標法の規定によって保護されており、その知的財産権、著作権、商標の所有者はインターネットコム株式会社、インターネットコム株式会社の関連会社または第三者にあたる権利者となっています。
本サイトの全てのコンテンツ、テキスト、グラフィック、写真、表、グラフ、音声、動画などに関して、その一部または全部を、japan.internet.comの許諾なしに、変更、複製、再出版、アップロード、掲示、転送、配布、さらには、社内LAN、メーリングリストなどにおいて共有することはできません。
ただし、コンテンツの著作権又は所有権情報を変更あるいは削除せず、利用者自身の個人的かつ非商業的な利用目的に限ってのみ、本サイトのコンテンツをプリント、ダウンロードすることは認められています。

Copyright 2012 internet.com K.K. (Japan) All Rights Reserved.