Szukaj…


Uwagi

Klasa NSURLSession i klasy pokrewne zapewniają interfejs API do pobierania treści. Ten interfejs API zapewnia bogaty zestaw metod delegowania do obsługi uwierzytelniania i umożliwia aplikacji pobieranie plików w tle, gdy aplikacja nie jest uruchomiona, lub w systemie iOS, gdy aplikacja jest zawieszona.

Na wysokim poziomie NSURLSession opiera się na koncepcji sesji i zadań. Zadanie reprezentuje pojedyncze żądanie jednego adresu URL (lub pojedynczego przesłania do jednego adresu URL). Sesja to grupa powiązanych żądań.

System operacyjny zapewnia jedną wcześniej istniejącą sesję - sesję współdzieloną, która w zasadzie działa jak NSURLConnection. Ponadto w razie potrzeby możesz tworzyć własne sesje w aplikacji.

Różne aplikacje używają sesji na różne sposoby. Wiele aplikacji tworzy pojedynczą sesję podczas uruchamiania i po prostu ją wykorzystuje. Inne aplikacje korzystają z możliwości anulowania grupy powiązanych zadań (np. Przeglądarka internetowa anulująca wszystkie zaległe żądania po zamknięciu karty), a zatem utworzenie jednej sesji dla każdej grupy powiązanych żądań.

Pierwszym krokiem podczas korzystania z NSURLSession jest utworzenie obiektu konfiguracji sesji. Obiekt (zwykle) wielokrotnego użytku zawiera różne ustawienia sesji, które można dostosować do określonych potrzeb, takie jak maksymalna współbieżność, dodatkowe nagłówki do wysłania przy każdym żądaniu, czy zezwalać na wysyłanie żądań przez radio komórkowe (tylko iOS), limity czasu, przechowywanie poświadczeń, minimalna wersja TLS, a nawet ustawienia proxy.

Istnieją trzy typy konfiguracji sesji, w zależności od tego, jak ma zachowywać się sesja wynikowa:

  • Domyślne konfiguracje tworzą sesje, które działają podobnie jak NSURLConnection.
  • Konfiguracje w tle tworzą sesje, w których żądania odbywają się poza procesem, umożliwiając kontynuowanie pobierania, nawet gdy aplikacja nie jest już uruchomiona.
  • Konfiguracje efemeryczne tworzą sesje, które niczego nie buforują na dysk, nie przechowują plików cookie na dysku itp., A zatem nadają się do tworzenia kopii zapasowych np. Okien przeglądarki incognito.

Podczas tworzenia konfiguracji w tle musisz podać identyfikator sesji, który pozwoli Ci ponownie powiązać sesję w tle później (jeśli aplikacja zostanie zamknięta lub zawieszona lub zakończona przez system operacyjny). W aplikacji nie może być aktywnych więcej niż jedna instancja sesji z tym samym identyfikatorem, więc z reguły konfiguracji tych nie można ponownie używać. Wszystkie pozostałe konfiguracje sesji można wykorzystać ponownie, aby utworzyć tyle sesji, ile chcesz. Jeśli więc chcesz utworzyć wiele sesji z podobnymi ustawieniami, możesz utworzyć konfigurację raz i użyć jej za każdym razem, gdy tworzysz nową sesję.

Po utworzeniu sesji możesz tworzyć zadania w tej sesji. Istnieją trzy rodzaje zadań:

  • Zadania danych zwracają dane jako obiekt NSData . Są odpowiednie do ogólnego użytku, ale nie są obsługiwane w sesjach w tle.
  • Zadania pobierania zwracają dane w postaci pliku na dysku. Są odpowiednie do większych żądań lub do użycia w sesjach w tle.
  • Zadania przesyłania przesyłają dane z obiektu NSData lub z pliku na dysku. Podajesz obiekt danych lub plik, który zawiera treść POST. Dane ciała / plik, które podajesz w zadaniu, zastępują wszelkie dane ciała / plik podane w obiekcie NSURLRequest (jeśli dotyczy).

Każdy z tych typów pozwala uzyskiwać dane odpowiedzi na kilka różnych sposobów - albo za pomocą wywołań zwrotnych opartych na blokach, albo przez zapewnienie uczestnika sesji i wdrożenie metod delegowania.

Ponadto NSURLSession pozwala udostępnić metody delegowania do obsługi uwierzytelniania, wykonywania niestandardowej obsługi certyfikatów TLS (zarówno dla certyfikatów klientów, jak i sprawdzania poprawności serwera), zmiany zachowania buforowania itd.

Proste żądanie 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()

Cel-C Utwórz zadanie sesji i danych

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

Konfigurowanie konfiguracji w tle

Aby utworzyć sesję w tle

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

Ponadto w systemie iOS należy skonfigurować obsługę ponownego uruchamiania aplikacji w tle. Gdy zostanie application:handleEventsForBackgroundURLSession:completionHandler: aplikacja application:handleEventsForBackgroundURLSession:completionHandler: method (Objective-C) lub application(_:handleEventsForBackgroundURLSession:completionHandler:) metoda (Swift) zostanie wywołana, oznacza to, że aplikacja została ponownie uruchomiona w tle w celu obsługi aktywności w sesji.

W tej metodzie powinieneś utworzyć nową sesję z podanym identyfikatorem i skonfigurować ją z delegatem do obsługi zdarzeń, tak jak normalnie na pierwszym planie. Dodatkowo należy przechowywać dostarczony moduł obsługi zakończenia w słowniku, używając sesji jako klucza.

