POST request iOS: what's happening under the hood? - ios

I'm facing a really strange issue and I hope somebody can help me understand where is the problem on which I'm stuck since three days ago.
In simple I make a NSMutableRequest to a simple endpoint where I send (using POST) a json and I'm supposed to receive a response 0 or 1.
The code works the first time the app is run on the device, but somehow it does not work anymore the following times.
To explain myself better, if I uninstall and reinstall the app every time, I get the correct response, but if I run the code twice the second time I get something like [CSRF verification failed] from the endpoint. This error means that I'm not sending the correct format ( or I'm sending something strange).
My question is: how is that possible? Is it possible that I'm sending something else?
The endpoint works correctly because with the android version I don't have any problems...
The code is the following, hope somebody can explain me what is happening under the hood and how I can manage to solve this problem.
NSString *mail = [profile valueForKey:#"email"];
NSString *provider = [profile valueForKey:#"provider"];
// making a GET request to endpoint
NSString *baseUrl = ENDPOINT_URL;
NSString *targetUrl = [NSString stringWithFormat:#"%#", baseUrl];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setHTTPMethod:#"POST"];
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request addValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSString *body = [NSString stringWithFormat:#"{\"mail\":\"%#\", \"provider\":\"%#\"}",mail,provider];
NSData *postData=[body dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postData];
[request setURL:[NSURL URLWithString:targetUrl]];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:
^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
if (data){
NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Data received: %#", myString);
if ([myString isEqualToString:#"[\"CSRF validation failed\"]"]){
NSLog(#"ENDPOINT ERROR");
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication] delegate] loginAborted];});
} else {
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"Data received: %#", json);
NSNumber *value = [NSNumber numberWithInt:[[json objectForKey:#"profile_exists"] intValue]];
if ([value intValue] == 1){
//Profile exists.
NSLog(#"Profile exists.");
[self silentLogin:profile];
} else if ([value intValue] == 0) {
//Profile does not exists.
NSLog(#"Profile does not exist.");
[self silentRegistration:profile];
}
else {
//Error.
NSLog(#"Error in ENDPOINT VALUE");
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication] delegate] loginAborted];});
}
NSLog(#"%#",json);
}
} else {
NSLog(#"No Data received");
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication] delegate] loginAborted];});
}
}] resume];

