Zoeken…


Opmerkingen

De NSURLSession- klasse en gerelateerde klassen bieden een API voor het downloaden van inhoud. Deze API biedt een uitgebreide set methoden voor het ondersteunen van authenticatie en geeft uw app de mogelijkheid om achtergronddownloads uit te voeren wanneer uw app niet actief is of, in iOS, terwijl uw app is opgeschort.

Op een hoog niveau is NSURLSession gebaseerd op het concept van sessies en taken. Een taak vertegenwoordigt een enkele aanvraag voor een enkele URL (of een enkele upload naar een enkele URL). Een sessie is een groep gerelateerde verzoeken.

Het besturingssysteem biedt één bestaande sessie - de gedeelde sessie, die in principe werkt als NSURLConnection. Bovendien kunt u indien nodig uw eigen sessies in uw app maken.

Verschillende apps gebruiken sessies op verschillende manieren. Veel apps maken een enkele sessie bij het opstarten en blijven deze hergebruiken. Andere apps profiteren van het kunnen annuleren van een groep gerelateerde taken (bijv. Een webbrowser die alle openstaande aanvragen annuleert wanneer u een tabblad sluit), en maken zo één sessie voor elke groep gerelateerde aanvragen.

De eerste stap bij het gebruik van NSURLSession is het maken van een sessieconfiguratieobject. Het (meestal) herbruikbare object bevat verschillende sessie-instellingen die u kunt aanpassen voor uw specifieke behoeften, zoals maximale concurrency, extra headers om met elk verzoek te verzenden, of toestaan dat aanvragen via de mobiele radio (alleen iOS) worden verzonden, time-outs, referentieopslag, minimale TLS-versie en zelfs proxy-instellingen.

Er zijn drie soorten sessieconfiguraties, afhankelijk van hoe u wilt dat de resulterende sessie zich gedraagt:

  • Standaardconfiguraties maken sessies die net zo werken als NSURLConnection.
  • Achtergrondconfiguraties creëren sessies waarin aanvragen buiten het proces gebeuren, waardoor downloads kunnen doorgaan, zelfs wanneer de app niet meer actief is.
  • Efemere configuraties creëren sessies die niets op de schijf opslaan, geen cookies op schijf opslaan, enz. En zijn dus geschikt voor het ondersteunen van dingen zoals incognito browservensters.

Wanneer u een achtergrondconfiguratie maakt, moet u een sessie-ID opgeven waarmee u de achtergrondsessie later opnieuw kunt koppelen (als uw app wordt afgesloten of wordt opgeschort of beëindigd door het besturingssysteem). U mag niet meer dan één exemplaar van een sessie met dezelfde ID actief hebben in uw app, dus deze configuraties zijn in de regel niet herbruikbaar. Alle andere sessieconfiguraties kunnen opnieuw worden gebruikt om zoveel sessies te maken als u wilt. Als u dus meerdere sessies met vergelijkbare instellingen moet maken, kunt u de configuratie eenmaal maken en deze elke keer opnieuw gebruiken wanneer u een nieuwe sessie maakt.

Nadat u een sessie hebt gemaakt, kunt u taken in die sessie maken. Er zijn drie soorten taken:

  • Gegevenstaken retourneren gegevens als een NSData- object. Deze zijn geschikt voor algemeen gebruik, maar worden niet ondersteund in achtergrondsessies.
  • Downloadtaken retourneren gegevens als een bestand op schijf. Deze zijn geschikt voor grotere verzoeken of voor gebruik in achtergrondsessies.
  • Upload-taken uploaden gegevens van een NSData-object of van een bestand op schijf. U geeft een gegevensobject of bestand op dat de POST-body levert. De body-gegevens / het bestand dat u voor de taak opgeeft, overschrijft alle body-gegevens / het bestand in het NSURLRequest-object (indien van toepassing).

