2012年2月29日水曜日

Core Dataの追加


ユーザーが選択した情報、ディスク保存したデータのメタ情報などを保存するのにCore Dataが利用できます。

Xcode 4.2ではSingle View Appplicationを選択してプロジェクト作成するとCore Dataを使用する設定が追加されないので、既存プロジェクトにCore Dataを追加します。
参照: Core Data Model Editor Help
  1. Data Model追加
    Xcodeのメニューから  File > New > New File... を選択。
    テンプレート選択ウインドウで iOS > Core Data > Data Model を選択。
    xcdatamodelファイルを保存。

  2. Entity追加(DBのTableに相当)
    Xcode左ペインでプロジェクト名をクリック。
    Xcode左ペインで追加されたxcdatamodelファイルを選択。
    Xcode下ペインの+Add Entityをクリック。
    追加された"Entity"の名前を変更。

  3. attribute追加(DBのITEMに相当)
    Attibutesグループの下側ツールバーの+をクリック。
    追加された"Attribute"の名前を変更。
    データ型を選択。
    必要に応じてPropertyを設定。

  4. NSManagedObjectのサブクラス追加
    Xcodeのメニューから Editor > Create Managed Object Subclass... を選択。
    ファイルの追加または置換を行うEntityを選択。
    既存ファイルがある場合は置換になるので注意。

  5. framework追加
    ビルド前にCoreData.frameforkをリンクに追加します。

以上で下準備完了ですが、当然これだけではCore Dataの読み書きはできません。

Appleのチュートリアルではアプリケーションデリゲートクラスに次の property、methodを追加します。
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSURL *)applicationDocumentsDirectory;
- (void)saveContext;

アプリケーション終了時、バックラウンド移行時にデータを保存するのが望ましいため、Core Dataアクセスは アプリケーションデリゲート の管理下で行うのが自然です。しかし、上記のプロパティーとメソッドは必ずしもApplicationDelegateに実装する必要はありません。また、マルチスレッドでCore Dataにアクセスする場合は各々でNSManagedObjectContextインスタンスを持つ必要があるため、アプリケーションデリゲート以外でも同様の手順が必要になる場合があります。

複数のNSManagedObjectContextインスタンスが保持する状態は異なる場合があるので、マルチスレッドでCore Dataを扱うような場合は注意が必要です。同じデータの異なる状態を異なるContextで持つような状況が避けらればよいのですが、さもなければ対策が必要になります。Core Dataでは楽観的ロック(Optimistic Lock)によりsave:時に元データに変更があった場合に対処する方法、notificationにより他のcontextで変更されたときに通知を受け取り、データを最新状態にする方法が用意されています。

実装例

MYAppDelegate.h

#import <CoreData/CoreData.h>

@interface IDAppDelegate : UIResponder <UIApplicationDelegate>
    @property (nonatomic, strong, readonly) NSManagedObjectContext *managedObjectContext;
    @property (nonatomic, strong, readonly) NSManagedObjectModel *managedObjectModel;
    @property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@end

MYAppDelegate.m

@implementation IDAppDelegate
@synthesize managedObjectContext = _managedObjectContext;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize managedObjectModel = _managedObjectModel;

//保存先ディレクトリ
- (NSString *)applicationDocumentsDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

//_persistentStoreCoordinatorがnilなら作って返す。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {  
    if (_persistentStoreCoordinator == nil) {
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
                                          initWithManagedObjectModel:[self managedObjectModel]];


       NSURL *url =  [NSURL fileURLWithPath:[[self applicationDocumentsDirectory]
                                          stringByAppendingPathComponent: @"CoreData.sqlite"]];

        NSError *error = nil;
        if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                          configuration:nil
                                                                                     URL:url
                                                                                 options:nil error:&error])
        {
            //エラー処理
            NSLog(@"persistentStoreCoordinator: Error %@, %@", error, [error userInfo]);
        }
    }
    return _persistentStoreCoordinator;
}

//_managedObjectModelがnilなら作って返す。
-(NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel == nil) {
        _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    }
    return _managedObjectModel;
}

