11

I have the following code in my iOS project and I want to convert to use NSURLSession instead of NSURLConnection. I am querying a REST API which uses a token-based HTTP Authentication scheme but I cannot find an example of how to do it.

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];

NSString *username = [[NSUserDefaults standardUserDefaults] stringForKey:@"Username"];

NSString *token = //GET THE TOKEN FROM THE KEYCHAIN


NSString *authValue = [NSString stringWithFormat:@"Token %@",token];
[request setValue:authValue forHTTPHeaderField:@"Authorization"];


if ([NSURLConnection canHandleRequest:request]){
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
    [NSURLConnection sendAsynchronousRequest:request queue:self.fetchQueue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

                               if (!connectionError) {
                                   NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                                   if (httpResponse.statusCode == 200){
                                       NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:nil];

                                       //Process the data
                                   }
                               }

                           }];
}
Julian
  • 9,299
  • 5
  • 48
  • 65
elenag
  • 153
  • 1
  • 2
  • 10

3 Answers3

22

You can rewrite it using NSURLSession as follows

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

    NSString *token ; //GET THE TOKEN FROM THE KEYCHAIN

    NSString *authValue = [NSString stringWithFormat:@"Token %@",token];

    //Configure your session with common header fields like authorization etc
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    sessionConfiguration.HTTPAdditionalHeaders = @{@"Authorization": authValue};

    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

    NSString *url;
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        if (!error) {
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
            if (httpResponse.statusCode == 200){
                NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:nil];

                //Process the data
            }
        }

    }];
    [task resume];
Bilal Saifudeen
  • 1,677
  • 14
  • 14
  • 13
    The documentation of NSMutableURLRequest says: `IMPORTANT The NSURLConnection class and NSURLSession classes are designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers: Authorization, ...` (see: [doc](https://developer.apple.com/library/IOs/documentation/Cocoa/Reference/Foundation/Classes/NSMutableURLRequest_Class/index.html#//apple_ref/occ/instm/NSMutableURLRequest/addValue:forHTTPHeaderField:)). But how to do it then without [request setValue: ...] then? – Paul Spieker Nov 07 '14 at 12:25
  • @PaulSpieker You can configure the session for common HTTP Header fields. I have updated my answer with that. – Bilal Saifudeen Nov 27 '14 at 14:05
  • 10
    The documentation of `NSURLSessionConfiguration` also says the same thing: `An NSURLSession object is designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers: Authorization, ...` ([doc](https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSessionConfiguration_class/#//apple_ref/occ/instp/NSURLSessionConfiguration/HTTPAdditionalHeaders)). Is your approach safe? – Jacob Dam Apr 11 '15 at 08:48
  • 1
    I have the same question as @JacobDam . I'm supposed to pass an OAuth 2.0 auth header (`Autorization: Bearer (token)`) to a service. I guess I will just do that, and if it works just ignore the waring in Apple's docs... – Nicolas Miari Feb 09 '16 at 02:10
  • ...well, my request succeeded and I got all the data I was expecting in the response body. Perhaps the API has a recommended, more appropriate way of passing authentication header info (i.e., tokens) to `NSURLSession`/`NSURLConnection` other than messing with the headers directly? – Nicolas Miari Feb 09 '16 at 02:25
  • I'm finding I cannot add an Authorization header to `HTTPAdditionalHeaders` in iOS 9.3 – rob Mar 31 '16 at 16:32
  • @PaulSpieker I don't know why the docs state that you can't set the Authorization field in the config (it currently still does). But you can still set it in the NSMutableURLRequest. With a mutable request variable, set it this way [request setValue:kAuthorizationHeaderValue forHTTPHeaderField:@"Authorization"]; – PostCodeism Nov 29 '16 at 19:32
  • @PostCodeism -- Neither solution works for me, neither setting it in NSMutableURLRequest nor in NSURLSessionConfiguration. And every time I call allHTTPHeaderFields to dump header contents and see what its value is it returns null, so nothing is getting set apparently. Any other ideas? – Alyoshak Jun 12 '17 at 20:58
  • I've had similar troubles, just had it again. And again the culprit was redirection. Not sure why the redirection happened in the first place, but it seems that on many occasions the redirection request, for some reason, does not get the auth http headers from the original request. But there is the URLSessionTaskDelegate which can be used to set that auth header again on the new request before passing it along again. Check out https://developer.apple.com/documentation/foundation/urlsessiontaskdelegate/1411626-urlsession – Jonny Dec 13 '17 at 09:44
3

This is in Swift, but the logic is the same:

    let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
    let url  = NSURL(string: "some url")

    let request = NSMutableURLRequest(URL: url!)
    request.setValue("value", forHTTPHeaderField: "header field")

    let urlSession = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: NSOperationQueue.mainQueue())


    let dataTask = urlSession.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
    }

    dataTask.resume()
BoygeniusDexter
  • 2,154
  • 1
  • 16
  • 14
1

Set the Authorization header on your URLSession configuration, or directly on your request.

Be advised that Apple says you "should not" attempt to modify the Authorization header in your URLSession configuration:

An URLSession object is designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers:

  • Authorization

  • ...

It is, however, possible. If you want to do this, ensure that you set the header configuration before you create the URLSession. It's not possible to modify headers on an existing URLSession.

Here is a Swift playground that shows different scenarios:

import Foundation

// 1: Wrong -- mutating existing config for existing URLSession is no-op

var session = URLSession.shared
session.configuration.httpAdditionalHeaders = ["Authorization": "123"]

let url = URL(string: "https://postman-echo.com/get")!

session.dataTask(with: url) { (data, resp, err) in
    if let data = data {
        let str = String(bytes: data, encoding: .utf8)!
        let _ = str
    }
}.resume() // no authorization header

// 2: Setting headers on an individual request works fine.

var request = URLRequest(url: url)
request.addValue("456", forHTTPHeaderField: "Authorization")
session.dataTask(with: request) { (data, resp, err) in
    if let data = data {
        let str = String(bytes: data, encoding: .utf8)!
        let _ = str
    }
}.resume() // "headers": { "authorization": "456" }

// 3: You can set headers on a new URLSession & configuration

var conf = URLSessionConfiguration.default
conf.httpAdditionalHeaders = [
    "Authorization": "789"
]
session = URLSession(configuration: conf)

session.dataTask(with: url) { (data, resp, err) in
    if let data = data {
        let str = String(bytes: data, encoding: .utf8)!
        let _ = str
    }
}.resume() // "headers": { "authorization": "789" }
Community
  • 1
  • 1
Matt Mc
  • 8,882
  • 6
  • 53
  • 89
  • 1
    First example doesn't work because mutating a `URLSessionConfiguration` after it has been added to a `URLSession` has no effect. Setting the `httpAdditionalHeaders` before adding it to a `URLSession` should work. – Jon Shier Feb 10 '20 at 04:05
  • Well, that detail would have saved me a lot of trouble and testing. :) Thanks @JonShier I updated my post to reflect that. – Matt Mc Feb 10 '20 at 06:58