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
Related
I was successful on getting a reply from the server with my post method however, I have problem with downloading the xml file data.
Here is my Post method (that I've searched in stackoverflow)
//We begin by creating our POST's body as an NSString, and converting it to NSData.
NSString *post = [NSString stringWithFormat:#"device_unique_key=%#&provision_type=c", deviceUniqueKey];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
//Next up, we read the postData's length, so we can pass it along in the request.
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
//Now that we have what we'd like to post, we can create an NSMutableURLRequest, and include our postData.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"http://192.168.1.166/autoprovision/sip_setup/downloadSipSetup"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
//And finally, we can send our request, and read the reply by creating a new NSURLSession:
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *requestReply = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
xmlData = data; //This is the data that I try to donwload as xml file.
NSLog(#"requestReply: %#", requestReply);
}] resume];
I have xmlData = data //from the dataTaskRequest
Then here is my code for saving the xmlData to the Document's Directory
// Display the network activity indicator
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// Perform the request on a new thread so we don't block the UI
dispatch_queue_t downloadQueue = dispatch_queue_create("Download queue", NULL);
dispatch_async(downloadQueue, ^{
NSError* err = nil;
NSHTTPURLResponse* rsp = nil;
// Perform the request synchronously on this thread
NSData *rspData = [NSURLConnection sendSynchronousRequest:request returningResponse:&rsp error:&err];
// Once a response is received, handle it on the main thread in case we do any UI updates
dispatch_async(dispatch_get_main_queue(), ^{
// Hide the network activity indicator
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (rspData == nil || (err != nil && [err code] != noErr)) {
// If there was a no data received, or an error...
} else {
// Cache the file in the cache directory
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"init.xml"];
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
[xmlData writeToFile:path atomically:YES];
// Do whatever else you want with the data...
[self loadDataFromXML];
}
});
});
The problem is that the xmlData is not working when I parse it using NSXMLParser. I tried to use an xml file and put it inside my project and it parses the xml but the xmlData that I downloaded doesn't get parsed (parse delegates are not called). I think my way of downloading the file is wrong. Can someone help me?
I found the problem in my project. My codes were correct after all! I didn't see that the code that I followed uses NSCachesDirectory and when I call the xmlData I used NSDocumentDirectory that's why it doesn't get parsed.
In my project I need to send data to server, for that I've used the following code to achieve the task:
- (void)sendJSONToServer:(NSString *) jsonString
{
// Create a new NSOperationQueue instance.
operationQueue = [NSOperationQueue new];
//
// Create a new NSOperation object using the NSInvocationOperation subclass to run the operationQueueTask method
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(operationQueueTask:)
object:jsonString];
// Add the operation to the queue and let it to be executed.
[operationQueue addOperation:operation];
}//End of sendJSONToServer method
-(void) operationQueueTask:(NSString *) jsonString
{
//NSOperationQueue *remoteResultQueue = [[NSOperationQueue alloc] init];
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
dispatch_async(myQueue, ^{
// Performing long running process
// Sending json data to server asynchronously
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
});
}//End of operationQueueTask method
By the above code I'm able to send data and get response.
But when there is no internet the data will not be sent to server. How to handle this situation based on the response we get.
Let's say we get response success on fair condition ans false on worst condition.
UPDATED CODE FOR RETRIES
-(id)init
{
self = [super init];
if (self != nil)
{
//initialize stuffs here
pendingOperationQueue = [[NSMutableArray alloc] init];
operationQueue = [NSOperationQueue new];
}
return self;
}//End of init method
- (void)sendJSONToServer:(NSString *) jsonString
{
NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationQueueTask:) object:[NSString stringWithString:[pendingOperationQueue objectAtIndex:0]]];
[operation start];
}//End of sendJSONToServer method
-(void) operationQueueTask:(NSString *) jsonString
{
//NSOperationQueue *remoteResultQueue = [[NSOperationQueue alloc] init];
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
dispatch_async(myQueue, ^{
// Performing long running process
// Sending json data to server asynchronously
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_http://www/example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
if([[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] rangeOfString:#"true"].location == NSNotFound)
{
// Add the operation to the queue and let it to be executed.
NSLog(#"Failed To Add To Server, Rerunning the task");
}
else
{
NSLog(#"Successfully Added To Server");
NSLog(#"ADDED_DATA_TO_SERVER: %#", jsonString);
if([pendingOperationQueue count] > 0)
{
[pendingOperationQueue removeObjectAtIndex:0];
if([pendingOperationQueue count] > 0)
{
NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationQueueTask:) object:[NSString stringWithString:[pendingOperationQueue objectAtIndex:0]]];
[operation start];
}
}
}
}];
});
}//End of operationQueueTask method
Heads up! This is a long answer. TL;DR: You can't re-run an NSOperation, but you can design your classes and methods to make it easy to retry requests.
First a quick answer to your title question: you can't re-run an NSOperation, they're not designed to do that. From the docs:
An operation object is a single-shot object — that is, it executes its
task once and cannot be used to execute it again.
With that out of the way, lets take a look at what you're currently doing and clean it up a bit so that re-using it is easier. There's a ton of async stuff going on in there that you don't need; I'll go through it piece by piece.
Let's start with your operationQueueTask: method. The first thing you do in the method is:
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
That means that every time that method is called, you're creating a new dispatch queue. While you can do that if you really want to, that's not what dispatch queues are really designed for. A better idea would be to use one of the background queues that are already available:
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
Next you are dispatching a block asynchronously to that queue. That block:
Sets up your NSMutableURLRequest.
Calls [NSURLConnection sendAsynchronousRequest:...].
Dispatches another block (which has a comment in it about updating the UI) to the main queue.
1 and 2 are fine, I don't see anything you need to change there. 3, however, is problematic because of where that dispatch is being called. The way you have it setup now, NSURLConnection will fire off its asynchronous request and then, before that even has a chance to run, you fire off the block to the main queue to update the UI. What you need to do instead is fire off that block in the completion handler passed to [NSURLConnection sendAsynchronousRequest:...]. Like so:
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
}];
Now, notice the name of the method you're calling on NSURLConnection? sendAsynchronousRequest:. It actually handles queuing the request on a background queue for you. Which mean, you don't actually need (or want) all the dispatch_* stuff at the beginning of this method. With that in mind, we can reduce it down to:
-(void) operationQueueTask:(NSString *) jsonString
{
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
}];
} //End of operationQueueTask method
Now, on to your sendJSONToServer: method. You're doing a similar thing here that you did at the start of operationQueueTask:: you're creating a new NSOperationQueue each time it runs; that's also not needed (nor typically wanted). What you should probably be doing is creating that operationQueue when your class is initialized (it looks like it's already an instance variable on your class, so you're good there):
// NOTE: I'm just using a default initializer here; if you already have an initializer, use that instead
- (instancetype)init {
if (self = [super init]) {
operationQueue = [NSOperationQueue new];
}
return self;
}
That gets rid of your first line. Next, you're creating an NSInvocationOperation which calls operationQueueTask: and then adding it to your operationQueue. Since you've been re-creating your operationQueue every time, I'm going to assume that it isn't used for anything other than these server requests. In that case, you actually don't need to do this on your operationQueue at all because, as we discovered in the previous method, NSURLConnection is already handling all the background threading for you. In that case, we can actually just copy the code from operationQueueTask: to sendJSONToServer: and get rid of operationQueueTask: altogether. That makes it look like:
- (void)sendJSONToServer:(NSString*)jsonString {
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
}];
}
Note: We still need to keep operationQueue around since we pass it to [NSURLConnection sendAsynchronousRequest:... as the queue that it should run on.
So, how do we go about retrying the request when it fails? The simplest method is to add a recursive function that calls itself when the request fails. You'll pass this method the jsonString you want to send and the maximum number of times it should try to send it before it gives up for good.
To facilitate that, lets make one more change to you existing function: instead of handling the completion block inside the function, lets make the completion block a parameter you pass to the function so that it can be processed elsewhere.
- (void)sendJSONToServer:(NSString*)jsonString withCompletionHandler:(void (^)(NSURLResponse *response, NSData *data, NSError *connectionError))completionHandler {
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:completionHandler];
}
Now, lets build that recursive function. I'll call it:
- (void)sendJSONToServer:(NSString*)jsonString withRetryAttempts:(NSUInteger)retryTimes;
The basic flow will be:
Check if retryTimes is greater than 0
If it is, attempt to send the request to the server
When the request finishes, check the response for success
If successful, update the UI on the main queue
If not successful, subtract one from retryTimes and call this function again
That looks something like:
- (void)sendJSONToServer:(NSString*)jsonString withRetryAttempts:(NSUInteger)retryTimes {
if (retryTimes > 0) {
[self sendJSONToServer:jsonString withCompletionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"Response is:%#",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
if (/* check response to make sure it succeeded */) {
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(#"Thread Process Finished");
});
} else {
// Note: you can add a dispatch_after here (or something similar) to wait before the next attempt
// You could also add exponential backoff here, which is usually good when retrying network stuff
[self sendJSONToServer:jsonString withRetryAttempts:(retryTimes - 1)];
}
}];
} else {
// We're out of retries; handle appropriately
}
}
Note: There are some bits in there that are just comments because they are application specific; they'll need to be implemented before that code will compile/run.
Now, instead of calling [yourClass sendJSONToServer:jsonString], call: [yourClass sendJSONToServer:jsonString withRetryTimes:maxRetries] and, if the request fails, it should retry up to maxRetries times.
One last note: As #Deftsoft mentioned, Apple's Reachability classes are a nice way to know if you have an active connection to the network or not. It's a good idea to check that first before trying to call sendJSONToServer:withRetryTimes:. That way you're not trying to make requests when it's not possible to even connect in the first place.
You can Apple reachability classes below is the reference code which will provide you the better idea.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(networkChanged:) name:kReachabilityChangedNotification object:nil];
reachability = [Reachability reachabilityForInternetConnection];
[reachability startNotifier];
- (void)networkChanged:(NSNotification *)notification
{
NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
if(remoteHostStatus == NotReachable) { NSLog(#"not reachable");}
else if (remoteHostStatus == ReachableViaWiFiNetwork) { NSLog(#"wifi"); }
else if (remoteHostStatus == ReachableViaCarrierDataNetwork) { NSLog(#"carrier"); }
}
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
});
});
I implement the JSON Parsing as follow:
-(void)getallEvent
{
SBJSON *json = [SBJSON new];
json.humanReadable = YES;
responseData = [[NSMutableData data] retain];
NSString *service = #"/GetAllVenue";
NSString *str;
str = #"Calagary";
NSString *requestString = [NSString stringWithFormat:#"{\"CityName\":\"%#\"}",str];
//NSLog(#"request string:%#",requestString);
// NSString *requestString = [NSString stringWithFormat:#"{\"GetAllEventsDetails\":\"%#\"}",service];
NSData *requestData = [NSData dataWithBytes: [requestString UTF8String] length: [requestString length]];
NSString *fileLoc = [[NSBundle mainBundle] pathForResource:#"URLName" ofType:#"plist"];
NSDictionary *fileContents = [[NSDictionary alloc] initWithContentsOfFile:fileLoc];
NSString *urlLoc = [fileContents objectForKey:#"URL"];
urlLoc = [urlLoc stringByAppendingString:service];
//NSLog(#"URL : %#",urlLoc);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: urlLoc]];
NSString *postLength = [NSString stringWithFormat:#"%d", [requestData length]];
[request setHTTPMethod: #"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody: requestData];
// self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
NSError *respError = nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: &respError ];
if (respError)
{
NSString *msg = [NSString stringWithFormat:#"Connection failed! Error - %# %#",
[respError localizedDescription],
[[respError userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Check your network connection" message:msg delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
else
{
NSString *responseString = [[NSString alloc] initWithData:returnData encoding: NSUTF8StringEncoding];
NSDictionary *results = [[responseString JSONValue] retain];
//NSLog(#" %#",results);
NSString *extractUsers = [[results objectForKey:#"d"] retain];
NSDictionary *finalResult = [[extractUsers JSONValue] retain];
NSLog(#"Final Results : %#",finalResult);
listOfEvents = [finalResult objectForKey:#"List of Event details of given Venue"];
}
Using this code, it slow down the app. How can I parse the json in background?
*Is this right for Post Method? what is the difference between Post & Get Method?*
Is there any other way to json parsing?
You are using synchronous request which is executed on Main thread so if you need to do it in background use asynchronous loading.
POST METHOD:
The POST method generates a FORM collection, which is sent as a HTTP request body. All the values typed in the form will be stored in the FORM collection.
GET METHOD: The GET method sends information by appending it to the URL (with a question mark) and stored as A Querystring collection. The Querystring collection is passed to the server as name/value pair. The length of the URL should be less than 255 characters.
An HTTP GET is a request from the client to the server, asking for a resource.
An HTTP POST is an upload of data (form information, image data, whatever) from the client to the server.
Check this answer for more details : what-is-the-difference-between-post-and-get
You are making synchronous communication request which slows down the application. You should make the asynchronous request to keep your app responsive.
It is not having any concern with parsing JSON data.
I would recommend using AFNetworking in your context which will simplify the connection management, background queue execution and parsing of the JSON you are getting back form the server.
The code example below will create an HTTP client with a base URL (<hostname>) and get a JSON payload from a given path. The network request runs in the background and runs a given block when completing
httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
// set the type to JSON
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
// Activate newtork indicator
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
// Request the <path> from the server and parse the response to JSON
// this calls a GET method to <hostname>/<path>
[httpClient getPath:<your path> parameters:Nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
// responseObject is a JSON object here
//
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// handle error
}];
Get: With the get method the value is send through the query string appended with the url. So you can see the the name, value, description on the addressbar when the page display in the browser.
Post: This method transfer the information through the complete form. You can not see the detail description on the addresss bar. When the page display.
NSString *myUrlString =[NSString stringWithFormat: #"your url];
NSString *postdata=[NSString stringWithFormat:#"emailId=%#&password=%#,username,password];
NSLog(#"%#",postdata);
//create a NSURL object from the string data
NSURL *myUrl = [NSURL URLWithString:myUrlString];
//create a mutable HTTP request
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:myUrl];
//sets the receiver’s timeout interval, in seconds
[urlRequest setTimeoutInterval:30.0f];
//sets the receiver’s HTTP request method
[urlRequest setHTTPMethod:#"POST"];
//sets the request body of the receiver to the specified data.
[urlRequest setHTTPBody:[postdata dataUsingEncoding:NSUTF8StringEncoding]];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//Loads the data for a URL request and executes a handler block on an
//operation queue when the request completes or fails.
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error) {
if ([data length] >0 && error == nil){
//process the JSON response
//use the main queue so that we can interact with the screen
dispatch_sync(dispatch_get_main_queue(), ^{
[self parseResponse:data];
});
}
else if ([data length] == 0 && error == nil){
NSLog(#"Empty Response, not sure why?");
}
else if (error != nil){
NSLog(#"Not again, what is the error = %#", error);
}
}];
}
- (void) parseResponse:(NSData *) data
{
responseData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"JSON = %#", responseData);
NSLog(#"Response ==> %#", responseData;
Finally u get the response from that specific url .and what ever u wanted to it do ur own way.
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];