UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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.