Run something before another thing

Hi,
I have this code that I need to run in the exact line order, but I can't figure out how to do it.

Things I have tried:
DispatchQueue Aysnc
completion handler functions

Code:
Code Block
let pickedCat = Int.random(in: 1...4)
//print("picked cat: \(pickedCat)")
if pickedCat == 1 {
getRandomJokes()
randomJoke.text = "Random Joke: \(self.DadJokes)"
//print("Random Joke: \(self.DadJokes)")
}else if pickedCat == 2 {
getAssistantJokes()
randomJoke.text = "Random Joke: \(self.AssistantJokes)"
//print("Random Joke: \(self.AssistantJokes)")
}else if pickedCat == 3 {
getKnockKnockJokes()
randomJoke.text = "Random Joke: \(self.KnockKnockJokes)"
//print("Random Joke: \(self.KnockKnockJokes)")
}else if pickedCat == 4 {
getRandomJokes()
randomJoke.text = "Random Joke: \(self.RandomJokes)"
//print("Random Joke: \(self.RandomJokes)")
}
if randomJoke.text == "Random Joke: " {
randomJoke.text = "Random Joke: Failed to connect to server"
}


Answered by Mcrich23 in 668731022
I figured out that for my case, I can just call it and then wait 0.6 seconds and call it again. To wait blank seconds use (replace 0.6 with the wanted amount):

Code Block
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
// Put your code which should be executed with a delay here
}


Generally, you should better not pick up some fragments of codes, but better show whole method.

I don’t really understand what you are saying, could you please try to phrase it differently?

I don’t really understand what you are saying, could you please try to phrase it differently?

Sorry, that was my best thing. Hope you can solve your issue soon.
Ok, thank you for trying.
Accepted Answer
I figured out that for my case, I can just call it and then wait 0.6 seconds and call it again. To wait blank seconds use (replace 0.6 with the wanted amount):

Code Block
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
// Put your code which should be executed with a delay here
}


I figured out that for my case, I can just call it and then wait 0.6 seconds and call it again. To wait blank seconds use (replace 0.6 with the wanted amount): 

That's a super bad idea. When communication is unstable, asynchronous call may take minutes. Do you want to replace 0.6 with 120?
What will 120 do? Also, I figured out what you were saying and here is the function header:
Code Block
func setRandomJoke() {


What will 120 do?

As I wrote, When communication is unstable, asynchronous call may take minutes. I am sure you have tested your super bad idea only on a usual communication status. There are many chances a simple communication would take tens of seconds.

But you have no need to wait hours. If the communication was too bad and took more than two minutes, it might end with timeout error.

 here is the function header:

Thanks for showing the code. A little more, can you show the whole method including the call to setRandomJoke()?

Here you go:
Code Block
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        if NetworkMonitor.shared.isConnected {
            Utilities.checkads(bannerView: bannerView, bannerViewHeight: bannerViewHeight)
            checkAppVersion()
            setRandomJoke()
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
               // Code you want to be delayed
                self.setRandomJoke()
                self.checkRandomJokeFail()
            }
        }else {
            randomJoke.text = "Random Joke: Failed to connect to server"
            adLoadError.text = "Error Loading Ad"
            Utilities.checkToHideAds(bannerViewHeight: bannerViewHeight)
        }
        Utilities.styleFilledButton(changeJoke)
        recentCat.delegate = self
        recentCat.dataSource = self
        checkRecentCat()
        if Utilities.openNum == 1 {
            Welcome.text = "Welcome! Check out our app. In the categories section you will find all of our jokes!"
        }
        bannerView.rootViewController = self
        bannerView.delegate = self
        print("saveCat Defaults = \(Utilities.saveCat)")
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if NetworkMonitor.shared.isConnected {
            print("connected to internet")
            adLoadError.text = "Loading Ad"
            Utilities.checkads(bannerView: bannerView, bannerViewHeight: bannerViewHeight)
            bannerView.rootViewController = self
            checkRecentCat()
            recentCat.reloadData()
            if randomJoke.text == "Random Joke: " || randomJoke.text == "Random Joke: Failed to connect to server" {
                setRandomJoke()
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
                   // Code you want to be delayed
                    self.setRandomJoke()
                    self.checkRandomJokeFail()
                }
            }
        }else {
            print("not connected to internet")
            adLoadError.text = "Error Loading Ad"
        }
        Utilities.checkToHideAds(bannerViewHeight: bannerViewHeight)
    }
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let backItem = UIBarButtonItem()
        backItem.title = "Back"
        navigationItem.backBarButtonItem = backItem // This will show in the next view controller being pushed
    }


