AS3 to Cocoa touch: JSON (Part 1: load json)
So in this tutorial I’m going to discuss what would be a possible way to transfer your data from a backend to your iphone. I have 2 primary things I have in mind doing this what are very important to me. It must be easy to implement (in the backend as well as in objective-c), And I need a format that is accessible from many program languages / approaches.
So from that point on I stumbled upon the use of JSON. It’s very easy in use, support for many languages (http://json.org). My Actionscript mind would always put amf in favor since its very well supported in actionscript and the flash player. As well as the backend side of the story ( fluorine, amfphp, weborb, blazeds, livecycle, ..).
The usercase that I came in contact with the most was, get something out of a database with php convert it to amf with amfphp or weborb and send it over to flash. So that’s the usercase I want to take as an example to do the comparison. So what is my setup ?
Mysql: Data -> php: encode json -> http -> obj-c: decode json
I’m going to keep it all as simple as possible. Just to give you a head start on what could be done.
So for the backend we have our mysql db (”iphone” in my case)
CREATE TABLE 'persons' ( 'id' int(11) NOT NULL auto_increment, 'name' varchar(255) NOT NULL, 'age' int(5) NOT NULL, PRIMARY KEY ('id') ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ; INSERT INTO 'persons' VALUES (1, 'Hans', 32); INSERT INTO 'persons' VALUES (2, 'John', 12); INSERT INTO 'persons' VALUES (3, 'Zaki', 34); INSERT INTO 'persons' VALUES (4, 'Dr. Cox', 88);
And for our php we have:
$link = mysql_pconnect("localhost", "root", "root") or die("Could not connect"); mysql_select_db("iphone") or die("Could not select database"); $arr = array(); $rs = mysql_query("SELECT name, age FROM persons"); while($obj = mysql_fetch_object($rs)) { $arr[] = $obj; } echo json_encode($arr);
Whatever backend you use it doesn’t matter as long as it useses the http protocol and the json format to transfer your data from the database. You can get the example to work.
package { import com.adobe.serialization.json.JSON; import flash.display.Sprite; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLRequest; public class MainJSON extends Sprite { public function MainJSON() { var loader:URLLoader = new URLLoader(); loader.load(new URLRequest( "http://andyj.be/iphone/json/persons.php" )); loader.addEventListener(Event.COMPLETE, decodeJSON) ; } private function decodeJSON(evt:Event):void { var persons:Array = JSON.decode( URLLoader( evt.target ).data ); for (var key:Object in persons) { var tmp : Object = persons[key]; var person : Person = new Person(tmp.name,int( tmp.age )); trace( person ); } } } }
*note: you must include the corelib api ( http://code.google.com/p/as3corelib/ ) because decoding and encoding JSON isn’t part of the flash player runtime.
So a quick overview of our AS3 code, we intantiate our URLLoader (that we provide with our link to our webservice) which is going to handle the loading of our data from the webservice. Just define an eventListener on “complete“. In that function were just going to decode our data with the static function “decode” provided by the JSON class ( from the corelib api ). Because we are parsing an array from our webservice we should get an Array in return from the static function.
Each object in the array has a key ( which is just a simple integer in reality ( so you could use a Three-expression for loop if you want) ) if we take the value for the key we provide we get our object in return which has a name and age property. Just put those values in our person Object et voila, were finished.
Ok now for our objective-c side of the story, just like in Actionscript, the cocoa framework doesn’t provide a JSON decoding and encoding API. But Stig Brautaset provided us with a JSON framework that is fairly easy to implement.
You have to do a couple of things before we can continue.
1. Download the DMG http://json-framework.googlecode.com/files/JSON_2.1.1.dmg
2. Copy the directory /Volumes/JSON_2.1.1/SDKs/JSON to ~/Library/SDKs/JSON
that wasn’t so hard was it ?.. Ok, create a window based application like we did in our previous examples and before we continue providing our appdelegate class with code we have to set a couple paramaters (just like we would add the corelib swc to the build path of our actionscript project in flex). We have to set 2 paramaters in our info panel ( apple-key+I ) in the build tab( note: make sure you select the “all configurations” under configuration so you don’t have to worry about the difference between debugging and releasing.
a. “Additional SDKs” must be set to “$HOME/Library/SDKs/JSON/$(PLATFORM_NAME).sdk”
b. “Other Linker Flags” must be set to “-ObjC -ljson”
So the JSON-framework is now a part of our current project. Just to be sure its working open up your appdelegate class and add the following line code at the top of your class.
#import #import "Person.h"
*note: we add Person.h because we are going to need it further on in the project but it has no relation to the JSON framework.
Now try to build your project( apple-key + B ) if you don’t get any errors you than your setup to go. If you do get any errors, you can always post them as a comment or mail me and I will see what I can do. In the DMG you downloaded is an INSTALL and README file, you can always check those files if you still get errors.
So assuming were setup and we don’t get any errors. We continue with our example. Just add the following code to the applicationDidFinishLaunching Method.
NSURLResponse *urlResponse; NSData *data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://andyj.be/iphone/json/persons.php"]] returningResponse:&urlResponse error:nil]; NSString *jsonData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; SBJSON *json = [SBJSON alloc]; NSArray *jsonArray = [json objectWithString:jsonData error:nil]; for (int i=0; i<[jsonArray count]; i++) { id tmp = [jsonArray objectAtIndex:i]; Person *p = [[Person alloc] initWithName:[tmp valueForKey:@"name"] andAge: [[tmp valueForKey:@"age"] intValue]]; NSLog(@"%@ is %i years old.", [p name], [p age]); [p release]; } [json release]; [jsonData release];
If you take a glance at the code, it might be a little strange at first sight. But if you give it a better look you will see that it’s actually pretty much the same path we are following as we would do in AS3.
We start by instantiating a pointer to urlResponse of type NSURLResponse because we are going to need that pointer in the next line of code. What it actually does is just addresses a pointer to a variable, it will be necessary for when we load some data, so that we can provide this pointer to the function we want to load data with.
Next we instantiate a variable called data of the type NSData that’s because were calling the class method sendSynchronousRequest:returningResponse:error: of the NSURLConnection object which will return an object of the type NSData. So since we use a class method we don’t need to allocate an object of the NSURLConnection class, so we need to feed the function 3 paramaters, the first is a NSURLRequest which we need to provide with a NSURL through the requestWithURL method, and in the NSURL we need to provide the actual url to the webservice through the function URLWithString. We need to provide the returningResponse with the earlier created urlResponse pointer, note that we use and ampersand “&” in front of the variable which denotes pass-by-reference. (I’m not going to go deeper on that topic but if you want to know more about it just use this article as a start point http://en.wikipedia.org/wiki/Reference_(C%2B%2B) it’s for c++ but has the sample principle.)
Since our JSON class needs a string for decoding our json we need to convert our data into a string we do that by using the initWithData:encoding: method from the NSString class we provide that the data and for encoding use the NSUTF8StringEncoding constant.
Now we have our data ready to be decoded we just have to allocate a variable of the type SBJSON which is a class from the JSON framework. And just like in actionscript we know that it will be an array so we declare a variable jsonArray of the type NSArray ( note: if you don’t know for sure which type it will be you can use “id” instead of NSArray than you will be sure your program doesn’t crash at runtime, but in our case I know I’m sure!) for decoding we just use the objectWithString:error: method and provide nil for error since we don’t care for any errors now.
Make a for loop that iterates through every object in the array. And in every object there will be a key and a value for each property in the json string ( just like in actionscript).
Allocate youre Person object and juse the function initWithName:andAge: to put the values of the tempory object into you person object. ( note, that we need to convert the age object into an intValue ( just like we did in the XML example ) because in our backend they all are strings ).
Release the allocated objects from the memory, eh voila. Open your debug log window and see the magic happen. In the next tutorial I will be discussion how to use json to send data from the iphone to the backend.
happy chirstmas to you to
Fantastic! And a happy holidays.
FYI there are some html entities in your appdidfinish function:
returningResponse:&urlResponse
and
(int i=0; i<[jsonArray count]; i++)
Also I needed to add
#import
to my ..AppDelegate.h file
Also FYI anyone who wants easy json access to their database check out AMFPHP’s json gateway. For my existing AMFPHP services I had to do nothing but use this url to get my data as json: http://mysite.com/myamfphpdir/json.php/MyClass.MyMethod/arg1/arg2 where MyClass.php is in my AMFPHP services dir.
Thanks a whole heap!
Fantastic post – exactly what i was looking for. I have bookmarked your post at http://www.iphonekicks.com/objectivec/iPhone_SDK_AS3_to_Cocoa_touch_JSON_Part_1_load_json
It seems you are missing the php code that fetches data from the database and encodes it into JSON. True?
seems like it got messed up by wordpress. should be fixed now
Andy,
I’m working through this and finding it useful. In getting it to run, I’m wondering if there is a sample file to “person.h” or whether it is neccessary to deduce the methods (e.g. andAge:) and implement them.
My other observation is that
#import
#import “Person.h”
makes it look like the 1st line here is blank, is that what is intended – it is hard to understand the connection between the blank import and the JSON sdk in the prior step. Other than that, very clear and helpful -
I recently updated a project to sdk 3 and JSON 2.2 and my json stopped working. the reason was I was using [SBJSON alloc]. when I changed this to [SBJSON new] things were back to normal.
nice job, very thanks
hahaha ! this is good shit