Downloading, saving and reloading UITableView is blocking UI - ios

I have method called collectData in my app which is the most important part of my View Controller. In that method I do a couple of signicant things (downloading, parsing, saving to persistent store), so it would be easier for you to take a look:
-(void)collectData
{
// Downloading all groups and saving them to Core Data
[[AFHTTPRequestOperationManager manager] GET:ALL_GROUPS parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSMutableDictionary* groups = [NSMutableDictionary new];
NSMutableArray* newIds = [NSMutableArray new];
NSError *error;
// Saving everything from response to MOC
for (id group in responseObject) {
Group *groupEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Group" inManagedObjectContext:self.moc];
groupEntity.name = [group valueForKey:#"name"];
groupEntity.cashID = [group valueForKey:#"id"];
groupEntity.caseInsensitiveName = [[group valueForKey:#"name"] lowercaseString];
groupEntity.selected = #NO;
// Filling up helping variables
groups[groupEntity.cashID] = groupEntity;
[newIds addObject:groupEntity.cashID];
}
// Fetching existing groups from Persistant store
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:#"Group"];
[r setIncludesPendingChanges:NO];
r.predicate = [NSPredicate predicateWithFormat:#"cashID IN %#",newIds];
NSArray *existingGroups = [self.moc executeFetchRequest:r error:&error];
// Deleting groups which already are in database
for (Group* g in existingGroups) {
Group* newGroup = groups[g.cashID];
g.name = [newGroup valueForKey:#"name"];
g.cashID = [newGroup valueForKey:#"cashID"];
g.caseInsensitiveName = [[newGroup valueForKey:#"name"] lowercaseString];
[self.moc deleteObject:newGroup];
}
// Saving Entity modification date and setting it to pull to refresh
[self saveModificationDate:[NSDate date] forEntityNamed:#"Group"];
[self.pullToRefreshView.contentView setLastUpdatedAt:[self getModificationDateForEntityNamed:#"Group"]
withPullToRefreshView:self.pullToRefreshView];
// Save groups to presistant store
if (![self.moc save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[[self fetchedResultsController] performFetch:&error];
[self.pullToRefreshView finishLoading];
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Show alert with info about internet connection
[self.pullToRefreshView finishLoading];
UIAlertView *internetAlert = [[UIAlertView alloc] initWithTitle:#"Ups!" message:#"Wygląda na to, że nie masz połączenia z internetem" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[internetAlert show];
}];
}
So when I start collecting data (first run or push to refresh) this method is blocking UI.
I want to avoid this but when I put the success block into another dispatch_async and get back to main queue only for [self.tableView reloadData] I face problem with saving to persistent store or something with bad indexes.
How can I do this whole thing in background and leave UI responsive to the user?

Just an idea, give it a try using dispatch_sync. Have a look at this explanation here where log result something similar to your need. Put [yourTableView reloadData] after synchronous block.
Hope it helps!

It seems AFNetwork call is not async so just try to call your method via performselector.

Related

Unpredictable result against predicate in multithreading environment

We are developing an application to collect beacon information and sync new/updated data to the server. We are using core data to collect beacon information, but occasionally we've found that the core data framework gives incorrect results when syncing to the server. Sometimes predicates are failing after the first iteration while fetching data from the core data to sync. So, we are seeking advice about any issues you have had with prolonged use of core data and persistent storage of the database.
I am using following steps to sync core data records to server one by one
-(void)syncDataToServer{
if( !isSycInProgress ){
//predicate fetch records which newly created and which are synced to server yet
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"isSynced == %#",[NSNumber numberWithBool:NO]];
//fetch only one record at one time to utilize memory
NSArray *beaconRecordArray = [[ISCoreDataManager sharedManager] fetchObjectList:#"BeaconRecords" predicate:predicate attributeName:#"endTime" batchSize:1 offset:0 isAscending:YES inContext:mainContext];
BeaconRecords *beacon = nil;
if(beaconRecordArray.count){
isSycInProgress = YES;
beacon = [beaconRecordArray firstObject];
{
//create request parameter dictionary
NSDictionary *dict = #{
#"uid": [beacon.uID description],
#"title":beacon.title.length?beacon.title:#"No title",
#"url": beacon.urlString?beacon.urlString:#"",
};
//call webservice to upload records on server
__block NSManagedObjectID *objId = beacon.objectID;
[[WebServiceHelper sharedInstance] callPostDataWithMethod:beconUrlPostAddBecon withParameters:dict withHud:YES success:^(NSDictionary *response)
{
if([response isKindOfClass:[NSDictionary class]] && [response[#"status"] intValue] == 200){
NSString *responseStr =response[#"response_crypt_data"];
NSError *error;
//Decrypt data to fetch parameters in response
NSData *decryptedData = [RNDecryptor decryptData:[[NSData alloc] initWithBase64EncodedString:responseStr options:0] withPassword:secretKey error:&error];
DLog(#"decrypted data %#", [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]);
//parse data
//convert data into collection format (for parsing)
NSError *parseJsonError = nil;
NSDictionary *decryptedResponse = [NSJSONSerialization JSONObjectWithData:decryptedData options:NSJSONReadingMutableContainers error:&parseJsonError];
//store Server id in local db for future reference to update record
BeaconRecords *beaconRec = [self.privateContext existingObjectWithID:objId error:nil];
[self.privateContext performBlock:^{
if(!error && !parseJsonError){
beaconRec.serverID = [NSString stringWithFormat:#"%#",decryptedResponse[#"id"]];
beaconRec.isSynced = #(YES);
[[ISCoreDataManager sharedManager] dbSaveInContext:self.privateContext];
}
//update isSynced flag in local db
isSycInProgressBeaconRecords = NO;
dispatch_async( dispatch_get_main_queue(), ^{
[self syncDataToServer]; // call this method recursively to upload all records which are pending to sync
});
}];
}
else
{
isSycInProgress = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[self syncDataToServer]; // call this method recursively to upload all records which are pending to sync
});
}
} errorBlock:^(id error) {
isSycInProgress = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[self syncDataToServer]; // call this method recursively to upload all records which are pending to sync
});
}];
}
return;
}
isSycInProgress = NO;
}
}
After successfully syncing 1st record to server, when I call same sync function recursively to upload 2nd record, that predicate is giving me same record which I uploaded before. And strange thing is isSynched property of fetched object is (yes) and my predicate says it should be (_NO).

UITableview not loaded data on 3rd time?

Don't know what to do with it. but i loaded data and load table when data is loaded,
[manager POST:path parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
arryGlobal = [NSMutableArray new];
[arryGlobal addObject:responseObject];
if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"1"]){
arryGlobal = [[arryGlobal valueForKey:#"Result"] objectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tblMainCategory reloadData];
});
}
else if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"0"]){
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
//here is place for code executed in error case
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error while sending"
message:#"Sorry, try again."
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
NSLog(#"Error: %#", [error localizedDescription]);
}];
It works perfectly two times, but when i go for 3rd time call this webservice it load data, get data, table successfully reloaded, but it not change content in table, it appear as it is in 2nd time.
SO what happens there ?? When I scroll table, then in cellForRowAtIndexPath array that i use to pass in table is contain data of 2nd time called Webservice.
EDIT:
i added this table view VievController in other view like :
MainCategory *objMain = [[MainCategory alloc] initWithNibName:#"MainCategory" bundle:nil];
[objMain LoadData:tag];
objMain.view.frame = CGRectMake(0, 0, self.bgViewCat.frame.size.width, self.bgViewCat.frame.size.height);
[self.bgViewCat insertSubview:objMain.view atIndex:1];
[self addChildViewController:objMain];
[objMain didMoveToParentViewController:self];
Try This
if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"1"])
{
[arryGlobal removeAllObjects];
arryGlobal=[NSarray alloc]init];
arryGlobal = [[arryGlobal valueForKey:#"Result"] objectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tblMainCategory reloadData];
});
}
the block:success() already run in main queue;you needn't use dispatch_async(dispatch_get_main_queue();
and arryGlobal is a kind of NSMutableArray;why you use the function called "thevalueforKey:" to get the value ?
I think maybe you should print the responseObject to check your data
ok so ther's error on :
[self.bgViewCat insertSubview:objMain.view atIndex:1];
Instead of this I use this and everything is fine, don't know why?:
[self.bgViewCat addSubview:objMain.view];

How to Fetch data out from block in AFNetworking ios?

Creating first app with webservices, I am using AFNetworking for webservices. Everything is working fine but i have no idea , that how to fetch data out from block which i am getting in response. This is what i have done so far
+(WebServices *)sharedManager{
static WebServices *managerServices = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
managerServices = [[self alloc] init];
});
return managerServices;
}
-(NSArray *)firstPostService{
//1
NSURL *url = [NSURL URLWithString:BaseURLString];
//2
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
[self methodUsingJsonFromSuccessBlock:responseObject];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[av show];
}];
if (list.count == 0) {
NSLog(#"Nothing in array yet!!");
}
else{
NSLog(#"Object 1 is : %#", [list objectAtIndex:1]);
}
return list;
}
- (void)methodUsingJsonFromSuccessBlock:(id)json {
// use the json
NSString *string = [NSString stringWithUTF8String:[json bytes]];
NSLog(#"This is data : %#", string);
list = [string componentsSeparatedByString:#"\n"];
NSLog(#"After sepration first object: %#", [list objectAtIndex:1]);
//NSLog(#"json from the block : %#", json);
}
What i understand reading from different blogs and tuts, that block is a separate thread and what every i do finishes with it. I read some where that this is normally use for it
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:#"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
and i was returning it in the of the function(firstPostService) but nothing happen. i still get an empty array outside the block. Kindly help me , suggest me some good reading stuff. Thanking you all in advance.
You say:
I need this data to my view controller i am trying to return in dispatch part but it is not allowing. Is it possible to get data into my viewcontroller class ?
Yes, it's possible. But, no, firstPostService should not return the results. It can't because it returns immediately, but the POST completion blocks won't be called until much later. There's nothing to return by the time firstPostService returns.
At the end of your original question, you said:
What i understand reading from different blogs and tuts, that block is a separate thread and what every i do finishes with it. I read some where that this is normally use for it
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:#"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
This is not the appropriate pattern of __block local variable. You generally use that __block pattern when dealing with some block that runs synchronously (for example the block of an enumeration method). But while you can use __block variable with asynchronous block, you almost never do (and it doesn't quite make sense to even try to do it). When you use appropriate completion block patterns, there's no need for any __block variable.
So, let's go back to your original code sample: So, you should take a page from AFNetworking and employ completion blocks yourself. When the AFNetworking POST method wanted to return data to your code asynchonously, it used a completion block pattern, instead. Thus, if your own firstPostService wants to pass back data asynchronously, it should do the same.
For example:
#interface WebServices ()
#property (nonatomic, strong) AFHTTPSessionManager *manager;
#end
#implementation WebServices
// note, use `instancetype` rather than actually referring to WebServices
// in the `sharedManager` method
+ (instancetype)sharedManager
{
static id sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
// I'd also suggest that you init the `AFHTTPSessionManager` only once when this
// object is first instantiated, rather than doing it when `firstPostService` is
// called
- (instancetype)init
{
self = [super init];
if (self) {
NSURL *url = [NSURL URLWithString:BaseURLString];
self.manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
}
return self;
}
// Notice:
//
// 1. This now has a return type of `void`, because when it instantly returns,
// there is no data to return.
//
// 2. In order to pass the data back, we use the "completion handler" pattern.
- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[self.manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *list = [self methodUsingJsonFromSuccessBlock:responseObject];
if (completionHandler) {
completionHandler(list, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
// // none of this code belongs here!!! You are dealing with asynchronous methods.
// // the `list` has not been returned by the time you get here!!! You shouldn't even
// // be using instance variable anyway!
//
// if (list.count == 0) {
//
// NSLog(#"Nothing in array yet!!");
// }
// else{
// NSLog(#"Object 1 is : %#", [list objectAtIndex:1]);
//
// }
// return list;
}
- (NSArray *)methodUsingJsonFromSuccessBlock:(NSData *)data {
// note, do not use `stringWithUTF8String` with the `bytes` of the `NSData`
// this is the right way to convert `NSData` to `NSString`:
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"This is string representation of the data : %#", string);
// Note, retire the `list` instance variable, and instead use a local variable
NSArray *list = [string componentsSeparatedByString:#"\n"];
NSLog(#"After sepration first object: %#", [list objectAtIndex:1]);
return list;
}
#end
Then, you could invoke that like so:
[[WebServices sharedManager] firstPostServiceWithCompletionHandler:^(NSArray *list, NSError *error) {
if (error) {
// handle the error here
} else {
// use the `list` results here
}
}];
// NOTE, DO NOT USE `list` HERE. By the time you get here, `list` has not been
// returned. Only use it in the above block.
//
// In fact, you can see that if you put a `NSLog` or breakpoint here, and again, above
// where it says "use the `list` results` here", you'll see that it's running the code
// inside that block _after_ this code down here!
I'd suggest you tackle the above first, to first make sure you completely understand the proper asynchronous technique of the completion block pattern. We don't want to complicate things quite yet. Make sure you're getting the sort of data you wanted before you proceed to what I will describe below.
But, once you've grokked the above, it's time to look at your JSON parsing. You make several reference to JSON, but if that's what it really is, then using componentsSeparatedByString is not the right way to parse it. You should use NSJSONSerialization. Or even better, you can let AFNetworking do that for you (right now, you're making it more complicated than it needs to be and your results will not be formatted correctly).
Above, I kept your methodUsingJsonFromSuccessBlock in the process, but if you're really dealing with JSON, you should eliminate that method entirely. Let AFNetworking do this for you.
You should eliminate the line that says:
responseSerializer = [AFHTTPResponseSerializer serializer];
The default serializer is AFJSONResponseSerializer which is what you want to use if handling JSON requests.
The methodUsingJsonFromSuccessBlock is then no longer needed because AFNetworking will do the JSON conversion for you. So firstPostServiceWithCompletionHandler should look like:
- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[self.manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
}

How to slow down ios process

I have an app that can send information to a server. This information is stacked up during the day (while the client uses the app), and when he so desires, he can hit the "update" button to send everything on the server.
This always worked fine until he recently had a flow increase and went from updating 10 objects to more than 100.
Obviously, the update takes more time, taht's not the issue.
The issue is, at some point, i'm getting
Error: Error Domain=NSURLErrorDomain Code=-1001 "La requête a expiré."
UserInfo=0x189874b0 {NSErrorFailingURLStringKey=http://www.*********.be/upload,
NSErrorFailingURLKey=http://www.************.be/upload,
NSLocalizedDescription=La requête a expiré.,
NSUnderlyingError=0x189abd70 "La requête a expiré."}
For the frenchophobes, " The request has expired " is what i get back, and i've hidden the url with ****, as you noticed.
Now, i've tried locally, it works fine with a small update, but when i loop 150 times on my update (i send 150 times the same thing), at some point i just get the above error X times. This error does not specificall occur with all the last items, it can be 20 in the middle, or 30, etc.
Is there a way i can change that?
Here is a piece of code that must be related to the issue.
// Set the max number of concurrent operations (threads)
//[operationQueue setMaxConcurrentOperationCount:3]; // Todo: try increasing max thread count
[operationQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount]; //dynamic thread count
self.queueCount = persons.count;
self.currentQueue = 1;
for (Person *person in persons) {
for (int i = 0 ; i<130 ; i++){ //this is where i try to break the app
[self createSendPersonOperation:person];
}}
Now what would probably work is put the last line in a "thing" that would slow down the process every 20 or so occurences, so the server or the app doesn't go crazy.
Is this possible? if so, how?
Note : I am a junior dev trying to get into a senior's code, and that guy is not available, so i'm open to all the help i can have.
Edit : also, do you think my error comes from a server-sided issue or is definitly an app-sided issue?
Edit : Complete HTTP request.
So for every person that is saved into the app, when the user decides to update, it does that for every Person in the array of persons.
- (void)createSendPersonOperation:(Person *)person
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"text/html", #"application/json", nil];
NSDictionary *params = #{
#"email": person.email,
#"gender": person.gender,
#"language": person.language,
#"hasFacebook": person.hasFacebook,
#"sendPostalCard": person.sendPostalCard
};
NSLog(#"params: %#", params);
[manager POST:kURLUpdate parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// Add picture to the form
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pictureFilePath = [documentsDirectory stringByAppendingPathComponent:person.picture];
NSURL *pictureURL = [NSURL fileURLWithPath:pictureFilePath];
[formData appendPartWithFileURL:pictureURL name:#"picture" error:nil];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", responseObject);
if ([responseObject isKindOfClass:[NSDictionary class]]) {
if ([responseObject objectForKey:#"error"]) {
NSLog(#"Error 1");
NSDictionary *error = [responseObject objectForKey:#"error"];
NSLog(#"Error message: %#", [error objectForKey:#"message"]);
} else {
// Set Person's sended attribute
person.sended = #YES;
[Person saveObject:[[PersistentStack sharedInstance] managedObjectContext] error:nil];
}
} else {
NSLog(#"Error 2");
}
[self decreaseQueueCount];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
NSLog(#"Parameter that failed : %#", [params objectForKey:#"email"]);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Erreur"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Fermer"
otherButtonTitles:nil];
[alertView show];
self.updateHud.mode = MBProgressHUDModeText;
self.updateHud.labelText = AMLocalizedString(#"update.failure.message", #"");
[self.updateHud hide:YES afterDelay:3];
}];
}
I don't really know the source of your problem, but if you think slowing the app will at least help you understand your problem you could do it with something like this:
NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:15];
while ([loopUntil timeIntervalSinceNow] > 0) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:loopUntil];
}
It will wait for 15 seconds before continue, so you can put this one after 20~30 requests as you suggested.
I really believe you should consider grouping your requests or something like that so you won't overload your server (if that is really your problem).

Downloading data, creating core data object and then using those to make next request

I am struggling to come up with an elegant solution to a problem involving multiple network requests. If you need further information than what has been provided then please don't hesitate to ask.
I need to download data from a server, create core data objects from it and then use information from those objects to download the next set of data. So I am transversing a hierarchy.
So for example:
I make my first request to the server and pull down the Regions which is made up of 4 objects (North, South, East, West). I am saving these to core data.
Once that is done (not sure best way to track this) I then need to do a fetch request on the region entity to get back those 4 objects. Each region contains a number of counties which I need to request from the server. So I loop through the regions and make a network request for each region.
I loop through each returned dictionary (one for each region) to create each county.
Here is my code to download the regions and county:
+ (void)downloadRegions
{
NSString *search = #"organisation";
NetworkHandler *networkHandler = [[NetworkHandler alloc] init];
[networkHandler downloadData:search];
}
+ (void)downloadCounty
{
NSManagedObjectContext *context = [DatabaseHandler sharedHandler].managedObjectContext;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Region" inManagedObjectContext:context];
[request setEntity:entity];
//NSPredicate *searchFilter = [NSPredicate predicateWithFormat:#"attribute = %#", searchingFor];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
NSLog(#"%#", results);
NSString *search = #"organisation?code=";
for (Region *region in results) {
NSString *s = [search stringByAppendingString:[NSString stringWithFormat:#"%#", region.code]];
NetworkHandler *networkHandler = [[NetworkHandler alloc] init];
[networkHandler downloadData:s];
}
}
Both of the above methods call:
- (void)downloadData:(NSString *)searchUrl
{
NSString *apiURL = [kBaseURL stringByAppendingPathComponent:#"api"];
NSString *finalURL = [apiURL stringByAppendingPathComponent:searchUrl];
self.dataTask = [self.session dataTaskWithURL:[NSURL URLWithString:finalURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
//self.jsonData[searchUrl] = json;
[self createObjectInDatabase:json andSearchURL:searchUrl];
NSLog(#"This has been reached");
}];
[self.dataTask resume];
}
- (void)createObjectInDatabase:(id)data andSearchURL:(NSString *)searchURL
{
if ([searchURL isEqual:#"organisation"]) {
[Region createNewRegionWithData:data inManagedObjectContext:[DatabaseHandler sharedHandler].managedObjectContext];
}
else {
[LAT createNewLATWithData:data inManagedObjectContext:[DatabaseHandler sharedHandler].managedObjectContext];
}
}
I am not sure if I am doing this the best way. In regards to making the request, creating the object and then making the next request.
My biggest issue is knowing when to make the next request. i.e - knowing when the download has completed and all the core data objects have been created successfully before making a request for those objects and using them in the next request. I am currently making the second request manually but need it to be done automatically.
I hope that is clear. I am finding it hard to explain :-). Thanks in advance.
What if you passed a string into downloadData: that the completion handler then used to post a notification as it was finishing? When you receive each notification, you know which step of the process to go to next.

Resources