Sequential delegate callback queue - ios

I have got an API I can’t impact on. API has a lot of methods with delegate callbacks like -getInfo, -apiDidGetInfo: . There are some limitations, you can only call these methods sequentially and delegate callback might not be called after request. Two methods never should be called at once. I want to convert this API to something like -getInfoWithCompletion: and use some kind of queue inside + some type of watchdog timer to finish current stalled task. How would you do that?

Related

Asynchronous swift 3

I need to make an asynchronous call so that the second method is only called after the first one is completed.Both methods are network calls. Something like this:
signIn()
getContacts()
I want to make sure that getContacts only gets called after the signIn is completed. FWIW, I can't edit the methods signatures because they are from a Google SDK.
This is what I tried:
let queue = DispatchQueue(label: "com.app.queue")
queue.async {
signIn()
getContacts()
}
Async calls, by their nature, do not run to completion then call the next thing. They return immediately, before the task they were asked to complete has even been scheduled.
You need some method to make the second task wait for the first to complete.
NauralInOva gave one good solution: use a pair of NSOprations and make them depend on each other. You could also put the 2 operations into a serial queue and the second one wouldn't begin until the first is complete.
However, if those calls trigger an async operation on another thread, they may still return and the operation queue may trigger the second operation (the getContacts() call without waiting for signIn() to complete.
Another option is to set up the first function to take a callback:
signIn( callback: {
getContacts()
}
A third option is to design a login object that takes a delegate, and the login object would call a signInComplete method on the delegate once the signIn is complete.
This is such a common thing to do that most networking APIs are built for it "out out of the box." I'd be shocked if the Google API did not have some facility for handling this.
What Google framework are you using? Can you point to the documentation for it?
You're looking for NSOperation. You can use NSOperation to chain operations together using dependencies. Once one operation complete's it can pass it's completion block to the next operation. An example for your use case might be:
// AuthOperation is a subclass of NSOperation
let signInOperation = AuthOperation()
// ContactsOperation is a subclass of NSOperation
let getContactsOperation = ContactsOperation()
getContactsOperation.addDependency(signInOperation)
Ray Wenderlich has a great tutorial covering NSOperation. The tutorial uses a downloading operation to load images asynchronously with a dependency that will filter the photo upon completion of the network request.
There is also a great sample project by Apple that uses operations with asynchronous network requests.

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.

iOS blocks are called on what thread?

I'm learning about blocks from a Stanford video. I'm now at the part which explains core data. The teachers mentions something about:
- (void)openWithCompletionHandler:(void (^)(BOOL success))completionHandler;
He said that completionhandler block will be called in the thread which called the method. So basically the method runs async but the blocks get called on the thread, lets assume main.
So my question is do all blocks run on the thread from where the method call was made. To illustrate why I ask this question, I have a Async class which does request to a server.
The format of all these methods is like this:
- (void) getSomething:(id <delegateWhatever> const)delegate{
goto background thread using GCD..
Got result from server...
Go back to main thread and call the delegate method...
}
When I use blocks I do not need to worry about going back to main thread if they will be called where the call was made?
Hope this is clear,
Thanks in advance
If something runs asynchronously, you should read a documentation to know on which thread, e.g. the completion block will be executed. If it is your code, you are in charge here, you can use global GCD queues, you can create your own queue and execute it there or whatever...
In general, blocks behaves like a function or a method call, it is executed on thread, which calls it. It is even possible that the same block will be executed from 2 different threads at the same time.
And just to be clear: Even if you are using blocks, you need to care about going back to main thread, of course if it is necessary
Nothing forces blocks to be called on a particular thread, so it depends on the specific method whether or not you need to worry about its callback being on the main thread. (In practice I don't remember ever seeing a library where a method called on the main thread would not call its completion handler also on the main thread. But you still need to read the documentation of the specific library and method you are using, as always.)

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

How to replace async calls with mocks and predefined answers?

I have simple class for perform network stuff. It's a singleton and it encapsulates NSOperationQueue inside it. When class' user calls some method to getting data from network, this class creates proper instance of operation class inherited from NSOperation sets up it and adds to queue for performing. Obviously, that performing is making asynchronously in separated threads. After getting data from network NSOperation inherited object notifies my network class and it notifies interested delegates about data getting finished or error.
Question is, how can I make unit tests for checking network class' logic? Also, I don't actually want to test server side behavior. I just want to replace actual async call to server with mock and predefined answers to after test handlers' behavior. I want to check how are my classes work, not server side. I understand commonly logic for testing stuff like that but I little bit confused with using OCMock for it.
Best answer will be code example. I'm using OCUnit and OCMock in my project for unit testing.
Also any articles or github links will be perfect.
If all the asynchronous calls go through an internal method in your class, you can simply create a partial mock on your object and use stub/expect on that method. You can then call the public methods as normal and use the mock to verify that the internal method is called. Using the partial mock stops the real implementation from being called, so no network activity should occur.
As to the other half, the call-backs from the asynchronous operation, simply call the method that would be called directly from your tests, then check that your class does the right thing, either by checking its state with OCUnit asserts, or, if it in turn uses callbacks, with another mock.
So I know this is regarding OCMock... but I thought I'd put it out there that I do this successfully with Kiwi and it looks like this.
it(#"should refresh the client's temporary API key if it is stale before sending the request", ^{
ISLDataServiceAdd *addRequest = [ISLDataServiceAdd withRecord:#{ISLFieldContact_FirstName: #"Jason"} table:ISLTableContact];
[[clientMock shouldEventually] receive:#selector(apiKey) andReturn:VALID_API_KEY];
[[clientMock shouldEventually] receive:#selector(hasTemporaryAPIKey) andReturn:theValue(YES)];
[[clientMock shouldEventually] receive:#selector(isTemporaryAPIKeyStale) andReturn:theValue(YES)];
[[clientMock shouldEventually] receive:#selector(refreshTemporaryAPIKeyAndWait:)];
[addRequest sendRequestUsingClient:clientMock completion:nil failure:nil];
});
sendRequestUsingClient:completion:failure: is an asynchronous call, so by using shouldEventually with Kiwi, it knows that it needs to wait some time (default is 1 second) before those selectors will be called.

Resources