I was able to solve the problem deleting the cookies every time I make a request. It seems that these cookies are re sent every time.
+ (void)clearCookiesForURL: (NSString *)url {
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [cookieStorage cookiesForURL:[NSURL URLWithString:url]];
for (NSHTTPCookie *cookie in cookies) {
NSLog(#"Deleting cookie for domain: %#", [cookie domain]);
[cookieStorage deleteCookie:cookie];
}
}

Related

Null value after parse json

My app sends receipt to server after restore purchase. To dictionary adds two additional keys: "bundleId" (app bundle id), "UUID" (app identifierForVendor).
After approved and first run, application everything is ok (after restoring i getting all keys). When the user deleted the application and reinstall, these to keys has null value.
Fetch current appStoreReceipt:
if(!self.receiptData){
NSURL *receiptURL = [[NSBundle mainBundle]
appStoreReceiptURL];
self.receiptData = [NSData
dataWithContentsOfURL:receiptURL]
receipt = [self.receiptData bkrBase64EncodedString];
}
else{
receipt = [self.receiptData bkrBase64EncodedString];
}
Apple request:
if(receipt){
NSError *error;
NSDictionary *requestContents = #{
#"receipt-data" : receipt,
#"password" : //purchaseAppSecreatKey
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { /* ... Handle error ... */
}
// Create a POST request with the receipt data.
NSURL *storeURL = ///iTunesVerificationURL
NSMutableURLRequest *storeRequest =
[NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:#"POST"];
[storeRequest setHTTPBody:requestData];
// Make a connection to the iTunes Store on a background queue.
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:storeRequest
queue:operationQueue
completionHandler:^(NSURLResponse *response, NSData *data,
NSError *connectionError) {
if (connectionError) {
NSLog(#"response error %#", connectionError.localizedDescription);
} else {
NSError *error;
NSDictionary *jsonResponse =
[NSJSONSerialization JSONObjectWithData:data
options:0
error:&error] ;
if(!error){
//success, sending to server
}else{
NSLog(#"parse error %#", error.localizedDescription);
}
}
}];
}
Send to server code
NSMutableDictionary *requestBodyDictonary = [NSMutableDictionary dictionaryWithDictionary:reciptDic];
[requestBodyDictonary setObject:[self bundleId] forKey:#"bundleId"];
[requestBodyDictonary setObject:[self UUID] forKey:#"uuid"];
NSURL *url = [NSURL URLWithString:];
NSError *error = nil;
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:requestBodyDictonary options:0 error:&error];
if(error == nil){
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:15.0];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:bodyData];
NSURLSession *defaultstSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionDataTask *task = [defaultstSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(#"complete %ld", (long)[(NSHTTPURLResponse *)response statusCode]);
}];
[task resume];
}else{
NSLog(#"error parshe %#", [error localizedDescription]);
}
Fetched uuid and bundleid
#pragma mark - User ID
-(NSString *)UUID{
if(!_UUID){
_UUID = [[NSUserDefaults standardUserDefaults] stringForKey:#"identifierForVendor_UUID"];
if(!_UUID){
_UUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[[NSUserDefaults standardUserDefaults]setObject:_UUID forKey:#"identifierForVendor_UUID"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
}
return _UUID;
}
#pragma mark bundle id
-(NSString *)bundleId{
if(!_bundleId){
_bundleId = [[NSUserDefaults standardUserDefaults] stringForKey:#"app_BundleId"];
if(!_bundleId){
_bundleId = [[NSBundle mainBundle]bundleIdentifier];
if(!_bundleId){
_bundleId = (__bridge_transfer NSString *)CFDictionaryGetValue(CFBundleGetInfoDictionary(CFBundleGetMainBundle()),
(const void *)(#"CFBundleIdentifier"));
}
[[NSUserDefaults standardUserDefaults] setObject:_bundleId forKey:#"app_BundleId"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
return _bundleId;
}
Why my app return null after reinstall?
In sandbox mode everything is ok
If you delete an APP then it will clear all NSUserDefault values also, we have to use keychain to store values, it will remain same if you delete app.
use KeychainItemWrapper files to store both UDID and BundleID
download keychanItemwrapper from github
#import "KeychainItemWrapper.h"
then set values do Like this:
UIDevice *device = [UIDevice currentDevice];
NSString *div = [[device identifierForVendor]UUIDString];
keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"TestUDID" accessGroup:nil];
userDevice_id = [keychain objectForKey:(__bridge id)(kSecAttrAccount)];
if([userDevice_id isEqualToString:#""])
{
keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"TestUDID" accessGroup:nil];
[keychain setObject:div forKey:(__bridge id)(kSecAttrAccount)];
}
get values from variable:
keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"TestUDID" accessGroup:nil];
userDevice_id = [keychain objectForKey:(__bridge id)(kSecAttrAccount)];

Unable to perform operations on response object?

I am struggling with a response object from one of my API calls. I receive it fine, but if I try to perform a count or valueForKey operation on the object, I get a "Unrecognized selector sent to instance" error. I have a feeling I am not correctly decoding the response object, any input would be greatly appreciated!
Method for the API call:
- (void)callRegisterAccount:(NSString *)email
password:(NSString *)password
confirmPassword:(NSString *)confirmPassword
completionBlock:(void (^)(NSMutableArray *resultsArray))completion{
NSLog(#"REGISTER ACCOUNT CALLED!");
NSString *appendUrl = [NSString stringWithFormat:#"Account/Register"];
NSURL *aUrl = [NSURL URLWithString:[NSString stringWithFormat:#"%#""%#", #"xxx", appendUrl]];
NSLog(#"URL: %#",aUrl);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
[request setHTTPMethod:#"POST"];
[request addValue:#"application/json, text/plain, */*" forHTTPHeaderField:#"Accept"];
[request addValue:#"application/json;charset=UTF-8" forHTTPHeaderField:#"Content-Type"];
//build an info object and convert to json
NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
email,
#"Email",
password,
#"Password",
confirmPassword,
#"ConfirmPassword",
nil];
//convert object to data
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:info
options:NSJSONWritingPrettyPrinted error:&error];
NSString *strData = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"%#",strData);
[request setHTTPBody:[strData dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// NSLog(#"REGISTER PUSH NOTIFICATIONS RESPONSE: %#", response);
// NSLog(#"REGISTER PUSH NOTIFICATIONS ERROR: %#", error);
// NSLog(#"REGISTER PUSH NOTIFICATIONS DATA: %#", data);
NSData *_data = data;
NSMutableString *_string = [NSMutableString stringWithString:#""];
for (int i = 0; i < _data.length; i++) {
unsigned char _byte;
[_data getBytes:&_byte range:NSMakeRange(i, 1)];
if (_byte >= 32 && _byte < 127) {
[_string appendFormat:#"%c", _byte];
} else {
[_string appendFormat:#"[%d]", _byte];
}
}
NSLog(#"REGISTER ACCOUNT RESPONSE: %#", _string);
if(_string) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
completion((NSMutableArray*)_string);
}
}];
}
Response object:
This line is all wrong:
completion((NSMutableArray*)_string);
_string is an instance of NSMutableString, and here you're telling the compiler to trust you that it's actually an instance of NSMutableArray. This is obviously a lie and when you try to use it as an array you get an exception.
Note that the JSON could be an array or a dictionary, and in your example it is actually a dictionary, so you need to check this and work out how to return an array - or change the completion block. You need to explicitly deal with this container type variation somewhere...
You don't need to do the funky data to string processing you have, you can just use NSJSONSerialization to go straight from data to JSON object (dictionary or array), and it'll return an error if there is some issue with the JSON.
Hmmm, looking at that data processing again indicates some reformatting of the data, which is a bit weird but ok. After you've done that and got your string you should turn it back into data and then use NSJSONSerialization.

login and completion handler

I'm developing an app with a login page. When the app is launched, the login screen is shown, and you cannot access the app until you are connected. To connect to the app, you enter your username and your password. When you press the "connect" button, json data containing the username and password is sent to a web service, which check if the credentials exists. If they exists, the server send a json file containing "exists":"true"
The problem is that the code checking this Json file is in completionHandler of my NSURLSession, and the method return "NO" before the Json data is checked, so I can not connect to my app. As it's hard to explain, here is my code:
GSBconnexion.m:
#import "GSBconnexion.h"
#implementation GSBconnexion
-(bool)logConnexionWithUserName:(NSString *)username
password:(NSString *)password{
__block BOOL allowConnexion;
NSDictionary *connexion = #{
#"username": username,
#"password": password,
#"target": #"app"
};
NSError *error;
NSData *jsonLogData = [NSJSONSerialization dataWithJSONObject:connexion options:NSJSONWritingPrettyPrinted
error:&error];
if (! jsonLogData) {
NSLog(#"Got an error: %#", error);
}
NSData *logData = jsonLogData;
NSString *testString = [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding];
NSString *logLength = [NSString stringWithFormat:#"%lu", (unsigned long)[testString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"http://192.168.5.133:1337/login"]];
[request setHTTPMethod:#"POST"];
[request setValue:logLength forHTTPHeaderField:#"Content-lenght"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:logData];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSDictionary *serverResponse = [NSJSONSerialization JSONObjectWithData:data options:
NSJSONReadingMutableContainers error:&error];
int canIConnect = [serverResponse[#"exist"] intValue];
NSLog(#"%d",canIConnect);
if (canIConnect == 1) {
NSLog(#"OKKK");
allowConnexion = YES;
NSString *sessionID = [[NSString alloc]initWithString:serverResponse[#"_id"]];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:sessionID forKey:#"SessionID"];
[userDefaults synchronize];
NSLog(#"ID Session:%#",[userDefaults objectForKey:#"sessionID"]);
}
else {
allowConnexion=NO;
}
}] resume];
NSLog(#"JSON envoyé: \n\n%#",testString);
return allowConnexion;
}
#end
GSBLoginController:
- (IBAction)connect:(id)sender {
connectButton.hidden = YES;
loading.hidden = NO;
UIViewController* homePage = [self.storyboard instantiateViewControllerWithIdentifier:#"homePage"];
GSBconnexion *login = [[GSBconnexion alloc]init];
NSString *username = [[NSString alloc]initWithFormat:#"%#",usernameTextField.text];
NSString *password = [[NSString alloc]initWithFormat:#"%#",pwdTextField.text];
BOOL authorized = [login logConnexionWithUserName:username password:password];
if (authorized) {
[self presentViewController:homePage animated:YES completion:nil];
}
else {
connectButton.hidden = NO;
loading.hidden=YES;
usernameTextField.text=#"";
pwdTextField.text=#"";
errorLabel.text = #"Connexion impossible, merci de réessayer.\nSi le problème persiste, veuillez contacter un administrateur.";
}
NSLog(authorized ? #"Yes" : #"No");
}
I hope you understood me, thanks for your help!
Simon
The problem is that you're expecting a return value from a method that is executing asynchronously. So basically return allowConnexion is happening immediately even though the dataTask is still ongoing in the background. Thus, you're relying on an incorrect value. Basically what you want to do is copy what is happening in the dataTask w/ a completion handler.
So you could say something like typedef void (^CompletionBlock) (BOOL isFinished);
Then change your login method to include the completion block as its last argument and return nothing:
-(void)logConnexionWithUserName:(NSString *)username
password:(NSString *)password
withCompletion:(CompletionBlock)completionBlock
Then inside of the dataTask's completionHandler call the completionBlock passing in the value of allowConnexion.
Finally once you've done all that in your login view controller you'll implement this new method, and inside of the completion block you can update your view accordingly. Its going to look something like this:
- (void)thingWithCompletion:(CompletionBlock)completionBlock
{
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(YES);
});
}
- (void)viewDidLoad {
[super viewDidLoad];
[self thingWithCompletion:^(BOOL isFinished) {
//update UI
}];
}
Be advised that since you're on a background thread and going to update UI on completion you're going to want to dispatch to the main queue as well. That is why the call to completionBlock(YES); is wrapped in the dispatch_async call.

How to enumerate through NSArray of NSDictionaries with a block call inside the loop?

I get an self.usersArray with 2 elements in the format:
(
{
userCreated = "2012-01-05 12:27:22";
username = Simulator;
},
{
userCreated = "2013-01-01 14:27:22";
username = "joey ";
}
)
This is gotten in a completion block after which I call another method to fetch points for these 2 users through a helper class:
-(void)getPoints{
self.usersPointsArray = [[NSMutableArray alloc] init];
for (NSDictionary *usersDictionary in self.usersArray) {
[SantiappsHelper fetchPointsForUser:[usersDictionary objectForKey:#"username"] WithCompletionHandler:^(NSArray *points){
if ([points count] > 0) {
[self.usersPointsArray addObject:[points objectAtIndex:0]];
}
NSLog(#"self.usersPointsArray %#", self.usersPointsArray);
}];
}
}
The final self.usersPointsArray log looks like:
(
{
PUNTOS = 5;
username = Simulator;
},
{
PUNTOS = 2;
username = joey;
}
)
But the problem is that the way the call for points is structured, the self.usersPointsArray is returned twice, each time with an additional object, due to the for loop, I know.
Here is the Helper class method:
+(void)fetchPointsForUser:(NSString*)usuario WithCompletionHandler:(Handler2)handler{
NSURL *url = [NSURL URLWithString:#"http://myserver.com/myapp/readpoints.php"];
NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:usuario, #"userNa", nil];
NSData *postData = [self encodeDictionary:postDict];
// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
__block NSArray *pointsArray = [[NSArray alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
// Peform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error) {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"HTTP Error: %d %#", httpResponse.statusCode, error);
return;
}
return;
}
NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
pointsArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
if (handler)
handler(pointsArray);
});
}
I cannot use the self.usersPointsArray with the initial objects, only with the finalized object. It wont always be 2 elements, i actually dont know how many it will be.
What would be the way to structure it so I get a final call when the self.usersPointsArray is complete and then I reload my tableview?
I think of your problem as a standard consumer-producer problem. You can create a queue count for the amount of items that will be processed (int totalToProcess=self.usersArray.count). Each time the completion handler is hit, it will do totalToProcess--. When totalToProcess reaches 0 you have processed all of the elements in your queue and can refresh your table.
If I understand your question correctly I believe this solves your problem. If not, hopefully I can with a bit more information.

Why loading data Indicator is not working properly?

In my code I am using an UIActivityIndicatorView on an UIAlertView. It is working fine but my problem is it is not showing up on correct time. I mean to say when the device get data from web service after that this loading indicator is appearing in the end and its not rite thing I think because I want it to be appear when the web service is sending or receiving data.
I need help as I am new to iOS app development. If there is any other easy way to do this thing then suggest me.
I hope my question is clear, my problem is according to this code the loading indicator is appearing after i get reply from web service but i want to run this indicator as the user will press update button and web service should be called after that. Tell me where i am wrong.
Here is the code I am using
-(IBAction)update:(id)sender
{
av=[[UIAlertView alloc] initWithTitle:#"Updating Image..." message:#"" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
ActInd=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[ActInd startAnimating];
[ActInd setFrame:CGRectMake(125, 60, 37, 37)];
[av addSubview:ActInd];
[av show];
{
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
int gid=[defaults integerForKey:#"gid"];
NSString *gameid=[NSString stringWithFormat:#"%i", gid];
NSLog(#"%#",gameid);
img=mainImage.image;
NSData *imgdata=UIImagePNGRepresentation(img);
NSString *imgstring=[imgdata base64EncodedString];
NSLog(#"%#",imgstring);
NSString *escapedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)imgstring,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8);
NSLog(#"escapedString: %#",escapedString);
#try
{
NSString *post =[[NSString alloc] initWithFormat:#"gid=%#&image=%#",gameid,escapedString];
NSLog(#"%#",post);
NSURL *url=[NSURL URLWithString:#"http://mywebspace/updategameimage.php"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"Response code: %d", [response statusCode]);
if ([response statusCode] >=200 && [response statusCode] <300) {
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
SBJsonParser *jsonParser = [SBJsonParser new];
NSDictionary *jsonData = (NSDictionary *) [jsonParser objectWithString:responseData error:nil];
NSLog(#"%#",jsonData);
NSInteger type = [(NSNumber *)[jsonData objectForKey:#"type"] integerValue];
NSLog(#"%d",type);
if (type==1) {
[self alertStatus:#"You can Keep on Drawing" :#"Sketch Updated"];
}
}
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
[self alertStatus:#"Unable to connect with game." :#"Connection Failed!"];
}
}
[av dismissWithClickedButtonIndex:0 animated:YES];
[av release]; av=nil;
}
UI updates are done on main thread. You have started activity indicator on main thread.It's fine.
Now, you are making synchronous network call on main thread. It should be asynchronous. Here until you will receive the response from network call, your main thread will remain busy and your UI will not be updated.
To update the UI, you can either make the network call asynchronous or you can start the activity indicator in a separate function and then delay the call of network activity by performselector:afterdelay method.
You can use GCD, Raywenderlich Tutorial
-(IBAction)update:(id)sender
{
/*
Setup indicator and show it
*/
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
/*
Do network call
*/
dispatch_async(dispatch_get_main_queue(), ^{
/*
Update UI
*/
});
});
}

Resources