NSNotification - observer with multiple events to trigger - ios

As it stands, NSNotifications allow for a target-action mechanism in response to one post / event.
I would like to have a notification which triggers an action (runs a function) only after two events have been triggered.
The scenario is that I have two asynchronous processes which need to complete before I can call the function. Perhaps I'm missing something, but I haven't found a way to do this. Or maybe I'm not thinking of an obvious reason why this would be a really bad idea?
Also, some of my terminology may be off, so please feel free to edit and fix it.

There are many possibilities on how you can implement this. They all center around keeping track of which processes are finished. The best way depends on how your background processes are implemented.
If you are using NSOperationQueue you could add a third operation that has the other two operations as a dependency. That way you won't have to take care of notifications at all.
Otherwise you can can count how many operations have finished and execute your code when the counter reaches the right value. GCD has dispatch groups as a nice abstraction for this.
First you create a dispatch group:
let group = dispatch_group_create()
Then you enter the group for each background process:
dispatch_group_enter(group)
Finally you can register an block that gets called when the group becomes empty, that is when each dispatch_group_enter is balanced by an dispatch_group_leave:
dispatch_group_notify(group, dispatch_get_main_queue()) {
// All processes are done.
}
After each of your processes finish you leave the group again:
dispatch_group_leave(group)
It's important to call dispatch_group_enter before calling dispatch_group_notify or your block will be scheduled immediately as the group is already empty.
After your notify block was executed you can reuse the queue or discard it.

Related

Whether those two ways of dispatching work to main thread (CGD and NSOperationQueue) are equivalent?

I'm curious whether those two types to dispatch work to main queue are equivalent or maybe there are some differentials?
dispatch_async(dispatch_get_main_queue()) {
// Do stuff...
}
and
NSOperationQueue.mainQueue().addOperationWithBlock { [weak self] () -> Void in
// Do stuff..
}
There are differences, but they are somewhat subtle.
Operations enqueued to -[NSOperationQueue mainQueue] get executed one operation per pass of the run loop. This means, among other things, that there will be a "draw" pass between operations.
With dispatch_async(dispatch_get_main_queue(),...) and -[performSelectorOnMainThread:...] all enqueued blocks/selectors are called one after the other without spinning the run loop (i.e. allowing views to draw or anything like that). The runloop will continue after executing all enqueued blocks.
So, with respect to drawing, dispatch_async(dispatch_get_main_queue(),...) and -[performSelectorOnMainThread:...] batch operations into one draw pass, whereas -[NSOperationQueue mainQueue] will draw after each operation.
For a full, in-depth investigation of this, see my answer over here.
At a very basic level they are not both the same thing.
Yes, the operation queue method will be scheduled on GCD queue. But it also gets all the rich benefits of using operation queues, such as an easy way to add dependent operations; state observation; the ability to cancel an operation…
So no, they are not equivalent.
Yes there are difference in GCD and NSOperation.
GCD is light weight can be used to give flavor of multithreading like loading profile pic, loading web page, network call that surely returns at earliest.
NSOperation queue 1. Usually used to make heavy network calls, sort thousand's of record etc.2. Can add new operation, delete, get current status at any operation3. Add completion handler4. get operation count etc are added advantages over GCD

Why is there no dispatch_group_sync function for groups in GCD?

It is said that :
GCD lets us create groups, which allow you to place your tasks in one place, run all of them, and get a notification at the end from GCD. Those group blocks of code together ensure that all of them get executed by GCD one by one, as dependencies of one another.
As those blocks are getting executed one by one, then technically GCD function for dispatch_group should be dispatch_group_sync not dispatch_group_async.
Thanks in advance for great explanation.
Because here async is relative to the thread(Common main tread) that the block is submitted.
You do not need to sync groups to that thread,you just add code to that thread,code is executed one by one.
Besides,with dispatch group.
You can let tasks execute one by one if you add those tasks to a serial queue(DISPATCH_QUEUE_SERIAL).
You can also let tasks execute concurrent if you add those tasks to a concurrent queue(DISPATCH_QUEUE_CONCURRENT).

Better way to run multiple HealthKit sample queries?

