Di sini saya mengekstrak data sebagai DataModel. Tetapi saya ingin membuat kelas ini generik dan meneruskan model itu sendiri sehingga saya dapat menggunakannya untuk mem-parsing data dari beberapa API. Adakah yang Bisa Membantu?

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
        }
    }
}

3
Neeraj Gupta 5 Januari 2021, 13:09

3 jawaban

Jawaban Terbaik

Dengan tipe Result yang nyaman, Anda dapat menulis metode generik yang cukup kecil, metode ini mengembalikan tipe yang didekodekan jika berhasil dan kesalahan apa pun saat gagal

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()
}

Catatan: Pembukaan paksa data! 100% aman jika tidak terjadi kesalahan


Sadarilah bahwa Anda harus menentukan tipe konkret ketika Anda akan memanggil metode

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)
    }
}
3
vadian 5 Januari 2021, 11:04

Anda dapat menambahkan batasan tipe generik (disebut Model) yang sesuai dengan Decodable seperti di bawah ini:

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()
    }
}

Pemakaian

struct SampleModel: Decodable {
    let name: String
}

NetworkManager().fetchData(url: "") { (data: SampleModel?) in
    print(data)
}
1
son 5 Januari 2021, 10:39

Anda dapat menulis fungsi umum untuk mengambil data seperti ini:

    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()
    }
}

Saya kira Anda memiliki model data, jika belum, Anda harus membuat untuk setiap objek Anda. Juga dengan menggunakan URL dummy saya akan membuat permintaan dan mengambil JSON termasuk beberapa nama pengguna dan id dengan format JSON. Mari kita definisikan model data untuk ini:

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

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

Akhirnya Anda akan mengurai data dan mencetak seperti ini:

Rob
Matt
Vadian
1
zeytin 5 Januari 2021, 13:34