//_managedObjectContextがnilなら作って返す。
- (NSManagedObjectContext *)managedObjectContext {
    if (_managedObjectContext == nil) {
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        if (coordinator != nil) {
            _managedObjectContext = [[NSManagedObjectContext alloc] init];
            [_managedObjectContext setPersistentStoreCoordinator:coordinator];
        }
    }
    return _managedObjectContext;
}

//_managedObjectContextのデータを保存する。
- (void)saveContext {
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
        //エラー処理
        NSLog(@"saveContext: %@", error.debugDescription);
    }
}

//アプリケーション終了時に保存
- (void)applicationWillTerminate:(UIApplication *)application
{
    [self saveContext];
}

//バックグランド移行時に保存
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self saveContext];
}

//メモリ警告時に保存、リセット
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
    [self saveContext];
    [[self managedObjectContext] reset];
}

@end

開発中に項目の追加/削除があると古いデータを読み込めなくなります。
Core Data 自動マイグレーションを設定することをお勧めします。

Core Data 自動マイグレーション
core data modelの不要なバージョンを削除する方法

2012年2月28日火曜日

データのディスクへの保存


データの保存先が決まれば、NSData、NSStringのメソッドでディスクへ保存することができます。保存メソッドは何種類かあり、以下はその一例です。

//NSDataの場合
BOOL result = [someData writeToFile:@"somePath" atomically:YES];

//NSStringの場合
BOOL result = [someString writeToFile:@"somePath" atomically:YES];

atomically:YESの場合、まず別名のファイルにデータを書き出し、エラーがなければ指定のファイル名にりネームします。既に同名ファイルが存在している場合、書き込み中も直前のデータが正常な状態で残り、エラーがあった場合も直前のデータが残ります。

iOSでは、原則としてアプリケーション毎に隔離された領域であるSandbox内にファイルを保存します。データの種類により保存先が異なり、システム上の扱いも異なります。
いくつかの項目でSandboxのディレクトリを分類してみました。

Sandbox内のディレクトリ
変更 永続性 iTunes
Backup
App
データ
User
データ
ファイル
共有(*6)
NSSearchPathDirectory
<Application_Home> △(*5) × NSApplicationDirectory
<Application_Home>/AppName.app x(*1)
<Application_Home>/Documents/ NSDocumentDirectory
<Application_Home>/Documents/Inbox x(*2) ×(*2)
<Application_Home>/Library/ NSLibraryDirectory
<Application_Home>/Library/Caches △(*3) × NSCachesDirectory
<Application_Home>/tmp/ ×(*4) ×
<Application_Home>/(独自ディレクトリ) △(*5) ×
*1 AppStoreからの購入時に同期が行われる。
*2 主にメールアプリが管理する添付データなど。管理アプリ以外はRead、Deleteのみ。
*3 ディスクスペース逼迫時に削除されることがある。iTunesバックアップ対象外。システム復元時は破棄される。
*4 古いファイルは削除されることがある。iTunesバックアップ対象外。システム復元時は破棄される。
*5 iTunesバックアップ対象外。システム復元時は破棄される。
*6 URL Schemaにより他のアプリからのアクセスが可能。未確認のためブランク

iOS 5以前の古いバージョンでは異なる場合があります。
参照:File System Programming Guide
        ファイルシステムプログラミングガイド

デスクトップアプリと比べ、iOSの場合はiTunes(およびiCloud)のバックアップ対象かどうかが判断基準に加わります。バックアップが不要な場合はDocumentsではなく、Library/Cacheか独自ディレクトが保存先になるでしょう。

Library/Cacheに保存したデータはディスク残容量が少なくなった場合に削除されることがあり得ますが、再現可能なデータであればこちらの方が望ましいでしょう。

tmp内のデータは古くなるとシステムも削除しますが、アプリがアクティブな時は削除されません。不要になったらアプリで削除すべきです。

保存先が決まればNSFileManagerとNSSearchPathDirectoryを使ってディレクトリのパスを取得します。

NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirs = [fm URLsForDirectory:directory
                           inDomains:NSUserDomainMask];
NSURL *dirURL = ([dirs count] > 0) ? [dirs objectAtIndex:0] : nil;
(directoryはNSSearchPathDirectory定数)

また、次のような関数が用意されています。
NSString *NSHomeDirectory() <Application_Home>ディレクトリ
NSString * NSTemporaryDirectory() <Application_Home>/tmp ディレクトリ

サブディレクトリの場合はNSURLのメソッドでディレクトリを追加します。

dirURL = [dirURL URLByAppendingPathComponent:@"subdir"];

ディレクトリがない場合は作成します。

BOOL isDirectory;
BOOL found= [fm fileExistsAtPath:dirURL.path isDirectory:&isDirectory];
if (!found) {
  BOOL success = [fm createDirectoryAtURL:dirURL withIntermediateDirectories:YES];
  if (!success) {
    //エラー処理
  }
} else if (!isDirectory) {
  //エラー処理
}

マルチスレッドに関して
NSData、NSStringによるデータ書き込みはお手軽ですが、 マルチスレッド の場合は競合に注意する必要があります。SOAPリクエストで使用したNSURLRequestも マルチスレッドで実行されます。このような場合、バックグランド処理ではファイルがない場合のみ新規ファイルの追加を行い、既存ファイルの変更・削除は行わないなどの取り決めをすれば競合を避けられます。競合が避けられない場合はNSFileCoordinatorなどによる対策が必要になります。

関連Blog
データのディスクへの保存
ディレクトリ作成/削除

2012年2月23日木曜日

SOAPリクエストとの同期


NSURLConnection によるネットワークへのリクエスト/レスポンスは非同期で実行されます。
UITableViewCellが選択されたときにSOAPリクエストを送信すると同時に次の画面へ移動するという処理を行う場合、画面移動時点ではまだデータがないため、いったん空の画面の移動し、受信完了後に画面表示を行います。

//Cell選択時にUITebleViewContollerからUIWebViewを持つUIViewControllerへ移動します。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyWebViewController *vCtr = [[self storyboard] instantiateViewControllerWithIdentifier:@"MyWebViewController"];
    [self.navigationController pushViewController:vCtr animated:YES];
}

//UIViewControllerがNSConnectionのdelegateになっているとします。
//受信完了時のdelegateメソッドで画面表示を行います。
-(void)connectionDidFinishLoading:(NSConnection*)connection
{
     NSData *binData = [<受信したSOAPレスポンスからバイナリデータを復元>];
     UIWebView *webView = (UIWebView *)[self view]; //self = anUIViewController
     //PDFの場合
      [(UIWebView *)[self view] loadData:binData MIMEType:@"application/pdf"
                                                               textEncodingName:nil baseURL:nil];
}

なお、NSURLConnectionによるリクエスト/レスポンスは非同期で実行されるので、この呼び出しを更にNSOperationQueueに入れたタスクから呼び出すととフリーズしたりします。

関連Blog

iOS アプリからのSOAP/WebServiceへのアクセス
SOAP レスポンスの受信
SOAP レスポンスのパース
バイナリデータのSOAPレスポンス
SOAPリクエスト/レスポンスとの同期

Storyboard上のViewControllerをプログラムで作る方法


UITableViewでCellが選択されたときに次の画面へ遷移するにはdidSelectRowAtIndexPathメソッドをoverrideします。ところが、Xcodeが作るテンプレートのメソッドではStoryboardの設定が使えません。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Navigation logic may go here. Create and push another view controller.
    /*
     <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
     // ...
     // Pass the selected object to the new view controller.
     [self.navigationController pushViewController:detailViewController animated:YES];
     */
}

instantiateViewControlを使うとStoryboardに置いた画面を作ることができます。

    //@" PDFWebViewController  "はStoryboardで設定した値。
    PDFWebViewController *vCtr = [[self storyboard] instantiateViewControllerWithIdentifier:@"PDFWebViewController"];

