Thank you all!!!
@Fly0Strich
The async / await is part of the solution, but the trick is that the func that loads the JSON returns before the json parameter is updated.
@Obelix
Your guidance and hints did the trick! Code (simplified) of my Model and How I used it as attached for future adventurers!
- An optional URL var
- That calls a function in a Task once var has a value. Note: It didn't work without the Task.
- A call into an async func in the view to update the optional var
@nathanlott
I tried your approach, but didn't manage to complete, I guess I was implementing it wrong...
For all below is my Model and the call in the View
Model:
@MainActor class MyModel: ObservableObject {
@Published var myArray = [ArrayStruct]()
let storageRef = storage.reference() //Needed for my FirebaseStorage
//The URL to the JSON to be decoded.
var jsonUrl: URL? {
didSet {
Task {
await loadData(withUrl: jsonUrl)
}
}
}
//Get the URL of my JSON, I am using FirebaseStorage
func getJSONUrl() async {
storageRef.child("MyJSON.json").downloadURL(completion: { url, error in
guard let url = url, error == nil
else {
print(error!.localizedDescription)
return
}
self.jsonUrl = url
})
}
//Load the data into myArray
func loadData(withUrl: URL?) async {
guard let url = withUrl
else {
print("Invalid JSON Url")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
myArray = try JSONDecoder().decode([ArrayStruct].self, from: data)
} catch {
print("Invalid data")
}
}
}
struct ArrayStruct: Codable, Identifiable {
let id: String
let description: String?
}
Call on the View:
struct ContentView: View {
@StateObject var myModel = MyModel() //An instance of our Class
var body: some View {
Zstack {
//My view here..
}
.task {
await myModel.getJSONUrl() //Loads the JSON url and subsequent LoadData()
}
}
}