iOS - multiple http requests - do something after all is fetched - ios

I am using RestKit and I have following method. I have multiple requests in it and now I am thinking what is best approach (pattern maybe) to do something (for example hide loading alert) after all requests are done. I can set some global boolean values and in every request after it's done change it's own boolean value and check others if are done and then do something. But I am looking for some better solution. Are there some better way?
- (void)loadTypes
{
RKObjectManager *restManager = [RKObjectManager sharedManager];
[restManager getObjectsAtPath:#"remarkGetCategories"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
NSArray* statuses = [mappingResult array];
GetTypesResponse *response = [statuses firstObject];
categoryArray = response.Data;
[_tableView reloadData];
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
NSLog(#"Hit error: %#", error);
}];
[restManager getObjectsAtPath:#"remarkGetTypes"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
NSArray* statuses = [mappingResult array];
GetTypesResponse *response = [statuses firstObject];
typeArray = response.Data;
[_tableView reloadData];
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
NSLog(#"Hit error: %#", error);
}];
[restManager getObjectsAtPath:#"remarkGetSubTypes"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
NSArray* statuses = [mappingResult array];
GetTypesResponse *response = [statuses firstObject];
subtypeArray = response.Data;
[_tableView reloadData];
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
NSLog(#"Hit error: %#", error);
}];
[restManager getObjectsAtPath:#"transactionAccounts"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
NSArray* statuses = [mappingResult array];
GetTypesResponse *response = [statuses firstObject];
NSMutableArray *mutableArray = [[NSMutableArray alloc]init];
for (id object in response.Data) {
BankAccount *bankAccount = [[BankAccount alloc] init];
[bankAccount setValuesForKeysWithDictionary:object];
[mutableArray addObject:bankAccount];
}
accountArray = [NSArray arrayWithArray:mutableArray];
[_tableView reloadData];
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
NSLog(#"Hit error: %#", error);
}];
}
Edit:
Using ReactiveCocoa (RAC) for first method (remarkGetCategories). This is my helper class for RAC:
#implementation ReactiveCocoaHelper
+ (RACSignal *)signalGetCategories {
return [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
RKObjectManager *restManager = [RKObjectManager sharedManager];
[restManager getObjectsAtPath:#"remarkGetCategories"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
[subscriber sendNext:mappingResult];
[subscriber sendCompleted];
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
[subscriber sendError:error];
}];
return nil; // `nil` means there's no way to cancel.
}];
}
#end
This is my code for loading data:
RACSignal *signalCategories = [ReactiveCocoaHelper signalGetCategories];
[[RACSignal
merge:#[ signalCategories ]]
subscribeCompleted:^{
NSLog(#"They're both done!");
[_HUD hide:YES];
}];
It's okay and I guess it would be working the way I want when I implement for all methods but for now I am not sure where and how to map result from request to my categoryArray.

Check out ReactiveCocoa, it has an example especially for cases like this, it's the 6th code chunk in the Introduction section. It may seem complicated first, but it worth the effort of taking the time, it can seriously improve productivity, simplicity and stability.

From a RestKit point of view, the restManager has an operationQueue parameter. So, in the success block you can check the operationCount to determine if all of the download and mapping operations are complete.

Related

Anonymous response during service consumption iOS

I am facing anonymous behavior of service response. Sometime service fetched well and sometimes it gives an error message "A server with specified host name can't be found". I am using AFNetworking. Same service worked very well in android platform. Is there any better way to fetch them accurately in iPhone.
here is my piece of code:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSDictionary *params = #{#"email" : txtEmail.text,
#"password" : txtPassword.text,
};
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
operationManager.requestSerializer = [AFJSONRequestSerializer serializer];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager POST:#"http://somelink/someuser.svc/login" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", operation.responseString);
NSDictionary *responseDict = responseObject;
NSLog(#"%#",[responseDict valueForKey:#"result_code"]);
if ([[responseDict valueForKey:#"result_code"] isEqualToString:#"1"]) {
NSDictionary *data = [responseObject objectForKey:#"result_data"];
NSLog(#"%#",[data objectForKey:#"email"]);
[[NSUserDefaults standardUserDefaults]setObject:[data objectForKey:#"user_id"] forKey:#"UserId"];
NSString *query = [NSString stringWithFormat:#"insert into userMaster (user_id, name_first, name_last, email, password, image, gender, rec_limit, total_followers, total_following, is_brand, category) values ('%#','%#','%#','%#','%#','%#','%#',%d,'%#','%#',%d,%d)",[data objectForKey:#"user_id"],[data objectForKey:#"name_first"],[data objectForKey:#"name_last"],[data objectForKey:#"email"],[data objectForKey:#"password"],[data objectForKey:#"image"],[data objectForKey:#"gender"],[[data objectForKey:#"rec_limit"]intValue],[data objectForKey:#"total_followers"],[data objectForKey:#"total_following"],[[data objectForKey:#"is_brand"]intValue],[[data objectForKey:#"category"]intValue]];
// NSLog(#"%#",[data objectForKey:#"intrests"]);
NSLog(#"%#",query);
int n = [service insertUpdateDeleteWithQuery:query inDatabase:#"WaveDb.sql"];
NSLog(#"%d",n);
NSArray *arr = [data objectForKey:#"intrests"];
for (int i = 0; i < arr.count; i++) {
NSLog(#"%#",[arr objectAtIndex:i]);
query = [NSString stringWithFormat:#"insert into userCategory (user_id, cat_id) values ('%#','%#')",[[NSUserDefaults standardUserDefaults]objectForKey:#"UserId"],[arr objectAtIndex:i]];
[service insertUpdateDeleteWithQuery:query inDatabase:[DataClass databaseName]];
}
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication]delegate]hideProgress];
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:#"login"];
WallViewController *main = [self.storyboard instantiateViewControllerWithIdentifier:#"wallView"];
[self.navigationController pushViewController:main animated:YES];
});
}
else {
[(AppDelegate *)[[UIApplication sharedApplication]delegate]hideProgress];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert!" message:#"Invalid username and password" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}
} failure:^(AFHTTPRequestOperation operation, NSError error) {
NSLog(#"Error: %#", error);
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication]delegate]hideProgress];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert!" message:#"Some technical error occured. Try later." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
});
}];
});
check reachability before any code execution is solved my problem.

Returning from a block - Objective C

I have a class method containing a block, of AFNetworking in which i want to return one dictionary variable, code shown below:
+(NSMutableDictionary*) getFromUrl:(NSString*)url parametersPassed:(NSDictionary*)parameters;
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
__block NSMutableDictionary *resultarray;
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"JSON: %#", responseObject);
NSMutableDictionary *resultarrayTwo = (NSMutableDictionary *)responseObject;
resultarray = resultarrayTwo;
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#, %#", error, operation.responseString);
UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:#"Message" message:#"Try again" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
}];
return resultArray;
}
How can i return resultArray here, it returns nothing here due to the difference in control flow.
I don't have much knowledge in Objective C block. Waiting for your help.
Thank you.
Change your function design to the following function using Completion Blocks
+(void)getFromUrl:(NSString*)url parametersPassed:(NSDictionary*)parameters completion:(void (^) (NSMutableArray *values))completion;
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"JSON: %#", responseObject);
NSMutableDictionary *resultarray = (NSMutableDictionary *)responseObject;
completion(resultarray);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#, %#", error, operation.responseString);
UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:#"Message" message:#"Try again" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
completion(nil);
}];
}
And call the function
[YourClass getFromUrl:url parametersPassed:params completion:^(NSMutableArray *values) {
NSLog(#"%#",values);
}];
Update
Removed the extra array used.
Hope this helps.
The network call is asynchronous in nature, so having this method return its result isn't probably the right way of thinking as it implies you do a synchronous network call.
Have the method take a block parameter instead, and execute that block with the result at a later point of time from the AFNetworking completion block.
Before getting the response for GET call, it will execute the next lines. Thats why you are getting no data into Array.
You can call delegate method in success and failure block. This is the best solution.

Authenticate screen xcode 5 ios 7

I am new in ios and I am stuck for days about how to make a login screen and let the user connect to the application.
Can anyone help me or give me a tutorial because I'm searching for days without finding anything. I am getting my user from a web service.
-(void)listeDesUtilisateurs{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://127.0.0.1:8888/services/user.php" parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"JSON out : %#",responseObject);
userTab = (NSArray *) [responseObject valueForKeyPath:#"items"];
NSLog(#"nombre des elements : %d", userTab.count );
for (User *user in userMutTab) {
[user.managedObjectContext deleteObject:user];
}
[self.managedObjectContext save:nil];
for (int i=0; i<userTab.count; i++) {
User *user = [NSEntityDescription insertNewObjectForEntityForName:#"User"
inManagedObjectContext:self.managedObjectContext];
NSDictionary *consultationDic = [userTab objectAtIndex:i];
user.nom_utilisateur = [consultationDic objectForKey:#"username"];
user.mot_de_passe = [consultationDic objectForKey:#"password"];
user.nom_prenom = [consultationDic objectForKey:#"nom_prenom"];
user.nom_prenom_en_arabe = [consultationDic objectForKey:#"nom_prenom_arabe"];
user.specialite = [consultationDic objectForKey:#"specialite"];
user.specialite_en_arabe = [consultationDic objectForKey:#"specialite_en_arabe"];
user.gsm =[consultationDic objectForKey:#"gsm"];
user.telephone_cabinet = [consultationDic objectForKey:#"telephone_cabinet"];
user.adresse = [consultationDic objectForKey:#"adresse"];
user.adresse_en_arabe =[consultationDic objectForKey:#"adresse_arabe"];
}
NSError *error;
if (![self.managedObjectContext save:&error])
{
NSLog(#"Problème d'enregistrement : %#",[error localizedDescription ] );
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error : %#",error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"error"
message:[NSString stringWithFormat:#" %#",error]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}];
}

Hyper link in iOS Facebook

I need to share the link to Facebook. when user press that link that should take user to another page.
I tried the following code..but it only just share string..it doest not act like hyperlink..
-(void)fbShare
{
[hud show:YES];
NSString *title=[NSString stringWithFormat:#"www.google.com"];
MAAppDelegate *appdelegate = (MAAppDelegate *)[[UIApplication sharedApplication] delegate] ;
if (!appdelegate.session) {
appdelegate.session = [[FBSession alloc]initWithPermissions:[NSArray arrayWithObjects:#"publish_stream",nil]];
}
if (![appdelegate.session isOpen]) {
[appdelegate.session openWithCompletionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if (!error) {
[self Share:title];
}else {
NSLog(#"facebook errror %#",error.description);
[hud hide:YES];
}
}];
} else {
[self Share:title];
}
}
-(void)Share:(NSString *)text
{
MAAppDelegate *appdelegate = (MAAppDelegate *)[[UIApplication sharedApplication] delegate];
UIImage *image = [UIImage imageNamed:#"bath3.png"];
[FBSession setActiveSession:appdelegate.session];
NSData *dataImage=UIImageJPEGRepresentation(image,1.0);
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithObjectsAndKeys:text, #"name", dataImage, #"picture", #"image/png", #"content_type", nil];
FBRequest *request=[FBRequest requestWithGraphPath:#"me/photos" parameters:parameters HTTPMethod:#"POST"];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error) {
NSLog(#"Success");
[hud hide:YES];
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Audalize POC" message:#"Shared Successfully" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
} else {
[hud hide:YES];
NSLog(#"failed %#",error.localizedDescription);
}
}];
}
Read this : https://developers.facebook.com/docs/ios/share/
You need to use " link" key for sharing links to facebook.
Ex :
[NSMutableDictionary dictionaryWithObjectsAndKeys:text,#"link", // < ----
dataImage,#"picture",
#"image/png",#"content_type"
,nil];

AFNetworking download with progressive UIAlertView

I want to implement a UIAlertView which will show when [[AFHTTPRequestOperationManager manager] GET:] will start doing its job and will disappear automatically when the job is done. One cool extra feature would be if I could display UIProgressView with progress of AFHTTPRequestOperation.
For now I'm checking if a have anything in Core Data and based on that I initialize UIAlertView:
if (![self coreDataHasEntriesForEntityName:#"Group"]) {
downloadingAlert = [[UIAlertView alloc] initWithTitle:#"Pobieranie" message:#"Trwa pobieranie grup" delegate:nil cancelButtonTitle:#"Anuluj" otherButtonTitles:nil, nil];
[self collectData];
} else {
NSError *error;
[[self fetchedResultsController] performFetch:&error];
}
So to prevent from showing this alert when UITableView is already filled with data.
As you can see I'm calling this [self collectData] method which looks like:
-(void)collectData
{
[downloadingAlert show];
[[AFHTTPRequestOperationManager manager] GET:ALL_GROUPS parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSManagedObjectContext *tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tempContext.parentContext = self.moc;
[tempContext performBlock:^{
// Doing something with responseObject
if (![tempContext save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[self.moc performBlock:^{
NSError *error;
if (![self.moc save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[downloadingAlert dismissWithClickedButtonIndex:0 animated:YES];
[self.writer performBlockAndWait:^{
NSError *error;
if (![self.writer save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
}];
}];
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Show alert with info about failure
}];
}
As you can see I'm programatically showing this UIAlertView and dismissing it when downloading is completed and UITableView is reloaded. But I have no clue how to add UIProgressView or how to dismiss this UIAlertView without using dismissWithClickedButtonIndex:. Any ideas?
AFNetworking 2.0 HTTP POST Progress
You can receive progress information if you will use the following method
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
of AFHTTPSessionManager class

Resources