CPKeyedArchiver is the easiest way to serialize your data into some string value for client/server communication (for example using CPURLRequest and CPURLConnection objects).
-(void) sendAsynchRequest
{
request = [[CPURLRequest alloc] initWithURL:@"http://www.your-server.com/services/service1.php"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:@"value"];
[request setValue:"5" forHTTPHeaderField:@"Content-Length"];
[request setValue:"text/plain;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
urlConnection = [CPURLConnection connectionWithRequest:request delegate:self];
[urlConnection start];
}
We will improve our Person class in order to be able to use CPKeyedArchiver. We need to add two methods encodeWithCoder: and initWithCoder:. These methods are used to create a stream representation of the object (encodeWithCoder:) or to create an object from a stream (initWithCoder:).
- (void)encodeWithCoder:(CPCoder)coder
{
// debugger;
// [super encodeWithCoder:coder];
[coder encodeObject:name forKey:@"name"];
[coder encodeObject:jobTitle forKey:@"jobTitle"];
[coder encodeObject:company forKey:@"company"];
}
-(id)initWithCoder:(CPCoder)coder
{
// debugger;
//if (self = [super initWithCoder:coder]) {
if (self = [super init])
{
name = [coder decodeObjectForKey:@"name"];
jobTitle = [coder decodeObjectForKey:@"jobTitle"];
company = [coder decodeObjectForKey:@"company"];
}
return self;
}
The calls to super are needed if you subclass a class that is already defining these methods.
We can now create a Person object and create a stream representation with CPKeyedArchiver. [data string] will contain the string generated (for geeks, the stream is coded as a 280NPLIST plist - you can see an output in the code source). You can send this stream to your web server.
[[data plistObject] description] is usefull for debugging (see output example in the code source).
var john = [Person personWithName:@"Doe"];
[john setJobTitle:@"Engineer" company:@"Company name éàç with utf8"];
var data = [CPKeyedArchiver archivedDataWithRootObject:john];
CPLog.trace(@"john:" + [john description]);
CPLog.trace(@"data: " + [data string]);
CPLog.trace(@"data: " + [[data plistObject] description]);
If you retrieve your data from your web server, you will get a string that you need to convert again to an object. You only have to call CPKeyedUnarchiver.
var newData = [CPData dataWithString:objectAsString];
var clone = [CPKeyedUnarchiver unarchiveObjectWithData:newData];
CPLog.trace(@"clone:" + [clone description]);
As you can see in the source code, a 280NPLIST plist is not very easy to read for humans (well geeks). If you want to be able to easely see the communication between your server and client, JSON is an option.
Suppose that you have received a JSON string like the following from your web server.
{
"menu": "File",
"commands": [
{
"title": "New",
"action":"CreateDoc"
},
{
"title": "Open",
"action": "OpenDoc"
},
{
"title": "Close",
"action": "CloseDoc"
}
]
}
We can get a javascript object using [jsonString objectFromJSON]. Once this is done, accessing the data is show below (it is just normal javascript code since Objective-J is based on javascript).
// This is some JSON content in a CPString
var jsonString = @"{\"menu\": \"File\", \"commands\": [ { \"title\": \"New\", \"action\":\"CreateDoc\" }, { \"title\": \"Open\", \"action\": \"OpenDoc\" }, { \"title\": \"Close\", \"action\": \"CloseDoc\" } ] }";
CPLog.trace(@"jsonString: " + [jsonString description]);
// Create a javascript object from the JSON content
var jsObject = [jsonString objectFromJSON];
// Acess to the content of the object
var menu = jsObject.menu;
var menu_otherway = jsObject["menu"];
var firstTitle = jsObject.commands[0].title;
var firstAction = jsObject.commands[0].action;
Now it is time to send back some feedback to your server. We have to create some JSON object and serialize the object into a string.
// Create a javascript object from scratch
var myFirstJSONObject = { "firstName" : "John",
"lastName" : "Doe",
"age" : 23,
"tangoDancer" : true,
"rich" : false,
// "bankAccountInSwitzerland" : null generates an error in JSONFromObject
"bankAccountInSwitzerland" : "None"};
// Add some other properties to the object
myFirstJSONObject.isFrench = false;
myFirstJSONObject.favoriteNumbers = [10, 3, 1968];
myFirstJSONObject["isAmerican"] = true;
// Get the JSON representation of the object
var text = [CPString JSONFromObject:myFirstJSONObject];
Plist can be also interesting since it is an xml file format. The problem comes from the fact that currently (cappuccino 0.6) we can no use array or date in the plist as we could normaly. We have to restrict to dictionary, string, boolean and number.
Create a dictionary and use [CPPropertyListSerialization dataFromPropertyList format: errorDescription:] to serialze the dict to a string.
var keys = [CPArray arrayWithObjects:@"key1", @"key2", @"key3", @"key4", nil];
var objects = [CPArray arrayWithObjects:@"Some french characters éàç with utf8", [CPNumber numberWithBool:NO], [CPNumber numberWithDouble:9.9], [CPNumber numberWithDouble:8.8], nil];
var dict = [CPDictionary dictionaryWithObjects:objects forKeys:keys];
var errorString;
var data = [CPPropertyListSerialization dataFromPropertyList:dict format:CPPropertyListXMLFormat_v1_0 errorDescription:errorString];
CPLog.trace(@"data: " + [data string]);
CPLog.trace(@"data: " + [[data plistObject] description]);
When we need to transform the string to a dictionary, we use this time [CPPropertyListSerialization propertyListFromData: format: errorDescription:].
var dict2 = [CPPropertyListSerialization propertyListFromData:data format:CPPropertyListXMLFormat_v1_0 errorDescription:errorString];
//CPLog.trace(@"dict2: " + [dict2 description]);
var value1 = [dict2 objectForKey:@"key1"];
CPLog.trace(@"value1:" + value1);
We will play with the GUI (scrollview and image view) and timers.
If you'd like to see the complete code listing from the tutorial, you can download it all in a single file: Tutorial4.zip. The web application is available online: Tutorial4