Buscar..


Observaciones

La clase NSURLSession y las clases relacionadas proporcionan una API para descargar contenido. Esta API proporciona un amplio conjunto de métodos de delegado para admitir la autenticación y le brinda a la aplicación la capacidad de realizar descargas en segundo plano cuando la aplicación no se está ejecutando o, en iOS, mientras la aplicación está suspendida.

En un nivel alto, NSURLSession se basa en el concepto de sesiones y tareas. Una tarea representa una solicitud única para una URL única (o una carga única a una URL única). Una sesión es un grupo de solicitudes relacionadas.

El sistema operativo proporciona una única sesión preexistente: la sesión compartida, que básicamente funciona como NSURLConnection. Además, puede crear sus propias sesiones en su aplicación según sea necesario.

Diferentes aplicaciones utilizan las sesiones de diferentes maneras. Muchas aplicaciones crean una sola sesión en el inicio y simplemente la reutilizan. Otras aplicaciones se benefician de la posibilidad de cancelar un grupo de tareas relacionadas (por ejemplo, un navegador web que cancela todas las solicitudes pendientes al cerrar una pestaña) y, por lo tanto, crean una sesión para contener cada grupo de solicitudes relacionadas.

El primer paso cuando se usa NSURLSession es crear un objeto de configuración de sesión. El objeto (generalmente) reutilizable contiene varias configuraciones de sesión que puede modificar para sus necesidades particulares, como máxima concurrencia, encabezados adicionales para enviar con cada solicitud, ya sea para permitir el envío de solicitudes a través de la radio celular (solo iOS), tiempos de espera, almacenamiento de credenciales, versión mínima de TLS e incluso configuraciones de proxy.

Hay tres tipos de configuraciones de sesión, dependiendo de cómo desea que se comporte la sesión resultante:

  • Las configuraciones predeterminadas crean sesiones que funcionan de manera muy similar a NSURLConnection.
  • Las configuraciones en segundo plano crean sesiones en las que las solicitudes pasan fuera del proceso, lo que permite que las descargas continúen incluso cuando la aplicación ya no se ejecuta.
  • Las configuraciones efímeras crean sesiones que no almacenan en caché nada en el disco, no almacenan cookies en el disco, etc. y, por lo tanto, son adecuadas para respaldar cosas como las ventanas de incógnito del navegador.

Cuando crea una configuración en segundo plano, debe proporcionar un identificador de sesión que le permita volver a asociar la sesión en segundo plano más tarde (si su aplicación sale o es suspendida o terminada por el sistema operativo). No debe tener más de una instancia de una sesión con el mismo identificador activo en su aplicación, por lo que, como regla, estas configuraciones no son reutilizables. Todas las demás configuraciones de sesión se pueden reutilizar para crear tantas sesiones como desee. Por lo tanto, si necesita crear varias sesiones con configuraciones similares, puede crear la configuración una vez y reutilizarla cada vez que cree una nueva sesión.

Después de crear una sesión, puede crear tareas en esa sesión. Hay tres tipos de tareas:

  • Las tareas de datos devuelven datos como un objeto NSData . Estos son adecuados para uso general, pero no se admiten en sesiones de fondo.
  • Las tareas de descarga devuelven datos como un archivo en el disco. Estos son adecuados para solicitudes más grandes, o para uso en sesiones de fondo.
  • Cargar tareas cargar datos desde un objeto NSData o desde un archivo en el disco. Proporciona un objeto o archivo de datos que proporciona el cuerpo POST. Los datos / archivos de cuerpo que proporciona en la tarea anulan cualquier archivo / datos de cuerpo proporcionados en el objeto NSURLRequest (si corresponde).

Cada uno de estos tipos le permite obtener los datos de respuesta de un par de formas diferentes, ya sea utilizando devoluciones de llamadas basadas en bloques o proporcionando un delegado en la sesión e implementando métodos de delegado.

Además, NSURLSession le permite proporcionar métodos delegados para manejar la autenticación, realizar el manejo personalizado del certificado TLS (tanto para certificados de cliente y validación del servidor), cambiar el comportamiento de almacenamiento en caché, etc.

Solicitud 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 Crear una sesión y tarea de datos

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

Configuración de la configuración de fondo

Para crear una sesión de fondo

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

Además, en iOS, debe configurar el soporte para manejar el reinicio de la aplicación en segundo plano. Cuando se application:handleEventsForBackgroundURLSession:completionHandler: la application:handleEventsForBackgroundURLSession:completionHandler: de su application:handleEventsForBackgroundURLSession:completionHandler: método (Objective-C) o application(_:handleEventsForBackgroundURLSession:completionHandler:) (Swift), significa que su aplicación se ha relanzado en segundo plano para manejar la actividad de una sesión.

En ese método, debe crear una nueva sesión con el identificador provisto y configurarlo con un delegado para manejar eventos como lo haría normalmente en el primer plano. Además, debe almacenar el controlador de finalización proporcionado en un diccionario, utilizando la sesión como clave.

