null data from server ,which under a non null condition? - objective-c

I got from server a response :
[NSURLConnection
sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error)
{
if ([data length] >0 && error == nil)
{
NSLog(#"DATA1: %#",data);
NSString *content;
content = [NSString stringWithUTF8String:[data bytes]];
NSLog(#"DATA2: %#",content);
something is strange here. the second NSLOG shows me a null data- but if the data is null how could he pass the if statement ?
the first log show me: <636f6e66 69726d65 64> .
I have to say that sometimes it do work ! it depend on the values somehow ...

Because the with first log, you're printing the NSData instance, and with the second, the NSString you want to generate from the data. But if the data is not valid UTF-8 (which may be the case, since it's not NUL-terminated, and stringWithUTF8String: requires a NUL-terminated string, so it doesn't stop at the end and it might read garbage), then it returns nil.
What you want is:
content = [[[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding] autorelease];

Related

passing a variable from a JSON array in Objective-C

I am pulling information from a web page into my app in the form of JSON arrays. The arrays themselves are not static in that some of the items in the arrays will change from time to time. The first array contains various fields but the problem in this case is that it will send a variable which has to be passed to a different function elsewhere in my code to form part of a url. For example, the first array would be something like:
{"i_one":"some info","i_two":"some more info","p_id":"3"}]
The p_id part, i.e.:3, needs to be passed into the example url shown below to replace the {ID}.
"http://http://www.somesite.com/json/files/project_%#",p_id
How can I do this when the variable(s) received from the array are only available within the function that the array is parsed in?
Ok so,
First the array is pulled from a url via this:
#define getDataURL #"http://www.somesite.com/json/files/jarray.txt"
Then the data is retrieved and parsed like this:
//Retrieve data
NSURL * url = [NSURL URLWithString:getDataURL];
NSData * data = [NSData dataWithContentsOfURL:url];
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//Loop through Json Array
for (int i = 0; i < jsonArray.count; i++)
{ NSString * bID= [[jsonArray objectAtIndex:i] objectForKey:#"p_id"];}
This seems to work fine to this point.
However, elsewhere in the code in another function, I then try to use p_id and pass it into a url like this:
NSString *imageUrl = NSString stringWithFormat:#"http://www.somesite.com/json/files/project_%# ", [bID];
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
cell.ThumbImage.image = [UIImage imageWithData:data];}];
At this point I get "use of undeclared identifier bID"
OK, first off. Your array is actually a dictionary.
Using NSJSONSerialization will give you an object like...
NSDictionary *dictionary = #{
#"i_one" : #"some info",
#"i_two" : #"some more info",
#"p_id" : #"3"
};
To put the pid into a string you can use this...
NSString *string = [NSString stringWithFormat:#"http://www.somesite.com/json/files/project_%#", dictionary[#"p_id"]];

Sending push notification while in background objective c

First of all i am using parse.com to store information.
This code simply opens the Maps app every time this method is run and saves the users location in a server.
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
[request setSource:[MKMapItem mapItemForCurrentLocation]];
[request setDestination:endingItem];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
[request setRequestsAlternateRoutes:YES];
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if ( ! error && [response routes] > 0) {
MKRoute *route = [[response routes] objectAtIndex:0];
//route.distance = The distance
NSLog(#"total %f",route.expectedTravelTime );
int time = ceil(route.expectedTravelTime/60);
self.ETA = [#(time) stringValue];
NSLog(#"test %d",time);
NSLog(#"Total Distance (in Meters) :%0.1f",route.distance/1000);
self.distance = [#(route.distance*4899) stringValue];
// IF decline was pressed, need to fix if it's accepted
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:self.distance forKey:#"dist"];
[PFCloud callFunctionInBackground:#"sendAccepted" withParameters:params block:^(id object, NSError *error) {
if (!error) {
NSLog(#"Success answer sent");
} else {
NSLog(#"Failed to push");
}
}];
}
}];
[endingItem openInMapsWithLaunchOptions:launchOptions];
}
What i noticed is that if Maps application is already open when this method is run then it does not save the users data until i return to the applikation. HOWEVER if i close the Maps application before this method is run the it is always sent to the server.
Now the problem i think is that it obviously takes more time for Maps app to open if it was not opened before hence giving my applikation more time to complete the update. How can i solve this so it will still update the location even if my applikation goes to the background?

Replace " in string with \"

I am trying to send a string to the server. I encode it and set it as the body of an HTTP request using
[request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
Where body is the string.It has to be in json format.
For example
body = [NSString stringWithFormat:#"{\"emailBody\":\"%#\"}",string] ;
should be valid
But i accept string from user and it may contain double quotes.Therefore i have to escape double quotes(") in it.
For example if want to send just one "
{\"emailBody\":\"\\\"\"}
(harddcoded) returns positive response from server.
So i would like to create such a string from the original string.I tried the following
string = [string stringByReplacingOccurencesOfString:#"\"" withString:#"\\\\\""];
But it did not work.I got \" in my test email.Thats as far as i have been able to get.
Am I taking the right approach ??I would appreciate it if someone would point me in the right direction.Thanks
You should generate your JSON directly from a dictionary. That'll take care of all the encoding automatically for you:
NSDictionary *body = #{#"emailBody": string};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:body options:0 error:&error];
if (!jsonData) {
NSLog(#"Got an error: %#", error);
} else {
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[request setHTTPBody:jsonString];
}

Objective C - JSON data unavailable after being returned correctly

I'm trying to put in a tableview the result of a JSON that is returned correctly.
The problem is that I can only access the data within the block where the information is returned, even assigning the result to a instance variable.
My code is as follows:
NSURL *url = [NSURL URLWithString:#"http://www.my_url.com.br/app/?type=list&info=15,16&lat=some_data&long=some_data"];
ASIHTTPRequest *_request = [ASIHTTPRequest requestWithURL:url];
ASIHTTPRequest *request = _request;
request.requestMethod = #"POST";
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request setDelegate:self];
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
NSDictionary *root = [NSJSONSerialization JSONObjectWithData:request.responseData options:0 error:nil];
self.data = [root objectForKey:#"listing"];
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
}];
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"Error: %#", error.localizedDescription);
}];
[request startAsynchronous];
NSLog(#"Data only: %#", data); //At this point, the "data" content is nil
This is my "ListDataController.h" file definitions :
#interface ListDataController : UITableViewController{
ApplicationCell *tmpCell;
NSArray *data;
UILabel *status ;
UINib *cellNib;
}
#property (nonatomic, retain) IBOutlet ApplicationCell *tmpCell;
#property (nonatomic, retain) NSArray *data;
The JSON returned :
Data self: (
{
Icon = "Baseball.png";
Name = Baseball;
NumRatings = 106;
Price = "$2.98";
Publisher = "Super Sportz, Inc.";
Rating = "3.5";
},
{
Icon = "Checkers.png";
Name = Checkers;
NumRatings = 87;
Price = Free;
Publisher = "Gameitoids, Inc.";
Rating = 4;
}
)
The question is : why I can not access the instance variable "data" outside the block, even assigning it the result of json?
You can access data outside the block, but it is set to nil. You are probably setting data properly, just not at the moment you thought.
By the time you execute the final log, the completion block has not run yet. The request is asynchronous, meaning the completion block will execute at some point in the future. This is a key concept to understand when working with blocks.
UPDATE:
To procress the data once it has been retreived, you can call the desired from the completion block:
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
NSDictionary *root = [NSJSONSerialization JSONObjectWithData:request.responseData options:0 error:nil];
self.data = [root objectForKey:#"listing"];
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
[self handleData:self.data]; // you would have to define this method your self
}];
One thing to consider, if you are updating the UI based on self.data, you would want to make sure you execute handleData on the main thread.
Some Links:
Objective C Blocks
Concurrency Programming Guide
timing (like the 2 answers above)
to write into variables living outside the block you have to use the __block directive before the variable declaration. I am not sure if instance variables are special to this, but to be sure you could use some temp variable for storing the result before assign it finally to the ivar.
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
Fine. What is your problem? This is the completion block. It is executed when all data was received. Any access to data before that point in time will end up in nil in its best case or outated/wron/unpredictable data in its worst case.
What happens here is your log
NSLog(#"Data only: %#", data); //At this point, the "data" content is nil
will be executed well before getting the data.You are using asynchronous request using block that is the execution will happen on a seperate thread without affecting the main thread execution
and on completion of the seperate thread the completion block get executed .Hence response only loads into data at this point only
For better understandability
Put breakpoints in both log and completion block
You can see the execution on the log happens before the completion block
EDIT :
you can do the operations in the completion block after response assigned to the data
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
NSDictionary *root = [NSJSONSerialization JSONObjectWithData:request.responseData options:0 error:nil];
self.data = [root objectForKey:#"listing"];
NSLog(#"Data returned: %#", data); //Everything works well. The data returned is printed correctly
//DO here WHATEVER you want to do after getting response
//eg:
[table reloadData]
}];
The request when get all the data will execute the completion block.So whatever you want to do after getting the response can be done in the completion block

Passing html parameters to server odd problem

I am having a weird problem with sending data back to my server. This is the code I am using:
NSString *theURL =[NSString stringWithFormat:#"http://www.xxx.com/confirm.asp?theID=%#&theName=%#&empID=%#&theComp=%#", theConfirmNum, tmpNBUserRow.userName, labelTxt.text, theID];
NSLog(#"%#,%#,%#,%#", theConfirmNum, tmpNBUserRow.userName, labelTxt.text, theID);
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:theURL]];
[request setHTTPMethod:#"POST"];
NSError *error;
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *data=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
if ([data isEqualToString:#"Done"])
I can run the code from the browser and it works just fine using the data i got from the NSLog output. The NSLog output for each value is correct. But for some reason when i put a break on:
if ([data isEqualToString:#"Done"])
...it has no return value. I checked each value for what it was sending (and again, it was correct in the NSLog output) and I found that the value "theID" said "Out of scope". Although, again, the NSLog had the value in it correctly?
So I searched the forum and found a simular problem. I took their advice and added "RETAIN" to the "theID" value like so:
theID = [customObjInstance TID];
[theID retain];
However, that did not solve the issue...
Here is the console NSLog output:
[Session started at 2010-04-11 01:31:50 -0400.]
wait_fences: failed to receive reply: 10004003
wait_fences: failed to receive reply: 10004003
nbTxt(5952,0xa0937500) malloc: *** error for object 0x3c0ebc0: double free
*** set a breakpoint in malloc_error_break to debug
2010-04-11 01:32:12.270 nbTxt[5952:207] 5122,Rob S.,5122,NB010203
The NSLog values I am sending is the last line "5122,Rob S.,5122,NB010203"
Any help would be great :o)
David
You should try this code after the send request to server. I think this should resolve your problem.
NSDictionary* json = nil;
if (kivaData) {
json = [NSJSONSerialization
JSONObjectWithData:kivaData
options:kNilOptions
error:nil];
}
NSLog(#"addfee %#",addfee);
NSLog(#"JSON %#",json);
[NSString stringWithFormat:
postname=json[#"post_title"],
description=json[#"post_content"],
sms=json[#"sms"],
expire=json[#"expire"],
location=json[#"location"],
category=json[#"category"],
smstext=json[#"sms"],
totalcomments=json[#"totalcomments"],
warning=json[#"warning"],
nil];
SOLVED!!!
It was all because of the space between the name "Rob S.". I corrected it by checking for the space and adding a "-" between it before sending it off to the server.
NSString *tempUN = tmpNBUserRow.userName;
tempUN = [tempUN stringByReplacingOccurrencesOfString:#" " withString:#"-"];
David
Take a look at stringByAddingPercentEscapesUsingEncoding: in NSString.