In the previous sections, we used Alamofire to send network requests. Although Alamofire is very elegant and simple to use, there are times when we want to use native Swift to send network requests. We can do this with URLSession.
GET Requests
Let’s take a look at the Alamofire version from the previous section:
// 1. Create the functionfuncfetchRoster(completion: @escaping ([Member]) ->Void) {// 2. Specify the endpointlet endpoint ="<Enter URL String Here>"// 3. Create a decoderlet decoder =JSONDecoder()// decoder.dateDecodingStrategy = .iso8601 // Only if needed// decoder.keyDecodingStrategy = .convertFromSnakeCase // Only if needed// 4. Create the request AF.request(endpoint, method: .get) .validate() .responseDecodable(of: [Member].self, decoder: decoder) { response in// 5. Handle the responseswitch response.result {case .success(let members):print("Successfully fetched \(members.count) members")completion(members)case .failure(let error):print("Error in NetworkManager.fetchRoster: \(error)") } }}
We can do the same exact thing using URLSession (only steps after Step 3 are different):
// 1. Create the functionfuncfetchRoster(completion: @escaping ([Member]) ->Void) {// 2. Specify the endpointlet endpoint ="<Enter URL String Here>"// 3. Create a decoderlet decoder =JSONDecoder()// decoder.dateDecodingStrategy = .iso8601 // Only if needed// decoder.keyDecodingStrategy = .convertFromSnakeCase // Only if needed// 4. Create a URLRequestvar urlRequest =URLRequest(url: URL(string: endpoint)!) urlRequest.httpMethod ="GET"// 5. Create and resume the task URLSession.shared.dataTask(with: urlRequest) { data, response, error in// 6. Handle the response (you can also do something with `response`)guard error ==nil, let data else {print("Error in NetworkManager.fetchRoster: \(error)")return }iflet members =try? decoder.decode([Member].self, from: data) {completion(members) } } .resume()}
POST Requests
Let’s take a look at a POST request using Alamofire:
// 1. Create the functionfuncaddToRoster(member: Member, completion: @escaping (Member) ->Void) {// 2. Specify the endpointlet endpoint ="<Enter URL String Here>"// 3. Define the request bodylet parameters: Parameters = ["name": member.name,"subteam": member.subteam,"position": member.position ]// 4. Create a decoderlet decoder =JSONDecoder()// decoder.dateDecodingStrategy = .iso8601 // Only if needed// decoder.keyDecodingStrategy = .convertFromSnakeCase // Only if needed// 5. Create the request AF.request(endpoint, method: .post, parameters: parameters, encoding: JSONEncoding.default) .validate() .responseDecodable(of: Member.self, decoder: decoder) { response in// 5. Handle the responseswitch response.result {case .success(let member):print("Successfully added member \(member.name)")completion(member)case .failure(let error):print("Error in NetworkManager.addToRoster: \(error.localizedDescription)") } }}
If we were to use URLSession, we need to:
Change the httpMethod property of the URLRequest to "POST".
Call the setValue function with ("application/json", forHTTPHeaderField: "Content-Type").
Set the httpBody property of the URLRequest.
The code below uses JSONSerialization to serialize the dictionary parameters to JSON and use it as the request body.
Alternatively, we can also use a JSONEncoder to encode our object into JSON, but it won’t be shown here.
// 1. Create the functionfuncaddToRoster(member: Member, completion: @escaping (Member) ->Void) {// 2. Specify the endpointlet endpoint ="<Enter URL String Here>"// 3. Define the request bodylet parameters = ["name": member.name,"subteam": member.subteam,"position": member.position ]guardlet httpBody =try? JSONSerialization.data( withJSONObject: parameters, options: [])else { return }// 4. Create a decoderlet decoder =JSONDecoder()// decoder.dateDecodingStrategy = .iso8601 // Only if needed// decoder.keyDecodingStrategy = .convertFromSnakeCase // Only if needed// 5. Create a URLRequestvar urlRequest =URLRequest(url: URL(string: endpoint)!) urlRequest.httpMethod ="POST" urlRequest.setValue("application/json", forHTTPHeaderField:"Content-Type") urlRequest.httpBody = httpBody// 6. Create and resume the task URLSession.shared.dataTask(with: urlRequest) { data, response, error in// 7. Handle the response (you can also do something with `response`)guard error ==nil, let data else {print("Error in NetworkManager.addToRoster: \\(error)")return }iflet member =try? decoder.decode(Member.self, from: data) {completion(member) } } .resume()}
Note that we do not have to create the decoder inside of the function call. We can create a JSONDecoder object and store it as a property in our NetworkManager class to be used for all functions. The same can be applied to the endpoint variable containing a String value of our endpoint.