TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: How to sequence two functions that both depend on network request.

Forums > SwiftUI

@Bnerd  

I am experimenting and I came up with the following challenge: Function A calls a JSON url location from a server Function B is supposed to decode the JSON found at the location identified by Function A.

How can I trigger Function B only when (not if) the Function A has returned a result?

Is it something that AFnetworking would handle? I guess yes, but I was wondering if I am missing something.

Thanks!

1      

I'm not very good with concurrency stuff, but isn't this what the await keyword is for?

https://www.hackingwithswift.com/swift/5.5/async-await

2      

BNerd poses a challenge:

Function A calls a JSON url location from a server
Function B will decode the JSON found at the location identified by Function A.

You have two tasks here. Both may take some time to complete.

One strategy to consider. Define an object that has a property named projectURL. This is an optional, so it is nil until you, or some background task, gives it a value.

Consider adding a property observer to the projectURL. This property will be nil until the first function completes its background task. Your first background task will decode and populate the projectURL property. Then it will (it should) contain a well formed URL.

Because the property changed, the property observer can fire off and bundle up a second task. This second task will attempt to grab data from the provided URL. It will decode and populate your data structure as necessary.

You may also read up on TaskGroups. This may, or may not help you. I've not actually tried.

But here's a great vid on the subject from Nick!

See -> TaskGroup

Please return here and let us know how you solved this! (With code!)

Keep Coding!

1      

Completion Handlers work well for me. The completion handler on my api client then calls the subsequent functions that consume the json.

2      

@Bnerd  

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()
        }
    }
}

1      

Hacking with Swift is sponsored by Superwall.

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.