I have an iOS app that pulls JSON from a remote PHP file and displays that data in a table. When I use my database/JSON file, a piece of code won't run. However, when I use a tutorial's database (http://codewithchris.com/iphone-app-connect-to-mysql-database/) it works fine. I have used the exact code in the tutorial, the JSON looks the exact same, and the PHP works fine. How can I fix this?
Objective-C
#import "HomeModel.h"
#import "Location.h"
#interface HomeModel()
{
NSMutableData *_downloadedData;
}
#end
#implementation HomeModel
- (void)downloadItems
{
// Download the json file
NSURL *jsonFileUrl = [NSURL URLWithString:#"192.168.0.4/service.php"];
NSLog(#"Donwload the JSON file");
// Create the request
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:jsonFileUrl];
NSLog(#"Create the request");
// Create the NSURLConnection
[NSURLConnection connectionWithRequest:urlRequest delegate:self];
NSLog(#"Create the NSURLConnection");
}
#pragma mark NSURLConnectionDataProtocol Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Inititalize the data object");
// Initialize the data object
_downloadedData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the newly downloaded data
[_downloadedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Create an array to store the locations
NSMutableArray *_locations = [[NSMutableArray alloc] init];
// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_downloadedData options:NSJSONReadingAllowFragments error:&error];
// Loop through Json objects, create question objects and add them to our questions array
for (int i = 0; i < jsonArray.count; i++)
{
NSDictionary *jsonElement = jsonArray[i];
// Create a new location object and set its props to JsonElement properties
Location *newLocation = [[Location alloc] init];
newLocation.name = jsonElement[#"Name"];
newLocation.address = jsonElement[#"Address"];
newLocation.latitude = jsonElement[#"Latitude"];
newLocation.longitude = jsonElement[#"Longitude"];
// Add this question to the locations array
[_locations addObject:newLocation];
}
// Ready to notify delegate that data is ready and pass back items
if (self.delegate)
{
[self.delegate itemsDownloaded:_locations];
}
}
#end
EDIT: After further analysis, everything before - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response works. When this piece of code comes next, nothing happens.
`
Related
I never use web API and don't know what i may read about this. I read FAROO Return Values doc, but i don't understand how i may get result-array (or dictionary) in cocoa.
Please anybody give me example or tutorial how to use Faroo API (or other web API) in objective-c.
Thank you.
To use web API and FAROO API in particular i use NSURLConnection class and NSURLConnectionDelegate protocol:
- (IBAction)search:(id)sender {
NSString* requestString = [NSString stringWithFormat:#"http://www.faroo.com/api?q=%#&start=1&length=10&l=ru&src=news&f=xml&YOUR_API_KEY",[searchField stringValue]];
// NSLog(#"str %#",requestString);
NSURL* requestUrl = [NSURL URLWithString:requestString];
NSURLRequest* searchRequest = [NSURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:60];
[self performSelectorOnMainThread:#selector(startConnectionWithRequest:) withObject:searchRequest waitUntilDone:NO];
}
- (void)startConnectionWithRequest:(NSURLRequest*)request {
NSURLConnection* connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
if (connection) {
//update GUI and do something...
theData = [NSMutableData data];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"Receive data");
[theData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"Http status code %ld",(long)[httpResponse statusCode]);
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Finish");
//do something with data and update GUI
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSAlert* searchFailedAlert = [NSAlert alertWithError:error];
[searchFailedAlert runModal];
}
One other way of doing things is to declare the missing method yourself as a category of the class in question. This will get the compiler to stop complaining about not finding the method, though of course you'll still need the runtime check you're already doing to avoid actually calling the method. You might also want to wrap such a declaration using availability macros, so that it will be ignored once you do move up to using the 10.5/10.6 SDK and you won't get a different compiler complaint down the line. That would look something like this:
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 //ignore when compiling with the 10.5 SDK or higher
#interface NSPropertyListSerialization(MissingMethods)
+ (NSData *)dataWithPropertyList:(id)plist format:(NSPropertyListFormat)format options:(NSPropertyListWriteOptions)opt error:(NSError **)error;
#end
#endif
i'm quite a beginner at cocoa an objective-c - so please forgive me a possibly trivial question.
I'm currently working on an XCODE-Project which is fetching some data via NSJSONSerialization and store it for further work.
At this step I'm going to encapsulate the progress of fetching the data into an class which has some setter for the needed parameters (url to fetch from and the layer which should be parsed into an array). In order to use this procedure im creating the method inside this class which creates a connection and a request and returns the array which should contain the data. After some tests I tried to create an instance of this class and called the method which starts to fetch the data.
My problem is, that after calling the method data_array from my new instance "block_stats"
and store the data in an array of the same type - the array is empty
table_data = [block_stats data_array];
The reason of this behavior is that the usage of the methods in (didReceiveResponse,didReceiveData,connectionDidFinishLoading) are working asynchron and the return of the data_array was done before the download was finished.
the method inside the class which contains the downloading part:
- (NSMutableArray *)data_array
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if(data_array)
{
[data_array removeAllObjects];
} else {
data_array = [[NSMutableArray alloc] init];
}
NSURLRequest *request = [NSURLRequest requestWithURL:data_url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
if(connection)
{
webdata = [[NSMutableData alloc]init];
}
return data_array;
}
the IBAction in another view which creates the instance and calls the method to fetch the data
- (IBAction)refresh:(UIBarButtonItem *)sender {
KDJSONparser *block_stats = [[KDJSONparser alloc]init];
[block_stats setURL:[NSURL URLWithString:#"*JSONDATA_URL*"]];
[block_stats setLayer:#"blocks"];
table_data = [block_stats data_array];
}
I would be very glad if anyone could give some advice. It would be very nice if it would be as easy as possible to understand for me. Thanks in advance!
The solution to your problem lies in delegation (As the word suggests you will be appointing some one else to take action when a situation presents itself.)
You have already used this in the following piece of your code.
NSURLRequest *request = [NSURLRequest requestWithURL:data_url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
Here when you have set self as the delegate for your NSURLConnection you are telling the compiler to send you any appropriate messages related to the connection. These messages include didReceiveResponse,didReceiveData,connectionDidFinishLoading.
So let's implement these methods in your class and they will look something like this.
KDJSONParser.m
- (NSMutableArray *)fetchData
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURLRequest *request = [NSURLRequest requestWithURL:data_url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
if(connection)
{
webdata = [[NSMutableData alloc]init];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[webdata setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[webdata appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
//Parse the webdata here. Your array must be filled now.
[self parseTheJSONData];
}
-(void)parseTheJSONData
{
//Do your parsing here and fill it into data_array
[self.delegate parsedArray:data_array];
}
And in your other class add this line in you refresh method
block_stats.delegate = self;
and implement
-(void)parsedArray:(NSMutableArray *)data_array;
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
iPhone/iOS JSON parsing tutorial
I have been reading many tutorials on how to parse JSON data in Objective C but still I am unable to figure it out. I want to parse data from the JSON file and show it up on screen.
For example,
I want to parse data from here and get all the values of different retailers in different variables, so that I can use them later.
How can I do it?
Assuming that you have your data in a NSData object you can use NSJSONSerialization class available in iOS 5 and up.
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error
This is a class method that will convert your data into objects like NSArray, NSDictionary, NSNumber etc. depending on the contents of your data object.
Here is how you would download and parse the data from a web server. Note that all these methods are part of the same class and there are instance variables named _downloadData of type NSMutableData* and _downloadConnection of type NSURLConnection*. Also note this code assumes ARC is not being used. If it is, just remove the object releases and retains and make sure the instance variables are strong references.
-(void)startDownload {
NSURL* jsonURL = [NSURL URLWithString:#"http://isbn.net.in/9781449394707.json"];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:jsonURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
_downloadData = [[NSMutableData dataWithCapacity:512] retain];
_downloadConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[_downloadData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_downloadData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[_downloadConnection release];
_downloadConnection = nil;
[_downloadData release];
_downloadData = nil;
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError* jsonError = nil;
NSDictionary* jsonDict = nil; // your data will come out as a NSDictionry from the parser
jsonDict = [NSJSONSerialization JSONObjectWithData:_downloadData options:NSJSONReadingMutableLeaves error:&jsonError];
if ( nil != jsonError ) {
// do something about the error
return;
}
[_downloadConnection release];
_downloadConnection = nil;
[_downloadData release];
_downloadData = nil;
// now do whatever you want with your data in the 'jsonDict'
}
I am attempting to write a bit of code that checks the URL of a datasource, then populates an array with objects from that URL. It actually works well, but if there is a problem with the web connection or the address I want to populate the array with data from a bundled file. The issue I am having is that the connection didFailWithError method is never called. I tried passing a simple string but it does not call. I want the app to still function for people who are using ipod touch or are in airplane mode.
connection didReceiveResponse is working without issue.
This is what I'm working with.
- (void)loadListData{
NSLog(#"Loading data from sources");
NSURLRequest *listURLRequest = [NSURLRequest requestWithURL:integerPhoneListURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:1.0];
[[NSURLConnection alloc] initWithRequest:listURLRequest delegate:self];
if (!listConnectFail){
phoneListJSON =[NSData dataWithContentsOfURL:integerPhoneListURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:phoneListJSON waitUntilDone:YES];
} else {
//This will tell us if there is an error loading the file
NSLog(#"File not found on web init from file");
phoneListJSON =[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"contactlist" ofType:#"json"]];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:phoneListJSON waitUntilDone:YES];
}
//Initialize the filtered list with array of customer objects. Based on original data
filteredList = [[NSMutableArray alloc] init];
for (NSDictionary *dict in phoneListOriginal) {
contact *single = [[contact alloc] init];
single.fName = [dict objectForKey:#"fName"];
single.lName = [dict objectForKey:#"lName"];
single.extension = [dict objectForKey:#"extension"];
single.title = [dict objectForKey:#"title"];
single.department = [dict objectForKey:#"department"];
single.cellNumber = [dict objectForKey:#"cellNumber"];
//NSLog(#"%#", single.lName);
[filteredList addObject:single];
}
NSLog(#"Array filteredLIst contains %d records",[filteredList count]); }
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
listConnectFail = YES;
NSLog(#"Connection Failed, pulling from file"); }
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
listConnectFail = NO;
NSLog(#"Connection Succeeded, populating from API");
}
I know it is probably something stupid that I am not seeing, but I could use the help to see what I don't
Thanks in advance!
How did you confirm that your delegate did not receive the message? Did you check the log?
Your code seems to assume that 'listConnectFail' will be set immediately after the NSURLConnection's init is done, which is not necessarily the case.
[[NSURLConnection alloc] initWithRequest:listURLRequest delegate:self];
if (!listConnectFail){...}
The NSURLConnection documentation states that 'The delegate will receive delegate messages as the load progresses.'
However, I am not sure about the airplane mode, maybe this particular error can be detected synchronously.
I'm writing an iPhone native app using the JSON framework.
My app is accessing web services using JSON. The JSON data we send has nested objects, below is an example of the data served up:
{
"model": {
"JSONRESPONSE": {
"authenticationFlag": true,
"sessionId": "3C4AA754D77BFBE33E0D66EBE306B8CA",
"statusMessage": "Successful Login.",
"locId": 1,
"userName": "Joe Schmoe"
}
}
}
I'm having problem parsing using the objectForKey and valueForKey NSDictionary methods. I keep getting invalidArgumentException runtime errors.
For instance, I want to query the response data for the "authenticationFlag" element.
Thanks,
Mike
Seattle
It is hard to tell without some more details (e.g. the JSON parsing code that you are using), but two things strike me as possible:
you are not querying with a full path. In the case above, you'd need to first get the enclosing model, the json response, and only then ask the json response dictionary for the authenticationFlag value:
[[[jsonDict objectForKey:#"model"]
objectForKey:#"JSONRESPONSE"] objectForKey:#"authenticationFlag"]
perhaps you're using c-strings ("") rather than NSStrings (#"") as keys (although this would likely crash nastily or just not compile). The key should be something than can be cast to id.
While possible, both are probably false, so please include more detail.
The following is taken directly from Dan Grigsby's Tutorial at - http://mobileorchard.com/tutorial-json-over-http-on-the-iphone/ - Please attribute, stealing is bad karma.
Fetching JSON Over HTTP
We’ll use Cocoa’s NSURLConnection to issue an HTTP request and retrieve the JSON data.
Cocoa provides both synchronous and asynchronous options for making HTTP requests. Synchronous requests run from the application’s main runloop cause the app to halt while it waits for a response. Asynchronous requests use callbacks to avoid blocking and are straightforward to use. We’ll use asynchronous requests.
First thing we need to do is update our view controller’s interface to include an NSMutableData to hold the response data. We declare this in the interface (and not inside a method) because the response comes back serially in pieces that we stitch together rather than in a complete unit.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
IBOutlet UILabel *label;
NSMutableData *responseData;
}
To keep things simple, we’ll kick off the HTTP request from viewDidLoad.
Replace the contents of :
#import "JSON/JSON.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"XYZ.json"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
label.text = [NSString stringWithFormat:#"Connection failed: %#", [error description]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
}
- (void)dealloc {
[super dealloc];
}
#end
This mostly boilerplate code initializes the responseData variable to be ready to hold the data and kicks off the connection in viewDidload; it gathers the pieces as they come in in didReceiveData; and the empty connectionDidFinishLoading stands ready to do something with the results.
Using The JSON Data
Next, we’ll flesh out the connectionDidFinishLoading method to make use of the JSON data retrieved in the last step.
Update the connectionDidFinishLoading method :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray *luckyNumbers = [responseString JSONValue];
NSMutableString *text = [NSMutableString stringWithString:#"Lucky numbers:\n"];
for (int i = 0; i < [luckyNumbers count]; i++)
[text appendFormat:#"%#\n", [luckyNumbers objectAtIndex:i]];
label.text = text;
}
It creates an NSArray. The parser is very flexible and returns objects — including nested objects — that appropriately match JSON datatypes to Objective-C datatypes.
Better Error Handling
Thus far, we’ve been using the the convenient, high-level extensions to NSString method of parsing JSON. We’ve done so with good reason: it’s handy to simple send the JSONValue message to a string to accessed to the parsed JSON values.
Unfortunately, using this method makes helpful error handling difficult. If the JSON parser fails for any reason it simply returns a nil value. However, if you watch your console log when this happens, you’ll see messages describing precisely what caused the parser to fail.
It’d be nice to be able to pass those error details along to the user. To do so, we’ll switch to the second, object-oriented method, that the JSON SDK supports.
Update the connectionDidFinishLoading method in :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
NSArray *luckyNumbers = [json objectWithString:responseString error:&error];
[responseString release];
if (luckyNumbers == nil)
label.text = [NSString stringWithFormat:#"JSON parsing failed: %#", [error localizedDescription]];
else {
NSMutableString *text = [NSMutableString stringWithString:#"Lucky numbers:\n"];
for (int i = 0; i < [luckyNumbers count]; i++)
[text appendFormat:#"%#\n", [viewcontroller objectAtIndex:i]];
label.text = text;
}
}
Using this method gives us a pointer to the error object of the underlying JSON parser that we can use for more useful error handling.
Conclusion :
The JSON SDK and Cocoa's built-in support for HTTP make adding JSON web services to iPhone apps straightforward.
NSString* aStr;
aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSDictionary *dictionary = [aStr JSONValue];
NSArray *keys = [dictionary allKeys];
// values in foreach loop
for (NSString *key in keys) {
NSArray *items = (NSArray *) [dictionary objectForKey:key];
for (id *item in items) {
NSString* aStrs= item;
NSLog(#" test %#", aStrs);
NSDictionary *dict = aStrs;
NSArray *k = [dict allKeys];
for (id *it in k) {
NSLog(#"the child item: %#", [NSString stringWithFormat:#"Child Item -> %# value %#", (NSDictionary *) it,[dict objectForKey:it]]);
}
Now, Objective c has introduced in build class for JSON Parsing.
NSError *myError = nil;
NSDictionary *resultDictionary = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:&myError];
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html
So utilize this class and make your application error free...:)