简体   繁体   English

如何使我的网络 class 在 Swift 中通用?

[英]How can i make my Networking class generic in Swift?

Here i am extracting data as DataModel .在这里,我将数据提取为DataModel But i want to make this class generic and pass the model myself so that i can use it to parse data from multiple API's.但我想让这个 class 通用并自己传递 model 以便我可以使用它来解析来自多个 API 的数据。 Can Anyone Help?任何人都可以帮忙吗?

import Foundation

struct NetworkManager {
    func fetchData(url : String, completion : @escaping (DataModel?) -> ()) {
        print("Neeraj here")
        let sessionURL = URL(string: url)
        let session = URLSession(configuration: .default)
        let dataTask = session.dataTask(with: sessionURL!) { (data, response, error) in
            if error == nil {
                if let safeData = data {
                    if let parsedData = self.parseData(data : safeData) {
                        print("got data")
                        completion(parsedData)
                    }
                    else {
                        debugPrint("failed to fetch data")
                        completion(nil)
                    }
                }
            }
            else {
                print("error in data task is \(String(describing: error))")
                completion(nil)
            }
        }
        dataTask.resume()
        
    }
    
    func parseData(data : Data) -> DataModel? {
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode(DataModel.self, from: data)
            return decodedData
        } catch {
            print("error while parsing data \(error)")
            return nil
        }
    }
}

With the convenient Result type you can write a quite tiny generic method, it returns the decoded type on success and any error on failure使用方便的Result类型,您可以编写一个非常小的泛型方法,它在成功时返回解码类型,在失败时返回任何错误

func fetchData<T: Decodable>(urlString: String, completion: @escaping (Result<T,Error>) -> Void) {
    guard let url = URL(string: urlString) else { return } // or throw an error
    URLSession.shared.dataTask(with: url) { (data, _, error) in
        if let error = error { completion(.failure(error)); return }
        completion( Result{ try JSONDecoder().decode(T.self, from: data!) })
    }.resume()
}

Note: Force unwrapping data!注意:强制解包data! is 100% safe if no error occurs如果没有发生错误,是 100% 安全的


Be aware that you have to specify the concrete type when you are going to call the method请注意,当您要调用该方法时,您必须指定具体类型

fetchData(urlString: "https://example.com/api") { (result : Result<MyModel,Error>) in
    switch result {
        case .success(let model): print(model)
        case .failure(let error): print(error)
    }
}

You can add a generic type constraint (called Model ) which conforms Decodable like below:您可以添加符合Decodable的通用类型约束(称为Model ),如下所示:

struct NetworkManager {

    func fetchData<Model: Decodable>(url : String, completion : @escaping (Model?) -> ()) {
        let sessionURL = URL(string: url)
        let session = URLSession(configuration: .default)
        let dataTask = session.dataTask(with: sessionURL!) { (data, response, error) in
            if error == nil {
                if let safeData = data {
                    do {
                        let decodedData = try JSONDecoder().decode(Model.self, from: safeData)
                        completion(decodedData)
                    } catch {
                        print("error while parsing data \(error)")
                    }
                } else {
                    debugPrint("failed to fetch data")
                    completion(nil)
                }
            }
            else {
                print("error in data task is \(String(describing: error))")
                completion(nil)
            }
        }
        dataTask.resume()
    }
}

Usage用法

struct SampleModel: Decodable {
    let name: String
}

NetworkManager().fetchData(url: "") { (data: SampleModel?) in
    print(data)
}

You can write a generic function to fetch data like this one:您可以编写一个通用的 function 来获取这样的数据:

    func fetchGenericData<T: Decodable>(urlString: String, completion: @escaping (T) -> ()) {
        let url = URL(string: urlString)
        URLSession.shared.dataTask(with: url!) { (data, resp, err) in
            if let err = err {
                print("Failed to fetch data:", err)
                return
            }
            
            guard let data = data else { return }
            
            do {
                let obj = try JSONDecoder().decode(T.self, from: data)
                completion(obj)
            } catch let jsonErr {
                print("Failed to decode json:", jsonErr)
            }
            }.resume()
    }
}

I suppose that you have a data model, if you have not, you should create for your every object.我想你有一个数据 model,如果你没有,你应该为你的每个 object 创建。 Also by using a dummy URL i will make a request and fetch the JSON includes some users name and ids with JSON format.此外,通过使用虚拟 URL 我将发出请求并获取 JSON 包括一些用户名和 id 格式为 JSON。 Let`s define a data model for this:让我们为此定义一个数据 model:

struct StackUser: Decodable {
      let id: Int
      let name: String
  }

fetchGenericData(urlString: "https://api.stackoverexample.com/stackusers") { (stackUsers: [StackUser]) in
     stackUsers.forEach({print($0.name)})
}
        

Finally you will be parse the data and prints like this:最后,您将解析数据并打印如下:

Rob
Matt
Vadian

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM