【问题标题】:The transition from "fetching" to "displaying" JSON API data Swift Node.js从“获取”到“显示”JSON API 数据的转变 Swift Node.js
【发布时间】:2021-12-16 14:49:24
【问题描述】:

我了解如何从 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)
    }
}

【问题讨论】:

    标签: json swift api url


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 感谢您的回答以及您回答的许多其他问题。总体而言,Stack Overflow 非常有用,但您应该得到个人认可。
    猜你喜欢
    • 1970-01-01
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多