サーチ…
備考
NSURLSessionクラスおよび関連するクラスは、コンテンツをダウンロードするためのAPIを提供します。このAPIは、認証をサポートするための豊富なデリゲートメソッドセットを提供し、アプリが実行されていないとき、またはiOSでアプリが中断されているときにバックグラウンドダウンロードを実行できるようにします。
高いレベルでは、 NSURLSessionはセッションとタスクの概念に基づいています。タスクは、単一のURLに対する単一の要求(または単一のURLへの単一のアップロード)を表します。セッションは、関連する要求のグループです。
オペレーティングシステムは、既存の単一のセッション(NSURLConnectionのように基本的に動作する共有セッション)を提供します。さらに、必要に応じて独自のセッションをアプリケーションに作成することもできます。
異なるアプリはセッションをさまざまな方法で使います。多くのアプリは起動時に1つのセッションを作成し、そのまま再利用します。他のアプリは、関連するタスクのグループをキャンセルすることができます(たとえば、タブを閉じるときに未処理のリクエストをキャンセルするWebブラウザなど)。したがって、関連するリクエストの各グループを保持する1つのセッションを作成します。
NSURLSessionを使用する場合の最初の手順は、セッション構成オブジェクトを作成することです。再利用可能な(通常)再利用可能なオブジェクトには、最大同時実行性、各要求で送信する余分なヘッダー、セルラ無線(iOSのみ)で送信される要求を許可するかどうか、タイムアウトなどのような、資格情報の保存、最小TLSバージョン、さらにはプロキシ設定などがあります。
セッションの設定には、セッションの動作方法に応じて3種類あります。
- デフォルトの設定では 、NSURLConnectionとよく似たセッションが作成されます。
- バックグラウンド設定では、要求がプロセス外で発生するセッションが作成され、アプリがもう実行されなくてもダウンロードを続行できます。
- エフェメラル設定は 、ディスクに何もキャッシュしない、ディスクにクッキーを保存しないなどのセッションを作成するため、シークレットブラウザウィンドウのようなものをバックアップするのに適しています。
バックグラウンド設定を作成するときは、あとでバックグラウンドセッションを再設定できるようにするセッション識別子を指定する必要があります(アプリが終了するか、OSによって中断または中止された場合)。アプリケーションで同じ識別子を使用してセッションのインスタンスを複数持つことはできません。そのため、ルールは再利用できません。他のすべてのセッション構成を再利用して、必要な数のセッションを作成できます。したがって、同様の設定で複数のセッションを作成する必要がある場合は、設定を一度作成して、新しいセッションを作成するたびに再利用することができます。
セッションを作成したら、そのセッションでタスクを作成できます。タスクには3つのタイプがあります。
- データタスクはNSDataオブジェクトとしてデータを返します。これらは一般的な使用に適していますが、バックグラウンドセッションではサポートされていません。
- ダウンロードタスクは、データをディスク上のファイルとして返します。これらは、より大きなリクエストやバックグラウンドセッションでの使用に適しています。
- アップロードタスクは、NSDataオブジェクトまたはディスク上のファイルからデータをアップロードします。 POST本体を提供するデータオブジェクトまたはファイルを提供します。タスクに提供するボディデータ/ファイルは、NSURLRequestオブジェクト(該当する場合)で提供されるボディデータ/ファイルをオーバーライドします。
これらの各タイプを使用すると、ブロックベースのコールバックを使用するか、セッションでデリゲートを提供し、デリゲートメソッドを実装するなど、さまざまな方法で応答データを取得できます。
さらに、NSURLSessionを使用すると、認証処理、カスタムTLS証明書処理(クライアント証明書とサーバー検証の両方)の実行、キャッシングの動作の変更などのためのデリゲートメソッドを提供できます。
シンプルなGETリクエスト
// define url
let url = NSURL(string: "https://urlToGet.com")
//create a task to get data from a url
let task = NSURLSession.sharedSession().dataTaskWithURL(url!)
{
/*inside this block, we have access to NSData *data, NSURLResponse *response, and NSError *error returned by the dataTaskWithURL() function*/
(data, response, error) in
if error == nil
{
// Data from the request can be manipulated here
}
else
{
// An error occurred
}
}
//make the request
task.resume()
Objective-Cセッションとデータの作成タスク
NSURL *url = [NSURL URLWithString:@"http://www.example.com/"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// Configure the session here.
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
[[session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
// The response object contains the metadata (HTTP headers, status code)
// The data object contains the response body
// The error object contains any client-side errors (e.g. connection
// failures) and, in some cases, may report server-side errors.
// In general, however, you should detect server-side errors by
// checking the HTTP status code in the response object.
}] resume];
バックグラウンド構成の設定
バックグラウンドセッションを作成するには
// Swift:
let mySessionID = "com.example.bgSession"
let bgSessionConfig = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(mySessionID)
let session = NSURLSession(configuration: bgSessionConfig)
// add tasks here
// Objective-C:
NSString *mySessionID = @"com.example.bgSession";
NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: mySessionID];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:self]
さらに、iOSでは、バックグラウンドアプリの再起動のサポートを設定する必要があります。あなたのアプリケーションのapplication:handleEventsForBackgroundURLSession:completionHandler:
メソッド(Objective-C)またはapplication(_:handleEventsForBackgroundURLSession:completionHandler:)
メソッド(Swift)が呼び出されると、セッションでのアクティビティを処理するためにアプリケーションがバックグラウンドで再起動されたことを意味します。
その方法では、提供された識別子を使用して新しいセッションを作成し、それを通常のフォアグラウンドのようにイベントを処理するデリゲートで構成する必要があります。さらに、セッションをキーとして提供された補完ハンドラを辞書に格納する必要があります。
デリゲートのURLSessionDidFinishEventsForBackgroundURLSession:
(Obj-C)/ URLSessionDidFinishEventsForBackgroundURLSession
(Swift)メソッドが呼び出されて、処理するイベントがもうないことを通知すると、アプリケーションはそのセッションの完了ハンドラを検索し、そのセッションをディクショナリから削除し、完了ハンドラを呼び出して、セッションに関連する未処理の処理がなくなったことをオペレーティングシステムに通知します。 (デリゲートコールを取得して何らかの理由で何かをしている場合は、完了するまで待ってください。)そのメソッドを呼び出すと、すぐにバックグラウンドセッションが無効になります。
アプリケーションが次にアプリケーションを受信した場合、アプリケーションapplication:application:didFinishLaunchingWithOptions:
call(バックグラウンドイベントの処理中にユーザーがアプリケーションを前景にしたことを示す可能性が高い)、その同じ識別子を持つバックグラウンドセッションを作成することは安全です。識別子はもはや存在しません。
詳細について興味があり、高いレベルでバックグラウンドセッションを作成するときは、次の2つのことを行います。
- ダウンロードを処理するための外部デーモン(nsurlsessiond)でのセッションの作成
- NSXPCを介してその外部デーモンと通信するセッションをアプリケーション内に作成する
通常、バックグラウンドデーモンで同じセッションに通話しようとしているため、アプリケーションの1回の起動で同じセッションIDで2つのセッションを作成するのは危険です。このため、公式のドキュメントでは、同じ識別子を持つ複数のセッションを決して作成しないと言われています。ただし、最初のセッションがhandleEventsForBackgroundURLSession
呼び出しの一部として作成された一時セッションであった場合、無効化されたアプリ内セッションとバックグラウンドデーモンのセッションとの関連付けは存在しなくなりました。
Objective-CでNSURLSessionを使用して引数を指定したPOSTリクエストを送信する
POSTリクエスト本体をエンコードするには、URLエンコーディング(application / x-www-form-urlencoded)とフォームデータ(multipart / form-data)の2つの一般的な方法があります。コードの多くは似ていますが、ボディデータを構築する方法は異なります。
URLエンコーディングを使用してリクエストを送信する
あなたが小さなアプリケーション用のサーバーを持っているか、完全なバックエンドエンジニアを持つチームで作業しているなら、iOSアプリケーションを使ってそのサーバーと一箇所で話したいと思うでしょう。
次のコードでは、送信先サーバスクリプトがあなたのケースに応じて変更する何かを行うために使用する一連の引数を作成します。たとえば、次の文字列を送信することができます。
name = Brendon&password = abcde
ユーザーがアプリケーションにサインアップしたときにサーバーに送信するので、サーバーはこの情報をデータベースに格納できます。
始めましょう。次のコードを使用してNSURLSession POSTリクエストを作成します。
// Create the configuration, which is necessary so we can cancel cacheing amongst other things.
NSURLSessionConfiguration * defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
// Disables cacheing
defaultConfigObject.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
NSURLSession * defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSString * scriptURL = [NSString stringWithFormat:@"https://server.io/api/script.php"];
//Converts the URL string to a URL usable by NSURLSession
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:scriptURL]];
NSString * postDataString = [NSString stringWithFormat:@"name=%@&password=%@", [self nameString], [self URLEncode:passwordString]];
[urlRequest setHTTPMethod:@"POST"];
[urlRequest setHTTPBody:[postDataString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithRequest:urlRequest];
// Fire the data task.
[dataTask resume];
上記のコードは、POST要求を作成してサーバーに送信しました。状況に応じて、スクリプトURLとPOSTデータ文字列が変わることに注意してください。あなたがこれを読んでいるなら、それらの変数をどのように埋めるべきかを知るでしょう。
また、URLエンコーディングを行う小さなメソッドを追加する必要があります:
- (NSString *)URLEncode:(NSString *)originalString encoding:(NSStringEncoding)encoding
{
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge CFStringRef)originalString,
NULL,
CFSTR(":/?#[]@!$&'()*+,;="),
CFStringConvertNSStringEncodingToEncoding(encoding));
}
したがって、サーバーがこのデータの処理を終了すると、iOSアプリに戻ります。だから我々はこのリターンを処理する必要がありますが、どうですか?
イベントドリブンプログラミングを使用し、NSURLSessionのデリゲートメソッドを使用します。つまり、サーバーが応答を返すと、これらのメソッドはトリガーを開始します。次の5つのメソッドは、ENTIREリクエストが発生するたびにトリガされるメソッドです。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler;
以下に、上記の方法をコンテキストで使用する方法を示します。 Appleのおかげで、それぞれの目的はわかりやすくなりましたが、とにかくその用途についてコメントしました。
// Response handling delegates
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler{
// Handler allows us to receive and parse responses from the server
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
// Parse the JSON that came in into an NSDictionary
NSError * err = nil;
NSDictionary * jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&err];
if (!err){ // if no error occurred, parse the array of objects as normal
// Parse the JSON dictionary 'jsonDict' here
}else{ // an error occurred so we need to let the user know
// Handle your error here
}
}
// Error handling delegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if(error == nil){
// Download from API was successful
NSLog(@"Data Network Request Did Complete Successfully.");
}else{
// Describes and logs the error preventing us from receiving a response
NSLog(@"Error: %@", [error userInfo]);
// Handle network error, letting the user know what happened.
}
}
// When the session receives a challenge (because of iOS 9 App Transport Security blocking non-valid SSL certificates) we use the following methods to tell NSURLSession "Chill out, I can trust me".
// The following is not necessary unless your server is using HTTP, not HTTPS
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
if([challenge.protectionSpace.host isEqualToString:@"DomainNameOfServer.io"]){
NSURLCredential * credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
if([challenge.protectionSpace.host isEqualToString:@"DomainNameOfServer.io"]){
NSURLCredential * credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}
}
}
そうです!これは、iOS 9でAPIのリクエストを送信、受信、解析するために必要なコードです。さて、それはコードのようなものでした。しかし、上記のように正しく実装すれば、フェイルセーフになります!上で示唆したところでエラーを常に処理するようにしてください。
フォームエンコーディングを使用してリクエストを送信する
URLエンコーディングは、任意のデータをエンコードするための広く互換性のある方法です。しかし、ASCII以外のバイトはすべて3文字のコードになるため、バイナリデータ(写真など)をアップロードするのは比較的非効率的です。また、添付ファイルもサポートしていないため、ファイル名とファイルデータを別々のフィールドとして渡す必要があります。
効率的で実際にサーバー側のファイルのように見えるように写真をアップロードしたいとします。これを行う1つの方法は、代わりにフォームエンコーディングを使用することです。これを行うには、次のようにNSURLSessionを作成するコードを編集します。
UIImage * imgToSend;
// 2nd parameter of UIImageJPEGRepresentation represents compression quality. 0 being most compressed, 1 being the least
// Using 0.4 likely stops us hitting the servers upload limit and costs us less server space
NSData * imageData = UIImageJPEGRepresentation(imgToSend, 0.4f);
// Alternatively, if the photo is on disk, you can retrieve it with
// [NSData dataWithContentsOfURL:...]
// Set up the body of the POST request.
// This boundary serves as a separator between one form field and the next.
// It must not appear anywhere within the actual data that you intend to
// upload.
NSString * boundary = @"---------------------------14737809831466499882746641449";
// Body of the POST method
NSMutableData * body = [NSMutableData data];
// The body must start with the boundary preceded by two hyphens, followed
// by a carriage return and newline pair.
//
// Notice that we prepend two additional hyphens to the boundary when
// we actually use it as part of the body data.
//
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// This is followed by a series of headers for the first field and then
// TWO CR-LF pairs.
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"tag_name\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
// Next is the actual data for that field (called "tag_name") followed by
// a CR-LF pair, a boundary, and another CR-LF pair.
[body appendData:[strippedCompanyName dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Encode the filename and image data as the "userfile" CGI parameter.
// This is similar to the previous field, except that it is being sent
// as an actual file attachment rather than a blob of data, which means
// it has both a filename and the actual file contents.
//
// IMPORTANT: The filename MUST be plain ASCII (and if encoded like this,
// must not include quotation marks in the filename).
//
NSString * picFileName = [NSString stringWithFormat:@"photoName"];
NSString * appendDataString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"%@.jpg\"\r\n", picFileName];
[body appendData:[appendDataString dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imageData]];
// Close the request body with one last boundary with two
// additional hyphens prepended **and** two additional hyphens appended.
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Create the session
// We can use the delegate to track upload progress and disable cacheing
NSURLSessionConfiguration * defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultConfigObject.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
NSURLSession * defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
// Data uploading task.
NSURL * url = [NSURL URLWithString:@"https://server.io/api/script.php"];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
NSString * contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];
request.HTTPMethod = @"POST";
request.HTTPBody = body;
NSURLSessionDataTask * uploadTask = [defaultSession dataTaskWithRequest:request];
[uploadTask resume];
これは以前と同じようにNSURLSession要求を作成し起動します。その結果、デリゲートメソッドはまったく同じように動作します。イメージが送信されているスクリプト(変数url
)が画像を期待しており、正しく解析できることを確認してください。