iOS
NSURLSession
Recherche…
Remarques
La classe NSURLSession et les classes associées fournissent une API pour télécharger du contenu. Cette API fournit un ensemble complet de méthodes de délégation pour prendre en charge l'authentification et permet à votre application d'effectuer des téléchargements en arrière-plan lorsque votre application n'est pas en cours d'exécution ou, sous iOS, lorsque votre application est suspendue.
À un niveau élevé, NSURLSession est basé sur le concept de sessions et de tâches. Une tâche représente une requête unique pour une seule URL (ou un seul téléchargement vers une seule URL). Une session est un groupe de requêtes associées.
Le système d'exploitation fournit une seule session préexistante - la session partagée, qui fonctionne essentiellement comme NSURLConnection. De plus, vous pouvez créer vos propres sessions dans votre application si nécessaire.
Différentes applications utilisent des sessions de différentes manières. De nombreuses applications créent une seule session au lancement et continuent de la réutiliser. Les autres applications peuvent être en mesure d’annuler un groupe de tâches associées (par exemple, un navigateur Web annulant toutes les requêtes en attente lorsque vous fermez un onglet) et créer ainsi une session pour chaque groupe de requêtes associées.
La première étape lorsque vous utilisez NSURLSession consiste à créer un objet de configuration de session. L'objet (généralement) réutilisable contient divers paramètres de session que vous pouvez modifier pour vos besoins particuliers, tels que l'accès simultané maximal, des en-têtes supplémentaires à envoyer avec chaque requête, l'autorisation d'envoi de requêtes via la radio cellulaire (iOS uniquement), stockage des informations d'identification, version minimale de TLS et même paramètres de proxy.
Il existe trois types de configurations de session, en fonction de la manière dont vous souhaitez que la session résultante se comporte:
- Les configurations par défaut créent des sessions qui fonctionnent comme NSURLConnection.
- Les configurations d'arrière-plan créent des sessions dans lesquelles les demandes sont exécutées hors processus, permettant aux téléchargements de continuer même lorsque l'application n'est plus en cours d'exécution.
- Les configurations éphémères créent des sessions qui ne mettent rien en mémoire cache sur le disque, ne stockent pas les cookies sur le disque, etc.
Lorsque vous créez une configuration d'arrière-plan, vous devez fournir un identifiant de session qui vous permet de réassocier la session d'arrière-plan ultérieurement (si votre application se ferme ou est suspendue ou fermée par le système d'exploitation). Vous ne devez pas avoir plus d'une instance d'une session avec le même identifiant actif dans votre application. En règle générale, ces configurations ne sont pas réutilisables. Toutes les autres configurations de session peuvent être réutilisées pour créer autant de sessions que vous le souhaitez. Donc, si vous devez créer plusieurs sessions avec des paramètres similaires, vous pouvez créer la configuration une fois et la réutiliser chaque fois que vous créez une nouvelle session.
Après avoir créé une session, vous pouvez créer des tâches dans cette session. Il existe trois types de tâches:
- Les tâches de données renvoient des données en tant qu'objet NSData . Celles-ci conviennent à un usage général, mais ne sont pas prises en charge dans les sessions en arrière-plan.
- Les tâches de téléchargement renvoient des données sous forme de fichier sur le disque. Celles-ci conviennent aux requêtes plus importantes ou aux sessions en arrière-plan.
- Télécharger des tâches pour télécharger des données depuis un objet NSData ou depuis un fichier sur le disque. Vous fournissez un objet ou un fichier de données qui fournit le corps POST. Le fichier / données de corps que vous fournissez dans la tâche remplace tous les fichiers / données de corps fournis dans l'objet NSURLRequest (le cas échéant).
Chacun de ces types vous permet d'obtenir les données de réponse de différentes manières, soit en utilisant des rappels basés sur des blocs, soit en fournissant un délégué sur la session et en implémentant des méthodes déléguées.
De plus, NSURLSession vous permet de fournir des méthodes de délégation pour la gestion de l'authentification, la gestion personnalisée des certificats TLS (à la fois pour les certificats clients et la validation du serveur), la modification du comportement de mise en cache, etc.
Demande GET simple
// 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 crée une tâche de session et de données
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];
Configuration de la configuration d'arrière-plan
Pour créer une session d'arrière-plan
// 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]
De plus, dans iOS, vous devez configurer le support pour gérer le relancement des applications en arrière-plan. Lorsque l'application de votre application:handleEventsForBackgroundURLSession:completionHandler:
method (Objective-C) ou application(_:handleEventsForBackgroundURLSession:completionHandler:)
méthode (Swift) est appelée, cela signifie que votre application a été relancée en arrière-plan pour gérer l'activité sur une session.
Dans cette méthode, vous devez créer une nouvelle session avec l'identifiant fourni et le configurer avec un délégué pour gérer les événements comme vous le feriez normalement au premier plan. En outre, vous devez stocker le gestionnaire d'achèvement fourni dans un dictionnaire, en utilisant la session comme clé.
Lorsque la URLSessionDidFinishEventsForBackgroundURLSession:
(Obj-C) / URLSessionDidFinishEventsForBackgroundURLSession
(Swift) du délégué est appelée pour vous informer qu'il n'y a plus d'événements à gérer, votre application doit rechercher le gestionnaire d'achèvement pour cette session, supprimer la session du dictionnaire et appeler le gestionnaire d'achèvement, indiquant ainsi au système d'exploitation que vous n'avez plus aucun traitement en attente lié à la session. (Si vous faites toujours quelque chose pour une raison quelconque lorsque vous recevez cet appel de délégué, attendez d'avoir terminé.) Dès que vous appelez cette méthode, la session d'arrière-plan est immédiatement invalidée.
Si votre application reçoit ensuite une application:application:didFinishLaunchingWithOptions:
call (indiquant probablement que l’utilisateur a mis au premier plan votre application alors que vous étiez en train de traiter des événements en arrière-plan), créer une session en arrière avec ce même identifiant l'identifiant n'existe plus.
Si vous êtes curieux des détails, à un niveau élevé, lorsque vous créez une session d'arrière-plan, vous faites deux choses:
- Création d'une session dans un démon externe (nsurlsessiond) pour gérer les téléchargements
- Créer une session dans votre application qui communique avec ce démon externe via NSXPC
Normalement, il est dangereux de créer deux sessions avec le même ID de session dans un seul lancement de l'application, car elles tentent toutes deux de communiquer avec la même session dans le démon d'arrière-plan. C'est pourquoi la documentation officielle dit de ne jamais créer plusieurs sessions avec le même identifiant. Toutefois, si la première session était une session temporaire créée dans le cadre d'un appel handleEventsForBackgroundURLSession
, l'association entre la session in-app maintenant invalidée et la session du démon d'arrière-plan n'existe plus.
Envoi d'une requête POST avec des arguments à l'aide de NSURLSession dans Objective-C
Il existe deux méthodes courantes pour coder un corps de requête POST: le codage d'URL (application / x-www-form-urlencoded) et les données de formulaire (multipart / form-data). Une grande partie du code est similaire, mais la façon dont vous construisez les données du corps est différente.
Envoi d'une requête à l'aide du codage URL
Que vous ayez un serveur pour votre petite application ou que vous travailliez en équipe avec un ingénieur back-end complet, vous voudrez parler à ce serveur à un moment donné avec votre application iOS.
Dans le code suivant, nous allons composer une chaîne d’arguments que le script du serveur de destination utilisera pour faire quelque chose qui change en fonction de votre cas. Par exemple, nous pouvons vouloir envoyer la chaîne:
name = Brendon & password = abcde
Pour le serveur lorsqu'un utilisateur s'inscrit à votre application, le serveur peut stocker ces informations dans une base de données.
Commençons. Vous souhaiterez créer une requête NSURLSession POST avec le code suivant.
// 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];
Le code ci-dessus vient d'être créé et a déclenché la requête POST sur le serveur. N'oubliez pas que l'URL du script et la chaîne de données POST changent en fonction de votre situation. Si vous lisez ceci, vous saurez comment remplir ces variables.
Vous devrez également ajouter une petite méthode qui effectue le codage de l'URL:
- (NSString *)URLEncode:(NSString *)originalString encoding:(NSStringEncoding)encoding
{
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge CFStringRef)originalString,
NULL,
CFSTR(":/?#[]@!$&'()*+,;="),
CFStringConvertNSStringEncodingToEncoding(encoding));
}
Ainsi, lorsque le serveur aura fini de traiter ces données, il enverra un retour à votre application iOS. Nous devons donc traiter ce retour, mais comment?
Nous utilisons une programmation pilotée par les événements et utilisons les méthodes de délégué de NSURLSession. Cela signifie que lorsque le serveur renvoie une réponse, ces méthodes commenceront à se déclencher. Les 5 méthodes suivantes sont celles qui seront déclenchées au cours de la requête ENTIRE, à chaque fois:
- (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;
Vous trouverez ci-dessous les méthodes ci-dessus utilisées en contexte. Chacun de leurs objectifs est assez explicite grâce à Apple, mais j'ai commenté leurs utilisations de toute façon:
// 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);
}
}
}
Alors c'est tout! C'est tout le code dont vous avez besoin pour envoyer, recevoir et analyser une demande pour une API dans iOS 9! D'accord ... c'était du code. Mais si implémenté comme ci-dessus, ce sera sûr! Assurez-vous de toujours traiter les erreurs suggérées ci-dessus.
Envoi d'une demande à l'aide du codage de formulaire
Le codage d'URL est un moyen largement compatible d'encoder des données arbitraires. Cependant, il est relativement inefficace de télécharger des données binaires (telles que des photos), car chaque octet non-ASCII se transforme en un code à trois caractères. Il ne prend pas non plus en charge les pièces jointes, vous devrez donc transmettre les noms de fichiers et les données de fichier en tant que champs distincts.
Supposons que nous voulions télécharger une photo de manière efficace et ressembler à un fichier du côté du serveur. Une façon de procéder consiste à utiliser le codage de formulaire à la place. Pour ce faire, modifiez le code qui crée la NSURLSession comme suit:
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];
Cela crée et déclenche la requête NSURLSession comme auparavant et, par conséquent, les méthodes déléguées se comportent exactement de la même manière. Assurez-vous que le script auquel l'image est envoyée (situé dans l'URL de la variable url
) attend une image et peut l'analyser correctement.