I am working on iOS application.In one scenario I am sending phone number and some text message to server like this
- (void)postData:(NSString *)message :(NSString *)mobileno
{
NSLog(#"postData is called");
#try
{
NSString *urlString = [NSString stringWithFormat:#"http://anlgelist.commonshell.net/api/account"];
NSURL *url = [[NSURL alloc] initWithString:urlString];
NSMutableURLRequest *urlRequest1=[NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSMutableDictionary *postDictionary=[[NSMutableDictionary alloc]init];
[postDictionary setValue:message forKey:#"Message"];
[postDictionary setValue:mobileno forKey:#"MobileNumber"];
SBJsonWriter *jsonWriter = [SBJsonWriter new];
NSError *error = nil;
NSLog(#"~~~~~~~~ postDictionary is %#",postDictionary);
NSString * jsonDataString =[jsonWriter stringWithObject:postDictionary];
if ( ! jsonDataString )
{
NSLog(#"Error: %#", error);
}
NSData *requestData = [NSData dataWithBytes:[jsonDataString UTF8String] length:[jsonDataString length]];
[urlRequest1 setHTTPMethod:#"PUT"];
[urlRequest1 setValue:#"application/json" forHTTPHeaderField:#"content-type"];
[urlRequest1 setHTTPBody: requestData];
connection1 = [NSURLConnection connectionWithRequest:urlRequest1 delegate:self];
}
#catch (NSException *exception)
{
////NSLog(#"exception is %#",exception);
#throw exception;
}
}
I am continuously calling this web service to send the details.But sometimes I am getting the following error and app getting crashed.
<Error>: -[__NSCFDictionary _expandedCFCharacterSet]: unrecognized selector sent to instance 0x14fdf350
I had not faced this kind of error previously.I googled but not found the solution.
Crash Report is
I am not sure it will work fine, but in your case the object has released and you try to access that, So i have prefered this solution,please try this.
SBJsonWriter *jsonWriter = [[SBJsonWriter new]retain];
Related
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];
}
}
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.
I have the following code to get data from server;
-(void)loginForFaceBook
{
GTMOAuth2ViewControllerTouch *viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:#"https://www.googleapis.com/auth/plus.me"
clientID:#"27615...6qdi60qjmachs.apps.googleusercontent.com"
clientSecret:#"Fs8A...u2PH"
keychainItemName:#"OAuth2 Sample:
Google+"
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[[self navigationController] pushViewController:viewController
animated:YES];
}
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (error != nil) {
// Authentication failed (perhaps the user denied access, or closed the
// window before granting access)
NSLog(#"Authentication error: %#", error);
NSData *responseData = [[error userInfo] objectForKey:#"data"]; //
kGTMHTTPFetcherStatusDataKey
if ([responseData length] > 0) {
// show the body of the server's authentication failure response
// NSString *str = [[NSString alloc] initWithData:responseData
// encoding:NSUTF8StringEncoding];
// NSLog(#"%#", str);
}
// self.auth = nil;
} else {
// NSString *authCode = [NSString alloc]in;
NSMutableURLRequest * request;
request = [[NSMutableURLRequest alloc] initWithURL:[NSURL
URLWithString:#"http://api.kliqmobile.com/v1/tokens"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60] ;
NSLog(#"%#",auth);
NSLog(#"ho gya success %# :::: %# :::: %#", auth.accessToken,
auth.refreshToken, auth.code);
NSMutableURLRequest * response;
NSError * error;
request.URL = [NSURL URLWithString:#"http://api.kliqmobile.com/v1/tokens"];
NSString *post = [NSString stringWithFormat:#"
{\"token\":\"%#\",\"secret\":\"%#\",\"service\":\"%#\",\"handle\":\"%#\"}",
auth.accessToken,auth.code,#"google",nil];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding
allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d",[postData length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded"
forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
error = nil;
response = nil;
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request
delegate:self];
[connection start];
}
I have implemented the NSURLConnection delegtes method and data is printing well like this
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSMutableURLRequest * response;
NSError * error;
NSLog(#"Did Receive Data %#", [[NSString alloc]initWithData:data
encoding:NSUTF8StringEncoding]);
NSMutableURLRequest * requestContacts;
requestContacts = [[NSMutableURLRequest alloc] initWithURL:[NSURL
URLWithString:#"http://api.kliqmobile.com/v1/contacts"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60] ;
[requestContacts setHTTPMethod:#"GET"];
[requestContacts setAllHTTPHeaderFields:headers];
error = nil;
response = nil;
NSData* data1 = [NSURLConnection sendSynchronousRequest:requestContacts
returningResponse:&response error:&error];
NSLog(#"WE GET THE REQUIRED TOKAN DATA %# :: %# :: %#", [[NSString alloc]
initWithData:data1 encoding: NSASCIIStringEncoding], error ,response);
}
but after that my app get crashed and it is giving following error;
[NSHTTPURLResponse release]: message sent to deallocated instance 0xcb51070.
please suggest me how to do this.
A couple of thoughts:
What is the intent of your didReceiveData method? There are a bunch of issues here:
You really shouldn't be doing a synchronous network request in the middle of a NSURLConnectionDataDelegate method.
You shouldn't be doing synchronous requests at all, but rather do them asynchronously.
What is the connection between receiving data and your creation of this new request? You're not using the data in the request, so why do it here?
The typical pattern is:
The didReceiveResponse should instantiate a NSMutableData object in some class property.
The only function of didReceiveData should be to append the received data to the NSMutableData. Note, this method may be called multiple times before all the data is received.
In connectionDidFinishLoading, you should initiate any next steps that you take upon successful completion of the request. If you wanted to do start another asynchronous network request when the initial request is done, do that here.
In didFailWithError, you obviously handle any failure of the connection.
When you call connectionWithRequest, you should not use the start method. Only use start when you use initWithRequest:delegate:startImmediately: with NO for the startImmediately parameter. Otherwise the connection starts automatically for you and you're only starting it a second time.
Unrelated to your original question, but your creation of post string cannot be right. You're missing a parameter value. Even better, rather than creating JSON manually, use NSDictionary and then use NSJSONSerialization to make the NSData object containing the JSON from this dictionary. That's much safer:
NSDictionary *params = #{#"token" : auth.accessToken,
#"secret" : auth.code,
#"service" : #"google",
#"handle" : #""};
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
Clearly, supply whatever you need for the handle value.
A tangential process-related observation, but I'm wondering if you're taking advantage of everything Xcode offers. For example, your declaration of response as a NSMutableURLRequest but then using that as a parameter to sendSynchronousRequest should have generated a compiler warning. The same thing is true with your stringWithFormat for your post string (my third point). That should have generated a warning, too.
Neither of these are immediately relevant, but I wonder if you are failing to heed any other compile-time warnings. These warnings are your best friend when writing robust code and I would recommend resolving all of them. To go a step further, you should also run the project through the static analyzer ("Analyze" on "Product" menu, or shift+command+B), and resolve anything it points out, too.
I use the following code to create a dictionary based on a JSON string received from the server. (I have downloaded JSONKit and embedded it into the project). The code below returns a legal JSON string from the server (parsed well on Android) but crashes when I try to convert it to a dictionary.
- (IBAction)submit
{
bool useSSL = true;
char *c_url="http://(rest of URL)";
NSString* url = [NSString stringWithFormat:#"%s" , c_url];
url = [NSString stringWithFormat:#"%#%#%s", url, self.label.text, "/keys"];
NSString * response = [self getDataFrom:url];
NSDictionary *dict = [response objectFromJSONString]; //generates SIGABRT!!
NSLog(#"%#",dict);
NSString *success = [dict valueForKey:#"success"];
}
- (NSString *) getDataFrom:(NSString *)url{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setHTTPMethod:#"GET"];
[request setURL:[NSURL URLWithString:url]];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *responseCode = nil;
NSData *oResponseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&responseCode error:&error];
if([responseCode statusCode] != 200){
NSLog(#"Error getting %#, HTTP status code %i", url, [responseCode statusCode]);
return nil;
}
return [[NSString alloc] initWithData:oResponseData encoding:NSUTF8StringEncoding];
}
THANKS,
Simon
Found an answer here.
Answer says: "Figured it out... I had JSONKIt.h included in the project but for some weird reason, JSONKit.m was not included in the 'Compile Sources' under 'Build Phases' - once I added it manually it started working fine."
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
*/
});
});
}