iOSで削除されたデータを取得する

これは、第6章「アクションでiOS7からリモートデータ取得する」の元の翻訳です。 本とは異なり、インターフェイス全体がプログラムで作成されるため、ストーリーボードでこれをすべて行う方法を説明するテキストは削除されます。 簡単にするために、ポートレート画面とターゲットのiPhoneプラットフォームの位置のみが選択されています。



画面上に単一のラベルを持つアプリケーションを作成します。これには、アプリケーションの起動時にapi.icndb.com/jokes/randomサイトAPIを介してダウンロードされたチャックノリスに関するジョークがランダムに表示されます。





図1チャックノリスに関するジョークを表示するアプリ



NSURLSessionを使用したデータの取得



HTTPリクエスト理論


たとえば、HTTPインタラクションの発生方法を思い出してください。 ブラウザでURLを印刷します。 google.com/?q=Hello&safe=off



google.com/?q=Hello&safe=off







次のコンポーネントを区別できます。





HTTPリクエストを行う場合、常にメソッドを指定します。 メソッドは、サーバーから送信された情報をどう処理するかを決定します。



つまり、ブラウザーはgoogle.comに接続し、ルートプロトコル/にHTTPプロトコルに対応するGETリクエストを作成し、パラメーターとしてq = Hello&safe = offを渡します。



ブラウザがURLを解析すると、HTTPリクエストは次のように表示されます。



 GET /?q=Hello&safe=off HTTP/1.1 Host: google.com Content-Length: 133 (…)
      
      





クエリはASCIIテキスト文字列で構成されます。 最初の行にはGETメソッドが含まれ、その後にパラメーター付きのパス、HTTPバージョンが続きます。 この後に、要求されたホストや要求の長さなどの詳細を含むヘッダーが続きます。 ヘッダーは、2行の空白行で本文と区切られています。 GETの場合、本文は空です。



図2はクエリ図を示しています。 最初に要求が作成され、次にリモートサーバーへの接続が確立され、要求がプレーンテキストで送信されます。 リクエストのサイズとネットワークの品質に応じて、リクエストは数秒、数時間、または数日続くこともあります。





図2 HTTPリクエストのステップ:(a)通常のHTTPインタラクションと(b)Objective-C実装の観点からの各ステップの同等物。



アプリケーションのプログラムインターフェイスを作成します


Xcodeで空のアプリケーションを作成し、その中に次のファイルを作成します。



THSAppDelegate.h



 #import <UIKit/UIKit.h> @class THSViewController; @interface THSAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) THSViewController *viewController; @end
      
      





THSAppDelegate.m



 #import "THSAppDelegate.h" #import "THSViewController.h" @implementation THSAppDelegate - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.viewController = [[THSViewController alloc] initWithNibName:nil bundle:nil]; self.window.rootViewController = self.viewController; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; } @end
      
      





THSViewController.h



 #import <UIKit/UIKit.h> @interface THSViewController : UIViewController @property (nonatomic, strong) UILabel *jokeLabel; @end
      
      





THSViewController.m



 #import "THSViewController.h" #import "THSViewController+Interface.h" @implementation THSViewController - (void)viewDidLoad { [super viewDidLoad]; [self addLabel]; } @end
      
      





THSViewController + Interface.h



 #import "THSViewController.h" @interface THSViewController (Interface) - (void)addLabel; @end
      
      





THSViewController + Interface.m



 #import "THSViewController+Interface.h" @implementation THSViewController (Interface) - (void)addLabel { CGFloat width = self.view.frame.size.width - 40.0f; CGFloat y = self.view.frame.size.height / 2.0f - 200.0f; CGRect labelFrame = CGRectMake(20.0f, y, width, 200.0f); self.jokeLabel = [[UILabel alloc] initWithFrame:labelFrame]; self.jokeLabel.text = @"Quotation goes here and continues and continues until I am fed up to type."; self.jokeLabel.lineBreakMode = NSLineBreakByWordWrapping; self.jokeLabel.textAlignment = NSTextAlignmentCenter; self.jokeLabel.numberOfLines = 0; self.jokeLabel.font = [UIFont systemFontOfSize:16.0f]; [self.view addSubview:self.jokeLabel]; } @end
      
      





インターフェイスの作成は、この章の主な機能を将来ラベルコードやボタンと混同しないように分類されています。



アプリケーションを起動し、インターフェースを確認します。





図3アプリケーションインターフェイスの起動



CocoaクラスTHSHTTPCommunicationを作成します


すべてのUI操作はメインスレッドで実行されます。 メインスレッドをブロックすると、すべてのタッチイベントがブロックされ、グラフィックス、アニメーション、サウンドが描画され、最終的にアプリケーションがフリーズします。 したがって、アプリケーションを中断してリクエストを待つことはできません。 この問題を解決するには、2つの手法があります。2つの同時操作を管理する新しいスレッドを作成するか、クラスのインスタンスをデリゲートとして構成し、デリゲートプロトコルで定義されたメソッドを実装します。



Cocoaのほとんどのメソッドは、元々、時間のかかる操作を非同期にするデリゲートパターンとして設計されました。 つまり、操作が完了するまでメインスレッドが続行され、その後、特定のメソッドが呼び出されます。



iOS 5では、Objective-Cはブロックをサポートしています。



この例では、ネットワークの理解を深めるためにデリゲートを使用します。 ただし、iOS7にはブロックを使用してHTTPリクエストの実行を簡素化する構文糖衣があります。



THSHTTPCommunicationクラスを作成します。



THSHTTPCommunication.h:



 #import <Foundation/Foundation.h> @interface THSHTTPCommunication : NSObject @end
      
      





THSHTTPCommunication.m:



 #import "THSHTTPCommunication.h" @interface THSHTTPCommunication () @property(nonatomic, copy) void(^successBlock)(NSData *); @end @implementation THSHTTPCommunication @end
      
      





successBlockには、要求が完了したときに呼び出されるブロックが含まれます。



HTTPインタラクションを実装します


次に、HTTP相互作用を担当するTHSHTTPCommunication.mでメソッドを作成します。 このメソッドは、icndbと呼ばれるパブリックAPIからジョークを引き出し、ブロックを使用して非同期的に情報を返します。



THSHTTPCommunication.m:



 @implementation THSHTTPCommunication - (void)retrieveURL:(NSURL *)url successBlock:(void(^)(NSData *))successBlock { //   successBlock    self.successBlock = successBlock; //  ,   url NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; //  ,           NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:conf delegate:self delegateQueue:nil]; //   NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request]; //  HTTP  [task resume]; } @end
      
      





このメソッドは、コンテンツを取得するURL(この場合、これはランダムなジョークを受信するためのicndb API URL)と、リクエストが完了した直後に呼び出されるブロックの2つのパラメーターを取ります。 最初に、このブロックを保存して、後でリクエストが完了したときに呼び出す必要があります。 次の手順では、このURLのNSURLRequstオブジェクトを作成し、この要求を使用してHTTP接続を確立します。 [タスク再開]は実行をブロックしません。 このメソッドでは、THSHTTPCommunicationクラスがNSURLSessionDownloadDelegateプロトコルに準拠していることをまだ報告していないため、コンパイラーは警告を表示します。 これが次に行うことです。



セッションデリゲート


NSURLSessionDownloadDelegateプロトコルを実装して、新しいリクエストを受信したときなど、メッセージの一部をキャッチします。



最初に、THSHTTPCommunicationがこのプロトコルに従うことをコンパイラーに知らせます。



THSHTTPCommunication.h:



 @interface THSHTTPCommunication : NSObject<NSURLSessionDownloadDelegate> @end
      
      





NSURLSessionDownloadDelegateプロトコルは、NSURLConnectionクラスのインスタンスがHTTP接続中に実行できる一連のメソッドを定義します。 URLSession:downloadTask:didFinishDownloadingToURL:を使用します。



ダウンロードプロセスの追跡やリクエストを再開する機能など、より複雑な場合に必要になる可能性のある2つの方法があります。 名前はそれ自身を物語っています:



URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:

URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:



しかし、それだけではありません。 さらに、NSURLSession APIは3つのプロトコルを提供します。



NSURLSessionDelegate-このプロトコルは、セッションや資格情報の取り消しなど、セッションレベルのイベントを処理するデリゲートメソッドを定義します。

NSURLSessionTaskDelegate-このプロトコルは、接続イベントをリダイレクト、エラー、データ転送として処理するデリゲートメソッドを定義します。

NSURLSessionDataDelegate-このプロトコルは、データおよびロードタスクに固有のタスクレベルイベントを処理するデリゲートメソッドを定義します。



回答からデータを受け取ります


ここで、THSHTTPCommunication.mの応答からデータを受信できるようにするメソッドを実装します。 NSURLSessionは、データが利用可能になりダウンロードが完了するとすぐにこのメソッドを呼び出します。



THSHTTPCommunication.m:



 @implementation THSHTTPCommunication … - (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { //       NSData *data = [NSData dataWithContentsOfURL:location]; // ,   successBlock     dispatch_async(dispatch_get_main_queue(), ^{ //       self.successBlock(data); }); } @end
      
      





このコードは、対話の最後のステップです。 完全な回答を得て、先ほど保存したブロックをすぐに呼び出します。 最初に、サーバーから受信したローカルに保存されたデータを取得します。 リポジトリはNSURLクラスのインスタンスで表されますが、今回はURLはリモートURLではなく、応答データが含まれるファイルへのパスです。



successBlockコールバック呼び出しがメインスレッドから来ることを確認する必要があります。 ほとんどの場合、クラスを実装するメソッドがUIアクションなどの主要なタスク固有のタスクを実行するため、これは一般的な方法です。



場合によっては、リモートサーバーから情報を受信すると、要求は宛先に到達する前に複数のサーバーを通過する場合があります。 図4上の写真を取得しようとしています t.co



t.co



ですが、最初の答えはイメージを含むサーバーへのリダイレクトです。 必要なのは最後の答え(写真そのもの)だけです。





図4短縮されたTwitter URLを使用して写真を受信すると、リダイレクトが生成されます。



NSURLSessionTaskDelegateを実装することでリダイレクトを制御できますが、NSURLSessionにすべての詳細を処理させることができます。これはデフォルトの動作です。



メインコントローラーの新しく作成されたretrieveURL:successBlock:メソッドを利用可能にします。 THSHTTPCommunication.hを開き、メソッド宣言を追加します。



THSHTTPCommunication.h:



 @interface THSHTTPCommunication : NSObject <NSURLSessionDownloadDelegate> - (void)retrieveURL:(NSURL *)url successBlock:(void(^)(NSData *))successBlock; @end
      
      





データのシリアル化とサードパーティのサービスとの相互作用を理解する



アプリケーションを作成し、リモートサーバーからデータを受信するためのロジックを作成しました。 icndb.comが提供するAPIを使用して、Chuck Norrisについてのランダムなジョークをいくつか入手してください。 このAPIは、サードパーティサービスとの対話を提供するすべてのサービスと同様に、情報をフォーマットする正規化された方法を備えています。 したがって、この形式を使用および操作が簡単な形式に変換する必要があります。 つまり、フォーマットされたデータをObjective-Cオブジェクトに変換する方法が必要です。



連載


図5は、シリアル化プロセスの仕組みを示しています。 左側に送信者(icndbサーバー)、右側に受信者(クライアント)が表示されます。 最初にジョークが生成され、icndbはそれをバイナリデータとして保存します(データベース、メモリ、ファイルシステム、またはその他の種類のストレージとして保存できます)。 アプリケーションからのリクエストが発生すると、ジョークに関する情報がシリアル化され、私たち(受信者)に送信されます。 アプリケーションは情報を解析し、受信したデータをネイティブのObjective-Cオブジェクトに変換します。





図5メッセージのシリアル化と逆シリアル化のアーキテクチャ。



情報を共有するにはさまざまな方法がありますが、最も広く使用されているシリアル化形式であるJavaScript Object Notation(JSON)に焦点を当てます。 JSONは、さまざまなタイプのデータ構造をテキスト形式で表す標準的な方法です。 JSONは、文字列、数値、およびブール値を表すための小さなルールセットを定義します。 XMLと合わせて、これは現在最もよく使用されているシリアル化方法の1つです。 実際のJSONの例を見てみましょう。



 { "name": "Martin Conte Mac Donell", "age": 29, "username": "fz" }
      
      





このコードは、{}で囲まれたキー/値のペアで構成される辞書を表します。 キーを繰り返すことはできません。 この例では、名前、年齢、ユーザー名がキー、Martin Conte Mac Donell、29、fzが値です。



ジョークコード


これで、アプリケーションに戻って、JSON形式が定義され、シリアル化プロセスがどのように機能するかがわかりました。 ジョークを取得するコードを実装します。 THSViewController.mで、内部変数jokeIDに名前のないカテゴリとretrieveRandomJokesメソッドを追加します。



THSViewController.m:



 #import "THSViewController.h" #import "THSViewController+Interface.h" #import "THSHTTPCommunication.h" @interface THSViewController () { NSNumber *jokeID; } @end @implementation THSViewController ... - (void)retrieveRandomJokes { THSHTTPCommunication *http = [[THSHTTPCommunication alloc] init]; NSURL *url = [NSURL URLWithString:@"http://api.icndb.com/jokes/random"]; //  ,    THSHTTPCommunication [http retrieveURL:url successBlock:^(NSData *response) { NSError *error = nil; //    NSDictionary *data = [NSJSONSerialization JSONObjectWithData:response options:0 error:&error]; if (!error) { NSDictionary *value = data[@"value"]; if (value && value[@"joke"]) { jokeID = value[@"id"]; [self.jokeLabel setText:value[@"joke"]]; } } }]; } @end
      
      





retrieveRandomJokesメソッドを定義し、コードの観点からシリアル化がどのように行われるかを確認します。 このメソッドでは、以前に作成したTHSHTTPCommunicationクラスを使用して、icndb.comからデータを取得します。 したがって、THSHTTPCommunicationクラスのインスタンスをすぐに作成してから、データの受信を担当するretrieveURL:successBlock:を呼び出します。 THSHTTPCommunicationは、icndb.comから応答を受信するとすぐに、パラメーターとして渡されたブロック内のコードを呼び出します。 この時点で、解析可能なデータが用意できました。



情報を受け取ったら、それを理解する必要があります。 ダウンロードしたテキストを簡単に操作できるものに変換する方法が必要です。 答えからジョークとIDを強調表示する必要があります。 シリアル化されたデータ(JSON)をデータ構造に変換するプロセスは、逆シリアル化と呼ばれます。 さいわい、iOS 5以降、CocoaフレームワークにはJSONを解析するためのクラスが含まれています。 これはNSJSONSerializationクラスであり、応答データの解析はブロックで最初に行うことです。



icndb APIからの答えは、JSONで表される連想配列です。



 { "type": "success", "value": { "id": 201, "joke": "Chuck Norris was what Willis was talkin' about" } }
      
      





答えは連想配列であり、キー「値」には別の連想配列が含まれていることがわかります。 NSJSONSerializationのデシリアライゼーションが完了すると、JSON連想配列はObjective-C NSDictionariesに、配列はNSArrayに、数値はNSNumberに、文字列はNSStringに変換されます。 その後、アプリケーションで使用できるオブジェクトを取得します。



retrieveRandomJokesに戻ります。デシリアライズ後、デシリアライズされた応答の「値」キーからNSDictionary辞書に連想配列を割り当てます。 最後に、受け取ったジョークテキストをインターフェイスのラベルテキストとして作成します。



ビューがロードされたときにretrieveRandomJokes:メソッドを呼び出すことは残ります。



THSViewController.m:



 - (void)viewDidLoad { [super viewDidLoad]; [self addLabel]; [self retrieveRandomJokes]; }
      
      





以上で、アプリケーションを実行して、起動するたびに新しいジョークを確認できます。



All Articles