itentifierはXcodeのStoryboradに配置したUIViewControllerのプロパティー画面で
設定します。

UIWebViewでPDF/Excelファイルまたはバイナリデータを表示


サーバーからバイナリデータで取得したがPDFやExcelデータをUIWebViewで表示する方法です。
PDFの場合はloadDataメソッドでUIWebViewにそのまま渡して表示することができます。
 
    UIWebView *webView = (UIWebView *)[self view]; //self = anUIViewController
    [webView loadData:binData MIMEType:@"application/pdf"
                      textEncodingName:nil
                               baseURL:nil];

Excelの場合はこの方法では表示できませんが、いったんディスクに保存し、そのファイルのURLを使ってloadRequestUIWebViewで表示させることができます。
 
    UIWebView *webView = (UIWebView *)[self view]; //self = anUIViewController
    NSString *path = @"somePath";
    NSURL *url = [NSURL fileURLWithPath:path];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];
    [webView loadRequest:req];

この方法はPDFにも適用できますし、それ以外にもUIWebViewが表示可能な全てのデータに適用できるはずです。

バイナリデータのSOAPレスポンス


前項「SOAP レスポンスのパース」で受信したSOAPレスポンスが文字列の場合はResultの値をそのまま使えますが、バイナリデータの場合はbase64エンコードされた文字列で送られてくるため、これをデコードする必要があります。ところがiOS SDKにはこの機能が含まれていません。

BASE64エンコード/デコードのソースはいくつか見つかりましが、最初にダウンロードしたMatt Gallagher氏のソースをそのまま利用しています。ダウンロードしたソース中の次のふたつのファイルをプロジェクトに加えるだけです。
NSData+Base64.h
NSData+Base64.m

NSDataのCategoryでメソッドが追加されているので、次の要領でbase64のNSStringからNSDataオブジェクトを作れます。

NSData *binData = [NSData dataFromBase64String:soapResult];

BASE64エンコードしたバイナリデータとファイル名などのメタ情報をString配列として返すことができます。

関連Blog

iOS アプリからのSOAP/WebServiceへのアクセス
SOAP レスポンスの受信
SOAP レスポンスのパース
バイナリデータのSOAPレスポンス
SOAPリクエスト/レスポンスとの同期


<参照>
Base64 encoding options on the Mac and iPhone
AES128暗号化ライブラリ FBEncryptor 公開

SOAP レスポンスのパース



iOSにはDOMのパーサーは含まれていないため、SAXパーサーのNSXMLParserでSOAPレスポンスのXMLをパースします。
処理の手順は概ね次のようになります。

//インスタンス変数
NSMutableString *soapResults;
NSDictionary *attributes;
BOOL elementFound;

//dataをパースしsoapResultsに結果をセットする。
- (void)someMethod:(NSData *)data
{
    //受信したデータ(NSData)を渡してNSXMLParserを作る。
    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:data];

    //delegateセット
    [xmlParser setDelegate:self];

    //パース開始。パース中にNSXMLParserDelegateメソッドが呼ばれる。
    soapResults = [[NSMutableString alloc] init];
    [xmlParser parse];
}

#pragma mark - NSXMLParserDelegate

//要素タグの開始
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
                                        namespaceURI:(NSString *)namespaceURI
                                       qualifiedName:(NSString *)qName
                                          attributes:(NSDictionary *)attributeDict
{
    //Resultタグが見つかったらフラグをセット
    if ([elementName hasSuffix:@"Result"]) {
        elementFound = YES;
        //属性のセット
        attributes = [NSDictionary dictionaryWithDictionary:attributeDict ];
    }
}

//タグのtextノードの値が渡される。複数回呼ばれることがある。
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if (elementFound)
    {
        [soapResults appendString:string];
    }
}

//タグ終了時に呼ばれる。
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
                                     namespaceURI:(NSString *)namespaceURI
                                    qualifiedName:(NSString *)qName
{
    elementFound = NO;
}

私はサーバーでXMLを作り文字列で返すようにしましたが、この場合はResultにそのXMLが入っているため、再度上記の要領でパースします。

