iOS
NSURLSession
Suche…
Bemerkungen
Die NSURLSession- Klasse und verwandte Klassen stellen eine API zum Herunterladen von Inhalten bereit. Diese API bietet eine Vielzahl von Delegatmethoden für die Unterstützung der Authentifizierung und gibt Ihrer App die Möglichkeit, Hintergrund-Downloads durchzuführen, wenn Ihre App nicht ausgeführt wird oder in iOS, wenn Ihre App angehalten ist.
Auf hoher Ebene basiert NSURLSession auf dem Konzept von Sitzungen und Aufgaben. Eine Aufgabe stellt eine einzelne Anforderung für eine einzelne URL (oder einen einzelnen Upload zu einer einzelnen URL) dar. Eine Sitzung ist eine Gruppe verwandter Anforderungen.
Das Betriebssystem stellt eine einzige bereits vorhandene Sitzung bereit - die gemeinsam genutzte Sitzung, die im Wesentlichen wie NSURLConnection funktioniert. Darüber hinaus können Sie nach Bedarf eigene Sitzungen in Ihrer App erstellen.
Verschiedene Apps verwenden Sitzungen auf unterschiedliche Weise. Viele Apps erstellen beim Start eine einzige Sitzung, die sie einfach wiederverwenden. Andere Apps profitieren von der Möglichkeit, eine Gruppe verwandter Aufgaben abzubrechen (z. B. ein Webbrowser, der alle ausstehenden Anforderungen abbricht, wenn Sie eine Registerkarte schließen) und somit eine Sitzung für jede Gruppe verwandter Anforderungen erstellen.
Der erste Schritt bei der Verwendung von NSURLSession ist das Erstellen eines Sitzungskonfigurationsobjekts. Das (normalerweise) wiederverwendbare Objekt enthält verschiedene Sitzungseinstellungen, die Sie für Ihre speziellen Bedürfnisse anpassen können, z. B. maximale Parallelität, zusätzliche Header, die mit jeder Anfrage gesendet werden sollen, ob Anfragen über das Mobilfunkgerät (nur iOS) gesendet werden dürfen, Timeouts, Anmeldeinformationsspeicher, minimale TLS-Version und sogar Proxy-Einstellungen.
Es gibt drei Arten von Sitzungskonfigurationen, je nachdem, wie sich die resultierende Sitzung verhalten soll:
- Standardkonfigurationen erstellen Sitzungen, die ähnlich wie NSURLConnection funktionieren.
- Hintergrundkonfigurationen erstellen Sitzungen, in denen Anforderungen außerhalb des Prozesses ausgeführt werden, sodass Downloads auch dann fortgesetzt werden können, wenn die App nicht mehr ausgeführt wird.
- Ephemere Konfigurationen erstellen Sitzungen, in denen nichts auf der Festplatte zwischengespeichert wird, keine Cookies auf der Festplatte usw. gespeichert werden und sich daher zum Sichern von Objekten wie inkognito Browserfenstern eignen.
Wenn Sie eine Hintergrundkonfiguration erstellen, müssen Sie einen Sitzungsbezeichner angeben, mit dem Sie die Hintergrundsitzung später erneut zuordnen können (wenn Ihre App beendet ist oder vom Betriebssystem angehalten oder beendet wird). Es darf nicht mehr als eine Instanz einer Sitzung mit der gleichen ID in Ihrer App aktiv sein. Diese Konfigurationen sind in der Regel nicht wiederverwendbar. Alle anderen Sitzungskonfigurationen können wiederverwendet werden, um beliebig viele Sitzungen zu erstellen. Wenn Sie also mehrere Sitzungen mit ähnlichen Einstellungen erstellen müssen, können Sie die Konfiguration einmal erstellen und bei jeder neuen Sitzung erneut verwenden.
Nachdem Sie eine Sitzung erstellt haben, können Sie in dieser Sitzung Aufgaben erstellen. Es gibt drei Arten von Aufgaben:
- Datenaufgaben geben Daten als NSData- Objekt zurück. Diese sind für die allgemeine Verwendung geeignet, werden jedoch in Hintergrundsitzungen nicht unterstützt.
- Download-Aufgaben geben Daten als Datei auf der Festplatte zurück. Diese eignen sich für größere Anforderungen oder für die Verwendung in Hintergrundsitzungen.
- Aufgaben hochladen Daten von einem NSData-Objekt oder von einer Datei auf der Festplatte hochladen. Sie stellen ein Datenobjekt oder eine Datei bereit, die den POST-Body bereitstellt. Die Body-Daten / -Datei, die Sie für die Task bereitstellen, überschreibt alle Body-Daten / -Dateien, die im NSURLRequest-Objekt (falls vorhanden) bereitgestellt werden.
Mit jedem dieser Typen können Sie die Antwortdaten auf verschiedene Arten abrufen, indem Sie blockbasierte Rückrufe verwenden oder einen Delegaten für die Sitzung bereitstellen und Delegatmethoden implementieren.
Darüber hinaus können Sie mit NSURLSession Delegierungsmethoden für die Authentifizierung, die benutzerdefinierte TLS-Zertifikatsverarbeitung (sowohl für Clientzertifikate als auch für die Serverüberprüfung), das Ändern des Cache-Verhaltens usw. bereitstellen.
Einfache GET-Anfrage
// 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()
Ziel-C Erstellen Sie eine Session- und Datenaufgabe
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];
Hintergrundkonfiguration einrichten
So erstellen Sie eine Hintergrundsitzung
// 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]
In iOS müssen Sie außerdem die Unterstützung für die Behandlung von Hintergrund-App-Relaunch einrichten. Wenn die application:handleEventsForBackgroundURLSession:completionHandler:
Ihrer application:handleEventsForBackgroundURLSession:completionHandler:
Methode (Objective-C) oder application(_:handleEventsForBackgroundURLSession:completionHandler:)
aufgerufen wird, bedeutet dies, dass Ihre App im Hintergrund neu gestartet wurde, um die Aktivität in einer Sitzung application(_:handleEventsForBackgroundURLSession:completionHandler:)
.
In dieser Methode sollten Sie eine neue Sitzung mit dem angegebenen Bezeichner erstellen und diese mit einem Delegaten konfigurieren, um Ereignisse so zu behandeln, wie Sie es normalerweise im Vordergrund tun würden. Außerdem sollten Sie den bereitgestellten Beendigungs-Handler in einem Wörterbuch speichern, wobei die Sitzung als Schlüssel verwendet wird.
Wenn die URLSessionDidFinishEventsForBackgroundURLSession:
des Delegaten URLSessionDidFinishEventsForBackgroundURLSession:
(Obj-C) / URLSessionDidFinishEventsForBackgroundURLSession
(Swift), um Ihnen mitzuteilen, dass keine weiteren Ereignisse verarbeitet werden müssen, sollte Ihre URLSessionDidFinishEventsForBackgroundURLSession
den Abschluss-Handler für diese Sitzung suchen Rufen Sie den Completion-Handler auf und teilen Sie dem Betriebssystem mit, dass Sie keine ausstehende Verarbeitung mehr für die Sitzung haben. (Wenn Sie aus irgendeinem Grund immer noch etwas tun, wenn Sie diesen Delegatenanruf erhalten, warten Sie, bis er fertig ist.) Sobald Sie diese Methode aufrufen, wird die Hintergrundsitzung sofort ungültig.
Wenn Ihre Anwendung dann eine application:application:didFinishLaunchingWithOptions:
empfängt application:application:didFinishLaunchingWithOptions:
call (wahrscheinlich, dass der Benutzer Ihre application:application:didFinishLaunchingWithOptions:
den Vordergrund gestellt hat, während Sie Hintergrundereignisse verarbeiten), ist es sicher, eine Hintergrundsitzung mit derselben Kennung zu erstellen, da die alte Sitzung dies verwendet Kennung existiert nicht mehr.
Wenn Sie neugierig sind auf Details, wenn Sie eine Hintergrundsitzung erstellen, tun Sie zwei Dinge:
- Erstellen einer Sitzung in einem externen Dämon (nsurlsessiond) zur Abwicklung der Downloads
- Erstellen Sie eine Sitzung in Ihrer App, die über NSXPC mit diesem externen Dämon kommuniziert
Normalerweise ist es gefährlich, zwei Sitzungen mit derselben Sitzungs-ID bei einem einzigen Start der App zu erstellen, da beide versuchen, mit derselben Sitzung im Hintergrund-Daemon zu sprechen. Daher heißt es in der offiziellen Dokumentation, niemals mehrere Sitzungen mit derselben Kennung zu erstellen. Wenn es sich bei der ersten Sitzung jedoch um eine temporäre Sitzung handelt, die als Teil eines handleEventsForBackgroundURLSession
Aufrufs erstellt wurde, ist die Zuordnung zwischen der nun ungültig gewordenen In-App-Sitzung und der Sitzung im Hintergrunddämon nicht mehr vorhanden.
Senden einer POST-Anforderung mit Argumenten mithilfe von NSURLSession in Objective-C
Es gibt zwei Möglichkeiten, einen POST-Request-Body zu codieren: URL-Codierung (application / x-www-form-urlencoded) und Formulardaten (Multipart- / Formulardaten). Ein Großteil des Codes ist ähnlich, aber die Art und Weise, wie Sie die Körperdaten erstellen, ist unterschiedlich.
Senden einer Anfrage mit URL-Kodierung
Egal, ob Sie einen Server für Ihre kleine Anwendung haben oder in einem Team mit einem kompletten Back-End-Ingenieur arbeiten, Sie möchten mit diesem Server an einem Punkt mit Ihrer iOS-Anwendung sprechen.
Im folgenden Code werden wir eine Folge von Argumenten zusammenstellen, die das Ziel-Server-Skript verwendet, um etwas zu tun, das sich je nach Fall ändert. Zum Beispiel möchten wir die Zeichenfolge senden:
name = Brendon & Passwort = abcde
Auf dem Server, wenn sich ein Benutzer bei Ihrer Anwendung anmeldet, damit der Server diese Informationen in einer Datenbank speichern kann.
Lass uns anfangen. Sie möchten eine NSURLSession-POST-Anforderung mit dem folgenden Code erstellen.
// 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];
Der obige Code hat die POST-Anforderung an den Server erstellt und ausgelöst. Denken Sie daran, dass sich die Skript-URL und die POST-Datenzeichenfolge je nach Ihrer Situation ändern. Wenn Sie dies lesen, wissen Sie, womit Sie diese Variablen füllen sollen.
Sie müssen auch eine kleine Methode hinzufügen, die die URL-Kodierung übernimmt:
- (NSString *)URLEncode:(NSString *)originalString encoding:(NSStringEncoding)encoding
{
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge CFStringRef)originalString,
NULL,
CFSTR(":/?#[]@!$&'()*+,;="),
CFStringConvertNSStringEncodingToEncoding(encoding));
}
Wenn der Server die Verarbeitung dieser Daten abgeschlossen hat, sendet er eine Rückmeldung an Ihre iOS-App. Also müssen wir diese Rückkehr bearbeiten, aber wie?
Wir verwenden ereignisgesteuerte Programmierung und verwenden die Delegat-Methoden von NSURLSession. Wenn der Server eine Antwort zurücksendet, werden diese Methoden ausgelöst. Die folgenden 5 Methoden werden während der gesamten ENTIRE-Anforderung jedes Mal ausgelöst, wenn eine gemacht wird:
- (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;
Nachfolgend sehen Sie die oben im Kontext verwendeten Methoden. Jeder ihrer Zwecke ist dank Apple ziemlich selbsterklärend, aber ich habe ihre Verwendung trotzdem kommentiert:
// 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);
}
}
}
So, das war es! Das ist alles Code, den Sie zum Senden, Empfangen und Analysieren einer API-Anforderung in iOS 9 benötigen! Okay ... es war eine Menge Code. Aber wie oben beschrieben, ist es ausfallsicher! Vergewissern Sie sich, dass Sie Fehler immer behandeln, wenn dies oben vorgeschlagen wurde.
Senden einer Anfrage mit Formularverschlüsselung
Die URL-Codierung ist eine weitgehend kompatible Methode zum Codieren beliebiger Daten. Das Hochladen binärer Daten (z. B. Fotos) ist jedoch relativ ineffizient, da jedes Nicht-ASCII-Byte zu einem dreistelligen Code wird. Es unterstützt auch keine Dateianhänge, daher müssen Sie Dateinamen und Dateidaten als separate Felder übergeben.
Angenommen, wir möchten ein Foto so hochladen, dass es effizient ist und tatsächlich auf Server-Seite wie eine Datei aussieht. Eine Möglichkeit, dies zu tun, besteht darin, stattdessen die Formularkodierung zu verwenden. Bearbeiten Sie dazu den Code, der die NSURLSession erstellt, wie folgt:
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];
Dadurch wird die NSURLSession-Anforderung wie zuvor erstellt und ausgelöst, und die Delegat-Methoden verhalten sich folglich genauso. Stellen Sie sicher, dass das Skript, an das das Bild gesendet wird (an der URL in der Variablen url
), ein Bild erwartet und es korrekt analysieren kann.