Application UI hangs due to networking , iOS - ios

I am developing an iOS application where i am implementing push notifications.
In the AppDelegate and more specifically in the didRegisterForRemoteNotificationsWithDeviceToken function , after i register for push notifications , i make an http post request to send to my server the users credentials (cfuuid , os , etc..) and the push token.
When the server is app everything goes smoothly. However , if for any reason the server is unreachable the UI of the application hangs for around 30 seconds (till the connection timeOut) and the only thing i see is a white screen.
How could i separate the "networking" from the UI ? I guess the answer is by using another thread.
How exactly could i do that ? The only thing i am doing inside the didRegisterForRemoteNotificationsWithDeviceToken is use ASIHTTPRequest library to send the credentials to the server.
The code that needs to be executed in the different thread looks like this :
NSString *jsonString;
//jsonString = [[NSString alloc] initWithFormat:#"{\"deviceUUID\":\"%#\",\"os\":\"ios\", \"active\":\"%d\", \"pushToken\":\"%#\"}",deviceUUID,active,token];
jsonString = [[NSString alloc] initWithFormat:#"{\"deviceUUID\":\"%#\",\"os\":\"ios\", \"pushToken\":\"%#\"}",deviceUUID,token];
NSLog(#"%#",jsonString);
//NSString *urlStr= [[NSString alloc] initWithFormat:CITYINFO_SERVER_URL,#"push_notifications/register"];
NSString *urlStr= [[NSString alloc] initWithFormat:CITYINFO_SERVER_URL,#"register.php"];
//send json file , using ASIHttpClass
NSURL *url = [NSURL URLWithString:urlStr];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
request.timeOutSeconds = TIME_OUT_SECONDS;
[request setRequestMethod:#"PUT"];
//NSString *credentials= [self encodeCredentials];
//[request addRequestHeader:#"Authorization" value:[[NSString alloc] initWithFormat:#"Basic %#",credentials]];
[request addRequestHeader:#"Content-Type" value:#"application/json; charset=utf-8"];
[request appendPostData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[request startSynchronous];
if([request responseStatusCode]==200){
NSLog(#"Server reached. Response Status : 200");
return true;
} else {
NSLog(#"Server could not be reached");
return false;

[request startSynchronous];
Never. Do. Synchronous. Networking. On the main thread.
Move it to another thread, or even better, make the request async.

Instead of sending a synchronous request just send Asynchronous request
[request startAsynchronous];
And call the method which is hitting service after some time interval from delegate method.

use [request startAsynchronous];
instead of [request startSynchronous];

I think , you are use main thread for send device token to server,please use the secondary thread for this functionality
try this code :-
[self performSelectorInBackground:#selector(pushNotifictionJson) withObject:nil];
-(void) pushNotifictionJson
{
NSString *jsonString;
//jsonString = [[NSString alloc] initWithFormat:#"{\"deviceUUID\":\"%#\",\"os\":\"ios\", \"active\":\"%d\", \"pushToken\":\"%#\"}",deviceUUID,active,token];
jsonString = [[NSString alloc] initWithFormat:#"{\"deviceUUID\":\"%#\",\"os\":\"ios\", \"pushToken\":\"%#\"}",deviceUUID,token];
NSLog(#"%#",jsonString);
//NSString *urlStr= [[NSString alloc] initWithFormat:CITYINFO_SERVER_URL,#"push_notifications/register"];
NSString *urlStr= [[NSString alloc] initWithFormat:CITYINFO_SERVER_URL,#"register.php"];
//send json file , using ASIHttpClass
NSURL *url = [NSURL URLWithString:urlStr];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
request.timeOutSeconds = TIME_OUT_SECONDS;
[request setRequestMethod:#"PUT"];
//NSString *credentials= [self encodeCredentials];
//[request addRequestHeader:#"Authorization" value:[[NSString alloc] initWithFormat:#"Basic %#",credentials]];
[request addRequestHeader:#"Content-Type" value:#"application/json; charset=utf-8"];
[request appendPostData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[request startSynchronous];
if([request responseStatusCode]==200){
NSLog(#"Server reached. Response Status : 200");
return true;
} else {
NSLog(#"Server could not be reached");
return false;
}
}
and second method
use [request startAsynchronous];
instead of [request startSynchronous];

Related

NSURLRequest not posting values to a Codeigniter API

Good day,
I am trying to use a Codeigniter based API to connect with iOS and using NSURLRequest.
The API is in debugMode and for now it returns the same key value pair as json as the one that you are posting. I have tried posting the values to the link through postman and it works correctly, however when I post it through my iOS application, the json response is received but the array that should contain the post values is empty.
Here is the iOS Code snippet :
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#",BASEURL,service]];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSString * params = #"authkey=waris";
NSData * postData = [params dataUsingEncoding:NSUTF8StringEncoding];
NSString *postLength = [NSString stringWithFormat:#"%lu",(unsigned long)[postData length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];;
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
NSLog(#"Posting : '%#' to %#",params,url);
[connection start];
This is the response when I post the same parameters through postman ( A RESTFUL Client for Chrome )
{
"status": "1",
"data": {
"authkey": "warisali"
}
}
However when I query the same API from the above iOS Code I am getting this :
{
data = 0;
status = 1;
}
Any help on the matter will be highly appreciated!
I had same issue (not with CodeIgniter but with Ruby ...)
Try something like this, solved my problem.
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#",BASEURL,service]];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSDictionary *paramDict = #{#"authkey": #"waris"};
NSError *error = nil;
NSData *postData = [NSJSONSerialization dataWithJSONObject:paramDict options:NSJSONWritingPrettyPrinted error:&error];
if (error)
{
NSLog(#"error while creating data %#", error);
return;
}
NSString *postLength = [NSString stringWithFormat:#"%lu",(unsigned long)[postData length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];;
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
NSLog(#"Posting : '%#' to %#",params,url);
[connection start];
I ended up using the ASIHttpRequest + SBJson combo and that worked like Charm!
After adding the ASIHttpRequest core classes and SBJson Classes to parse the JSON, I was able to achieve what I wanted !
The problem is that because of the way you're creating the connection, it will start immediately, before you've finished configuring the request. Thus, you're creating a mutable request, creating and starting the connection, then attempting to modify the request and then trying to start the request a second time.
You can fix that by changing the line that says:
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
To say:
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
Or, easier, just move the original instantiation of the NSURLConnection (without the startImmediately:NO) after you've finished configuring your request, and then eliminate the [connection start] line altogether.

Objective-C URL Request while locking iPhone

I'm coding an iOS App which uses an URL Request to a PHP document to send and receive data. Here is my code.
NSString *myRequestString = [NSString stringWithFormat:#"userid=%#&recieverid=%#&messege=%#&type=%#&password=%#", ownID, _friendID, content_encoded, msg_type, password];
NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"something.php"]];
[request setHTTPMethod: #"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody: myRequestData];
//My activiy Indicator starts here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Now send a request and get Response
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil];
NSString *result = [[NSString alloc] initWithBytes:[returnData bytes] length:[returnData length] encoding:NSUTF8StringEncoding];
//Here some internal coding happens...
dispatch_async(dispatch_get_main_queue(), ^(void) {
//Stopping activity indicator
});
});
But if the user locks his Phone while he sends the data (possible in other apps like Snapchat etc as well) the app freezes when the user returns and has to reopen it.
I want to know if theres a better way of doing if the app connects to the Server and the user closes the app that doesn't let this error occur.
Thank you :)
Anton
And sorry about my poor english Im not a native speaker.
I'd suggest:
Specifying a background task identifier, as suggested by John Woods, so that if the user leaves the app in the middle of the request, it will attempt to continue the network request in the background.
Use sendAsynchronousRequest rather than dispatching sendSynchronousRequest to the background.
Make sure you correctly detect and handle errors (because it's not entirely clear to me whether the problem rests in the code of your question or whatever processing you're doing with it later).
Unrelated, but I'd avoid using the bytes-related NSData methods.
Thus:
// I'm guessing that you're percent encoding `messege` [sic], but I'd do it for all of those parameters (notably password)
NSString *myRequestString = [NSString stringWithFormat:#"userid=%#&recieverid=%#&messege=%#&type=%#&password=%#", ownID, _friendID, content_encoded, msg_type, password];
// Use `dataUsingEncoding` rather than bytes rendition:
//
// NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSData *myRequestData = [myRequestString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"something.php"]];
[request setHTTPMethod: #"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody: myRequestData];
// start background task
UIBackgroundTaskIdentifier __block task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}];
// activity indicator starts here
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!data) {
NSLog(#"%s: sendAsynchronousRequest error: %#", __PRETTY_FUNCTION__, connectionError);
} else {
// use NSData rendition rather than bytes rendition:
//
// NSString *result = [[NSString alloc] initWithBytes:[returnData bytes] length:[returnData length] encoding:NSUTF8StringEncoding];
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// here some internal coding happens...
}
// stop activity indicator here
// stop background task
if (task != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}
}];
You have to implement a background task. When the phone locks, all apps transition to a background "inactive" state. You need to implement a long running background task.
https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html

Present HUD before posting values to server

Hi I'm using this code to send post values to a server but I want the HUD to appear during the time the request is being done, because it appears only when it ends the request.
-(IBAction)sendk:(id)sender {
/*HUD*/
SLHUD *hudView = [SLHUD Mostrar:self.view]; // Creates a Hud object.
hudView.text = #"Please Wait"; // Sets the text of the Hud.
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.alpha = 1.0;
activityIndicator.center = CGPointMake(160, 280);
activityIndicator.hidesWhenStopped = NO;
[activityIndicator setTag:899];
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
/*FIN HUD*/
NSString *post =[[NSString alloc] initWithFormat:#"user=%#&pass=%#",[username text],[password text]];
NSLog(#"%#",post);
NSURL *url=[NSURL URLWithString:#"URL TO SERVER"];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding 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];
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"%ld",(long)[response statusCode]);
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"%#",responseData);
The problem is that the code is blocking the main thread until the network request finishes. The screen will only update after the sendk method returns, but the method won't return until the sendSynchronousRequest method is finished. The solution is to dispatch the networking code (everything after /*FIN HUD*/) onto a background thread, or use sendAsynchronousRequest, and use the completion block to notify the main thread when the response arrives.
The code framework for using a background thread looks like this
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// do networking stuff here
dispatch_async( dispatch_get_main_queue(), ^{
// turn off the HUD and remove the spinner here
// also do something with the network response here
});
});

Consume ASP web service with JSON response on iOS

I am trying to call a web service that is developed with ASP.NET. The purpose is to pass a username and password to the web service to simulate a log-in procedure.
In order to call the service i used the following method:
NSError *errorReturned = nil;
NSString *urlString = #"http:myDomain/myMethod?Operation=SignIn";
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod: #"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:#"test" forKey:#"userName"];
[dict setObject:#"test" forKey:#"passWord"];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:kNilOptions error:&errorReturned];
[request setValue:[NSString stringWithFormat:#"%d", [jsonData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: jsonData];
NSURLResponse *theResponse =[[NSURLResponse alloc]init];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&errorReturned];
if (errorReturned)
{
NSLog(#"%#", errorReturned);
}
else
{
NSString *retVal = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", retVal);
}
After running the app and clicking on the UIButton that fires the above method, nothing is shown in the console window.
The service returns the response in JSON format.
I want to know if i am missing something here since i am neither getting an error nor success log?
Any help would be appreciated!
Thank you.
Granit
A couple of thoughts:
If this method is getting called, you'd see something, even if retVal was empty and your
NSLog(#"%#", retVal);
just logged the app name and timestamp. Maybe change that NSLog to
NSLog(#"retVal = %#", retVal);
to remove any ambiguity. Or put in breakpoints in your code and single step through it to see what path the app takes.
Are you confident of your server interface? For example, is it possible that the Operation value of SignIn belongs in the JSON request, itself? Also, some services are case sensitive, so you might want to check that, too.
I don't know what access you have to the server, but it would be worthwhile to check the logs to make sure the request was received, possibly temporarily adding some logging within the code so you can confirm that the parameters were all received properly. Or, if nothing else, make sure that the server properly logs/reports any errors.
BTW, your instantiation of theResponse is unnecessary, and should just be
NSURLResponse *theResponse = nil;
The sendSynchronousRequest call doesn't populate an existing NSURLResponse instance, but rather creates a new instance and updates theResponse to point to it.
You should fix your request first, but you probably want, at the very least, to change this to use sendAsynchronousRequest instead of sendSynchronousRequest. You should never do synchronous calls on the main thread.
I solved my issue by using ASIHHTPRequest. Also i checked the server interface and it turned out that the parameters had to be sent with the URL.
-(void)signInAction:(id)sender{
NSURL *url = [NSURL URLWithString:#"http://mydomaain.com/UserService/SignIn/test/test"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDidFinishSelector:#selector(requestCompleted:)];
[request setDidFailSelector:#selector(requestError:)];
[request setDelegate:self];
[request setRequestMethod:#"GET"];
[request startAsynchronous];
}
- (void)requestCompleted:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
//[responseString UTF8String];
NSLog(#"ResponseString:%s",[responseString UTF8String]);
}
- (void)requestError:(ASIHTTPRequest *)request
{
NSError *error = [request error];
NSLog(#"Error:%#",[error description]);
}

ASIHTTPRequest delegation

im using ASIHTTPRequest to call webservice (soap):
-(void)callWebService:(NSString*)URL:(NSString*)SOAP{
NSURL *url = [NSURL URLWithString:URL];
NSString *SOAPMessage = [NSString stringWithFormat:SOAP];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
request.shouldAttemptPersistentConnection = NO;
[request setValidatesSecureCertificate:NO];
[request setRequestMethod:#"POST"];
[request appendPostData:[SOAPMessage dataUsingEncoding:NSUTF8StringEncoding]];
[request setDidFinishSelector:#selector(requestCompleted:)];
[request setDidFailSelector:#selector(requestFailed:)];
[request setDelegate:self];
[request startAsynchronous];
}
-(void)requestCompleted:(ASIHTTPRequest * )r{
NSString *responseString = [r responseString];
NSLog(#"%#",responseString);
}
-(void)requestFailed:(ASIHTTPRequest * )r{
NSError *Err = [r error];
NSLog(#"%#",Err);
}
If i call this in appDelegate.m, it works fine, requestCompleted handler throws the response...But when i use this same code in my own class it throws BAD ACCESS error, which i figured tells me i cannot delegate:self to handle response. if i setDelegate to appdelgate pointer (passed as ID sender) it works (and have handlers there). So why cant my own class handle its own events ? Im new to objective-c so i guess im missing something major here. Thanks
You have to have the requestCompleted and requestFailed in your "own class". Also that class has to live which means it can't be released while the service is being called. You have to save the instance of "your own class" in a strong/retained property or something.
Add this code to dealloc or viewWillDisappear
[request setDelegate:nil];
Check if your self is getting released while the delegate call happens. Make sure it is retained properly.

Resources