Cuando se llama al método URLSessionDidFinishEventsForBackgroundURLSession: del delegado URLSessionDidFinishEventsForBackgroundURLSession: (Obj-C) / URLSessionDidFinishEventsForBackgroundURLSession (Swift) para informarle que no hay más eventos que controlar, su aplicación debe buscar el controlador de finalización para esa sesión, eliminar la sesión del diccionario y llame al controlador de finalización y, por lo tanto, le dice al sistema operativo que ya no tiene ningún procesamiento pendiente relacionado con la sesión. (Si aún está haciendo algo por alguna razón cuando recibe la llamada de delegado, espere hasta que termine). Tan pronto como llame a ese método, la sesión en segundo plano se invalidará de inmediato.

Si su aplicación recibe una application:application:didFinishLaunchingWithOptions: call (es probable que indique que el usuario application:application:didFinishLaunchingWithOptions: primer plano su aplicación mientras estaba ocupado procesando eventos de fondo), es seguro crear una sesión en segundo plano con ese mismo identificador, porque la sesión anterior con ese El identificador ya no existe.

Si tiene curiosidad acerca de los detalles, a un alto nivel, cuando crea una sesión en segundo plano, está haciendo dos cosas:

  • Creando una sesión en un demonio externo (nsurlsessiond) para manejar las descargas
  • Creando una sesión dentro de su aplicación que habla con ese demonio externo a través de NSXPC

Normalmente, es peligroso crear dos sesiones con el mismo ID de sesión en un solo lanzamiento de la aplicación, ya que ambos intentan hablar con la misma sesión en el daemon de fondo. Por esta razón, la documentación oficial dice que nunca se deben crear varias sesiones con el mismo identificador. Sin embargo, si la primera sesión fue una sesión temporal creada como parte de una llamada handleEventsForBackgroundURLSession , ya no existe la asociación entre la sesión dentro de la aplicación ahora invalidada y la sesión en el daemon de fondo.

Enviar una solicitud POST con argumentos utilizando NSURLSession en Objective-C

Hay dos formas comunes de codificar un cuerpo de solicitud POST: codificación de URL (application / x-www-form-urlencoded) y datos de formulario (multipart / form-data). Gran parte del código es similar, pero la forma en que construye los datos del cuerpo es diferente.

Enviando una solicitud usando codificación de URL

Ya sea que tenga un servidor para su pequeña aplicación o que trabaje en un equipo con un ingeniero completo, querrá hablar con ese servidor en un momento dado con su aplicación iOS.

En el siguiente código, vamos a componer una serie de argumentos que la secuencia de comandos del servidor de destino usará para hacer algo que cambia según su caso. Por ejemplo, podemos querer enviar la cadena:

nombre = Brendon y contraseña = abcde

Al servidor cuando un usuario se registra en su aplicación, para que el servidor pueda almacenar esta información en una base de datos.

Empecemos. Querrá crear una solicitud POST de NSURLSession con el siguiente código.

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

El código anterior acaba de crear y disparó la solicitud POST al servidor. Recuerde que la URL del script y la cadena de datos de la POST cambian según su situación. Si estás leyendo esto, sabrás con qué llenar esas variables.

También deberá agregar un pequeño método que haga la codificación de la URL:

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

Por lo tanto, cuando el servidor haya terminado de procesar estos datos, enviará un retorno a su aplicación iOS. Entonces necesitamos procesar este retorno, pero ¿cómo?

Usamos programación dirigida por eventos y usamos los métodos de delegado de NSURLSession. Esto significa que cuando el servidor envía una respuesta, estos métodos comenzarán a activarse. Los siguientes 5 métodos son los que se activarán a lo largo de la solicitud ENTERA, cada vez que se realice uno:

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

A continuación verá los métodos anteriores utilizados en contexto. Cada uno de sus propósitos se explica por sí mismo gracias a Apple, pero he comentado sus usos de todos modos:

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

¡Eso es todo! ¡Es todo el código que necesita para enviar, recibir y analizar una solicitud de API en iOS 9! Está bien ... era una especie de código. Pero si se implementa como en el caso anterior, ¡será a prueba de fallas! Asegúrese de manejar siempre los errores donde se sugiere anteriormente.

Enviando una solicitud utilizando codificación de formulario

La codificación de URL es una forma ampliamente compatible de codificar datos arbitrarios. Sin embargo, es relativamente ineficiente para cargar datos binarios (como fotos) porque cada byte no ASCII se convierte en un código de tres caracteres. Tampoco admite archivos adjuntos, por lo que tendría que pasar los nombres de archivo y los datos del archivo como campos separados.

Supongamos que queremos cargar una fotografía de una manera eficiente y que se vea como un archivo en el lado del servidor. Una forma de hacerlo es utilizar la codificación de formularios en su lugar. Para hacer esto, edite el código que crea la NSURLSession de la siguiente manera:

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

Esto crea y dispara la solicitud NSURLSession como antes, y como resultado, los métodos de delegado se comportarán exactamente de la misma manera. Asegúrese de que la secuencia de comandos a la que se envía la imagen (ubicada en la url en la variable url ) esté esperando una imagen y pueda analizarla correctamente.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow