gpt4 book ai didi

ios - 使用 URLSession 为 SwiftUI View 加载 JSON 数据

转载 作者:行者123 更新时间:2023-12-01 15:33:30 24 4
gpt4 key购买 nike

构建 SwiftUI 应用程序的网络层的行之有效的方法是什么?具体来说,您如何使用 URLSession 来构建要在 SwiftUI View 中显示的 JSON 数据并处理从 iOS 13.4 开始可以正确发生的所有不同状态?

最佳答案

这是我在上一个项目中想到的:

  • 将加载过程表示为 ObservableObject模型类
  • 使用 URLSession.dataTaskPublisher用于装载
  • 使用 CodableJSONDecoder使用 Combine support for decoding 解码对 Swift 类型的响应
  • 跟踪模型中的状态作为@Published 属性,以便 View 可以显示加载/错误状态。
  • 将加载的结果作为 @Published 属性记录在单独的属性中,以便在 SwiftUI 中使用(您也可以使用 View#onReceive 直接在 SwiftUI 中订阅发布者,但将发布者封装在模型类中总体上看起来更干净)
  • 使用 SwiftUI .onAppear如果尚未加载,则触发加载的修饰符。
  • 使用 .overlay修饰符可以方便地根据状态显示进度/错误 View
  • 为重复发生的任务提取可重用的组件(这里是一个例子:EndpointModel)

  • 该方法的独立示例代码(也可在我的 SwiftUIPlayground 中找到):

    // SwiftUIPlayground
    // https://github.com/ralfebert/SwiftUIPlayground/

    import Combine
    import SwiftUI

    struct TypiTodo: Codable, Identifiable {
    var id: Int
    var title: String
    }

    class TodosModel: ObservableObject {

    @Published var todos = [TypiTodo]()
    @Published var state = State.ready

    enum State {
    case ready
    case loading(Cancellable)
    case loaded
    case error(Error)
    }

    let url = URL(string: "https://jsonplaceholder.typicode.com/todos/")!
    let urlSession = URLSession.shared

    var dataTask: AnyPublisher<[TypiTodo], Error> {
    self.urlSession
    .dataTaskPublisher(for: self.url)
    .map { $0.data }
    .decode(type: [TypiTodo].self, decoder: JSONDecoder())
    .receive(on: RunLoop.main)
    .eraseToAnyPublisher()
    }

    func load() {
    assert(Thread.isMainThread)
    self.state = .loading(self.dataTask.sink(
    receiveCompletion: { completion in
    switch completion {
    case .finished:
    break
    case let .failure(error):
    self.state = .error(error)
    }
    },
    receiveValue: { value in
    self.state = .loaded
    self.todos = value
    }
    ))
    }

    func loadIfNeeded() {
    assert(Thread.isMainThread)
    guard case .ready = self.state else { return }
    self.load()
    }
    }

    struct TodosURLSessionExampleView: View {

    @ObservedObject var model = TodosModel()

    var body: some View {
    List(model.todos) { todo in
    Text(todo.title)
    }
    .overlay(StatusOverlay(model: model))
    .onAppear { self.model.loadIfNeeded() }
    }
    }

    struct StatusOverlay: View {

    @ObservedObject var model: TodosModel

    var body: some View {
    switch model.state {
    case .ready:
    return AnyView(EmptyView())
    case .loading:
    return AnyView(ActivityIndicatorView(isAnimating: .constant(true), style: .large))
    case .loaded:
    return AnyView(EmptyView())
    case let .error(error):
    return AnyView(
    VStack(spacing: 10) {
    Text(error.localizedDescription)
    .frame(maxWidth: 300)
    Button("Retry") {
    self.model.load()
    }
    }
    .padding()
    .background(Color.yellow)
    )
    }
    }

    }

    struct TodosURLSessionExampleView_Previews: PreviewProvider {
    static var previews: some View {
    Group {
    TodosURLSessionExampleView(model: TodosModel())
    TodosURLSessionExampleView(model: self.exampleLoadedModel)
    TodosURLSessionExampleView(model: self.exampleLoadingModel)
    TodosURLSessionExampleView(model: self.exampleErrorModel)
    }
    }

    static var exampleLoadedModel: TodosModel {
    let todosModel = TodosModel()
    todosModel.todos = [TypiTodo(id: 1, title: "Drink water"), TypiTodo(id: 2, title: "Enjoy the sun")]
    todosModel.state = .loaded
    return todosModel
    }

    static var exampleLoadingModel: TodosModel {
    let todosModel = TodosModel()
    todosModel.state = .loading(ExampleCancellable())
    return todosModel
    }

    static var exampleErrorModel: TodosModel {
    let todosModel = TodosModel()
    todosModel.state = .error(ExampleError.exampleError)
    return todosModel
    }

    enum ExampleError: Error {
    case exampleError
    }

    struct ExampleCancellable: Cancellable {
    func cancel() {}
    }

    }

    关于ios - 使用 URLSession 为 SwiftUI View 加载 JSON 数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61855811/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com