SwiftUI发布的变量未更新

时间:2020-05-17 23:37:25

标签: swift swiftui swift5

我正在尝试设置一个名为allCountries的已发布变量。在我的DispatchQueue中,正在设置它,实际上是在向控制台输出正确的信息。在我的内容视图中,数组中什么都没有。在内容视图的init函数中,我正在调用将获取数据并设置变量的函数,但是当我打印到控制台时,该变量为空白。有人可以告诉我我在做什么错吗?

这是我要提取数据的地方

class GetCountries: ObservableObject {

@Published var allCountries = [Countries]()

func getAllPosts() {

    guard let url = URL(string: "apiURL")
        else {
            fatalError("URL does not work!")
    }

    URLSession.shared.dataTask(with: url) { data, _, _ in
        do {
            if let data = data {
                let posts = try JSONDecoder().decode([Countries].self, from: data)


                DispatchQueue.main.async {

                    self.allCountries = posts
                    print(self.allCountries)

                }
            } else  {
                DispatchQueue.main.async {
                    print("didn't work")
                }
            }
        }
        catch {
            DispatchQueue.main.async {
                print(error.localizedDescription)
            }
        }

    }.resume()
}

}

这是我的内容视图

struct ContentView: View {

var countries = ["Select Your Country", "Canada", "Mexico", "United States"]

@ObservedObject var countries2 = GetCountries()

@State private var selectedCountries = 0

init() {
    GetCountries().getAllPosts()
    print(countries2.allCountries)

}

var body: some View {
    NavigationView {

        ZStack {

            VStack {
                Text("\(countries2.allCountries.count)")}}}

2 个答案:

答案 0 :(得分:0)

问题发生在您的init的{​​{1}}中。您正在调用TYPE的函数ContentView,但随后尝试访问该类型getAllPosts的新INSTANCE的数据。您的countries2实例属性实际上与countries2.allCountries类型属性没有任何关系。

它甚至可以编译吗?如果要在Type本身而不是在其实例上调用它们,通常需要将类的函数和属性定义为GetCountries.allCountries

也许这很奇怪,因为您正在尝试在static的init中进行此操作,而不是在初始化后进行此操作。

您可能需要做两件事来解决此问题。

第一步是让您的自定义初始化程序实际初始化ContentView

第二步是在该自定义初始化程序中调用countries2,它将填充您尝试在countries2.getAllPosts中访问的实例属性。

尝试一下:

countries2.allCountries

但是我不确定您是否会在struct ContentView: View { var countries = ["Select Your Country", "Canada", "Mexico", "United States"] // You'll initialize this in the custom init @ObservedObject var countries2: GetCountries @State private var selectedCountries = 0 init() { countries2 = GetCountries() countries2.getAllPosts() print(countries2.allCountries) } var body: some View { NavigationView { ZStack { VStack { Text("\(countries2.allCountries.count)")}}} 初始化程序中遇到异步调用问题。最好是将其移动到加载视图时调用的函数中,而不是在初始化SwiftUI结构时调用该函数。

为此,您可以将网络调用移至ContentView(而不是其ContentView)的函数中,然后对视图使用init修饰符,该修饰符将在以下情况下调用该函数.onAppear出现。 (现在,我们不使用自定义初始化程序,我们需要返回到初始化NavigationView属性。)

countries2

请注意这种方法。根据您的UI架构,每次NavigationView出现时,它将调用struct ContentView: View { var countries = ["Select Your Country", "Canada", "Mexico", "United States"] // We're initializing countries2 again @ObservedObject var countries2 = GetCountries() @State private var selectedCountries = 0 // This is the function that will get your country data func getTheCountries() { countries2.getAllPosts() print(countries2.allCountries) } var body: some View { NavigationView { ZStack { VStack { Text("\(countries2.allCountries.count)") } } } .onAppear(perform: getTheCountries) // Execute this when the NavigationView appears } } 。如果您对新视图进行getTheCountries调用,那么如果您返回第一页,则可能会重新加载该视图。但是您可以在NavigationLink内轻松添加支票,以查看是否确实需要这项工作。

答案 1 :(得分:0)

应该使用一个相同的实例来发起请求并读取结果,因此这是固定的变体:

struct ContentView: View {

    var countries = ["Select Your Country", "Canada", "Mexico", "United States"]

    @ObservedObject var countries2 = GetCountries()   // member
    @State private var selectedCountries = 0

    init() {
        // GetCountries().getAllPosts()      // [x] local variable
        // print(countries2.allCountries)
    }

    var body: some View {
        NavigationView {
            ZStack {
                VStack {
                    Text("\(countries2.allCountries.count)")
                }
            }
        }
        .onAppear { self.countries2.getAllPosts() } // << here !!
    }
}