Met elk van deze typen kunt u de responsgegevens op een aantal verschillende manieren verkrijgen - hetzij door op blokken gebaseerde callbacks te gebruiken of door een gemachtigde voor de sessie te bieden en de gemachtigde methoden te implementeren.

Bovendien kunt u met NSURLSession gedelegeerde methoden bieden voor de afhandeling van authenticatie, het uitvoeren van aangepaste TLS-certificaatafhandeling (zowel voor clientcertificaten als servervalidatie), het cachegedrag wijzigen, enzovoort.

Eenvoudig GET-verzoek

    // 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 Maak een sessie- en gegevenstaak

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];

Achtergrondconfiguratie instellen

Een achtergrondsessie maken

 // 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]

Bovendien moet u in iOS ondersteuning instellen voor het opnieuw opstarten van de achtergrondapp. Wanneer de application:handleEventsForBackgroundURLSession:completionHandler: uw app application:handleEventsForBackgroundURLSession:completionHandler: methode (Objective-C) of application(_:handleEventsForBackgroundURLSession:completionHandler:) methode (Swift) wordt aangeroepen, betekent dit dat uw app opnieuw op de achtergrond is gestart om activiteit tijdens een sessie af te handelen.

Bij die methode moet u een nieuwe sessie met de opgegeven ID maken en deze configureren met een gemachtigde om gebeurtenissen af te handelen zoals u normaal op de voorgrond zou doen. Bovendien moet u de geleverde voltooiingshandler in een woordenboek opslaan, waarbij u de sessie als sleutel gebruikt.

Wanneer de URLSessionDidFinishEventsForBackgroundURLSession: de URLSessionDidFinishEventsForBackgroundURLSession: (Obj-C) / URLSessionDidFinishEventsForBackgroundURLSession (Swift) wordt opgeroepen om u te vertellen dat er geen gebeurtenissen meer zijn om af te handelen, moet uw app de voltooiingshandler voor die sessie opzoeken, de sessie uit het woordenboek verwijderen, en bel de voltooiingshandler en vertel het besturingssysteem dat u geen openstaande verwerking meer heeft met betrekking tot de sessie. (Als u om de een of andere reden nog steeds iets doet wanneer u die gedelegeerde oproep ontvangt, wacht dan totdat u klaar bent.) Zodra u die methode aanroept, wordt de achtergrondsessie onmiddellijk ongeldig.

Als uw toepassing vervolgens een application:application:didFinishLaunchingWithOptions: ontvangt application:application:didFinishLaunchingWithOptions: call (geeft waarschijnlijk aan dat de gebruiker uw app op de voorgrond heeft geplaatst terwijl u bezig was met het verwerken van achtergrondgebeurtenissen), is het veilig om een achtergrondsessie met dezelfde identificatie te maken, omdat de oude sessie daarmee ID bestaat niet meer.

Als je nieuwsgierig bent naar de details, op een hoog niveau, wanneer je een achtergrondsessie maakt, doe je twee dingen:

  • Een sessie maken in een externe daemon (nsurlsessiond) om de downloads af te handelen
  • Een sessie in uw app maken die via NSXPC met die externe daemon praat

Normaal gesproken is het gevaarlijk om twee sessies met dezelfde sessie-ID te maken in een enkele lancering van de app, omdat ze allebei proberen met dezelfde sessie in de achtergronddaemon te praten. Dit is de reden waarom de officiële documentatie zegt nooit meerdere sessies met dezelfde ID te maken. Als de eerste sessie echter een tijdelijke sessie was die is gemaakt als onderdeel van een handleEventsForBackgroundURLSession aanroep, bestaat de koppeling tussen de nu ongeldige in-app-sessie en de sessie in de achtergronddaemon niet meer.

Een POST-aanvraag met argumenten verzenden met NSURLSession in Objective-C

