TL;DR: Apple’s documentation is lies. Set httpAdditionalHeaders
on URLSessionConfiguration
.
I have no idea why Authentication headers are such a pain to use in Cocoa.
A great use case for one is to get some details about a movie from TMDB’s new v4 API:
curl --request GET \
--url 'https://api.themoviedb.org/3/movie/76341' \
--header 'Authorization: Bearer <<access_token>>' \
--header 'Content-Type: application/json;charset=utf-8'
With CURL, you can just arbitrary build whatever kind of response you’d like and go on with your day.
Naively, I thought that it’d be as easy as that using a NSURLSessionDataTask
, you can set any header values that you want after all:
let movieDetail = URL( //1
string: "https://api.themoviedb.org/3/movie/76341"
)!
var movieRequest = URLRequest( //2
url: movieDetail
)
movieRequest.setValue( //3
"Bearer \(v4apiKey)",
forHTTPHeaderField: "Authentication"
)
movieRequest.setValue( //4
"application/json;charset=utf-8",
forHTTPHeaderField: "Content-Type"
)
let cancellable = URLSession.shared
.dataTaskPublisher(for: movieRequest) //5
.sink(receiveCompletion: { completion in
switch completion {
case let .failure(reason):
print(reason)
case .finished:
print("Done without errors")
}
}) { receivedValue in
print( //6
String(data: receivedValue.data, encoding: .utf8) ?? "Unknown"
)
}
- Create the URL to the request
- Create a
URLRequest
object to handle the request - Add the “Bearer
<<access_token>>
as anAuthentication
header - Set the content type
- Create the data task
Publisher
- Subscribe to the completion closure returned
- Print out the received data (cast to a String for easy printing)
{
"status_code" : 7,
"status_message" : "Invalid API key: You must be granted a valid key.",
"success" : false
}
Even after several laps around the internet, the answer is almost always in the official Documentation.
From the URLRequest documentation
This is for both the headers in the URLRequest as well as the httpAdditionalHeaders
in the URLSessionConfiguration
passed in during creation. Both places officially recommend against it.
But hey, let’s live on the wild side.
let movieDetail = URL( // 1
string: "https://api.themoviedb.org/3/movie/76341"
)!
var movieRequest = URLRequest( // 2
url: movieDetail
)
movieRequest.setValue( // 3
"Bearer <<access-token>>",
forHTTPHeaderField: "Authentication"
)
movieRequest.setValue( // 4
"application/json;charset=utf-8",
forHTTPHeaderField: "Content-Type"
)
var sessionConfiguration = URLSessionConfiguration.default // 5
sessionConfiguration.httpAdditionalHeaders = [
"Authorization": "Bearer \(v4apiKey)" // 6
]
let session = URLSession(configuration: sessionConfiguration) // 7
let cancellable = session
.dataTaskPublisher(for: movieRequest) // 8
.sink(receiveCompletion: { completion in //9
switch completion {
case let .failure(reason):
print(reason)
case .finished:
print("Done without errors")
}
}) { receivedValue in //10
print(
String(data: receivedValue.data, encoding: .utf8) ?? "Unknown"
)
}
- Create the URL to the request
- Create a
URLRequest
object to handle the request - Add the “Bearer
<<access_token>>
as anAuthentication
header - Set the content type
- Create a copy of the default
URLSessionConfiguration
- Set the Bearer token in the
httpAdditionalHeaders
array - Create the
URLSession
with the config with the headers - Create the data task
Publisher
- Subscribe to the completion closure returned
- Print out the received data (cast to a String for easy printing)
And we get:
A GIANT BLOG OF JSON RELATED TO MAX MAD FURY ROAD
The most concise explanation came from Quinn at Apple on the developer forums:
So there we have it. Straight from the horses Developer Relation mouth: