Wait for a function with an async operation to finish [closed] - swift

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
How can I wait for a function that itself has an async operation to finish?
Let me give you an example:
Function_A fetches a photo from gallery. Fetching operation will be executed asynchronously using fetching closure.
I call Function_A in Function_B and I want to wait the photo fetching operation to finish, and after that continue my code.
What I've tried:
I've used dispatch group but it has not worked. I've also tried to call Function_A in a sync block, and this was unsuccessful too.
How can I wait for this operation to finish? Can be this problem because of running fetching operation into another queue?
Update:
This is what I did:
class imageFetch: NSObject{
var theImage: PHAsset? = nil
func Function_A(){
let fetchOption = PHFetchOptions()
PHPhotoLibrary.requestAuthorization { (authStatus) in
// It seems code will be executed asynchronously from here
if authStatus == .authorized{
let imageAsset = PHAsset.fetchAssets(with: .image, options: fetchOption)
// Select image and save it in theImage
// I know I can send a closure and run it here
}
}
}
}
And in another class I call that function:
class edit: NSObject{
funct Function_B(){
var imageFetchIns = imageFetch()
// I want below code to execute asynchronously
imageFetchIns.Function_A()
// Now I want to wait last operation to finish
}
}

Without code is quite difficult to understand what is going on. If you take a look to Using dispatch groups to wait for multiple web services you will find what is going on.
Otherwise you can start using NSOperation and NSOperationQueue. In fact, if you have two operations, you can add a dependency between them. In other words, you can say that an operation can start only after the one it depends on has finished is execution. In addition to that, an operation has also the benefits to be cancellable. Useful when you want to deal with user actions, for example.
NSOperation and NSOperationQueue Tutorial in Swift will give you a very good intro on how to use them.
What I would like to stress out with operations is that an operation (especially when you subclass it) represents a self-contained task that can be reusable - in your project or in different projects as well.

Related

The callback inside Task is automatically called on the main thread

Once upon the time, before Async/Await came, we use to make a simple request to the server with URLSession dataTask. The callback being not automatically called on the main thread and we had to dispatch manually to the main thread in order to perform some UI work. Example:
DispatchQueue.main.async {
// UI work
}
Omitting this will lead to the app to crash since we try to update the UI on different queue than the main one.
Now with Async/Await things got easier. We still have to dispatch to the main queue using MainActor.
await MainActor.run {
// UI work
}
The weird thing is that even when I don't use the MainActor the code inside my Task seems to run on the main thread and updating the UI seems to be safe.
Task {
let api = API(apiConfig: apiConfig)
do {
let posts = try await api.getPosts() // Checked this and the code of getPosts is running on another thread.
self.posts = posts
self.tableView.reloadData()
print(Thread.current.description)
} catch {
// Handle error
}
}
I was expecting my code to lead to crash since I am trying to update the table view theorically not from the main thread but the log says I am on the main thread. The print logs the following:
<_NSMainThread: 0x600003bb02c0>{number = 1, name = main}
Does this mean there is no need to check which queue we are in before performing UI stuff?
Regarding Task {…}, that will “create an unstructured task that runs on the current actor” (see Swift Concurrency: Unstructured Concurrency). That is a great way to launch an asynchronous task from a synchronous context. And, if called from the main actor, this Task will also be on the main actor.
In your case, I would move the model update and UI refresh to a function that is marked as running on the main actor:
#MainActor
func update(with posts: [Post]) async {
self.posts = posts
tableView.reloadData()
}
Then you can do:
Task {
let api = API(apiConfig: apiConfig)
do {
let posts = try await api.getPosts() // Checked this and the code of getPosts is running on another thread.
self.update(with: posts)
} catch {
// Handle error
}
}
And the beauty of it is that if you’re not already on the main actor, the compiler will tell you that you have to await the update method. The compiler will tell you whether you need to await or not.
If you haven’t seen it, I might suggest watching WWDC 2021 video Swift concurrency: Update a sample app. It offers lots of practical tips about converting code to Swift concurrency, but specifically at 24:16 they walk through the evolution from DispatchQueue.main.async {…} to Swift concurrency (e.g., initially suggesting the intuitive MainActor.run {…} step, but over the next few minutes, show why even that is unnecessary, but also discuss the rare scenario where you might want to use this function).
As an aside, in Swift concurrency, looking at Thread.current is not reliable. Because of this, this practice is likely going to be prohibited in a future compiler release.
If you watch WWDC 2021 Swift concurrency: Behind the scenes, you will get a glimpse of the sorts of mechanisms underpinning Swift concurrency and you will better understand why looking at Thread.current might lead to all sorts of incorrect conclusions.

AKMetronome countdown, how to publish to main thread from background thread to start recorder?

I've been looking into how to implement a Metronome countdown with the AudioKit lib but ran into an issue related to threads and my implementation.
My implementation uses the AudioKit AKMetronome, a method to handle the metronome.start and the metronome.callback handler to start recording after a given number of bars are completed.
init () {
metronome.callback = handler
}
func record () {
self.metronome.start()
}
The handler computes the metronome position in time and if the desired number of bars completes (countdown), the recorder starts.
Unfortunately, running the .record of AKNodeRecorder in the .callback handler of AKMetronome causes the warning message:
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
For that reason, the call to start recording in the metronome.callback handler is passed to the main thread through the GCD API:
DispatchQueue.main.sync {
do {
try self.recorder.record()
} catch {
print("[ERROR] Oops! Failed to record...")
}
}
I've used the .sync blocking method to resolve the computation immediately as possible since
timing is critical in audio applications (the call should be executed in a real-time thread, ideally); Is my understanding that the GCP API main thread provides the highest priority, but I'm not certain if that's the best option for a time-sensitive application?
Would like to get some feedback or direction if possible from other users, thank you!
OK, so the actual question has nothing to do with metronomes or countdowns? What you really want to know is: will using sync from a background thread get me onto the main thread faster?
If so: Basically no. This is not what sync is for or what it means. The async/sync difference has absolutely no effect on speed. If you get onto the main thread using async, you will get on as soon it is free, so you gain nothing whatever by saying sync.
Moreover, if you already arrived into the callback in an async background thread, any damage that can be done is already done; your timing is now inexact and there is absolutely nothing you can do about it unless you happen to have a time machine in your pocket.
Following up on #matt advice regarding calls inside callbacks, the approach I took change based in a .scheduledTimer of Timer.
func record () {
metronome.restart()
Timer.scheduledTimer(withTimeInterval: self.barLength, repeats: false) { _ in
self.startRecording()
print("Timer fired!")
}
}
The record is a handler that initiates the AKMetronome, opted to use .restart so that every time's requested starts from the very start.
A property .barLength holds the value for the computed length of the desired countdown. The .startRecording method that is called, is a handler that takes care of AKRecorder .record call.
Future reader, have in mind that Timer is not meant to be accurate according to (Accuracy of NSTimer) but
unfortunately, this is the best approach I've tested and I found so far.

Do you need to use, and can you use, perform() and performAndWait() inside performBackgroundTask()?

If I am performing CoreData operations (delete local persistent data, fetch new data from online, save to persistent store) inside a storeContainer.performBackgroundTask() { context in ... } block,
1) Do I NEED to use context.perform() { } inside this to ensure it is thread safe?
2) CAN I use context.performAndWait() { } for part or all of the function inside the curly brackets if I wish to ensure, for example, deletion occurs before downloading and re-saving?
I'm having user crashes associated with CoreData saving which don't appear on testing. I suspect I am failing to understand something about CoreData. I haven't managed to find the answer to this question elsewhere in tutorials or StackOverflow despite searching for ages!
The main job of performBackgroundTask is to create an appropriate background context and call that context on respective queue. You don't need to use "perform" again to switch to private queue.
performAndWait is useful when ever you are on main queue but context is private and you want to finish the database update to move forward(similar cases). You don't need to call performAndWait inside perform because code inside perform executes serially. There is no harm in using though.

Is there any point in querying realm on a background thread and resolving a ThreadSafeReference on the UI thread?

It appears that ThreadSafeReference was added recently to help move across thread boundaries. Prior, according to the sources I read (which were probably not exhaustive) the recommendation to was to just query realm on the thread you intend to use the results on; effectively query it on the UI thread.
Is there a benefit to querying Realm on a background thread or does resolving the ThreadSafeReference basically run the query again?
Using RxSwift here's an example of this:
import RxSwift
import RealmSwift
public static func getAllMyModels() -> Observable<Results<MyModel>>{
return Observable<ThreadSafeReference<Results<MyModel>>>.create{
observer in
// using this queue in this example only
DispatchQueue.global(qos: .default).async {
let realm = try! Realm()
let models = realm.objects(MyModel.self)
let safe = ThreadSafeReference(to: models)
observer.onNext(safe)
observer.onCompleted()
}
return Disposables.create()
}
.observeOn(MainScheduler.instance) // push us back to the UI thread to resolve the reference
.map{
safeValue in
let realm = try! Realm()
let value = realm.resolve(safeValue)!
return value
}
.shareReplayLatestWhileConnected()
}
Did I gain anything by querying on some background thread and resolving on the UI thread?
Seems unnecessary. According to the docs, queries are already being done on a background thread, as long as you have attached a notification block:
Once the query has been executed, or a notification block has been added, the Results is kept up to date with changes made in the Realm, with the query execution performed on a background thread when possible.
- https://realm.io/docs/swift/latest/#queries
ast's guidance is correct, but I dug a little more and wanted to post some extra to confirm his answer further.
kishikawa-katsumi, currently a software engineer at Realm, provided this response to the question in Realm's public slack (https://realm-public.slack.com/archives/general/p1488960777001796):
For querying, it is fast enough in UI thread in most cases. If you're facing about a few slow complex queries, you can use background query.
To execute queries in the background, use addNotificationBlock ().
notificationToken = realm
.objects(...)
.filter(...)
.addNotificationBlock { (changes) in
// The query is executed in background.
// When the query is completed, then call this block
...
}
Using addNotificationBlock(), the query is excuted in background, when the query is completed, then call the callback closure will be called.
So ThreadSafeReference is rarely used in queries. ThreadSafeReference is used when you want to pass an object to another thread (for example, to specify it as a condition of a query or to use it as a parameter of an API request).
Additional information about subscribing to this block from a GCD thread (background thread) can be found here, as it requires a runloop.
https://stackoverflow.com/a/41841847/1060314

A solution to track a batch of HTTP requests in swift 3.0

I am using swift 3.0 running under iOS 10.0 and I want to craft some code that fires when a batch condition is met.
for i in 0 ..< rex {
async code, disappears and does it stuff
}
Imagine the async code is a collection of URL requests, that basically background as soon as I loop thru them. Now how can I fire off more code when "rex" requests have completed?
I thought of setting up a timer to watch and check every second, but its surely not a good solution.
I thought kicking off another thread to simply watch the data being collected, and fire when its quota is full, but well that's worse then the timer really.
I am thinking to include a test at the end of each URL request to see if it was the last that completed and than uses the NotificationCenter, but is this the optimal solution?
While OperationQueue (aka NSOperationQueue) is a good choice in many cases, it's not suitable for your use case. The problem is that URL requests are called asynchronously. Your NSOperation will finish before you get a response from the webservice.
Use DispatchGroup instead
let group = DispatchGroup()
// We need to dispatch to a background queue because we have
// to wait for the response from the webservice
DispatchQueue.global(qos: .utility).async {
for i in 0 ..< rex {
group.enter() // signal that you are starting a new task
URLSession.shared.dataTask(with: urls[i]) { data, response, error in
// handle your response
// ....
group.leave() // signal that you are done with the task
}.resume()
}
group.wait() // don't ever call wait() on the main queue
// Now all requests are complete
}
So I'm pretty sure what you want can be found here. Basically you want to use GCD and have a completion closure. It's one line of code, which always makes me giggle. A longer post on the topic is here.
What you're looking for is NSOperationQueue (or OperationQueue in Swift 3). Here's a Swift tutorial (might be a bit out of date). Here's Apple's documentation on it -- in Swift 3 they drop all the NS prefixes, so it's OperationQueue / Operation.
Basically you should add each of your URL tasks as an Operation to an OperationQueue, and have a "done" Operation with each of your URL tasks as a dependency, and add it to the queue. Then as soon as all your URL tasks are done, it will call your done operation, which you can set up to do whatever you want.
You will probably need to subclass Operation so you can update the isExecuting and isFinished properties properly. This question may be of some help here.