在SwiftUI中从服务器下载数据的最佳实践

时间:2019-06-20 16:46:18

标签: swiftui

我的问题是一个概念性问题。

我有以下代码:

struct CategoriesList : View {

    @State private var categories: [CategoryMetadata] = []

    var body: some View {
        NavigationView {
            List(self.categories) { category in
                NavigationButton(destination: PostsList(category: category)) {
                    CategoryRow(category: category)
                }
            }
        }.navigationBarTitle(Text("Categorie"))
    }
}

那只是使用元素(类别)列表来构成我的UI,并且我具有与视图关联的状态,该状态会在视图更改时自动更新。

一切都很好,但对我来说尚不清楚世卫组织应触发网络请求以填充模型

我了解绑定和@state的想法,但是,总的来说,我应该如何构建代码以在开始时具有所需的模型?

在旧方法中,我会在viewDidLoad中实现此行为,但是使用SwiftUI的新范例,什么是获取数据的最佳方法?

3 个答案:

答案 0 :(得分:2)

SwiftUI社区尚未真正建立任何最佳实践,因为该技术太新了。我的答案基于我从不同的WWDC19会话中看到的内容。

首先,创建一个具有BindableObject属性的categories。然后编写您的网络请求代码,并将self.categories设置为新下载的类别。

import SwiftUI
import Combine

final class CategoryStore: BindableObject {
    var didChange = PassthroughSubject<Void, Never>()

    var categories = [String]()

    init() {
        // TODO: Fetch categories from API
        self.categories = ["A", "B", "C"]
    }
}

然后,将CategoryStore添加到View并将其与List一起使用以遍历类别。

import SwiftUI

struct ContentView : View {
    @ObjectBinding private var store = CategoryStore()

    var body: some View {
        List(store.categories.identified(by: \.self)) { category in
            Text(category)
        }
    }
}

只要categories属性更新,您的UI就会更新为新类别(类型Combine

答案 1 :(得分:1)

您可以看看我的答案here

基本上,您创建一个符合BindableObject的模型对象:

class LoginModel : BindableObject {

    var didChange = PassthroughSubject<LoginModel, Never>()

    private(set) var username: String? {
        didSet {
            didChange.send(self)
        }
    }

    func load() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
            self.username = "Sorin"
        }
    }
}

此示例通过使用普通ol'asyncAfter模拟一个异步服务器调用。

然后,视图与之链接,并且在模型更改时会自动更新。

public struct LoginScreen: View {

    @ObjectBinding var loginObject = LoginModel()

    public var body: some View {
        Group {
            if login.username == nil {
                Text("Trying to login, please wait...")
            } else {
                Text("Successful login, the username is \(loginObject.username!)")
            }
        }.onAppear {
            self.loginObject.load()
        }
    }
}

这里的关键是要避免使View执行与Model相关的任何事情,除了显示它。 SwiftUI会一路抵抗您:-)

答案 2 :(得分:1)

iOS开发人员似乎不像Mac OS开发人员那样熟悉此思想,在Mac应用程序中,我们将控制器层分为ViewController和ModelControllers,ViewController负责视图和模型之间的同步,ModelController负责管理模型的归档等,因此SwiftUI取消了ViewControllers,但是如果您具有网络处理功能,那么那在ModelControllers有用的地方,它们就可以处理您的远程源和模型之间的同步,这就是我目前正在做的我正在工作的一个示例应用程序,尽管我一直在想是否也可以使用Combine替换它,这将是我接下来要尝试的事情。