首页 > 解决方案 > 从“获取”到“显示”JSON API 数据的转变 Swift Node.js

问题描述

我了解如何从 JSON API(实际上是我的本地服务器)“获取”数据,但我应该如何考虑从仅拥有数据到在视图中显示数据的管道?我直观地想做的是从获取函数中“返回”数据,尽管我知道这不是 Swift URL 函数操作的范例。我的想法是,如果我可以“返回”数据(作为结构),那么将很容易传递到可视化视图中。

示例代码:

这是获取的 JSON 的结构以及我想要传递给视图的变量类型。

struct User: Codable {
    let userID: String
    let userName: String
    let firstName: String
    let lastName: String
    let fullName: String
}

我希望 printUser 函数可以返回而不是打印成功的获取。

func printUser() {
    fetchUser { (res) in
        switch res {
        case .success(let user):
            print(user.userName) // return here? 
                              // I know it won't work, but that's what seems natural
        case .failure(let err):
            print("Failed to fetch user: ", err)
        }
    }
}

func fetchUser(completion: @escaping (Result<User, Error>) -> ()) {
    let urlString = "http://localhost:4001/user"
    guard let url = URL(string: urlString) else { return }
    URLSession.shared.dataTask(with: url) { (data, resp, err) in
    
        if let err = err {
            completion(.failure(err))
            return
        }
    
        do {
            let user = try JSONDecoder().decode(User.self, from: data!)
            completion(.success(user))
        
        } catch let jsonError {
            completion(.failure(jsonError))
        }

    }.resume()
}

将采用用户结构的示例视图

struct DisplayUser: View {
    var user: User
    var body: some View {
        Text(user.userID)
        Text(user.userName)
        Text(user.lastName)
        Text(user.firstName)
        Text(user.fullName)
    }
}

标签: jsonswiftapiurl

解决方案


你不能只是“返回”的原因是你fetchUser异步的。这意味着它可能会相对即时地返回,或者可能需要很长时间(或根本没有完成)。因此,您的程序需要准备好应对这种可能性。当然,正如您所说,“很容易进入可视化视图”,但不幸的是,它不符合实际情况。

您可以做的(在您的示例中)将其设置User为 Optional - 这样,如果尚未设置,您可以显示某种加载视图,如果设置(即您的异步函数已返回值),您可以显示它。看起来像这样:

class ViewModel : ObservableObject {
  @Published var user : User? //could optionally store the entire Result here

  func runFetch() {
    fetchUser { (res) in
        switch res {
        case .success(let user):
            DispatchQueue.main.async {
               self.user = user
            }
        case .failure(let err):
            print("Failed to fetch user: ", err)
            //set an error message here? Another @Published variable?
        }
    }
  }

  func fetchUser(completion: @escaping (Result<User, Error>) -> ()) {
    //...
  }
}

struct DisplayUser: View {
    @StateObject private var viewModel = ViewModel()

    var body: some View {
        VStack {
          if let user = viewModel.user {
            Text(user.userID)
            Text(user.userName)
            Text(user.lastName)
            Text(user.firstName)
            Text(user.fullName)
          } else {
             Text("Loading...")
          }
        }.onAppear {
          viewModel.fetchUser()
        }
    }
}

注意:如果这是我的程序,我可能会重构异步内容以使用组合,但这是个人偏好问题


推荐阅读