Leave a rating/review
Notes: 04. Challenge: Run Code on the Main Thread
Welcome to the fourth episode! We haven’t talked about URLSession yet, but don’t worry, that is coming up in the next video.
For now, time for a little challenge for you. Remember that, when your app is downloading a video or a song, you don’t want the user thinking your app has crashed because it’s unresponsive. That’s why you do stuff in the background.
What happens when you finish doing a long running task. Can you just update your controls from the background? The answer is yes, followed by “you probably don’t want to do that!”
The UI isn’t concurrency-safe, or thread-safe, meaning, it’s not designed for multiple threads to access it at once. It’ll corrupt the state of the user interface and probably cause your app to crash.
You saw about that in the previous episode; and how actors and MainActor can help with this.
All updates to your user interface must be performed on the main thread, and sometimes you may want to perform other code specifically on the main thread.
That’s precisely what this challenge will help cover.
To get started, open this episode’s Starter playground. Take a look at the existing printTitle function.
Your goal is to update this function in order to ensure both print statements are run on the main thread. Remember that you can use MainActor both as an attribute of Task or by itself to run code on the main thread. Good luck! :)
Welcome back. How did it go? There are two ways to achieve your goal, each with some important differences.
Update your printTitle function as follows:
await MainActor.run {
print("Found the title. Are we on the main thread? \(Thread.isMainThread)")
print(line)
}
Before calling both print statements, you use MainActor.run, with await preceding it, in order to indicate that you want the contents of the closure to execute on the MainActor, or main thread. Run your playground now and look at the results. Fantastic!
As I mentioned, there is an alternative way to also run this code on the MainActor. Add the following function below printTitle:
@MainActor func printTitleAnnotated(url: URL) async throws {
for try await line in url.lines {
if line.contains("<title>") {
print("Found the title. Are we on the main thread? \(Thread.isMainThread)")
print(line)
return
}
}
}
This time, you use the @MainActor attribute to indicate that you want this function to execute on the MainActor. Next, update your task at the bottom of the playground:
Task {
print("Starting task. Are we on the main thread? \(Thread.isMainThread)")
try await printTitleAnnotated(url: URL(string: "https://www.raywenderlich.com")!)
}
And run your playground one last time. You get the same, expected result! The important thing to note about the difference between both approaches is that this last implementation, using the MainActor attribute, runs the entire function’s code on the MainActor, which may or may not be what you want.
If your goal is to run your loop in the background until you find the line that contains the title and then print out the results on the main thread, then the printTitle method is the correct implementation.
Should you not care about having your loop run on the MainActor, find the line that contains the title and then print the results still on the main thread, then printTitleAnnotated() is what you want to use.
And there you have it! Congrats on making it all the way to the end of this challenge. In the next episode, we’ll begin looking at URLSession and how to work with it. See ya there! :)