Asynchronous Control Flow

I've been playing around with DispatchQueue.main.async/sync, but I'm still not able to make the methods run in the order I need them to. I split up the nested loop to make it simpler, but it's still giving me issues. How can I modify the code below so that it runs the entire first part, then the entire second part, then the entire third part? Adding DispatchQueue.main.sync around parts 1 and 2 didn't seem to do it.

func fetchRecipes(query: String) {
    print("method start")
    var finalRecipes = [Recipe]()
    let owned = self.loadIngredients()
    var count = 0
    var ownedRecipes = [Recipe]()

    UIApplication.shared.isNetworkActivityIndicatorVisible = true


   //FIRST PART
    print("fetching recipes")
    print("1")
    self.viewModel.recipes(matching: query) { recipes in
        print("2")
        for item in recipes {
            ownedRecipes.append(item)
            print("3")
        }
    }
    print("recipes fetched")



    //SECOND PART
    print("converting recipes")
    for item in ownedRecipes {
        self.viewModel.recipe(id: item.recipeId) { recipe in

            let ingredients = recipe?.ingredients
            for ingredient in ingredients! {
                for thing in owned! {
                    if (ingredient.localizedCaseInsensitiveContains(thing.name)) {
                        count += 1
                    }
                }
            }

            if true {
                finalRecipes.append(item)
            }
            count = 0
        }
    }
    print("recipes converted")


    //THIRD PART
    print("done!")
    self.insertRecipes(recipes: finalRecipes)

    UIApplication.shared.isNetworkActivityIndicatorVisible = false
    self.loadingMore = false

}

Also, for context, the recipe functions in my view model looks like this:

func recipe(id: String, completion: @escaping (Recipe?) -> Void) {
    apiClient.recipe(id: id, completion: completion)
}

func recipes(matching query: String, completion: @escaping ([Recipe]) -> Void) {
    apiClient.recipe(matching: query, page: currentSearchPage, completion: completion)
}

Replies

First up, I edited your post to use code style for your code blocks; that makes things easier to read in general, and also allows us to use line numbers to talk about specific ranges of code.

Secondly, it’s hard to offer specific suggestions here without knowing more about the context of your app. Specifically:

  • What concurrency guarantees do the various

    apiClient
    calls provide? That is, do they guarantee to call the completion handler on some specific queue? Or the main thread?
  • It looks like

    fetchRecipes(query:)
    is being called from the main thread (because it modifies
    isNetworkActivityIndicatorVisible
    , a main-thread-only UIKit property). Is that correct?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"