I am using GCDAsyncSocket (CocoaAsyncSocket) for the socket communication in my app. Due to the asynchronous nature of GCDAsyncSocket, my network request (submitMessage below) is decoupled from the callback block that runs when data is received (socket:didReadData).
- (void)submitMessage:(NSDictionary *)messageObject onCompletion:(completionBlock)block {
...
[_socket writeData:requestData withTimeout:self.timeout tag:0];
[_socket readDataToLength:4 withTimeout:self.timeout tag:TAG_HEADER];
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
...
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
if (self.completionBlock != nil)
self.completionBlock(responseObject);
}
}
This approach works fine for one-off exchanges. But there are some cases when I need to post a request, then using the received data, post another request. I can't get this to work properly. Basically, I need something like this:
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
(...callback 1...)
}];
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
(...callback 2...)
}];
}];
or
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
(...callback 1...)
}];
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
(...callback 2...)
}];
where the order is strictly request1 - callback1 - request2 - callback2.
So the question is, how can I block the second request to run after the callback of the first request? Would GCD (dispatch_sync?) be the way to go?
Edit
I ended up using a solution similar to what #tigloo suggested (hence accepting his answer), but using NSCondition instead of GCD (if anyone's interested in details, I followed this great discussion). I am already running multiple threads (UI in main, high-level socket comms in another thread, and the socket operations in a third thread). Setting a class property and using NSCondition to lock the GCDAsyncSocket delegate until the response arrive seems the cleanest approach.
I think you were almost there. What about
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
// here, do something with response1 and create request2...
// then you can make request2 directly at the end of the callback:
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
// here, do something with response2...
}];
}];
No need for the GCD directives, no need to block execution (which is a bad practice anyway). Does this solve your problem?
The easiest approach is to append your requests to a serial dispatch queue and then wait for them to be completed by using dispatch_sync(). A discussion on StackOverflow can be found here.
The actual way of implementing it is up to your preferences. A possible idea is the following:
Create a new class "SyncRequest"
This class ideally has a private property of type bool "requestFinished", initialized to NO in the class' init method
In a method such as "sendSyncRequest" you call submitMessage:completionBlock:
The completion block will set the "requestFinished" property to YES
The last line in "sendSyncRequest" will be dispatch_sync(syncRequestQueue, ^(void){while(!requestFinished);});
This way you can construct multiple instances of SyncRequest, each handling a synchronized request. Rough sketch implementation:
#interface SyncRequest
#property bool requestFinished;
#end
#implementation SyncRequest
dispatch_queue_t syncRequestQueue;
-(id)init
{
self = [super init];
if ( !self )
return nil;
self.requestFinished = NO;
syncRequestQueue = dispatch_queue_create("com.yourid.syncrequest", DISPATCH_QUEUE_SERIAL);
return self;
}
-(void) sendSyncRequest:(NSDictionary*)messageObject
{
// submit message here and set requestFinished = YES in completion block
// wait for completion here
dispatch_sync(syncRequestQueue, ^(void){while(!self.requestFinished);});
}
#end
NOTE: I wrote the code without having the compiler at hand, you may have to create an indirect reference to "self" in the dispatch_sync call in order to avoid a cyclic reference.
Related
I am sure this has been asked before, but I don't know the keywords to search for it.
Suppose that I have a function validateConnection that returns a bool. This function makes a call to the server (using anetworking) and returns true if the request was successful (this is just a example).
I want my method to wait until the response is back in order to return the bool: how to achieve that?
Any help would be much appreciated!
Its Called Block you can learn more about block by Introduction to Objective-C Blocks
Following is the sample method to create block and its call back:
-(void)callmethodwith:(NSString*)string withCompletion:(void(^)(BOOL success, NSError* error, id responce))completion
{
NSString *str =[NSString stringWithFormat:#"MY FUNTn CALLBACK %#",string];
if (completion){
dispatch_async(dispatch_get_main_queue(), ^{
completion(YES,nil,str); // here that call when method complete
});
}
}
And you can call it like following:
[self callmethodwith:#"My new" withCompletion:^(BOOL success, NSError *error, id responce) {
if(success)
{
NSLog(#"==%#",responce); // here you get response once method camplet
}
}];
I think, once the method is executing then there is no way of stopping it.
But you can cancel if it is not fired.
Like following way;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(yourMethod) object:nil];
[self performSelector:#selector(yourMethod) withObject:nil afterDelay:5.0];
You can use AFNetworking success and failure methods to wait for response. Alternatively, you can use performSelectorOnMainThread method
[yourViewController performSelectorOnMainThread:#selector(yourFunction:) withObject:nil
waitUntilDone:YES];
While working with blocks or Completion Handler, make sure you are not using a strong reference of your class or any view, as it affects retain count.
While working with blocks or completion handler use weak reference inside block, something like this,
__weak Class *weakSelf = self;
[manager communicateUsingGETMethod:#"www.google.com" parameterDictionary:#{} success:^(id successDicitoanary) {
// Call using weak reference.
[weakSelf handleSuccess];
} failure:^(NSError *error) {
[weakSelf handleFailure];
}];
and write whole code separately.This will make code more readable and you will get rid of retain cycle too as you are calling your completionCode with weak reference,
For your reference, read apple doc for Avoid Strong Reference Cycles when Capturing self
I am building a simple messaging app using Parse's framework. I have a method called displayMessages. This is called each time the phone receives a push.
However, as this message is doing work in the Parse database I don't want to call it again if it's already running. I want to wait until it is finished and then call it.
I am using the following code:
-(void)receivedPush
{
[self displayMessages];
}
and:
-(void)displayMessages
{
//code here
}
If received push is called I want it to wait until displayMessages is finished before calling it. In displayMessages I have a Parse call:
[PFObject deleteAllInBackground:toDelete block:^(BOOL succeeded, NSError *error) {
}];
It's actually this that I need to wait for, deleteAllInBackground. How can I get around this? I tried using NSOperation queue and that's fine for queuing displayMessages but this won't give my app the desired result because although displayMessages finishes execution at some point it still has deleteAllInBackground running.
If I understand correctly, your requirement is that you only want one "instance" of displayMessages running at one time. What you are asking to do is make displayMessages "threadsafe" What you should do is wrap the code in displayMessages inside #synchronized tags.
What does #synchronized() do?
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html
Alternately, you could create some kind of queueing system that uses flags, so recievedPush would actually add the push to a queue and there would be some kind of timer loop that calls display messages repeatedly but only fires when it is done processing.
Edit in response to comment
Create a manual "operation queue" that does what you want it to
- (void) methodThatRunsWhenFiredByTimer {
if (self.flag) {
[self displayMessages];
}
}
- (void) displayMessages {
PushData *data = [self dequeueData]; //maybe, not sure how you're accessing the push data
self.flag = NO;
//Code Here
[PFObject deleteAllInBackground:toDelete block:^(BOOL succeeded, NSError *error) {
//Code Here...
self.flag = YES;
}];
}
- (void) recievedPush:(Pushdata) data {
[self enqueuePushData:data];
}
While building my app, Marco Polo (getmarcopolo.com), I found that one of the most challenging parts of the app was pulling data from the server without slowing down the interface and without it crashing. I've got it settled now, and wanted to share my knowledge with any other developers having the same issue.
When pulling data from a server, there are a number of factors that need to be taken into consideration:
Data integrity - No data is ever missed from the server
Data persistence - Data is cached and can be accessed even when offline
Lack of interference with the interface (main thread) - Achieved using multithreading
Speed - Achieved using thread concurrency
Lack of thread collisions - Achieved using serial thread queues
So the question is, how do you achieve all 5?
I've answered this below, but would love to hear feedback on how to improve the process (with this example), as I feel it is not very easy to find in one place right now.
I'll be using the example of refreshing the marco's in the notification feed. I'll also be referring to Apple's GCD library (see https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html). First, we create a singleton (see http://www.galloway.me.uk/tutorials/singleton-classes/):
#implementation MPOMarcoPoloManager
+ (MPOMarcoPoloManager *)instance {
static MPOMarcoPoloManager *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
#end
This allows for us to call [MPOMarcoPoloManager instance] at any time, from any file, and access the properties in the the singleton. It also ensures that there is always only one instance of the marco polos. 'static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{' ensures thread stability.
The next step is to add the data structure we will be accessing publicly. In this case, add an NSArray for the marcos to the header file, as well as a public declaration of 'instance':
#interface MPOMarcoPoloManager : NSObject
+ (MPOMarcoPoloManager *)instance;
#property (strong, nonatomic) NSArray *marcoPolos;
#end
Now that the array and the instance are accessible publicly, it's time to ensure data persistence. We will achieve this by adding the ability to cache the data. The following code will
1. Initializes our serverQueue to the global queue, which allows multiple threads to run concurrently
2. Initializes our localQueue to a serial queue, which allows only one thread to be run at a time. All local data manipulation should be done on this thread to ensure no thread collisions
3. Gives us a method to call for caching our NSArray, with objects that conform to NSCoding (see http://nshipster.com/nscoding/)
4. Attempts to pull the data structure from the cache, and initializes a new one if it cannot
#interface MPOMarcoPoloManager()
#property dispatch_queue_t serverQueue;
#property dispatch_queue_t localQueue;
#end
#implementation MPOMarcoPoloManager
+ (MPOMarcoPoloManager *)instance {
static MPOMarcoPoloManager *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)init {
self = [super init];
if (self) {
_marcoPolos = [NSKeyedUnarchiver unarchiveObjectWithFile:self.marcoPolosArchivePath];
if(!self.marcoPolos) {
_marcoPolos = [NSArray array];
}
//serial queue
_localQueue = dispatch_queue_create([[NSBundle mainBundle] bundleIdentifier].UTF8String, NULL);
//Parallel queue
_serverQueue = dispatch_queue_create(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
}
return self;
}
- (NSString *)marcoPolosArchivePath {
NSArray *cacheDirectories = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [cacheDirectories objectAtIndex:0];
return [cacheDirectory stringByAppendingFormat:#"marcoPolos.archive"];
}
- (BOOL)saveChanges {
BOOL success = [NSKeyedArchiver archiveRootObject:self.marcoPolos toFile:[self marcoPolosArchivePath]];
return success;
}
#end
Now that we have the structure of the singleton, It's time to add the ability to refresh our marco's. Add the declarations of refreshMarcoPolosInBackgroundWithCallback:((^)(NSArray *result, NSError *error)) to the header file:
...
- (void)refreshMarcoPolosInBackground:((^)(NSArray *result, NSError *error))callback;
...
Now it's time to implement the refresh. Notice that all server calls are performed on the serverQueue (which is parallel), and any data manipulation is done on the localQueue (which is serial). When the method is completed, we use what is called a C block (see https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html) to callback the result to the main thread. Any task that acts on a background thread should have a callback to the main thread to inform the interface that the refresh has completed (whether it be successful or not).
...
- (void)refreshMarcoPolosInBackground:((^)(NSArray *result, NSError *error))callback {
//error checking ommitted
//Run the server call on the global parallel queue
dispatch_async(_serverQueue, ^{
NSArray *objects = nil;
NSError *error = nil;
//This can be any method with the declaration "- (NSArray *)fetchMarcoPolo:(NSError **)callbackError" that connects to a server and returns objects
objects = [self fetchMarcoPolo:&error];
//If something goes wrong, callback the error on the main thread and stop
if(error) {
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, error);
});
return;
}
//Since the server call was successful, manipulate the data on the serial queue to ensure no thread collisions
dispatch_async(_localQueue, ^{
//Create a mutable copy of our public array to manipulate
NSMutableArray *mutableMarcoPolos = [NSMutableArray arrayWithArray:_marcoPolos];
//PFObject is a class from Parse.com
for(PFObject *parseMarcoPoloObject in objects) {
BOOL shouldAdd = YES;
MPOMarcoPolo *marcoPolo = [[MPOMarcoPolo alloc] initWithParseMarcoPolo:parseMarcoPoloObject];
for(int i = 0; i < _marcoPolos.count; i++) {
MPOMarcoPolo *localMP = _marcoPolos[i];
if([marcoPolo.objectId isEqualToString:localMP.objectId]) {
//Only update the local model if the object pulled from the server was updated more recently than the local object
if((localMP.updatedAt && [marcoPolo.updatedAt timeIntervalSinceDate:localMP.updatedAt] > 0)||
(!localMP.updatedAt)) {
mutableMarcoPolos[i] = marcoPolo;
} else {
NSLog(#"THERE'S NO NEED TO UPDATE THIS MARCO POLO");
}
shouldAdd = NO;
break;
}
}
if(shouldAdd) {
[mutableMarcoPolos addObject:marcoPolo];
}
}
//Perform any sorting on mutableMarcoPolos if needed
//Assign an immutable copy of mutableMarcoPolos to the public data structure
_marcoPolos = [NSArray arrayWithArray:mutableMarcoPolos];
dispatch_async(dispatch_get_main_queue(), ^{
callback(marcoPolos, nil);
});
});
});
}
...
You may be wondering why we would manipulate the data on a queue for something like this, but lets add a method where we can mark the marco as viewed. We don't want to have to wait for the server to update, but we also don't want to manipulate the local object in a manor that can cause a thread collision. So let's add this declaration to the header file:
...
- (void)setMarcoPoloAsViewed:(MPOMarcoPolo *)marcoPolo inBackgroundWithlocalCallback:((^)())localCallback
serverCallback:((^)(NSError *error))serverCallback;
...
Now it's time to implement the method. Notice that the local manipulation is done on the serial queue, then immediately calls back to the main thread, allowing the interface to update without waiting for a server connection. It then updates the server, and calls back to the main thread on a separate callback to inform the interface that the server save was completed.
- (void)setMarcoPoloAsViewed:(MPOMarcoPolo *)marcoPolo inBackgroundWithlocalCallback:(MPOOrderedSetCallback)localCallback
serverCallback:(MPOErrorCallback)serverCallback {
//error checking ommitted
dispatch_async(_localQueue, ^{
//error checking ommitted
//Update local marcoPolo object
for(MPOMarcoPolo *mp in self.marcoPolos) {
if([mp.objectId isEqualToString:marcoPolo.objectId]) {
mp.updatedAt = [NSDate date];
//MPOMarcoPolo objcts have an array viewedUsers that contains all users that have viewed this marco. I use parse, so I'm going to add a MPOUser object that is created from [PFUser currentUser] but this can be any sort of local model manipulation you need
[mp.viewedUsers addObject:[[MPOUser alloc] initWithParseUser:[PFUser currentUser]]];
//callback on the localCallback, so that the interface can update
dispatch_async(dispatch_get_main_queue(), ^{
//code to be executed on the main thread when background task is finished
localCallback(self.marcoPolos, nil);
});
break;
}
}
});
//Update the server on the global parallel queue
dispatch_async(_serverQueue, ^{
NSError *error = nil;
PFObject *marcoPoloParseObject = [marcoPolo parsePointer];
[marcoPoloParseObject addUniqueObject:[PFUser currentUser] forKey:#"viewedUsers"];
//Update marcoPolo object on server
[marcoPoloParseObject save:&error];
if(!error) {
//Marco Polo has been marked as viewed on server. Inform the interface
dispatch_async(dispatch_get_main_queue(), ^{
serverCallback(nil);
});
} else {
//This is a Parse feature that your server's API may not support. If it does not, just callback the error.
[marcoPoloParseObject saveEventually];
NSLog(#"Error: %#", error);
dispatch_async(dispatch_get_main_queue(), ^{
serverCallback(error);
});
}
});
}
With this setup, a refresh can be occuring the background, while setting a marco as viewed at the same time, while ensuring that the local model is not manipulated at the same time. While the necessity of the localQueue may not be obvious with only two methods, when having many different types of manipulation available, it becomes critical.
I use a dataManager that contains two sub managers, core data fetch manager and restkit manager, which maps to core data.
for example:
anywhereInApp.m
[dataManager getData: someSearchPrecate withCompletionBlock: someBlock];
dataManager.m
- (void) getData: somePredicate withCompletionBlock: someblock{
[self.coreDataManager fetchData: somePredicate withCompletionBlock: some block];
[self.restkitManager fetchData: somePredicate withCompletionBlock: some block];
}
and then core data manger runs on a thread to fetch data and executes completion block.
and reskitmanager runs a thread and executes completion block when http request and object mapping complete.
usually the completion block updates the data shown in a collection view.
only need to worry about old data getting removed from core data, but that's another story and can involve comparing the results from the two different calls and taking appropriate action. I try to picture a venn diagram of result sets and it all makes sense or I am too tired & drinking too good of beer.
I am developing an app and when it starts its execution it has to get some data from the webService, categories, Image of loading(it changes sometimes), info "how to use" ( also can change in the server, client specifications..). To get this data I call some methods like this one (I have four similar methods, one for each thing I need) :
-(void) loadAppInfo
{
__weak typeof(self) weakSelf = self;
completionBlock = ^(BOOL error, NSError* aError) {
if (error) {
// Lo que sea si falla..
}
[weakSelf.view hideToastActivity];
};
[self.view makeToastActivity];
[wpNetManager getApplicationInfoWithCompletionBlock:completionBlock];
}
In my Network manager I have methods like this one :
- (void)getApplicationInfoWithCompletionBlock:(CompletionBlock)completionBlock
{
NSString * lang = #"es";//[[NSLocale preferredLanguages] objectAtIndex:0];
NSString *urlWithString = [kAPIInfoScreens stringByAppendingString:lang];
NSMutableURLRequest *request = nil;
request = [self requestWithMethod:#"GET" path:urlWithString parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[self registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:nil];
NSDictionary *informations = [json objectForKey:kTagInfoSplash];
if([json count]!= 0){
for (NSDictionary *infoDic in informations) {
Info *info = [Info getInfoByTitle:[infoDic objectForKey:kTagInfoTitle]];
if (info) {
// [User updateUserWithDictionary:dic];
} else {
[Info insertInfoWithDictionary:infoDic];
}
}
[wpCoreDataManager saveContext];
}
if (completionBlock) {
completionBlock(NO, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error Registro: %#", error);
if (completionBlock) {
completionBlock(YES, error);
}
}];
[self enqueueHTTPRequestOperation:operation];
}
So what I do is call this methods in the viewDidLoad:
[self loadAppInfo];
[self loadCountriesFromJson];
[self loadCategoriesFromWS];
[self loadSplashFromWS];
So, instead of call this methods one by one. I think I can use GCD to manage this while a load image is called until everything is done and then call the next ViewController. It is a good solution what I believe? if it is the problem is that I do not know how to add some blocks to a gcd.
I am trying to do this instead of calling he last four methods in ViewDidLoad. But it works weird:
-(void)myBackGroundTask
{
[self.view makeToastActivity];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self loadAppInfo];
[self loadCountriesFromJson];
[self loadCategoriesFromWS];
[self loadSplashDataFromWS ];
dispatch_async(dispatch_get_main_queue(), ^{
[self.view hideToastActivity];
[self nextController];
});
});
}
[self nextController] method is called before I had everything save in Core Data and I have errors..
Since all your four methods
[self loadAppInfo];
[self loadCountriesFromJson];
[self loadCategoriesFromWS];
[self loadSplashFromWS];
are asynchronous, it should be clear why the statement
[self nextController];
is executed before those four methods finish. Right?
Thus, there are completion handlers which get invoked when the asynchronous method finished. Too bad, none of your asynchronous methods have completion handlers. ;)
The key to approach the problem seems to have completion handlers for your asynchronous methods:
typedef void (^completion_t)(id result, NSError* error);
- (void) loadAppInfo:(completion_t)completionHandler;
- (void) loadCountriesFromJson:(completion_t)completionHandler;
- (void) loadCategoriesFromWS:(completion_t)completionHandler;
- (void) loadSplashFromWS:(completion_t)completionHandler;
It seems, you want to start ALL four asynchronous methods concurrently.
How and when you have to invoke the statement [self nextController] depends on whether there are any dependencies for this call to the eventual result of the above four asynchronous methods.
For example, you may state:
A. [self nextController] shall be executed when loadAppInfo: finishes successfully. All other asynchronous methods are irrelevant.
The solution looks like this:
[self loadAppInfo:^(id result, NSError*error){
if (error == nil) {
[self nextController];
}
}];
[self loadCountriesFromJson:nil];
[self loadCategoriesFromWS:nil];
[self loadSplashFromWS:nil];
If the above statement depends only on one of those methods, the solution is quite obvious and simple. It will get immediately more complex when you have a requirement like this:
B. [self nextController] shall be executed when ALL four asynchronous methods finished successfully (or more than one, and all other methods are irrelevant).
There are a few approaches how one can solve that. One would be to use a dispatch group, or a semaphore and a few state variables and dispatch queues to ensure concurrency. However, this is quite elaborate, would ultimately cause to block a thread, cannot be cancelled, and is also quite suboptimal (besides that it also looks hackish). Thus, I will not discuss that solution.
Using NSOperation and Dependencies
Another approach is to utilize NSOperation's dependencies. This requires to wrap each asynchronous method into a NSOperation subclass. Your methods are already asynchronous, this means that you need to take this into account when designing your subclasses.
Since one can only establish a dependency from one to another NSOperation, you also need to create a NSOperation subclass for your statement
[self nextController]
which needs to be wrapped into its own NSOperation subclass.
Well assuming you correctly subclassed NSOperation, at the end of the day, you get five modules and five header files:
LoadAppInfoOperation.h, LoadAppInfoOperation.m,
LoadCountriesFromJsonOperation.h, LoadCountriesFromJsonOperation.m,
LoadCategoriesFromWSOperation.h, LoadCategoriesFromWSOperation.m,
LoadSplashFromWSOperation.h, LoadSplashFromWSOperation.m
NextControllerOperation.h, NextControllerOperation.m
B. NextControllerOperation shall be started when ALL four Operations finished successfully:
In code this looks as follows:
LoadAppInfoOperation* op1 = ...;
LoadCountriesFromJsonOperation* op2 = ...;
LoadCategoriesFromWSOperation* op3 = ...;
LoadSplashFromWSOperation* op4 = ...;
NextControllerOperation* controllerOp = ...;
[controllerOp addDependency:op1];
[controllerOp addDependency:op2];
[controllerOp addDependency:op3];
[controllerOp addDependency:op4];
NSOperationQueue *queue = [NSOperationQueue new];
[queue addOperation: op1];
[queue addOperation: op2];
[queue addOperation: op3];
[queue addOperation: op4];
[queue addOperation: controllerOp];
Looks nice? No?
A more appealing approach: Promises
If this solution with NSOperations doesn't look nice, is too elaborated (five NSOperation subclasses!) or whatever, here is a more appealing approach which uses a third party library which implements Promises.
Before I explain how Promises work and what they are for (see wiki for a more general description), I would like to show the final code right now here, and explain how to get there later.
Disclosure: The example code here utilizes a third party library RXPromise which implements a Promise according the Promise/A+ specification. I'm the author of the RXPromise library.
There are a few more Promise libraries implemented in Objective-C, but you may take a look into RXPromise anyway ;) (see below for a link)
The key is to create asynchronous methods which return a promise. Assuming ALL your methods are now asynchronous and have a signature like below:
- (RXPromise*) doSomethingAsync;
Then, your final code will look as follows:
// Create an array of promises, representing the eventual result of each task:
NSArray* allTasks = #[
[self loadAppInfo],
[self loadCountriesFromJson],
[self loadCategoriesFromWS],
[self loadSplashFromWS]
];
...
This above statement is a quite a short form of starting a number of tasks and holding their result objects (a promise) in an array. In other words, the array allTasks contains promises whose task has been started and which now run all concurrently.
Now, we continue and define what shall happen when all tasks within this array finished successfully, or when any tasks fails. Here we use the helper class method all::
...
[RXPromise all: allTasks]
.then(^id(id results){
// Success handler block
// Parameter results is an array of the eventual result
// of each task - in the same order
... // do something with the results
return nil;
},^id(NSError*error){
// Error handler block
// error is the error of the failed task
NSLog(#"Error: %#, error");
return nil;
});
See the comments in the code above to get an idea how the success and the error handler - which get called when all tasks have been finished - is defined with the "obscure" then.
The explanation follows:
Explanation:
The code below uses the RXPromise library. You can obtain the source code of RXPromise Library which is available at GitHub.
There are a few other implementations (SHXPromise, OMPromises and more) and with a little effort it should be possible to port the code below to other promise libraries as well.
First, you need a variant of your asynchronous methods which looks as follows:
- (RXPromise*) loadAppInfo;
- (RXPromise*) loadCountriesFromJson;
- (RXPromise*) loadCategoriesFromWS;
- (RXPromise*) loadSplashFromWS;
Here, note that the asynchronous methods don't have a completion handler. We don't need this since the returned object -- a Promise -- represents the eventual result of the asynchronous task. This result may also be an error when the task fails.
I've refactored your original methods in order to better utilize the power of promises:
An asynchronous task will create the promise, and it must eventually "resolve" it either with the eventual result via fulfillWithValue:, or when it fails, with an error via rejectWithReason:. See below how a RXPromise is created, immediately returned from the asynchronous method, and "resolved" later when the task finished or failed.
Here, your method getApplicationInfo returns a promise whose eventual value will be the HTTP response data, that is a NSData containing JSON (or possibly an error):
- (RXPromise*)getApplicationInfo
{
RXPromise* promise = [[RXPromise alloc] init];
NSString * lang = #"es";//[[NSLocale preferredLanguages] objectAtIndex:0];
NSString *urlWithString = [kAPIInfoScreens stringByAppendingString:lang];
NSMutableURLRequest *request = nil;
request = [self requestWithMethod:#"GET" path:urlWithString parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[self registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[promise fulfillWithValue:responseObject]
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[promise rejectWithReason:error];
}];
[self enqueueHTTPRequestOperation:operation];
return promise;
}
A few further notes about promises:
A client can obtain the eventual result respectively the error through registering handler blocks through using the property then:
promise.then(<success_handler>, <error_handler>);
Handlers or optional, but you usually set either one or both which handle the result.
Note: With RXPromise you can register handler blocks when and where you want, and as many as you want! RXPromise is fully thread safe. You just need to keep a strong reference to the promise somewhere or as long as needed. You don't need to keep a reference, even when you setup handlers, though.
The handler block will be executed on a private queue. This means, you don't know the execution context aka thread where the handler will be executed, except you use this variant:
promise.thenOn(dispatch_queue, <success_handler>, <error_handler>);
Here, dispatch_queue specifies the queue where the handler (either the success OR the error handler) will be executed.
Two or more asynchronous tasks can be executed subsequently (aka chained), where each task produces a result which becomes the input of the subsequent task.
A short form of "chaining" of two async methods looks like this:
RXPromise* finalResult = [self asyncA]
.then(^id(id result){
return [self asyncBWithResult:result]
}, nil);
Here, asyncBWithResult: will be executed only until after asyncA has been finished successfully. The above expression returns a Promise finalResult which represents the final result of what asyncBWithResult: "returns" as its result when it finishes, or it contains an error from any task that fails in the chain.
Back to your problem:
Your method loadAppInfo now invokes asynchronous method getApplicationInfo in order to obtain the JSON data. When that succeeded, it parsers it, creates managed objects from it and saves the managed object context.
It returns a promise whose value is the managed object context where the objects have been saved:
- (RXPromise*) loadAppInfo {
RXPromise* promise = [[RXPromise alloc] init];
[self getApplicationInfo]
.then(^(id responseObject){
NSError* err;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&err];
if (json == nil) {
return err;
}
else {
[wpCoreDataManager.managedObjectContext performBlock:^{
NSDictionary *informations = [json objectForKey:kTagInfoSplash];
if([json count]!= 0){
for (NSDictionary *infoDic in informations) {
Info *info = [Info getInfoByTitle:[infoDic objectForKey:kTagInfoTitle]];
if (info) {
// [User updateUserWithDictionary:dic];
} else {
[Info insertInfoWithDictionary:infoDic];
}
}
[wpCoreDataManager saveContext]; // check error here!
[promise fulfillWithValue:wpCoreDataManager.managedObjectContext];
}
else {
[promise fulfillWithValue:nil]; // nothing saved
}
}];
}
}, nil);
return promise;
}
Notice how performBlock has been utilized to ensure the managed objects are properly associated to the execution context of its managed object context. Additionally, the asynchronous version is used, which fits nicely into the solution utilizing promises.
Having refactored these two methods, which merely perform what you intend to accomplish, and also having refactored the other asynchronous methods which now return a promise like the refactored above methods, you can now finish your task as shown at the start.
GCD to manage this while a load image is called until everything is done and then call the next ViewController. It is a good solution what I believe?
The general rule of thumb is to operate on the highest level of abstraction available.
In this case it means using NSOperation subclasses. You can create a private queue, and schedule you operations in such a way that turning off the loading image will happen only after all operations are complete, e.g. by
NSOperation *goForward = [MyGoForwardOperation new]; // you define this subclass
NSOperation *loadSomething = [MyLoadSomethingOperation new];
NSOperation *loadAnother = [MyLoadAnotherThingOperation new];
[goForward addDependency: loadOperation];
[goForward addDependency: loadAnother];
NSOperationQueue *queue = [NSOperationQueue new];
[queue addOperation: loadSomething];
[queue addOperation: loadAnother];
[[NSOperationQueue mainQueue] addOperation: goForward];
Note that in this example goForward will run on main thread, but after background operations finish.
You'll need to carefully program your MyLoadSomethingOperation for this to work, read up on subclassing NSOperation or subclass AFHTTPRequestOperation since you're using it anyway.
[self nextController] method is called before I had everything
Yes, you should search on saving to Core Data on background thread; this is a big topic in itself.
I'm working on an application that create contents and send it to an existing backend. Content is a title, a picture and location. Nothing fancy.
The backend is a bit complicated so here is what I have to do :
Let the user take a picture, enter a title and authorize the map to use its location
Generate a unique identifier for the post
Create the post on the backend
Upload the picture
Refresh the UI
I've used a couple of NSOperation subclasses to make this work but I'm not proud of my code, here is a sample.
NSOperation *process = [NSBlockOperation blockOperationWithBlock:^{
// Process image before upload
}];
NSOperation *filename = [[NSInvocationOperation alloc] initWithTarget: self selector: #selector(generateFilename) object: nil];
NSOperation *generateEntry = [[NSInvocationOperation alloc] initWithTarget: self selector: #selector(createEntry) object: nil];
NSOperation *uploadImage = [[NSInvocationOperation alloc] initWithTarget: self selector: #selector(uploadImageToCreatedEntry) object: nil];
NSOperation *refresh = [NSBlockOperation blockOperationWithBlock:^{
// Update UI
[SVProgressHUD showSuccessWithStatus: NSLocalizedString(#"Success!", #"Success HUD message")];
}];
[refresh addDependency: uploadImage];
[uploadImage addDependency: generateEntry];
[generateEntry addDependency: filename];
[generateEntry addDependency: process];
[[NSOperationQueue mainQueue] addOperation: refresh];
[_queue addOperations: #[uploadImage, generateEntry, filename, process] waitUntilFinished: NO];
Here are the things I don't like :
in my createEntry: for example, I'm storing the generated filename in a property, which mees with the global scope of my class
in the uploadImageToCreatedEntry: method, I'm using dispatch_async + dispatch_get_main_queue() to update the message in my HUD
etc.
How would you manage such workflow ? I'd like to avoid embedding multiple completion blocks and I feel like NSOperation really is the way to go but I also feel there is a better implementation somewhere.
Thanks!
You can use ReactiveCocoa to
accomplish this pretty easily. One of its big goals is to make this kind of
composition trivial.
If you haven't heard of ReactiveCocoa before, or are unfamiliar with it, check
out the Introduction
for a quick explanation.
I'll avoid duplicating an entire framework overview here, but suffice it to say that
RAC actually offers a superset of promises/futures. It allows you to compose and
transform events of completely different origins (UI, network, database, KVO,
notifications, etc.), which is incredibly powerful.
To get started RACifying this code, the first and easiest thing we can do is put
these separate operations into methods, and ensure that each one returns
a RACSignal. This isn't strictly necessary (they could all be defined within
one scope), but it makes the code more modular and readable.
For example, let's create a couple signals corresponding to process and
generateFilename:
- (RACSignal *)processImage:(UIImage *)image {
return [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
// Process image before upload
UIImage *processedImage = …;
[subscriber sendNext:processedImage];
[subscriber sendCompleted];
}];
}
- (RACSignal *)generateFilename {
return [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
NSString *filename = [self generateFilename];
[subscriber sendNext:filename];
[subscriber sendCompleted];
}];
}
The other operations (createEntry and uploadImageToCreatedEntry) would be very similar.
Once we have these in place, it's very easy to compose them and express their
dependencies (though the comments make it look a bit dense):
[[[[[[self
generateFilename]
flattenMap:^(NSString *filename) {
// Returns a signal representing the entry creation.
// We assume that this will eventually send an `Entry` object.
return [self createEntryWithFilename:filename];
}]
// Combine the value with that returned by `-processImage:`.
zipWith:[self processImage:startingImage]]
flattenMap:^(RACTuple *entryAndImage) {
// Here, we unpack the zipped values then return a single object,
// which is just a signal representing the upload.
return [self uploadImage:entryAndImage[1] toCreatedEntry:entryAndImage[0]];
}]
// Make sure that the next code runs on the main thread.
deliverOn:RACScheduler.mainThreadScheduler]
subscribeError:^(NSError *error) {
// Any errors will trickle down into this block, where we can
// display them.
[self presentError:error];
} completed:^{
// Update UI
[SVProgressHUD showSuccessWithStatus: NSLocalizedString(#"Success!", #"Success HUD message")];
}];
Note that I renamed some of your methods so that they can accept inputs from
their dependencies, giving us a more natural way to feed values from one
operation to the next.
There are huge advantages here:
You can read it top-down, so it's very easy to understand the order that
things happen in, and where the dependencies lie.
It's extremely easy to move work between different threads, as evidenced by
the use of -deliverOn:.
Any errors sent by any of those methods will automatically cancel all the
rest of the work, and eventually reach the subscribeError: block for easy
handling.
You can also compose this with other streams of events (i.e., not just
operations). For example, you could set this up to trigger only when a UI
signal (like a button click) fires.
ReactiveCocoa is a huge framework, and it's unfortunately hard to distill the
advantages down into a small code sample. I'd highly recommend checking out the
examples for when to use
ReactiveCocoa
to learn more about how it can help.
A couple of thoughts:
I would be inclined to avail myself of completion blocks because you probably only want to initiate the next operation if the previous one succeeded. You want to make sure that you properly handle errors and can easily break out of your chain of operations if one fails.
If I wanted to pass data from operation to another and didn't want to use some property of the caller's class, I would probably define my own completion block as a property of my custom operation that had a parameter which included the field that I wanted to pass from one operation to another. This assumes, though, that you're doing NSOperation subclassing.
For example, I might have a FilenameOperation.h that defines an interface for my operation subclass:
#import <Foundation/Foundation.h>
typedef void (^FilenameOperationSuccessFailureBlock)(NSString *filename, NSError *error);
#interface FilenameOperation : NSOperation
#property (nonatomic, copy) FilenameOperationSuccessFailureBlock successFailureBlock;
#end
and if it wasn't a concurrent operation, the implementation might look like:
#import "FilenameOperation.h"
#implementation FilenameOperation
- (void)main
{
if (self.isCancelled)
return;
NSString *filename = ...;
BOOL failure = ...
if (failure)
{
NSError *error = [NSError errorWithDomain:... code:... userInfo:...];
if (self.successFailureBlock)
self.successFailureBlock(nil, error);
}
else
{
if (self.successFailureBlock)
self.successFailureBlock(filename, nil);
}
}
#end
Clearly, if you have a concurrent operation, you'll implement all of the standard isConcurrent, isFinished and isExecuting logic, but the idea is the same. As an aside, sometimes people will dispatch those success or failures back to the main queue, so you can do that if you want, too.
Regardless, this illustrates the idea of a custom property with my own completion block that passes the appropriate data. You can repeat this process for each of the relevant types of operations, you can then chain them all together, with something like:
FilenameOperation *filenameOperation = [[FilenameOperation alloc] init];
GenerateOperation *generateOperation = [[GenerateOperation alloc] init];
UploadOperation *uploadOperation = [[UploadOperation alloc] init];
filenameOperation.successFailureBlock = ^(NSString *filename, NSError *error) {
if (error)
{
// handle error
NSLog(#"%s: error: %#", __FUNCTION__, error);
}
else
{
generateOperation.filename = filename;
[queue addOperation:generateOperation];
}
};
generateOperation.successFailureBlock = ^(NSString *filename, NSData *data, NSError *error) {
if (error)
{
// handle error
NSLog(#"%s: error: %#", __FUNCTION__, error);
}
else
{
uploadOperation.filename = filename;
uploadOperation.data = data;
[queue addOperation:uploadOperation];
}
};
uploadOperation.successFailureBlock = ^(NSString *result, NSError *error) {
if (error)
{
// handle error
NSLog(#"%s: error: %#", __FUNCTION__, error);
}
else
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// update UI here
NSLog(#"%#", result);
}];
}
};
[queue addOperation:filenameOperation];
Another approach in more complicated scenarios is to have your NSOperation subclass employ a technique analogous to how the standard addDependency method works, in which NSOperation sets the isReady state based upon KVO on isFinished on the other operation. This not only allows you to not only establish more complicated dependencies between operations, but also to pass database between them. This is probably beyond the scope of this question (and I'm already suffering from tl:dr), but let me know if you need more here.
I wouldn't be too concerned that uploadImageToCreatedEntry is dispatching back to the main thread. In complicated designs, you might have all sorts of different queues dedicated for particular types of operations, and the fact that UI updates are added to the main queue is perfectly consistent with this mode. But instead of dispatch_async, I might be inclined to use the NSOperationQueue equivalent:
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// do my UI update here
}];
I wonder if you need all of these operations. For example, I have a hard time imagining that filename is sufficiently complicated to justify its own operation (but if you're getting the filename from some remote source, then a separate operation makes perfect sense). I'll assume that you're doing something sufficiently complicated that justifies it, but the names of those operations make me wonder, though.
If you want, you might want to take a look at couchdeveloper's RXPromise class which uses promises to (a) control the logical relationship between separate operations; and (b) simplify the passing of data from one to the next. Mike Ash has a old MAFuture class which does the same thing.
I'm not sure either of those are mature enough that I'd contemplate using them in my own code, but it's an interesting idea.
I'm probably totally, biased - but for a particular reason - I like #Rob's approach #6 ;)
Assuming you created appropriate wrappers for your asynchronous methods and operations which return a Promise instead of signaling the completion with a completion block, the solution looks like this:
RXPromise* finalResult = [RXPromise all:#[[self filename], [self process]]]
.then(^id(id filenameAndProcessResult){
return [self generateEntry];
}, nil)
.then(^id(id generateEntryResult){
return [self uploadImage];
}, nil)
.thenOn(dispatch_get_main_queue() , ^id(id uploadImageResult){
[self refreshWithResult:uploadImageResult];
return nil;
}, nil)
.then(nil, ^id(NSError*error){
// Something went wrong in any of the operations. Log the error:
NSLog(#"Error: %#", error);
});
And, if you want to cancel the whole asynchronous sequence at any tine, anywhere and no matter how far it has been proceeded:
[finalResult.root cancel];
(A small note: property root is not yet available in the current version of RXPromise, but its basically very simple to implement).
If you still want to use NSOperation, you can rely on ProcedureKit and use the injection properties of the Procedure class.
For each operation, specify which type it produces and inject it to the next dependent operation. You can also at the end wrap the whole process inside a GroupProcedure class.