SwiftUI:寻找映射API响应的最佳方法

时间:2019-11-09 09:27:14

标签: swift swiftui

我开始学习SwiftUI。现在,我正在考虑在ObservableObject中映射API响应的最佳方法。

首先,这是API响应:

{
    "food": [
        "fish",
        "meat"
    ],
    "go": [
        "London",
        "Bangkok"
    ],
    "party": [
        "Family",
        "Friends"
    ]
}

这是我的ObservableObject

struct MyViewModel: ObservableObject {

    var food: [String]
    var go: [String]
    var party: [String]

    func fetchTagMeResponse() {
        let url = URL(string: "domain.com/api/tagmes/")
        var result = nil
        URLSession.shared.dataTask(with: url!) { (data, res, err) in
            DispatchQueue.main.async {
               // WHAT IS THE BEST WAY TO MAP IT?
            }
        }.resume()
        return result
    }

}

如您所见,在代码行中,我需要最好的方法来从API获取数据响应

DispatchQueue.main.async {
   // WHAT IS THE BEST WAY TO MAP IT?
}

我的解决方案

我正在考虑使用Decodable,但我认为这不是最好的方法。因为如果我不再使用MyResponse。没必要吗?

struct MyResponse: Decodable {
   var food: [String]
   var go: [String]
   var party: [String]
}

如果你们对此有更好的解决方案,请告诉我。

2 个答案:

答案 0 :(得分:1)

这是我的NetworkManager类,对我来说,这是在SwiftUI中处理API的最佳方法

class NetworkManager: ObservableObject {
    var objectWillChange = PassthroughSubject<NetworkManager,Never>()

    var courses = [Course](){
        didSet{
            objectWillChange.send(self)
        }
    }

    init() {
        guard let url = URL(string: "https://myDomain/courses") else{ return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in

            guard let data = data else { return }
            let courses = try! JSONDecoder().decode([Course].self , from: data)

            DispatchQueue.main.async {
                self.courses =  courses

                debugPrint(self.courses)
            }
        }.resume()
    }
}

//这就是我绑定数据的方式。

struct ContentView: View {
    @ObservedObject var networkManager = NetworkManager()


    var body: some View {
        NavigationView {
            List {
                ForEach(networkManager.courses, id: \.id) { course in
                    RowUI(course: course)
                }
            }
            .navigationBarTitle(Text("Courses"))
        }
    }
}

答案 1 :(得分:1)

您可以使用Combine来这样做。顺便说一句,最好使用Apple生产的Codable来为您减轻辛苦。这是一个功能强大的工具。

import SwiftUI
import Combine

//MARK: - Your model
struct MyResponse: Identifiable, Codable {
  let id: Int
  var food: [String]
  var go: [String]
  var party: [String]
}

//MARK: - Your network manager
class NetworkManager: ObservableObject {

  @Published var myResponseFood = [String]()
  @Published var myResponseGo = [String]()
  @Published var myResponseParty = [String]()

  func fetchTagMeResponse() {
    _ = URLSession.shared
      .dataTaskPublisher(for: URL(string: "domain.com/api/tagmes/")!)
      .map(\.data)
      .decode(type: MyResponse.self, decoder: JSONDecoder())
      .sink(receiveCompletion: { (completion) in
        if case .failure(let error) = completion {
          print("You have an error: \(error)")
        }
      }, receiveValue: { (object) in
        //MARK: - Your map objects are here
        self.myResponseFood = object.food
        self.myResponseGo = object.go
        self.myResponseParty = object.party
      })
  }
}

//MARK: - Your SwiftUI view
struct MyViewModel: View {

  @ObservedObject var networkManager = NetworkManager()

  var body: some View {
    NavigationView {
      List(networkManager.myResponseParty) { party in
        Text(party)
      }
    }
    .onAppear {
      self.networkManager.fetchTagMeResponse()
    }
  }
}