SOAPレスポンスのResultタグにはattributeはありませんが、このXMLにはattributeがあり 、didStartElementのattributeDictにセットされて渡されてきます。

関連Blog

iOS アプリからのSOAP/WebServiceへのアクセス
SOAP レスポンスの受信
SOAP レスポンスのパース
バイナリデータのSOAPレスポンス
SOAPリクエスト/レスポンスとの同期


<参照>
Consuming XML Web Services in iPhone Applications
iPhoneアプリケーションでXML Webサービスを利用する

2012年2月12日日曜日

SOAP レスポンスの受信


SOAPレスポンスは非同期で受信され、受信経過に応じて次の通知がdelegateに送られてきます。

(1) connection:didReceiveResponse:レスポンスが届いたことの通知
(2) connection:didReceiveData:データ受信中に何度か呼ばれる。
(3) connection:didFailWithError:エラーが発生したときだけ呼ばれる。
(4) connectionDidFinishLoading:受信完了時に通知。エラー発生時は呼ばれない。

コーディングサンプル。

NSData *soapData = [NSMutableData data];

//レスポンスが届いたことの通知
-(void)connection:(NSURLConnection*) connection didReceiveResponse:(NSURLResponse *)response {
    [soapData setLength:0];
}

//受信中に何度か呼ばれる。webDataに追加。
-(void)connection:(NSURLConnection*)connention didReceiveData:(NSData *)data {
    [soapData appendData:data];
}

//エラー発生時に呼ばれる。
-(void)connection:(NSURLConnection*)connection didFailWithError:(NSError *)error {
    soapData = nil;
    connection = nil;
   //エラー処理
}

//受信完了時に呼ばれる。
-(void)connectionDidFinishLoading:(NSConnection*)connection {
    NSLog(@"受信データ: %dbytes", [soapData  length]);
    NSString *returnedXml =
      [[NSString alloc] initWithBytes:[webData mutableBytes]
                               length:[webData length]
                             encoding:NSUTF8StringEncoding];
    connection = nil;
    //受信データの処理
}

レスポンスのタグ名について
DOT.NETではレスポンスタグの名前に"Response"、"Result"が付加されたものがメソッド名となっており、レスポンスからデータ部分を抜き出すのに"Result"で終わっているかどういかで判断するようにしました。これはDOT.NETの慣例で、SOAPの仕様ではそこまで決まっていないようです。DOT.NET以外のサーバーの場合には変更が必要になるでしょう。

戻り値が文字列の場合
レスポンス全体がXMLで返されますが、Resultタグ内はXMLのエスケープが適用された文字列となっています。Wei-Meng Lee氏のSampleのようにNSXMLParserでパースすればアンエスケープされます。

戻り値がbyte[]の場合
Resultタグ内にbase64エンコードされて送られてきます。iOSアプリ側でデコードする必要があります。

String配列の場合
参照: String配列として返す方法


関連Blog

iOS アプリからのSOAP/WebServiceへのアクセス
SOAP レスポンスの受信
SOAP レスポンスのパース
バイナリデータのSOAPレスポンス
SOAPリクエスト/レスポンスとの同期


<参照>
Consuming XML Web Services in iPhone Applications
iPhoneアプリケーションでXML Webサービスを利用する



2012年2月10日金曜日

iOS アプリからのSOAP/WebServiceへのアクセス


iOSアプリからDOT.NETで作成したWebServiceにアクセスしようとしたところ、iOS SDKにはSOAPのサポートがありません。WebObjectsには含まれているようです。
Wei-Meng Lee氏のブログを見つけ、それを参考に実装してみました。
Consuming XML Web Services in iPhone Applications
日本語版もあります。
iPhoneアプリケーションでXML Webサービスを利用する

対象バージョンがちょっと古いためXcode4.2では若干の読み替えの必要はあります。
  • ARCではreleaseが使えないと警告が出るので削除する。(またはbuildオプション-fno-objc-arcを使う。)
  • サンプルで使用されているWebServiceがなくなっている。
    http://www.ecubicle.net/iptocountry.asmx
    自開発のWebServiceを対象にするならそれを使えばよいし、試していませんがiptocountryに類似のサイトもあります。
    WSIP2Country
