从"fetching"到"displaying"的过渡JSONAPI数据SwiftNode.js

The transition from "fetching" to "displaying" JSON API data Swift Node.js

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

示例代码:

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

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

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

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

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

你可以做的(在你的例子中)是将 User 设置为可选——这样,如果它没有被设置,你可以显示某种加载视图,如果它 已经设置了(即你的async函数已经returned了一个值),你可以显示它。那看起来像这样:

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,但这是个人偏好问题