Gdy URLSessionDidFinishEventsForBackgroundURLSession: jest URLSessionDidFinishEventsForBackgroundURLSession: delegata URLSessionDidFinishEventsForBackgroundURLSession: (Obj-C) / URLSessionDidFinishEventsForBackgroundURLSession (Swift), aby poinformować, że nie ma więcej zdarzeń do obsłużenia, aplikacja powinna wyszukać URLSessionDidFinishEventsForBackgroundURLSession obsługi zakończenia dla tej sesji, usunąć sesję ze słownika i wywołać moduł obsługi zakończenia, informując w ten sposób system operacyjny, że nie ma już zaległego przetwarzania związanego z sesją. (Jeśli nadal robisz coś z jakiegoś powodu po otrzymaniu połączenia delegowanego, poczekaj, aż skończysz.) Natychmiast po wywołaniu tej metody sesja w tle zostaje natychmiast unieważniona.

Jeśli następnie aplikacja otrzyma application:application:didFinishLaunchingWithOptions: call (prawdopodobnie wskazująca, że użytkownik application:application:didFinishLaunchingWithOptions: na wierzch, gdy użytkownik był zajęty przetwarzaniem zdarzeń w tle), można bezpiecznie utworzyć sesję w tle z tym samym identyfikatorem, ponieważ stara sesja z tym identyfikator już nie istnieje.

Jeśli jesteś ciekawy szczegółów, podczas tworzenia sesji w tle robisz dwie rzeczy:

  • Tworzenie sesji w zewnętrznym demonie (nsurlsessiond) do obsługi pobierania
  • Tworzenie sesji w aplikacji, która rozmawia z tym zewnętrznym demonem przez NSXPC

Zwykle niebezpieczne jest utworzenie dwóch sesji z tym samym identyfikatorem sesji podczas jednego uruchomienia aplikacji, ponieważ obie próbują rozmawiać z tą samą sesją w demonie w tle. Dlatego oficjalna dokumentacja mówi, aby nigdy nie tworzyć wielu sesji z tym samym identyfikatorem. Jeśli jednak pierwsza sesja była sesją tymczasową utworzoną w ramach wywołania handleEventsForBackgroundURLSession , powiązanie między teraz unieważnioną sesją w aplikacji a sesją w demonie w tle już nie istnieje.

Wysyłanie żądania POST z argumentami przy użyciu NSURLSession w Objective-C

Istnieją dwa typowe sposoby kodowania treści żądania POST: kodowanie adresu URL (application / x-www-form-urlencoded) i danych formularza (multipart / form-data). Znaczna część kodu jest podobna, ale sposób konstruowania danych ciała jest inny.

Wysyłanie żądania za pomocą kodowania adresu URL

Niezależnie od tego, czy masz serwer dla swojej małej aplikacji, czy pracujesz w zespole z pełnym zewnętrznym inżynierem, będziesz chciał porozmawiać z tym serwerem w jednym momencie z aplikacją na iOS.

W poniższym kodzie będziemy tworzyć ciąg argumentów, których skrypt serwera docelowego użyje do zrobienia czegoś, co zmieni się w zależności od przypadku. Na przykład możemy chcieć wysłać ciąg:

nazwa = Brendon i hasło = abcde

Do serwera, gdy użytkownik zarejestruje się w aplikacji, aby serwer mógł przechowywać te informacje w bazie danych.

Zacznijmy. Będziesz chciał utworzyć żądanie POST NSURLSession z następującym kodem.

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

Powyższy kod właśnie utworzył i wysłał żądanie POST do serwera. Pamiętaj, że adres URL skryptu i ciąg danych POST zmieniają się w zależności od sytuacji. Jeśli to czytasz, będziesz wiedział, co wypełnić tymi zmiennymi.

Musisz także dodać małą metodę kodowania adresu URL:

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

Tak więc, gdy serwer zakończy przetwarzanie tych danych, wyśle powrót do aplikacji na iOS. Musimy więc przetworzyć ten zwrot, ale jak?

Używamy programowania sterowanego zdarzeniami i stosujemy metody delegowania NSURLSession. Oznacza to, że gdy serwer odeśle odpowiedź, te metody zaczną się uruchamiać. Następujące 5 metod zostanie uruchomionych w trakcie CAŁEGO żądania za każdym razem:

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

Poniżej zobaczysz powyższe metody zastosowane w kontekście. Każdy z ich celów jest dość oczywisty dzięki Apple, ale i tak skomentowałem ich zastosowania:

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

Więc to jest to! To wszystko, czego potrzebujesz, aby wysłać, odebrać i przeanalizować żądanie interfejsu API w iOS 9! Okej ... to było trochę kodu. Ale jeśli zostanie zaimplementowany tak jak powyżej, będzie bezpieczny w razie awarii! Upewnij się, że zawsze radzisz sobie z błędami zgodnie z powyższymi zaleceniami.

Wysyłanie żądania za pomocą kodowania formularza

Kodowanie adresów URL to szeroko zgodny sposób kodowania dowolnych danych. Jest to jednak stosunkowo nieefektywne przy przesyłaniu danych binarnych (takich jak zdjęcia), ponieważ każdy bajt spoza ASCII zamienia się w kod składający się z trzech znaków. Nie obsługuje także załączników, więc nazwy plików i dane plików należy przekazywać jako osobne pola.

Załóżmy, że chcemy przesłać zdjęcie w sposób, który jest wydajny i faktycznie wygląda jak plik po stronie serwera. Jednym ze sposobów jest użycie zamiast tego kodowania formularzy. W tym celu edytuj kod tworzący sesję NSURL w następujący sposób:

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

Spowoduje to utworzenie i uruchomienie żądania NSURLSession tak jak poprzednio, w wyniku czego metody delegowane będą zachowywać się dokładnie tak samo. Upewnij się, że skrypt, do którego wysyłany jest obraz (znajdujący się pod adresem URL w zmiennej url ), oczekuje obrazu i może go poprawnie przeanalizować.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow