我正在尝试设置一个名为allCountries的已发布变量。在我的DispatchQueue中,正在设置它,实际上是在向控制台输出正确的信息。在我的内容视图中,数组中什么都没有。在内容视图的init函数中,我正在调用将获取数据并设置变量的函数,但是当我打印到控制台时,该变量为空白。有人可以告诉我我在做什么错吗?
这里是我获取数据的地方
class GetCountries: ObservableObject {
@Published var allCountries = [Countries]()
func getAllPosts() {
guard let url = URL(string: "http://test.stonhard.com/umbraco/api/products/getlanguages")
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)")}}}
问题出现在init
的ContentView
中。您正在调用TYPE的函数getAllPosts
,但随后尝试访问该类型countries2
的新INSTANCE的数据。您的countries2.allCountries
实例属性实际上与GetCountries.allCountries
类型属性没有关系。
甚至可以编译吗?如果要在Type本身而不是在其实例上调用它们,通常需要将类的函数和属性定义为static
。
也许这很奇怪,因为您正在尝试在ContentView
的init中进行此操作,而不是在初始化后进行。
可能需要做两件事来解决这个问题。
第一步是让您的自定义初始化程序实际初始化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)")}}}
但是我不确定您是否会在ContentView
初始化程序中遇到异步调用问题。最好是将其移动到加载视图时调用的函数中,而不是在初始化SwiftUI结构时调用该函数。
为此,将网络调用移到ContentView
的功能中(而不是init
的功能),然后对视图使用.onAppear
修饰符,当NavigationView
出现时,该修饰符将调用一个功能。 (现在,我们不使用自定义初始化程序,我们需要返回到初始化countries2
属性。)
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
}
}
注意这种方法。根据您的UI架构,每次出现NavigationView时,它将调用getTheCountries
。如果您对新视图进行NavigationLink
调用,那么如果您返回此第一页,则可能会重新加载该视图。但是您可以轻松地在getTheCountries
中添加检查,以查看是否确实需要执行工作。
应该使用同一实例来发起请求并读取结果,因此这是固定的变体:
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 !!
}
}