Here you go:

How nice! It will take a few hours, but I will write an example code as soon as possible.
Thank you so much!
OK. The basic principle of using async calls:
If you want to Run something after acync thing,

Do the next thing inside the completion handler.


Your getDadJokes would look like this:
Code Block
func getDadJokes(completion: @escaping (Error?)->Void) {
let collectionRef = self.db.collection("jokes")
let documentRef = collectionRef.document("Dad Jokes")
documentRef.getDocument(completion: { documentSnapshot, error in
if let error = error {
print(error) //# Use `print(error)` rather than `print(error.localizedDescription)`
completion(error)
return
}
countDadJokes = (documentSnapshot?.data()!.count)! + 1
//print("count = \(count)")
//# Do the next thing inside the completion handler
self.db.collection("jokes").document("Dad Jokes").addSnapshotListener { document, error in
//check for error
if let error = error {
print(error)
completion(error)
return
}
//check if document exists
if let doc = document, doc.exists {
if let joke = doc.get("\(Int.random(in: 0...countDadJokes))") as? String {
self.DadJokes = joke
print("Joke: \(self.DadJokes)")
//# Do the next thing inside the completion handler
completion(nil)
return
} else {
completion(JokeError.noJoke)
return
}
} else {
completion(JokeError.noDocument)
return
}
}
})
}

(Assuming you have reverted the changes to before introducing DispatchQueue.main.asyncAfter. The definition of JokeError will be shown later.)

Your setRandomJoke (and JokeError) as follows:



And use it in this way:
Code Block
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if NetworkMonitor.shared.isConnected {
Utilities.checkads(bannerView: bannerView, bannerViewHeight: bannerViewHeight)
checkAppVersion()
setRandomJoke { error in
if let error = error {
print(error)
//Do things on error
return
}
//# Do the next thing inside the completion handler
self.checkRandomJokeFail()
Utilities.styleFilledButton(self.changeJoke)
self.checkRecentCat()
if Utilities.openNum == 1 {
self.Welcome.text = "Welcome! Check out our app. In the categories section you will find all of our jokes!"
}
print("saveCat Defaults = \(Utilities.saveCat)")
}
} else {
randomJoke.text = "Random Joke: Failed to connect to server"
adLoadError.text = "Error Loading Ad"
Utilities.checkToHideAds(bannerViewHeight: bannerViewHeight)
}
//# These may not be `the next thing`
recentCat.delegate = self
recentCat.dataSource = self
bannerView.rootViewController = self
bannerView.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if NetworkMonitor.shared.isConnected {
print("connected to internet")
adLoadError.text = "Loading Ad"
Utilities.checkads(bannerView: bannerView, bannerViewHeight: bannerViewHeight)
bannerView.rootViewController = self
checkRecentCat()
recentCat.reloadData()
if randomJoke.text == "Random Joke: " || randomJoke.text == "Random Joke: Failed to connect to server" {
setRandomJoke { error in
if let error = error {
print(error)
//Do things on error
return
}
//# Do the next thing inside the completion handler
self.checkRandomJokeFail()
Utilities.checkToHideAds(bannerViewHeight: self.bannerViewHeight)
}
}
} else {
print("not connected to internet")
adLoadError.text = "Error Loading Ad"
}
}


Your code still have many hidden things so I could not test this code. But hoping you can see what I mean by Do #3 in the completion handler of #2 or something like that.
Thank you! However, now the jokes just will not load. Do you have an idea why?

I also decided to take out the auto counting.

Thank you! However, now the jokes just will not load. Do you have an idea why?

No idea. Have you put breakpoints in each completion handler and traced what's going on?
I don't know what you mean, would you like to see my code?




I don't know what you mean

Have you never used breakpoints?!?!?!

would you like to see my code?

Yes, please.
Run something before another thing
 
 
Q