Er zijn twee veel voorkomende manieren om een POST-aanvraagcodering te coderen: URL-codering (application / x-www-form-urlencoded) en formuliergegevens (multipart / formulier-gegevens). Veel van de code is vergelijkbaar, maar de manier waarop u de lichaamsgegevens samenstelt, is anders.

Een verzoek verzenden met URL-codering

Of je nu een server hebt voor je kleine applicatie of je werkt in een team met een volledige back-end engineer, je wilt op een gegeven moment met die server praten met je iOS-applicatie.

In de volgende code zullen we een reeks argumenten opstellen die het script van de doelserver zal gebruiken om iets te doen dat verandert, afhankelijk van uw geval. We willen bijvoorbeeld de tekenreeks verzenden:

name = Brendon & password = ABCDE

Aan de server wanneer een gebruiker zich aanmeldt bij uw toepassing, zodat de server deze informatie in een database kan opslaan.

Laten we beginnen. U wilt een NSURLSession POST-aanvraag maken met de volgende code.

// 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];

De bovenstaande code heeft zojuist het POST-verzoek gemaakt en naar de server gestuurd. Onthoud dat de script-URL en de POST-gegevensreeks veranderen afhankelijk van uw situatie. Als je dit leest, weet je waarmee je die variabelen moet vullen.

U moet ook een kleine methode toevoegen die de URL-codering uitvoert:

- (NSString *)URLEncode:(NSString *)originalString encoding:(NSStringEncoding)encoding
{
    return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
        kCFAllocatorDefault,
        (__bridge CFStringRef)originalString,
        NULL,
        CFSTR(":/?#[]@!$&'()*+,;="),
        CFStringConvertNSStringEncodingToEncoding(encoding));
}

Dus wanneer de server klaar is met het verwerken van deze gegevens, stuurt deze een retour naar uw iOS-app. Dus we moeten dit rendement verwerken, maar hoe?

We maken gebruik van gebeurtenisgestuurde programmering en gebruiken de gedelegeerde methoden van NSURLSession. Dit betekent dat als de server een reactie terugstuurt, deze methoden zullen beginnen met activeren. De volgende 5 methoden worden tijdens het hele verzoek geactiveerd, telkens wanneer er een wordt gemaakt:

- (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;

Hieronder ziet u de bovenstaande methoden die in context worden gebruikt. Elk van hun doelen is vrij vanzelfsprekend dankzij Apple, maar ik heb hoe dan ook hun opmerkingen gemaakt:

// 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);
        }
    }
}

Dus dat is het! Dat is alle code die u nodig hebt om een aanvraag voor een API in iOS 9 te verzenden, ontvangen en parseren! Oké ... het was nogal wat code. Maar als het net als hierboven wordt geïmplementeerd, is het faalveilig! Zorg ervoor dat u altijd fouten behandelt zoals hierboven wordt voorgesteld.

Een aanvraag verzenden met behulp van formuliercodering

URL-codering is een breed compatibele manier om willekeurige gegevens te coderen. Het is echter relatief inefficiënt voor het uploaden van binaire gegevens (zoals foto's) omdat elke niet-ASCII-byte een code van drie tekens wordt. Het ondersteunt ook geen bestandsbijlagen, dus u zou bestandsnamen en bestandsgegevens moeten doorgeven als afzonderlijke velden.

Stel dat we een foto willen uploaden op een manier die efficiënt is en er daadwerkelijk uitziet als een bestand op de server. Een manier om dat te doen is om in plaats daarvan formuliercodering te gebruiken. Hiertoe bewerkt u de code die de NSURLSession maakt als volgt:

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];

Hierdoor wordt het NSURLSession-verzoek gemaakt en geactiveerd, net als voorheen, en als gevolg hiervan zullen de gedelegeerde methoden zich precies hetzelfde gedragen. Zorg ervoor dat het script waarnaar de afbeelding wordt verzonden (te vinden op de URL in de variabele url ) een afbeelding verwacht en deze correct kan parseren.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow