Swift - Reusable URL Request with Delegates

孤人 提交于 2020-03-23 11:59:05

问题


Hi I'm new to Swift and I am trying to create a reusable generic Download Manager for URL Request that can be reused throughout my project in different View Controllers or reused within the same VC for a different URL Request calls. The problem that I have is how do I pass the Data Type from the Request into the Download Manager and then return the Downloaded Data back to the VC with the corresponding Data Type. I am able to pass the Data Type in a call to downloadRequest but I can't figure out how to pass the Data Type back to the VC via a delegate DownloadManagerDelegate. Any help would be greatly appreciate it!

Generic Download Manager:

protocol DownloadManagerDelegate {
        func didUpdateData<T: Codable>(modelType: T.Type, downloadedData: T.Type)
}

struct DownloadManager {

    var delegate: DownloadManagerDelegate?

    func downloadRequest<T: Codable>(modelType: T.Type, parameters: [String: Any]) {
       guard let url = URL(string: "https://www.someAPI...") else {return}
       var request = URLRequest(url: url)
       request.httpMethod = "POST"
       request.addValue("application/json", forHTTPHeaderField: "Content-Type")
       guard let httpBodyWithParameters = try? JSONSerialization.data(withJSONObject: parameters, options: []) else 
         {
          print("error")
          return
         }

       request.httpBody = httpBodyWithParameters 
       let session = URLSession.shared
       session.dataTask(with: request) { (data, response, error) in
           if error != nil {
           print("error")
           return
           }

           if let safeData = data {
              if let downloadedData = parseDownloadedData(data: safeData) {
                  self.delegate?.didUpdateData(modelType: modelType, downloadedData: downloadedData)
               }
            }
        }.resume()

   func parseDownloadedData(data: Data) -> T?{
      let decoder = JSONDecoder()
      do {
         let decodedData = try decoder.decode(T.self, from: data)
         return decodedData
      } catch {
         print(error)
         return nil
        }
    } 

 }

Delegate in my VC:

override func viewDidLoad() {
  super.viewDidLoad()
  downloadManager.delegate = self
}


func didUpdateData(modelType: modelType,downloadedData:downloadedData){
   DispatchQueue.main.async {
     print(downloadedData)
   }
}

To call download downloadRequest:

downloadManager.downloadrequest(modeType: Type1.self, parameters: parameters)

The Data Model is defined as a struct:

   struct DataModel1: Codable {
       let ItemID: String
    }

Then in the same VC I call the same function downloadManager that will call a different API which should return data for a different Model Type (defined as Struct)

downloadManager.downloadRequest(modeType: Type2.self, parameters: parameters)

The Data Model is defined as a struct:

  struct DataModel2: Codable {
       let EmployeeeID: String
    }

回答1:


In the Swift times Protocol/Delegate smells a bit objective-c-ish.

I recommend a completion handler with the versatile Result type.

It returns the generic type non-optional on success and any error on failure.

The force unwrapping of data is safe because if error is nil then data has a value

struct DownloadManager {

    func downloadRequest<T: Decodable>(modelType: T.Type, parameters: [String: Any], completion : @escaping (Result<T, Error>) -> Void) {
        guard let url = URL(string: "https://www.someAPI...") else {return}
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        do {
            let httpBodyWithParameters = try JSONSerialization.data(withJSONObject: parameters)
            request.httpBody = httpBodyWithParameters
            let session = URLSession.shared
            session.dataTask(with: request) { (data, response, error) in
                if let error = error {
                    completion(.failure(error))
                } else {
                    completion( Result { try JSONDecoder().decode(T.self, from: data!)})
                }
            }.resume()
        } catch {
            completion(.failure(error))
        }
    }
}

And use it

downloadManager.downloadrequest(modeType: Type1.self, parameters: parameters) { result in 
    switch result {
        case .success(let data): print(data)
        case .failure(let error): print(error)
    }
}


来源:https://stackoverflow.com/questions/60579710/swift-reusable-url-request-with-delegates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!