How to Use Background Thread in Swift

Run a background thread on Swift 3

It's OK to load image on the background, but it's not OK to perform UI updates on background thread. That's why the function must contain two threads.

func setupImageViewWithURL(url: URL) {
var image: UIImage? = nil

DispatchQueue.global().async {
do {
try image = UIImage(data: Data(contentsOf: url))!
} catch {
print("Failed")
}
DispatchQueue.main.async(execute: {
if image != nil {
image = self.imageWithImage(sourceImage: image!, scaledToWidth: UIScreen.main.bounds.size.width)
self.imageImageView.image = image
self.imageImageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: (image?.size.height)!)
}
})
}
}

How to set a variable that runs on background thread and needs to access ui

Move the logic that needs to access the UI to the main thread, then pass the result as an argument to your function on the background thread.

Here, there's several issues:

  • The performSelector(…) methods are quite low-level and not a good solution with Swift. Avoid these, they have issues and make it cumbersome to pass arguments around. Use GCD or async/await instead.
  • Using the synchronous Data(contentsOf: …) is also not a good idea. If you would asynchronous solutions you wouldn't run into the threading issue in the first place.

I really suggest you look into the second problem (e.g. using a DataTask), as it completely eliminates your threading issues, but here's a simple way to refactor your existing code using GCD that should already work:

override func viewDidLoad() {
super.viewDidLoad()

let urlString: String

if navigationController?.tabBarItem.tag == 0 {a
urlString = "https://www.hackingwithswift.com/samples/petitions-1.json"
} else {
urlString = "https://www.hackingwithswift.com/samples/petitions-2.json"
}

DispatchQueue.global(qos: .utility).async {
self.fetchJSON(urlString)
}
}

func fetchJSON(_ urlString: String) {
if let url = URL(string: urlString) {
if let data = try? Data(contentsOf: url) {
parse(json: data)
return
}
}

DispatchQueue.main.async {
self.showError()
}
}

Swift Background Execution

You don’t have to dispatch this background task to a background queue at all. (Don’t conflate the “background task”, which refers to the app state, with the “background queue”, which governs which threads are used.)

Besides, as the documentation says, the expiration handler closure runs on the main thread:

The system calls the handler synchronously on the main thread, blocking the app’s suspension momentarily.

So you really want to keep all interaction with backgroundTaskID on the main thread, anyway, or else you would have to implement some other synchronization mechanism.


And as a matter of good practice, make sure to end your background task when your asynchronous request is done (rather than relying on the expiration/timeout closure).

How to start a background task with the new Swift Structured Concurrency?

The documentation is just telling you that in many cases, structured concurrency should be preferred, but where you need unstructured concurrency, feel free to use it.

That having been said, rather than creating a detached task, you can just start an unstructured task. See The Swift Programming Language: Concurrency: Unstructured Tasks. In short, rather than creating a detached task with Task.detached { … }, you can just use Task { … }

Is it possible to use background thread on NSFetchedResultsController for heavy read operation to ensure UI responsiveness?

I think it is possible that your problem is the assignment of the fetchedResultsControllerDelegate by the new controller. This seems to be inferable from where you encounter the multithreading violation.

From the code you provided, it is not clear what kind of object the delegate is, but presumably it manages the UI and thus is on the main thread. You can see who this can potentially lead to a threading problem.

Thanks for posting a link to this question from my answer here https://stackoverflow.com/a/14710475/427083. I still think that it should work for you if you assign the FRC as the datasource of you table once it is finished, and then reloadData.

On a side note: while I find this problem interesting and challenging from an engineering point of view, I doubt that it is good architecture to deal with millions of records directly on a mobile device.



Related Topics



Leave a reply



Submit