Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

XCode Swift httpsession response handling

I have a file for handling http post requests to a NestJS api. My website works great with it, it accepts any response from the api. But in xcode, i have to hardcode the response. The website can accept a bool response, but xcode only accepts json as i know.

Can someone correct me, and tell me, if you can make xcode accept any response from the api. Thanks!

HttpPOST.swift

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import Foundation
func apiCall(url: String, body: Dictionary<String, AnyHashable>, completion: @escaping (Response) -> Void){
    
    guard let url = URL(string: "http://192.168.1.71:3000/user/login")else{
        return
    }
    
    var request = URLRequest(url: url)
    // method, body, headers
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    let body: [String: AnyHashable] = body
    request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
    
    // make rqs
    let task = URLSession.shared.dataTask(with: request){ data, response, error in
        guard let data = data,error == nil else {
            return
        }
        
        do {
            let response = try JSONDecoder().decode(Response.self, from: data)
            completion(response)
        }
        
        catch {
            print(error)
        }
        
    }
    
    task.resume()
}

struct Response: Codable {
    var usernameError: String?;
    var passwordError: String?;
    var logOk: Bool?;
    var token: String?;
}

Response example

Response(usernameError: Optional("enteruname"), passwordError: Optional("enterpasswd"), logOk: Optional(false), token: nil)

>Solution :

Make the return type generic to be able to return any Decodable type even a single Bool value.

This is a modern version of your code, it throws any possible error

func apiCall<T: Decodable>(urlString: String = "http://192.168.1.71:3000/user/login", body: [String:AnyHashable]) async throws -> T {
    
    guard let url = URL(string: urlString) else { throw URLError(.badURL) }
    
    var request = URLRequest(url: url)
    // method, body, headers
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try JSONSerialization.data(withJSONObject: body)
    
    // make rqs
    let (data, _) = try await URLSession.shared.data(for: request)
    return try JSONDecoder().decode(T.self, from: data)
}

And use it

Task {
    do {
        let result : Response = try await apiCall(body: ["Foo":"Bar"])
    } catch {
        print(error)
    }
}

Edit:

To distinguish the success and failure cases declare Response as enum with associated values. It returns the token on success and the error messages on failure

enum Response: Decodable {
    case success(String), failure((String,String))
    
    private enum CodingKeys: String, CodingKey { case usernameError, passwordError, logOk, token}
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let ok = try container.decode(Bool.self, forKey: .logOk)
        if ok {
            self = .success(try container.decode(String.self, forKey: .token))
        } else {
            
            let usernameError = try container.decodeIfPresent(String.self, forKey: .usernameError) ?? ""
            let passwordError = try container.decodeIfPresent(String.self, forKey: .passwordError) ?? ""
            self = .failure((usernameError, passwordError))
        }
    }
}

and change the calling code to

Task {
    do {
        let result : Response = try await apiCall(body: ["Foo":"Bar"])
        switch result {
            case .success(let token): print(token)
            case .failure(let usernameError, let passwordError): print(usernameError, passwordError)
        }
    } catch {
        print(error)
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading