So just like the question suggests, I'm trying not to freeze up the UI after the user sends some data to the server. In my case, they're possibly sending a lot of data and server side I have to do a foreach loop for multiple queries.
While all that is happening, I don't want the user to wait for a response so I'm dismissing the modal VC after "Send" is clicked. The data still gets inserted into the database but what if there's an error? Right now I'm showing a UIAlertView after the modal VC is dismissed but I get a bad access error. What's the best way of showing an error?
- (void)send:(id)sender{
if ([[Data sharedInstance].someData objectForKey:#"time"] != NULL) {
[self dismissViewControllerAnimated:YES completion:^(){
NSMutableDictionary *paramDic = [NSMutableDictionary new];
[paramDic setValue:[[Data sharedInstance].someData objectForKey:#"oneArray"] forKeyPath:#"oneArray"];
[paramDic setValue:[NSArray arrayWithObjects:[[[Data sharedInstance].someData objectForKey:#"two"] valueForKey:#"Name"], [[[Data sharedInstance].someData objectForKey:#"two"] valueForKey:#"State"], [[[Data sharedInstance].someData objectForKey:#"two"] valueForKey:#"Country"], nil] forKeyPath:#"twoArray"];
[paramDic setValue:[[[Data sharedInstance].someData objectForKey:#"three"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKeyPath:#"three"];
[paramDic setValue:[[NSUserDefaults standardUserDefaults] valueForKey:#"username"] forKey:#"username"];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:paramDic options:NSJSONWritingPrettyPrinted error:nil];
NSURL *url = [NSURL URLWithString:#"http://localhost/myapp/handleData.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSString *length = [NSString stringWithFormat:#"%d", jsonData.length];
[request setValue:length forHTTPHeaderField:#"Content-Length"];
[request setValue:#"json" forHTTPHeaderField:#"Data-Type"];
[request setHTTPBody:jsonData];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if (![httpResponse statusCode] == 200 || ![[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding] isEqualToString:#"success"]) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:#"Problem on the server. Please try again later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
}];
}];
}
That's how I'm doing it now...what's a better way?
The above answer is valuable. But I think this is what causes the problem.
Change:
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:#"Problem on the server. Please try again later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
To:
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:#"Problem on the server. Please try again later." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
Hope this helps.
I'm thinking the issue has to do with your nested completion blocks. Because you place your web service call in the modal dismissal completion block, by the time it finishes and starts to execute your URL completion block, the class/controller in charge of that block has already been forgotten/destroyed/deallocated/whatever. At the very least, the view no longer exists where you're trying to present your alert view. You've listed self as the delegate of that alert view, but self doesn't exist anymore.
Try this first: instead of trying to display your alert on a no-longer-in-memory view from a background thread (big no-no even if the view still existed), try posting a notification to the main thread that you can pick up elsewhere in your app (such as your root view controller) and present the alert from there:
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if (![httpResponse statusCode] == 200 || ![[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding] isEqualToString:#"success"]) {
dispatch_async(dispatch_get_main_queue(),^{
// observe this notification in root view controller or somewhere
// that you know will be in memory when it fires
[[NSNotificationCenter defaultCenter] postNotificationName:#"ErrorAlert"
object:nil
userInfo:imageDict];
});
}
}];
If that still doesn't work because the app has discarded your completion block when the view was dismissed, you will need to create a separate class that is in charge of sending this web service request instead of doing it all directly in the completion block. But still, remember to present your alert on the main thread :)
Related
This problem has already been mentioned elsewhere but the solution was dealing with error. And here I think I manage my error correctly (but it sound like I am mistaken...).
I've got something strange I cannot figure out. My app crashes when I check if a server is not responding (offline) and not by getting back a JSON object. In my memories this was working before I dealt with iOS 9.2.
Using exception breakpoint, I can see it has to do with a data parameter which is "nil" when creating a dictionary (which is normal I guess since the server is offline and not returning anything) but the crash should be avoided by managing the error... which does not seem to be the case in my code.
A few lines:
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; // url with php script have been set before...
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[noteDataString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask * dataTask =[defaultSession dataTaskWithRequest:urlRequest completionHandler:^(NSData *dataRaw, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:dataRaw
options:kNilOptions error:&error];
// I thought this condition below should manage the error when the json dictionary cannot be set because of the nil "dataRay" paramater but my app crashes.
if (error) {
UIAlertView * av = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:#"Checking permission:\nGot error %#.\n", error] message:nil delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[av show];
// (I know UIAlertView is deprecated in iOS 9.0 :-)...)
}
NSString *status = json[#"status"];
if([status isEqual:#"1"]){
//Success in php script
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"It works" message:nil delegate:self cancelButtonTitle:#"Cool" otherButtonTitles:nil, nil];
[av show];
}
It sounds like the if (error) condition does not prevent from crashing...
Anyone could help me?
Thanks!
You should just add a if block after before initizaling your JSON. Such as:
if(rawData != nil) {
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:dataRaw
options:kNilOptions error:&error];
}
After that you can handle your error if it is not nil.
I am developing a login application. I have a registration view, I'm using sendAsynchronousRequest to send registration request.When I received a data back I want to show alert view displaying the registration if valid. Now my problem is When I received a data back the UI get blocked until alert view display.
why is that happen and how could I fix it ?
check the the code snippet:
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
error = connectionError;
NSMutableDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil ];
if ([dic count] == 1) {
[dic valueForKey:#"RegisterUserResult"];
UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:#"Registration" message:[dic valueForKey:#"RegisterUserResult"] delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:nil];
[alertview show];
}else {
UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:#"Registration" message:[dic valueForKey:#"ErrorMessage"] delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:nil];
[alertview show];
}
[self printData:data];
You try to perform UI operations on non-main thread because completion block executes on you custom operation queue.
You need to wrap all UI calls in
dispatch_async(dispatch_get_main_queue(), ^{
......
});
within custom operation queue non-main threads.
So, in your code any call for UIAlertView will looks like
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:#"Registration" message:[dic valueForKey:#"RegisterUserResult"] delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:nil];
[alertview show];
});
If you have no heavy operations in your completion handler you can dispatch it on main thread directly setting queue to [NSOperationQueue mainQueue]
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {...}];
I am performing JSON POST request by clicking UIButton and after the submission, segue perform can be done. So, After submitting values, I cannot perform POST request anymore. It shows status code 200 and response is OK. But, data is not reflected in the Backend. Here is my code:
(IBAction)transitsurveydone:(id)sender {
if([tempArray count]!=0){
/* alert= [[UIAlertView alloc] initWithTitle:#"Survey Submission"
message:#"Please Enter your location"
delegate:self
cancelButtonTitle:#"Modify"
otherButtonTitles:#"Submit",nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
alert.tag=2;
[alert show];*/
NSLog(#"Caption array is %# %#",captionArray,tempArray);
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"myURL"]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSMutableDictionary *postDict = [[NSMutableDictionary alloc]init];
NSMutableDictionary *d=[[NSMutableDictionary alloc] initWithObjectsAndKeys:#10,#"\"Bus\"", nil];
NSMutableArray *m=[[NSMutableArray alloc] init];
NSString *str1,*str2,*str3,*str4;
// Checking the format
if(tempArray.count==1){
for (int x=0; x<1; x++) {
str1=[NSString stringWithFormat:#"\"%#\"",[captionArray objectAtIndex:x]];
[d setObject:[tempArray objectAtIndex:x] forKey:str1];
}
[request setHTTPBody:[[NSString stringWithFormat: #"{\n \"instance\" : %#,\n \"response_method\": \"web\",\n \"routes\": [\n {%#:%#}\n ]\n}",randomString,str1,[d objectForKey:str1] ]dataUsingEncoding:NSUTF8StringEncoding]];
}
NSLog(#"%#:%#",str1,[d objectForKey:str1]);
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
// Handle error...
return;
}
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSLog(#"Response HTTP Status code: %ld\n", (long)[(NSHTTPURLResponse *)response statusCode]);
NSLog(#"Response HTTP Headers:\n%#\n", [(NSHTTPURLResponse *)response allHeaderFields]);
}
NSString* body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response Body:\n%#\n", body);
}];
[task resume];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *respData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"~~~~~ Status code: %d", [response statusCode]);
if([response statusCode]==200){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"Transit Survey submitted" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
alert.transform = CGAffineTransformMakeTranslation( 10, 740);
[alert show];
[self performSelector:#selector(dismissAlert:) withObject:alert afterDelay:1.0f];
submitteddonthide=NO;
}
else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"Transit Survey Submission Failed" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
alert.transform = CGAffineTransformMakeTranslation( 10, 740);
[alert show];
[self performSelector:#selector(dismissAlert:) withObject:alert afterDelay:1.0f];
}
if([prefs integerForKey:#"humandone"]==1){
[self performSegueWithIdentifier:#"transittohuman" sender:nil];
}
else{
[self performSegueWithIdentifier:#"gobackfromtransit" sender:nil];
}
}
`
The above code is in IBAction
Your code looks fine and there is no any issues.
If this issue is happens all the time, add "Advanced Rest Client" add-on to chrome browser and test your server URL passing respective input values. If this process also can't be able to update values on your backend there should be some issue on your backend.
A couple of thoughts:
You are using status code to determine whether the server inserted the data correctly or not. You should actually have your web service build an affirmative response (in JSON, would be great) that says whether data was successfully inserted or not. You should not be relying solely on the web server's request response code.
I would observe the request with something like Charles and make sure it looks OK.
You're building a JSON request manually, which is very fragile. I would suggest using Charles to observe the request, and copy and paste it into http://jsonlint.com and make sure it's OK.
Even better, use NSJSONSerialization which is more robust and easier to use.
Also, you're sending this request twice. Lose this second request (you shouldn't do synchronous requests, anyway) and put all of your confirmation logic inside the session's completion handler block.
Yes. After struggling a bit, Clearing cookies helped me a lot :-
Here is a chunk of code, which is pretty much simple
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [cookieStorage cookiesForURL:[NSURL URLWithString:urlString]];
for (NSHTTPCookie *cookie in cookies) {
NSLog(#"Deleting cookie for domain: %#", [cookie domain]);
[cookieStorage deleteCookie:cookie];
}
Thank you Mr. reddys and Rob for the suggestions
Correct me if I am wrong. please easy go on me. I am not expert in iOS.
I tried sending HTTP Request with two approaches.
1)
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
The response is so quick, the time delay to get response is constant. One additional effort is to handle the response data with delegate mechanism.
Hence I approached with (2)
2).
[NSURLConnection sendAsynchronousRequest:theRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
NSString *strData = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog (#" THE RESPONSE DATA IS %#", strData);
UIAlertView *alView = [[UIAlertView alloc] initWithTitle:#"Process Done" message:#"MY Message" delegate:nil cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
[alView show];
NSMutableData *mData = [NSMutableData dataWithData:data];
[self parseXML:mData]; // XML Parsing.
}];
With the (2) approach, I could see inconsistent delay of getting response.
My Understanding
Since I queue the block in NSOperationQueue, it is not guaranteed to be executed immediately.
If my understanding is correct, then what is the correct approach so, that the queued operation is handled immediately.
I have read all detail about stripe integration in iOS app from here. And run the sample app of stripe by downloading from here.
On running this app, I am getting following error when I am testing on iPhone Simulator.
Following method is calling after token received::
- (void)hasError:(NSError *)error
{
UIAlertView *message = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Error", #"Error")
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"OK")
otherButtonTitles:nil];
[message show];
}
//--Called after sucessfully token received ::
- (void)hasToken:(STPToken *)token
{
NSLog(#"Received token %#", token.tokenId);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://example.com"]];
request.HTTPMethod = #"POST";
NSString *body = [NSString stringWithFormat:#"stripeToken=%#", token.tokenId];
request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding];
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
if (error) {
[self hasError:error];
} else {
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"\nStr is :::%#",str);
NSLog(#"\n\nResponse is :::%#",response);
[self.navigationController popViewControllerAnimated:YES];
}
}];
}
Please tell me how to resolve this problem and make a credit card payment on iOS app because in this sample code, there is information about payment.
You are using example.com. Replace it first with your own server address.
For charging a card in Stripe you need a server module. Here is detail explanation of how to charge your card via server module. If you are using parse as backend you can always use cloudcode for this purpose.
Here is raywenderlich's explanation of making payment system with python backend.