Ensure that an async task is completed for iOS - ios

I have a UITableView and a Refresh button in order to get new data from the server (if any).
[self startUpdates]; // animations
[[User myUser] getDataFromServer]; //async
[[User myUser] refreshElements:[[[UpdateContext alloc] initWithContext:data_ with:self with:#selector(endUpdates)] autorelease]];
[self.tableView reloadData];
The code above does not work well, because the getDataFromServer is an asynchronous method that is completed when the server returns the new data (the response). I want to be 100% sure that the refreshElements is being executed only when getDataFromServer gets the response back.
The question is: what is the correct way to do this. I want line 3 to gets executed if and only if line 2 gets the response from the server. Any ideas?

The easiest way would be to change the getDataFromServer method to accept a block that will contain the code that needs to be executed after the data comes from the server. You should ensure that the block will be executed in the Main thread.
Here is an example:
Changed method:
- (void)getDataFromServer:(void (^)(NSError * connectionError, NSDictionary *returnData))completionHandler{
//perform server request
//...
//
NSDictionary * data; //the data from the server
NSError * connectionError; //possible error
completionHandler(connectionError, data);
}
And how to call the new method with the block:
[self getDataFromServer:^(NSError *connectionError, NSDictionary *returnData) {
if (connectionError) {
//there was an Error
}else{
//execute on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[[User myUser] refreshElements:[[[UpdateContext alloc] initWithContext:data_ with:self with:#selector(endUpdates)] autorelease]];
[self.tableView reloadData];
});
}
}];

Related

Core Data Performing Fetch Request with Completion Handler or execute in some other thread other than Main thread

In my iOS application I am using Core Data.
For table View listing I use NSFetchedResultsController and
Connecting to Remote store I use NSIncrementalStore.
My FetchedResultsController Context is having MainQueue Cuncurrency type.(I couldn't do it with a PrivateQueueCurrencyTYpe).
For resolving Fault, for a many relationship, the executeFetchResultsCall:withContext:error method is executed from my IncrementalStore subclass.
Inside the executeFetchResults method, I will invoke the API (connecting to remote server) if it is not available in my local database.
myarray = [object representationsForRelationship:#"manyconnection" withParams:nil];
Now I need the results array in return synchronously to be returned to the ExecuteFetchResultsMethod. Also this operation should be executed on Main thread.
So I am having only one option to fetch the results from server which causes the UI to unresponsive for the specified sleep time.
-(RequestResult*)makeSyncJsonRequest{
__block RequestResult *retResult = [[RequestResult alloc] init];
__block BOOL block = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
void (^resultBlock)(RequestResult*) = ^(RequestResult* result){
if(!retResult.error)
retResult = result;
block = NO;
dispatch_group_leave(group);
};
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
dispatch_group_enter(group);
[self makeAsyncJsonRequestWithBlock:resultBlock];
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return retResult;
}
As the above operation is being executed on main thread,the UI hangs.
Inorder to make the UI smoother, I need to carry out the executeFetchrequest in some other thread which is not possible.
It also expects the results array in return.
Is there any option to carry out this something like in a completion handler manner?
or
Any alternate methods or design to work this proper.
Any Help is highly appreciated.
This is a skeleton, using a dispatch_group, assuming you are using an NSFetchedResultsController to update your UITableView:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// do your setup (FetchedResultsController and such)
[self syncData];
}
- (void)syncData
{
NSArray<Entity*> *results = [self fetchData];
BOOL needsUpdateFromServer = YES; // check your results and set this bool accordingly
if (!needsUpdateFromServer) {
return;
}
__block ServerResponse *fromServer = nil;
__block dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self loadDataFromServer:^(ServerResponse *response) {
fromServer = response;
dispatch_group_leave(group);
}];
dispatch_group_notify(group,dispatch_get_main_queue(),^{
[self persistData:fromServer];
/*
According to our discussion, you are using an NSFetchedResultsController.
So your tableView should update automatically after persisting the data.
*/
});
}
- (void)loadDataFromServer:(void (^)(ServerResponse *response))completion
{
// [someDownloadService downloadDataFromServerInBackgroundWithCompletion:^(ServerResponse* response){
dispatch_async(dispatch_get_main_queue(), ^{
completion(response);
});
// }];
}
- (NSArray<Entity*>*)fetchData
{
NSArray<Entity*> *results = nil;
// fetch from core data and return it
return results;
}
- (void)persistData:(NSArray<ServerResponse*> *)serverResponses
{
// parse whatever you get from server
// ... and persist it using Core Data
}
#end

Code Not Completing Before Next Method Is Called

In my iOS app, I am using the forecast.io API to get a weather forecast for 3 specific days. Once I get the array from all 3, I want to create an NSMutableArray and add all of those objects to it. The problem I am getting is that it is trying to create the NSMutableArray before the forecast data is retrieved. Here is what I have so far:
typedef void(^myCompletion)(BOOL);
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
[self myMethod:^(BOOL finished) {
if(finished){
NSMutableArray *allOfIt = [[NSMutableArray alloc] initWithObjects:self.weatherSaturday, self.weatherSunday, self.weatherMonday, nil];
NSLog(#"%#", allOfIt);
}
}];
}
-(void) myMethod:(myCompletion) compblock{
//do stuff
ForecastKit *forecast = [[ForecastKit alloc] initWithAPIKey:#"MY-API-KEY"];
// Request the forecast for a location at a specified time
[forecast getDailyForcastForLatitude:37.438905 longitude:-106.886051 time:1467475200 success:^(NSArray *saturday) {
// NSLog(#"%#", saturday);
self.weatherSaturday = saturday;
} failure:^(NSError *error){
NSLog(#"Daily w/ time %#", error.description);
}];
[forecast getDailyForcastForLatitude:37.438905 longitude:-106.886051 time:1467561600 success:^(NSArray *sunday) {
// NSLog(#"%#", sunday);
self.weatherSunday = sunday;
} failure:^(NSError *error){
NSLog(#"Daily w/ time %#", error.description);
}];
[forecast getDailyForcastForLatitude:37.438905 longitude:-106.886051 time:1467648000 success:^(NSArray *monday) {
// NSLog(#"%#", monday);
self.weatherMonday = monday;
} failure:^(NSError *error){
NSLog(#"Daily w/ time %#", error.description);
}];
compblock(YES);
}
When the code is ran, it fires the NSLog for allOfIt, which shows as null, before it gets any of the forecast data. What am I missing?
The problem I am getting is that it is trying to create the NSMutableArray before the forecast data is retrieved
Yup, exactly. The problem is simply that you don't understand what "asynchronous" means. Networking takes time, and it all happens in the background. Meanwhile, your main code does not pause; it is all executed instantly.
Things, therefore, do not happen in the order in which your code is written. All three getDailyForcastForLatitude calls fire off immediately and the whole method ends. Then, slowly, one by one, in no particular order, the server calls back and the three completion handlers (the stuff in curly braces) are called.
If you want the completion handlers to be called in order, you need each getDailyForcastForLatitude call to be made in the completion handler of the getDailyForcastForLatitude call that precedes it. Or, write your code in such a way that it doesn't matter when and in what order the completion handlers come back to you.

Dispatch 100 HTTP Request in order

I am using objective-C to write an app which needs to dispatch 100 web request and the response will be handled in the call back. My question is, how can I execute web req0, wait for call back, then execute web req1 and so on?
Thanks for any tips and help.
NSURL *imageURL = [[contact photoLink] URL];
GDataServiceGoogleContact *service = [self contactService];
// requestForURL:ETag:httpMethod: sets the user agent header of the
// request and, when using ClientLogin, adds the authorization header
NSMutableURLRequest *request = [service requestForURL:imageURL
ETag: nil
httpMethod:nil];
[request setValue:#"image/*" forHTTPHeaderField:#"Accept"];
GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithRequest:request];
fetcher.retryEnabled = YES;
fetcher.maxRetryInterval = 0.3;
fetcher.minRetryInterval = 0.3;
[fetcher setAuthorizer:[service authorizer]];
[fetcher beginFetchWithDelegate:self
didFinishSelector:#selector(imageFetcher:finishedWithData:error:)];
}
- (void)imageFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data error:(NSError *)error {
if (error == nil) {
// got the data; display it in the image view. Because this is sample
// code, we won't be rigorous about verifying that the selected contact hasn't
// changed between when the fetch began and now.
// NSImage *image = [[[NSImage alloc] initWithData:data] autorelease];
// [mContactImageView setImage:image];
NSLog(#"successfully fetched the data");
} else {
NSLog(#"imageFetcher:%# failedWithError:%#", fetcher, error);
}
}
You can't simply call this code in a loop as GTMHTTPFetcher works asynchronously so the loop, as you see, will iterate and start all instances without any delay.
A simple option is to put all of the contacts into a mutable array, take the first contact from the array (remove it from the array) and start the first fetcher. Then, in the finishedWithData callback, check if the array contains anything, if it does remove the first item and start a fetch with it. In this way the fetches will run serially one after the other.
A better but more complex solution would be to create an asynchronous NSOperation (there are various guides on the web) which starts a fetch and waits for the callback before completing. The benefit of this approach is that you can create all of your operations and add them to an operation queue, then you can set the max concurrent count and run the queue - so you can run multiple fetch instances at the same time. You can also suspend the queue or cancel the operations if you need to.

how to table Reload ios when recieving response Data

Hi My problem is that i am getting a response from a web service and when i parse it and add to table and reload the table view it is not refreshing . Although if i call the [table reload] in keyboard is shown it gets updated there. Could someone tell me if im missing anything
This is what i am trying to do
- (void) longPoll {
//create an autorelease pool for the thread
#autoreleasepool {
NSLog(#"polling");
VSAppDelegate *var = (VSAppDelegate*)[[UIApplication sharedApplication] delegate];
//compose the request
NSError* error = nil;
NSHTTPURLResponse* response = nil;
//send the request (will block until a response comes back)
NSData* responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"polling response is %d",response.statusCode);
//pass the response on to the handler (can also check for errors here, if you want)
[self performSelectorOnMainThread:#selector(dataReceived:) withObject:responseData waitUntilDone:YES];
}
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) startPoll {
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) dataReceived: (NSData*) theData
{
//process the response here
NSError *error = nil;
NSLog(#"polling data is %#",[[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding]);
NSLog(#"polling data is %#",[[theData base64EncodedString]base64DecodedString]);
NSDictionary *notifDic= [NSJSONSerialization JSONObjectWithData:theData options:kNilOptions error:&error];
//VSViewControllerSplit *split = [[VSViewControllerSplit alloc]init];
[self RecieveFunction:notifDic];
}
try it
dispatch_async(dispatch_get_main_queue(), ^{
[tablrView reloaddata];
});
The dataReceived method doesn't appear to be calling reloadData. I'll assume that RecieveFunction method does, though, but you should confirm that. It's hard to say without seeing RecieveFunction.
The more fundamental issue would appear to be that dataReceived method is creating a new instance of VSViewControllerSplit, calling its RecieveFunction method, and then letting this new VSViewControllerSplit instance fall out of scope (and if using ARC, get deallocated unless you pushed to it, presented it, etc.). You presumably don't want to create a new VSViewControllerSplit every time longPoll calls dataReceived, but rather just reference the existing instance.

Best way to manage many block calls

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.

Resources