SOAP 1.1と1.2でヘッダ情報が異なりますが、このSampleの範囲ではどちらでも結果は同じです。
サンプルのiptocountryは1.1の形式になっているので、コードをコピペするときはバージョンが混在しないように気を付けましょう。

SOAPヘッダの相違点:

SOAP 1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://www.ecubicle.net/webservices/FindCountryAsXml"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

SOAP 1.2
Content-Type: application/soap+xml; charset=utf-8
SOAPActionなし
xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"

参考:たのしいXML: SOAP入門 6. SOAP 1.1 と SOAP 1.2 の違い

コーディングサンプル

    //SOAPメッセージ組み立て
    NSString *method = @"DoSomethins";
    NSString *namespace = @"http://myNameSpace/";
    NSString *paramStr = "<paramStr>abc</paramStr>";
    NSString *paramInt = "<paramInt>123</paramInt>";

    NSString *soapMsg = [NSString stringWithFormat:
                         @"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
                         "<soap:Envelope "
                         //"xmlns:soap=\"http://www.w3.org/2003/05/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>"
                         "<%@ xmlns=\"%@\">"
                         "%@"
                         "%@"
                         "</%@>"
                         "</soap:Body>"
                         "</soap:Envelope>"
                         ,method, namespace, paramStr, paramInt, method];

//NSURLConnection生成
    NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMsg length]];
    NSString *soapAction = [NSString stringWithFormat:@"%@%@%@", namespace, method];

    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];  
    [req addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    [req addValue:soapAction forHTTPHeaderField:@"SOAPAction"];
    [req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
    [req setHTTPMethod:@"POST"];
    [req setHTTPBody:[soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
 
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];

呼び出しメソッド名
メソッド名は名前だけで、パラメータの型や個数は関係ありません。

リクエストに含めるパラメータ
SOAPリクエストメッセージの組み立て時は文字列なので、リクエストを出すiOSアプリ側ではデータ型そのものを意識することはありません。しかし、当然ながらサーバー側で型変換に失敗するとエラーとなります。
サーバー側はメソッドの引数で指定した型に変換して引き渡します。もしなければ型に応じた規定値となります(null, 0など)。リクエストに含まれていてもサーバー側のメソッドに一致する名前のパラメータがない場合は無視されます。
SOAP 要求メッセージの構造 「入力パラメーターの処理」

関連Blog

iOS アプリからのSOAP/WebServiceへのアクセス
SOAP レスポンスの受信
SOAP レスポンスのパース
バイナリデータのSOAPレスポンス
SOAPリクエスト/レスポンスとの同期

iOS開発参戦


今年になってXcode 4.2でiOS SDKをいじり始めました。
Objective-CはNeXTSTEP時代に書いていましたが、かれこれ10年ほどご無沙汰していました。基本は同じですが、そこそこ違っていて、只今リハビリとキャッチアップ中です。
アプリを作りながら、つまづいたこと、調べたことなど書き綴っていこうと思います。

Xcodeをインストールしたら、まずは初めての iOSアプリケーション(Your First iOS App)で使い方をざっと見るのがよさそうです。

このチュートリアルはシングルウインドウのアプリを作るところまでなので、次のステップとしてUINavigatinControllerを使うアプリ作りのためにiOS 5 Storyboard + UITableView Tutorial IIを利用させてもらいました。同様のページは様々ありますが、これは画面キャプチャーが多く、手短なものなのでお勧めできます。

参考リンク:
iOS View Controllerプログラミングガイド (View Controller Programming Guide)
2つ目のiOSアプリケーション:ストーリーボード(Your Sencond iOS Appllication)
3つ目のiOSアプリケーション:iCloud(Your third iOS Appllication)


最初に気を付けること
  • "\"(半角¥)はバックスラッシュにあらず。Alt+\で"\"を入力する。