Search code examples
swiftnsurlrequesturlsession

Swift - Multiple URL Request - Code To Refactor and To Reuse


I'm new to Swift and I am trying to refactor my URL Post requests. I have multiple URL POST requests inside the same View Controller like this. Everything works fine but it seems to me that there is a lot of repetitive code that could be reused. Particularly, I don't know how to pass/handle different Data Models that should be used in parseRequest1 and parseRequest2. I also read that there should be only one session used for URL requests within the same project. Any help would be greatly appreciate it!

func request1() {        
    let parameters = [...//some parameters to send]
    guard let url = URL(string: "https//www.....") else {return}
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: []) 
       else {
         print("Error")
         return
       }
    request.httpBody = parametersToSend
    let session = URLSession.shared
    session.dataTask(with: request) { (data, response, error) in
       if let safeData = data {
          self.parseRequest1(data: safeData)
        }
   }.resume()
}

func parseRequest1(data: Data){
    let decoder = JSONDecoder()
    do{
      let decodedData = try decoder.decode(DataModelForRequest1.self, from: data)
        DispatchQueue.main.async {
           self.performAction1(request1Result)
        }
    } catch {
       print(error)
    }
} 

Then I have another URL request request2 which is almost identical except the parameters, and model to be used for decoding and action inside parseRequest2.

func request2() {        
        let parameters = [...//some parameters to send]
        guard let url = URL(string: "https//www.....") else {return}
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: []) 
           else {
             print("Error")
             return
           }
        request.httpBody = parametersToSend
        let session = URLSession.shared
        session.dataTask(with: request) { (data, response, error) in
           if let safeData = data {
              self.parseRequest2(data: safeData)
            }
       }.resume()
    }

    func parseRequest2(data: Data){
        let decoder = JSONDecoder()
        do{
          let decodedData = try decoder.decode(DataModelForRequest2.self, from: data)
            DispatchQueue.main.async {
               self.performAction2(request2Result)
            }
        } catch {
           print(error)
        }
    } 

Solution

  • The only differences seem to be:

    • request parameters
    • type of model returned
    • the action you do after the response is received

    This means that we can write this as one single method taking the above three values as parameters:

    func request<T: Codable>(modelType: T.Type, parameters: [String: Any], completion: (T) -> Void) {
    
        func parseResponse(data: Data){
            let decoder = JSONDecoder()
            do{
              let decodedData = try decoder.decode(T.self, from: data)
                DispatchQueue.main.async {
                  completion(decodedData)
                }
            } catch {
               print(error)
            }
        } 
    
        guard let url = URL(string: "https//www.....") else {return}
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: []) 
           else {
             print("Error")
             return
           }
        request.httpBody = parametersToSend
        let session = URLSession.shared
        session.dataTask(with: request) { (data, response, error) in
           if let safeData = data {
              parseResponse(data: safeData)
            }
       }.resume()
    }
    

    You can then call this method with the appropriate parameters as per your needs.