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 orasync/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
How to Clear the Entered Textfield When the User Taps on the Button
Autoresizing Masks Programmatically VS Interface Builder/Xib/Nib
How to Get My Ip Address Programmatically on Ios/Macos
Load Resources from Relative Path Using Local HTML in Uiwebview
How to Change Navigation Bar Color in iOS 7
How to Validate an Url on the Iphone
Nsdictionary With Ordered Keys
Swift Png Image Being Saved With Incorrect Orientation
Set Padding For Uitextfield With Uitextborderstylenone
Uiview Infinite 360 Degree Rotation Animation
Wkwebview Not Loading Local Files Under iOS 8
How to Have Stored Properties in Swift, the Same Way I Had on Objective-C
How to Change the Status Bar Background Color and Text Color on iOS 7
Change Paid App to Free But Know If User Previously Purchased It