iOS Main Threading
Apr 03 2018

A Simple Pattern for iOS Main Threading

Apple is becoming more and more strict about the proper use of the main thread. Warnings occur all the time in Xcode now, and app crashes occur frequently when UI operations take place outside the main thread.

The situation comes up when you have UI operations that take place after an asynchronous data load, or from a background thread, or perhaps while processing a notification. But there’s a simple design pattern that’s both easy to remember and will eliminate these warnings and crashes.

Ensure main thread usage where you need it

Simple. What that implies is that you don’t put a call to a function in the main thread, rather you put the function itself in the main thread. That way callers don’t have to understand what thread is necessary for a function call to succeed.

For instance, consider this:


// ViewController.swift
func updateUser() {
userPhoneLabel.text = user.phone
}

The function updateUser is not an obviously main-thread function like an IBAction or viewDidLoad. Instead, it’s something created by the developer that’s obviously accessing a UI label control. You might call this from viewDidLoad, which means it will execute in the main thread. But you might also be getting the data from iCloud or Firebase, or synchronizing it with other databases and operations.


// ViewController.swift
func userChangedNotification(notification: UserNotification) {
updateUser()
}


// User.swift
func getUser() {
APIClient.getUser(result: { (response) -> Void in
self.processResponse(response)
self.notifyUserChanged()
})
}

In this case, User.swift loads data and posts a notification that the user changed, and ViewController.swift is listening to that notification. What you want is to ensure that updateUser is run in the main thread. You can change the call that posts the notification and wrap it in `DispatchQueue.main.async`, but that’s forcing the caller to understand the needs of the function. It’s far superior when the function knows about itself!


// ViewController.swift
func updateUser() {
if !Thread.isMainThread {
performSelector(onMainThread: #selector(updateUser), with: nil, waitUntilDone: true)
return
}
userPhoneLabel.text = user.phone
}

With waitUntilDone: true, you know that it will execute in the same threading order you were originally expecting. So when viewDidLoad calls it, you are assured that the label is set before the viewDidLoad function ends. If you don’t care and the function can execute asynchronously, then you can set waitUntilDone: false.

That’s it! The pattern can also be stated as:

A function ensures it executes in the appropriate thread. The caller to a function does not need to know the thread needs of the function.

If you need more assistance with mobile app development, contact the experts at Five Pack Creative. We can help you build your app from the ground up, or work out those last few bugs to get it on the market. Call us or contact us online to learn more!

0 Comments
Share Post
No Comments

Post a Comment