Swift - Exit function with background thread inside - swift

I have a function that does some looping in the background and update the UI:
func doSomething() {
DispatchQueue.global(qos: .userInteractive).async {
for ... {
if ... {
...
DispatchQueue.main.async {
//Update UI
...
if ... {
// Show UIAlert
//Exit function
}
}
}
}
}
}
I want to exit the function (hence cancelling the background thread). If I use return, the alert shows up but the background thread keeps looping data to the end. I think the reason is that when swapping to the main thread, I am out of scope of the function.
I am new to Swift multi-threading, so any Idea?

As far as I know if you want to stop execution of for loop which is running on background thread, then you have to stop it from background thread itself which you are trying to stop from main thread block of code. try something line below :
func doSomething() {
var continueForLoop = true
DispatchQueue.global(qos: .userInteractive).async {
for ... {
if ... && continueForLoop{
...
DispatchQueue.main.sync {
//Update UI
...
if ... {
// Show UIAlert
//Exit function
continueForLoop = false
}
}
}else{
break;
}
}
}
}
P.S. to understand multithreading in iOS go thought this link : iOS Concurrency Note: try to understand the multithreading part only as code contains old swift syntaxes.

Related

Swift : telling callback to run on global queue

I'm running a background upload task but I found it's blocking the main thread. After looking I suspect this happens because 3rd party library (Firebase in this case) must be scheduling its async callback on the main thread.
Is there a way to explicitly make the callback run on the global thread?
Here's how I start the task from the main thread:
DispatchQueue.global(qos: .background).async {
PhotoUploadOperation().start()
}
Here's an oversimplified version of the upload task:
class PhotoUploadOperation {
func uploadCameraRoll() {
for element in photos {
self.uploadPhoto(element.image, uid) { url in
// Some work
if let url = url {
let photo = Photo(uid: uid, url: url, creationDate: element.date)
self.sendPhoto(photo: photo) { success in
// Some work
}
}
}
}
}
}
Could you try this solution?
Firebase or other 3party framework use "method swizling" to accomplish some network log or other things. But your scenario is a little bit different.
extension DispatchQueue {
static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil) {
DispatchQueue.global(qos: .background).async {
background?()
if let completion = completion {
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
completion()
})
}
}
}
}
Usage:
DispatchQueue.background(delay: 3.0, background: {
// do something in background
}, completion: {
// when background job finishes, wait 3 seconds and do something in main thread
})
DispatchQueue.background(background: {
// do something in background
}, completion:{
// when background job finished, do something in main thread
})
DispatchQueue.background(delay: 3.0, completion:{
// do something in main thread after 3 seconds
})
And you dont forget allow background processing from Signing and Capabilities

Why i should use the NSManagedObjectContext's perform() and performAndWait() while i can use DispatchQueue.global

I have some confusion about run the CoreData code on the background queue.
There is some methods we can use to perform a CoreData code on the background thread using the NSManagedObjectContext.
viewContext.perform { /*some code will run on the background thread*/ }
viewContext.performAndWait{ /*some code will run on the background thread*/ }
My question here why i should use these functions rather than using the ordinary way for running some code on the background thread using the DispatchQueue
DispatchQueue.global(qos: .background).async {
/*some code will run on the background thread*/
}
Because perform and performAndWait are thread safe.
Let's say you have two contexts.
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
let mainContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
By using perform or performAndWait you guarantee that they are executed in the queue they were created. Otherwise, you will have problems with concurrency.
So you can get the behavior below.
DispatchQueue.global(qos: .background).async {
//some code will run on the background thread
privateContext.perform {
//some code will run on the private queue
mainContext.perform {
//some code will run on the main queue
}
}
}
Otherwise, they will all be executed in the background as pointed out in the following code.
DispatchQueue.global(qos: .background).async {
//some code will run on the background thread
do {
//some code will run on the background thread
try privateContext.save()
do {
//some code will run on the background thread
try mainContext.save()
} catch {
return
}
} catch {
return
}
}
To get to know more about concurrency, here there is a link to Apple documentation.

Proper use of Dispatch to show activity indicator during a long task

During a computationally intensive task, I wish to show the user an activity indicator. What is the best way to do this?
My task (contrived of course), lasts a couple of seconds:
func startThinking(howMany: Int) {
for i in 0...howMany {
let p:Double = Double(i)
let _ = p / Double.pi
}
delegate?.finishedThinking()
}
This is called on a button tap:
#IBAction func startTap(_ sender: Any) {
Thinker.sharedInstance.startThinking(howMany: 500000000)
myActivity.startAnimating()
}
And stopped when the thinking task is finished:
func finishedThinking() {
print ("finished thinking")
myActivity.stopAnimating()
}
But the activity indicator is not showing up; the UI is blocked by the difficult thinking task.
I've tried putting the startAnimating on the main thread:
DispatchQueue.main.async {
self.myActivity.startAnimating()
}
or the difficult task onto its own thread:
DispatchQueue.global().async {
Thinker.sharedInstance.startThinking(howMany: 500000000)
}
and various other combinations that I've run across in Stack. What am I doing wrong?
Firstly, I would move the call to start animating to before the thinker call, and verify that it works if you don't start thinking. You also need to stop the animation from the main thread.
#IBAction func startTap(_ sender: Any) {
myActivity.startAnimating()
DispatchQueue.global(qos: .userInitiated).async {
Thinker.sharedInstance.startThinking(howMany: 500000000)
}
}
func finishedThinking() {
DispatchQueue.main.async {
myActivity.stopAnimating()
}
}
I adjusted a few things:
moved the .startAnimating() call to be first. It is already on the main thread since it was called from the interface
specify the qos as .userInitiated
run the .stopAnimating() on the main thread

How to check notification permissions on the main thread in swift?

I've got a function in my code to check if we can have permission to display a UILocalNotification.
open func hasPermission() -> Bool {
if let permissions = UIApplication.shared.currentUserNotificationSettings {
if permissions.types.contains(.alert) {
return true
}
}
return false
}
This code has triggered a warning in Xcode 9:
UI API called from background thread:
UIApplication.currentUserNotificationSettings must be used from main
thread only
How do I fix this? I know there is the DispatchQueue.main.async method, just not sure how to implemented that.
You can do it like this.
DispatchQueue.main.async {
if hasPermission() {
//
}
}

How do I cancel a completion handler?

I want to enhance the code below: when i click the "submitData" button, the added code should cancel the completion handler.
func returnUserData(completion:(result:String)->Void){
for index in 1...10000 {
print("\(index) times 5 is \(index * 5)")
}
completion(result: "END");
}
func test(){
self.returnUserData({(result)->() in
print("OK")
})
}
#IBAction func submintData(sender: AnyObject) {
self.performSegueWithIdentifier("TestView", sender: self)
}
Can you tell me how to do this?
You can use NSOperation subclass for this. Put your calculation inside the main method, but periodically check cancelled, and if so, break out of the calculation.
For example:
class TimeConsumingOperation : NSOperation {
var completion: (String) -> ()
init(completion: (String) -> ()) {
self.completion = completion
super.init()
}
override func main() {
for index in 1...100_000 {
print("\(index) times 5 is \(index * 5)")
if cancelled { break }
}
if cancelled {
completion("cancelled")
} else {
completion("finished successfully")
}
}
}
Then you can add the operation to an operation queue:
let queue = NSOperationQueue()
let operation = TimeConsumingOperation { (result) -> () in
print(result)
}
queue.addOperation(operation)
And, you can cancel that whenever you want:
operation.cancel()
This is, admittedly, a fairly contrived example, but it shows how you can cancel your time consuming calculation.
Many asynchronous patterns have their built-in cancelation logic, eliminating the need for the overhead of an NSOperation subclass. If you are trying to cancel something that already supports cancelation logic (e.g. NSURLSession, CLGeocoder, etc.), you don't have to go through this work. But if you're really trying to cancel your own algorithm, the NSOperation subclass handles this quite gracefully.