SBJSON parsing NSString to NSDictionary - objective-c

I'm trying to parse an NSString that contains JSON data into an NSDictionary using SBJson 3.0.4, but when I do it, I get this error:
"WebKit discarded an uncaught exception in the webView:shouldInsertText:replacingDOMRange:givenAction: delegate: -[__NSCFString JSONValue]: unrecognized selector sent to instance 0x6ab7a40"
As far as I know (which isn't very far), the JSON I'm getting is valid, so I don't know why this is happening. My code compiles fine too…
Here it is:
NSString *tempURL = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?address=%#&sensor=true",userInput.text];
NSURL *url = [NSURL URLWithString:tempURL];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30];
// fetch the JSON response
NSData *urlData;
NSURLResponse *response;
NSError *error;
// make the synchronous request
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
// construct a String around the Data from the response
NSString *data = [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
NSDictionary *feed = [data JSONValue];

The important part of the error message is this:
-[__NSCFString JSONValue]: unrecognized selector sent to instance 0x6ab7a40
The __NSCFString class is the private implementation class for the NSString interface, so you can just pretend it says NSString.
So we see that you are sending the JSONValue message to an NSString, and the NSString says it doesn't recognize that selector. The SBJson library adds a JSONValue method to the NSString class using a category.
So I deduce that you haven't linked NSObject+SBJson.o into your app. If you copied the SBJson source files into your app, make sure you copied in NSObject+SBJson.m, and make sure it is included in the “Compile Sources” build phase of your target.
If you built an SBJson library and are linked your app to that, you may need to add the -ObjC flag to your linker options, or even the -all_load flag.

Related

Cocoa error 3840 using JSON (iOS)

I'm trying to send data to a server and receive the response in JSON format. The problem is that the server has to return "success" or "fail" but it returns "(null)".
Here's the returned error:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be
completed. (Cocoa error 3840.)" (JSON text did not start with array or
object and option to allow fragments not set.) UserInfo=XXXXXXXXX
{NSDebugDescription=JSON text did not start with array or object and
option to allow fragments not set.}
Is it possible that the error is in the server script?
Here's my function to send the data and receive the response:
- (void) putData:(NSString *)parameter valor:(NSString *)valor {
NSString *rawString = [NSString stringWithFormat:#"%#=%#", parameter, valor];
NSData *data = [rawString dataUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:#"http://www.xxx.xxx/xxx.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"PUT"];
[request setHTTPBody:data];
NSURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSLog(#"responseData: %# error: %#", json, error);
}
Unless you pass the option NSJSONReadingAllowFragments to [NSJSONSerialization JSONObjectWithData:options:error:] the response from the server must be valid JSON with a top level container which is an array or dictionary.
for example:
{ "response" : "Success" }
P.S.
If you want a mutable dictionary you must also include NSJSONReadingMutableContainers in your options.
It may possible that, the response from your server doesn't contain valid JSON.
Technically, The JSON object must be start with either an "array" or an "object (dictionary)".
So, Whatever your server is returning isn't.
And, you can force the JSON to be consumed regardless by using the NSJSONReadingAllowFragments option.
by using ,
AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
You can get this issue if you're connected to VPN on your iOS device.

Windows Azure Authentication for Bing Search in Objective-C

On 01.08.12 Bing modified their search api to a Azure, How can I authenticate in Objective-C to use the new bing search api from Azure?
My best guess is to learn from the provided PHP example in the migration word document!! http://go.microsoft.com/fwlink/?LinkID=248077 (Oh god, can't you setup a web page!) or this Java Question - Bing Search API Azure Marketplace Authentication in Java
I'm using ASIHTTPRequest to authenticate with following code.
NSString *queryString = [NSString stringWithFormat:#"'%#'", queryString];
queryString = [queryString urlEncodeUsingEncoding:NSUTF8StringEncoding]; //You'll have to implement url encoding method, preferably in a string category file
NSString *urlString = [NSString stringWithFormat:#"https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1/Image?Query=%#&Market='en-US'&$top=50&$format=json", queryString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:urlString] ];
[request setAuthenticationScheme:(NSString *)kCFHTTPAuthenticationSchemeBasic];
[request setUsername:#"YOUR_KEY_HERE"];
[request setPassword:#"YOUR_KEY_HERE"];
[request setDelegate:self];
[request startAsynchronous];
Please note, no appID required. just instead pass your key as username and password. It is successfully getting the data.
However, can't really convert the data to NSString. tried every encoding but can't get the string from the data. Initial googling says it's UTF-8 encoded. But no success.
For above code to work, you must add ASIHTTP framework.
Another thing is, my guess is passing base64 encoded string with this format your_key:yourkey should also work with basic authentication.
I was able to get it to work using just NSUrlConnection. You must first base64encode
NSString *keyString = [NSString stringWithFormat:#"%#:%#", BING_SEARCH_API_KEY, BING_SEARCH_API_KEY];
NSData *plainTextData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64String = [plainTextData base64EncodedString];
Setup your request
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init];
[req setURL:[NSURL URLWithString:searchUrl]];
NSString *authValue = [NSString stringWithFormat:#"Basic %#", base64String];
[req setValue:authValue forHTTPHeaderField:#"Authorization"];
Make the request
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error];
Look at the documentation about how to form searchUrl, and then process data according to the format you specified in $format= (I used json, so mine looks like):
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
I left out error handling, dont forget to add that by checking response as well as error.

Problems with adding objects to NSMutableDictionary

I am making an iPhone app and I am loading information from a server. I send NSURLRequest to the server and get back a NSString value. This is working fine and the value I am getting back is the correct one. The problem is that when I try to add the value for the variable to a NSMutableDictionary I have made to store the values, it doesn't work. When I debug and look at the values of the NSMutableDictionary in Xcode it says 0 key/value pairs right after the line where I add the values. This is what my code looks like:
NSArray *varsToLoad = [fixedData objectForKey:#"varsToLoad"];
NSError *error;
for(NSString *var in varsToLoad){
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: someURL]];
[request setHTTPMethod:#"POST"];
NSMutableString *file = [NSMutableString stringWithString:#"file="];
NSString *file1 = [file stringByAppendingString:var];
NSString *file2 = [file1 stringByAppendingString:#".txt"];
[request setHTTPBody:[file2 dataUsingEncoding:NSUTF8StringEncoding]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
NSString *value = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
[varsLoaded setObject:value forKey:var];
}
varsLoaded is declared at the #implementation and is a NSMutableDictionary.
The problem may be that you haven't initialized your varsLoaded by saying self.varsLoaded = [NSMutableDictionary dictionary];
So you're adding objects to a non-existing dictionary, but you're not getting an exception because this is normal in objective-c :)
You should really check the response before processing any further. Wrap it in something like this:
if (response == nil) {
// Check for problems
}
else {
NSString *value = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
[varsLoaded setObject:value forKey:var];
}
Change the first line of your code from NSArray to NSMutableArray and see if you have better luck.
It doesn't matter that you declared your varsLoaded as a NSMutableDictionary somewhere else... in the local context of that code above, the compiler believes varsToLoad is a immutable array and probably isn't even compiling your setObject: forKey: line. You're not getting warnings in the build log or in the console while you're running??

Creating a POST/GET request using Objective -C [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Tutorials for using HTTP POST and GET on the iPhone in Objective-C
Is there away to create an NSArray with the correct information like id = 1, name = #"John", score = 100 then send it and receive a response from the server?
Maybe display it inside an NSLog();
Can anyone help answer this question by linking me to a good tutorial, I don't want to use ASIHTTPRequest either. I know it would be much simpler but if there is away to do something without using a load of prewritten code id rather learn how to make something using the functionality the the foundation framework offers before going off using someone elses classes.
What you're looking for is NSMutableURLRequest and the addValue:forHTTPHeaderField method.
Create the request with the URL you wish to communicate with. Load the values you wish to transmit into the header or into the HTTPBody, set your HTTPMethod and then use a NSURLConnection method to send and receive the response.
As for an array with the information you could simply enumerate through the array and add the values to the HTTPHeaderFields. It really depends on what the server is setup to receive.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html#//apple_ref/doc/uid/10000165i
Has more information.
NSString *urlString = #"http://yoururl.com";
NSURL *url = [NSUL URLWithString:urlString];
NSMutalbeURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSDictionary *headerInformation = [NSDictionary dictionaryWithObjectsAndKeys:#"1",#"id",#"John",#"name",#"100",#"score", nil];
for (NSString *key in [headerInformation allKeys])
{
[request addValue:[dict valueForKey:key] forHTTPHeaderField:key];
}
NSHTTPURLResponse *response = nil;
NSError *error = nil;
// this will perform a synchronous GET operation passing the values you specified in the header (typically you want asynchrounous, but for simplicity of answering the question it works)
NSData *responseData = [NSURLConnection sendSynchronousRequest:request reuturningResponse:&response error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#", responseString);
[responseString release];
It might be easier to just use NSData to send a url request and store the response then to reinvent the wheel. Here is some code similar to something in my production project:
+ (NSData *)getProfiles {
NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:#"token"];
// Create string of the URL
NSString *serviceURL = [NSString stringWithFormat:#"http://www.myurlhere.com/getProfiles.php?token=%#", token];
NSLog(#"Service URL : %#", serviceURL);
// Create a NSURL out of the string created earlier. Use NSASCIIStringEncoding to make it properly URL encoded (replaces " " with "+", etc)
NSURL *URL = [NSURL URLWithString:[serviceURL stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];
// Request the url and store the response into NSData
NSData *data = [NSData dataWithContentsOfURL:URL];
if (!data) {
return nil;
}
// Since I know the response will be 100% strings, convert the NSData to NSString
NSString *response = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
// Test response and return a string that an XML Parser can parse
if (![response isEqualToString:#"UNAUTHORIZED"]) {
response = [response stringByReplacingOccurrencesOfString:#"&" withString:#"&"];
data = [response dataUsingEncoding:NSASCIIStringEncoding];
return data;
} else {
return nil;
}
}
NSLog output:
[Line: 476] +[WebSupport getProfiles]: Service URL : http://www.myurlhere.com/getProfiles.php?token=abcdef0123456789abcdef0123456789

Objective C: Function returning correct data for the first time of call and null for other times

Am a beginner in objective C, i am implementing a function that would query a web server and display the returning string in console. I am calling the function (getDatafromServer) repeatedly in a loop. The problem is that the first time am getting the value whereas the other times, it returns me a (null) in console... I've searched about memory management and check out on the forums but none have worked. Can you please guys tell me where am wrong in the codes below? Thanks in advance....
#implementation RequestThread
+(void)startthread:(id)param{
while (true) {
//NSLog(#"Test threads");
sleep(5);
NSLog(#"%#",[self getDatafromServer]);
}
}
+(NSString *) getDatafromServer{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *myRequestString = #"name=Hello%20&email=essssss#live.com";
NSData *myRequestData = [NSData dataWithBytes:[myRequestString UTF8String] length:[myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString:#"http://192.168.1.32/gs/includes/widget/getcalls.php?user=asdasd&passw=asdasdasd"]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody: myRequestData];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *myString = [NSString stringWithUTF8String:[returnData bytes]];
[myRequestString release];
[request release];
[returnData release];
return myString;
[pool release];
}
#end
You have a problem with the autorelease pool. Firstly, as Nickolay has said, the release never happens because it is after the return. I'm amazed you aren't seeing compiler warnings. Make sure you have -Wall set in "other warning flags" and you have the "Run static analyzer" build option set.
Since you want to use the returned string outside of the function, the autorelease pool must also be outside the function, or the string may be deallocated before your log gets to it. Your code structure should look more like:
+(void)startthread:(id)param
{
while (true)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//NSLog(#"Test threads");
sleep(5);
NSLog(#"%#",[self getDatafromServer]);
[pool drain]; // use instead of release in case you move to GC
}
}
The other problem you have is that you are not doing any error checking. How can you be sure that:
the request to the server is working?
the response from the server is encoded as UTF-8.
You need to check if returnData is nil after you get it and you need to examine the NSError if it is. So you need something like this:
NSError* error = nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
if (returnData == nil)
{
the error object will be set and contain useful info.
}
You also need to check if myString is nil. If it is, it will be because the response was not encoded as UTF-8. With HTTP the default encoding is not UTF-8, it is ISO-8859-1. Also, the body of the response might not be character data at all. You need to examine the response to find out how to decode the data. So the code snippet I posted above should really look like:
NSError* error = nil;
NSURLResponse* response = nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (returnData == nil)
{
// the error object will be set and contain useful info.
}
else
{
// You can get the content type and encoding from the response here.
}
Edit
Also, your code violates the Memory Management Rules. You did not obtain myRequestString or returnData through alloc, copy or new, neither have you retained them, so you must not release them.
That's bad idea to use autorelease pool in this function, and release it after 'return'.
Remove pool and everything should be ok.