I have a scenario where I need to retrieve multiple sets of data from HealthKit -- body temperature, weight, and blood pressure. I need all 3 before I can continue processing because they're going to end up in a PDF.
My naive first approach is going to be run one, then in the HKSampleQuery's resultsHandler call the second, then in that resultsHandler call the third. That feels kind of -- I don't know -- it feels like I'm missing something.
Is there a better way or is the naive approach reasonable?
I ran into this same problem, and a much better approach for any kind of nested async call would be to use GCD's dispatch groups. These allow you to wait until multiple async tasks have completed.
Here's a link with an example: Using dispatch groups to wait for multiple web services
You're going to want to use GCD dispatch groups.
First, set up a global variable for the main thread
var GlobalMainQueue: dispatch_queue_t {
return dispatch_get_main_queue()
}
Next, create the dispatch group:
let queryGroup = dispatch_group_create()
Right before your queries execute, call:
dispatch_group_enter(queryGroup)
After your query executes, call:
dispatch_group_leave(queryGroup)
Then, handle your completion code:
dispatch_group_notify(queryGroup, GlobalMainQueue) {
// completion code here
}
You should try to run the queries in parallel for better performance. In the completion handler for each one, call a common function that notes a query has completed. In that common function, when you determine that all of the queries have finished then you can proceed to the next step.
One simple approach to tracking the completion of the queries in the common function is to use a counter, either counting up from zero to the number of queries, or down from the number of total queries to zero.
Since HealthKit query handlers are called on an anonymous background dispatch queue, make sure you synchronize access to your counter, either by protecting it with a lock or by modifying the counter on a serial dispatch queue that you control, such as the main queue.

objective-c, possible to queue async NSURLRequests?

I realize this question sounds contradictory. I have several Async requests going out in an application. The situation is that the first async request is an authentication request, and the rest will use an access token returned by the successful authentication request.
The two obvious solutions would be:
run them all synchronous, and risk UI block. (bad choice)
run them async, and put request 2-N in the completion handler for the first one. (not practical)
The trouble is that the subsequent requests may be handled anywhere in the project, at anytime. The failure case would be if the 2nd request was called immediately after the 1st authentication request was issued, and before the access token was returned.
My question thus is, is there any way to queue up Async requests, or somehow say not to issue them until the first request returns successfully?
EDIT:
Why (2) is not practical: The first is an authentication request, happening when the app loads. The 2nd+ may occur right away, in which case it is practical, but it also may occur in a completely separate class or any other part of a large application. I can't essentially put the entire application in the completion handler. Other accesses to the API requests may occur in other classes, and at anytime. Even 1-2 days away after many other things have occurred.
SOLUTION:
//pseudo code using semaphore lock on authentication call to block all other calls until it is received
// at start of auth
_semaphore = dispatch_semaphore_create(0)
// at start of api calls
if(_accessToken == nil && ![_apiCall isEqualToString:#"auth]){
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
}
// at end of auth with auth token
dispatch_semaphore_signal([[SFApi Instance] semaphore]);
_accessToken = ...;
This sounds like a case where you'd want to use NSOperation's dependencies
From apple docs:
Operation Dependencies
Dependencies are a convenient way to execute operations in a specific order. You can add and remove dependencies for an operation using the addDependency: and removeDependency: methods. By default, an operation object that has dependencies is not considered ready until all of its dependent operation objects have finished executing. Once the last dependent operation finishes, however, the operation object becomes ready and able to execute.
note that in order for this to work, you must subclass NSOperation "properly" with respect to KVO-compliance
The NSOperation class is key-value coding (KVC) and key-value observing (KVO) compliant for several of its properties. As needed, you can observe these properties to control other parts of your application.
You can't really have it both ways-- there's no built-in serialization for the NSURLConnection stuff. However, you are probably already funneling all of your API requests through some common class anyway (presumably you're not making raw network calls willy-nilly all over the app).
You'll need to build the infrastructure inside that class that prevents the execution of the later requests until the first request has completed. This suggests some sort of serial dispatch queue that all requests (including the initial auth step) are funneled through. You could do this via dependent NSOperations, as is suggested elsewhere, but it doesn't need to be that explicit. Wrapping the requests in a common set of entry points will allow you to do this any way you want behind the scenes.
In cases like this I always find it easiest to write the code synchronously and get it running on the UI thread first, correctly, just for debugging. Then, move the operations to separate threads and make sure you handle concurrency.
In this case the perfect mechanism for concurrency is a semaphore; the authentication operation clears the semaphore when it is done, and all the other operations are blocking on it. Once authentication is done, floodgates are open.
The relevant functions are dispatch_semaphore_create() and dispatch_semaphore_wait() from the Grand Central Dispatch documentation: https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008079-CH2-SW2
Another excellent solution is to create a queue with a barrier:
A dispatch barrier allows you to create a synchronization point within a concurrent dispatch queue. When it encounters a barrier, a concurrent queue delays the execution of the barrier block (or any further blocks) until all blocks submitted before the barrier finish executing. At that point, the barrier block executes by itself. Upon completion, the queue resumes its normal execution behavior.
Looks like you got it running with a semaphore, nicely done!
Use blocks... 2 ways that I do it:
First, a block inside of a block...
[myCommKit getPlayerInfoWithCallback:^(ReturnCode returnCode, NSDictionary *playerInfo) {
if (playerInfo) {
// this won't run until the first one has finished
[myCommKit adjustSomething: thingToAdjust withCallback:^(ReturnCode returnCode, NSDictionary *successCode) {
if (successCode) {
// this won't run until both the first and then the second one finished
}
}];
}
}];
// don't be confused.. anything down here will run instantly!!!!
Second way is a method inside of a block
[myCommKit getPlayerInfoWithCallback:^(ReturnCode returnCode, NSDictionary *playerInfo) {
if (playerInfo) {
[self doNextThingAlsoUsingBlocks];
}
}];
Either way, any time I do async communication with my server I use blocks. You have to think differently when writing code that communicates with a server. You have to force things to go in the order you want and wait for the return success/fail before doing the next thing. And getting used to blocks is the right way to think about it. It could be 15 seconds between when you start the block and when it gets to the callback and executes the code inside. It could never come back if they're not online or there's a server outage.
Bonus way.. I've also sometimes done things using stages:
switch (serverCommunicationStage) {
case FIRST_STAGE:
{
serverCommunicationStage = FIRST_STAGE_WAITING;
// either have a block in here or call a method that has a block
[ block {
// in call back of this async call
serverCommunicationStage = SECOND_STAGE;
}];
break;
}
case FIRST_STAGE_WAITING:
{
// this just waits for the first step to complete
break;
}
case SECOND_STAGE:
{
// either have a block in here or call a method that has a block
break;
}
}
Then in your draw loop or somewhere keep calling this method. Or set up a timer to call it every 2 seconds or whatever makes sense for your application. Just make sure to manage the stages properly. You don't want to accidentally keep calling the request over and over. So make sure to set the stage to waiting before you enter the block for the server call.
I know this might seem like an older school method. But it works fine.

How to update UI before nsoperation will start and or end

2 part question but related so will keep in the same thread:
I'm adding NSOperations to a NSOperationQueue. I need to know when the operation will start and when it ends in order to update the UI.
I thought about adding a "start handler" block to run in the nsoperation as well as a "completion handler" in the NSOperation
Something like
-(id)initOperationWithStartBlock:(StartBlock)startblock completionBlock:(CompletionBlock)completionBlock
but believe that there is a better way to get this from the queue itself.
How can this be done?
I would also like to know the index of the job sent by the NSOperationQueue.
I've tried doing
[[self.myQueue operations] indexForObject:operation]
but the index is always the zeroth index - because the completed jobs were removed from the nsoperationqueue array before I could check the jobs index.
Any way to preserve them?
You need to use Key-Value-Observing pattern in IOS. So for this you need to setup observers in your controller to look for changes to isFinished and isExecuting to catch start and finish hooks.
It depends if you want to perform something from within your object upon starting or elsewhere in your code. From what you are saying (you want to update the UI), this sounds like you want to act outside of your object, but I don't know your program. You have two options:
1) If you want to act in your object upon starting the operation from within the same object, use key-value observation and observe isExecuting with self as the observer and the observed. Don't forget that you will get called whether it goes from NO to YES (starting) or YES to NO (done).
2) If you want to perform an action outside of the object, I would rather recommend to use the very general NSNotification with NSNotificationCenter and within your main, post a notification such as #"willStart" and #"didComplete". In any other object, register as an observer for your notifications.
Either way, don't forget that notifications are sent in the current threads but the UI must be updated on the main thread. You don't know on what thread observe:keyPath: is called. You may need to call performSelectorOnMainThread to update the UI or you can even use the convenient and useful nsoperationqueue mainqueue with a addOperationWithBlock with your UI code. If you use the NotificationCenter, then you can simply yourself post on the main thread with nsobject performSelectorOnMainThread

Resources