Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Beyond JSON: Spearal Serialization Protocol for iOS

DZone's Guide to

Beyond JSON: Spearal Serialization Protocol for iOS

· Java Zone
Free Resource

Find your next Integration job at DZone Jobs. See jobs focused on integration, or create your profile and have the employers come to you!

Spearal is an ongoing attempt to provide a serialization protocol that works - unlike JSON - with arbitrary complex data models, while offering advanced features such as partial object serialization, built-in support for JPA uninitialized associations, divergent models reconciliation or object properties filtering.

With Spearal for iOS, which is presented in this article through a sample iPhone / Spring / JPA application, the Spearal open-source project has now reached its first major milestone: JavaScript, Android and iOS clients are all supported, together with popular Java backends (JAX-RS and Spring MVC).

This article focuses on the Spearal / iOS integration and its goal isn't to give a general overview of the Spearal project or how you can use it with a JavaScript or Android application. To know more about these subjects, please refer to the two following DZone articles:

The server-side application used in this demo is hosted on RedHat OpenShift. It is the exact same one which was originally set up for the Android demo. To know more about the Spring MVC configuration and, more generally, the server-side application, read the "Some explanations on the Spring configuration" section in the The Day After JSON: Spearal and Mobile Apps article.

Credits: the application used in this article is a slightly modified version of an excellent AngularJS / Java EE demo written by Roberto Cortez.

Cloning, building and starting the iOS demo

Because our iPhone application is going to connect to an existing remote server, you only need to have an Apple Mac, a working internet connection and XCode 6.1: Spearal iOS is written with the brand new Swift language, which is rapidly evolving, and other versions of XCode may not work with this demo.

If you fulfill these requirements, you can just enter the following command line in a terminal window:

$ git clone --recursive  https://github.com/spearal-examples/spearal-example-ios.git

Then, start XCode, select "File" / "Open..." in the main menubar and open the spearal-example-ios/spearal-example-ios.xcworkspace file. The project should build without errors and you should be able to start the demo with the usual run command (the black arrow at the top-left corner of the workspace window, with "SpearalIOSExample" selected as the target).

The application is based on the "Master-Detail Application" template you can select when creating a new iOS project. Here are the two views of the demo:

Note: if the master view is empty and if you get an error in your XCode console, this is likely due to OpenShift hibernating the server-side application if it wasn't used for a long time. Go to the following url (https://examples-spearal.rhcloud.com/spring-angular/index-spearal.html) and hit refresh until the demo is working again. Then, rerun the iOS application. In any case, feel free to contact us through our users forum here.

If everything goes well, you can then start playing with the application, which is self-explanatory: you can list, create, modify and delete some famous Manga characters.

The data model

Server-side, the application uses one entity and a pagination bean:

@Entity
public class Person implements Serializable {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String description;
    private String imageUrl;

    // skipped getters and setters...
}
 
public class PaginatedListWrapper<T> implements Serializable {

    private Integer currentPage;
    private Integer pageSize;
    private Integer totalResults;

    private String sortFields;
    private String sortDirections;
    private List<T> list;

    // skipped getters and setters...
}

Client-side, we need to replicate these structures in the Swift language as follow:

@objc(Person)
public class Person: SpearalAutoPartialable {

    public dynamic var id:NSNumber?
    public dynamic var name:String?
    public dynamic var description_:String?
    public dynamic var imageUrl:String?
}

The Person entity is mirrored into a Swift KVO (Key Value Observing) compliant class with optional properties. This class inherit from the SpearalAutoPartialable class which keeps track of defined properties. A defined property is a property which was set at least once, even to nil: if you construct a new Person without setting any of its four properties, they will all be undefined. However, for example, the following code defines the id and name properties:

var person = Person()
person.id = nil
person.name = "I just have a name"

We will see later how this distinction between defined and undefined properties can be used to serialize only partial entities (ie. only defined properties) for update. Then comes the PaginatedListWrapper class:

@objc(PaginatedListWrapper)
public class PaginatedListWrapper: NSObject {

    public var currentPage:NSNumber?
    public var pageSize:NSNumber?
    public var totalResults:NSNumber?

    public var sortFields:String?
    public var sortDirections:String?
    public var list:[Person]?
}

Unlike the Person class, the PaginatedListWrapper class is a direct subclass of NSObject and isn't KVO compliant: we don't use pagination in this demo, all instances of this class come from the server and all properties should always be defined.

Note: with Spearal, this is all you need to write for your data model classes. Unlike JSON, Spearal doesn't use any intermediate representations such as dictionaries and arrays: there is no need to write specific code to transform dictionaries into actual class instances or vice-versa.

The Spring MVC service, client-side

In addition to the data model replication in Swift, we obviously need some code to send and receive data to and from the Spring MVC server. You can find an implementation of such a service in the SpearalIOSExample/SpearalIOSExample/PersonService.swift file. For the sake of this demo, we have just put the Spearal initialization code and all REST requests configuration at the same location: a real-world application with a more complicated model would certainly split factories and requests into separate classes.

Let's have a look at the person service setup:

import Foundation
import SpearalIOS

class PersonService {

    private let personUrl = "https://examples-spearal.rhcloud.com/spring-angular/resources/persons"
    private let session:NSURLSession
    private let factory:SpearalFactory

    init() {
        self.session = NSURLSession(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration())

        self.factory = DefaultSpearalFactory()
        let aliasStrategy = BasicSpearalAliasStrategy(localToRemoteClassNames: [
            "Person": "org.spearal.samples.springangular.data.Person",
            "PaginatedListWrapper": "org.spearal.samples.springangular.pagination.PaginatedListWrapper"
        ])
        aliasStrategy.setPropertiesAlias("Person", localToRemoteProperties: [
            "description_" : "description"
        ])
        factory.context.configure(aliasStrategy)
    }

    // skipped code...
}

We first create an ephemeral NSURLSession which we will use for each server request. Then comes the initialization of the Spearal factory. We need an alias strategy to map server-side Java classes (fully qualified with their respective packages) to their Swift mirrors. For example, the Swift "Person" class is mapped to the server-side "org.spearal.samples.springangular.data.Person" class. This configuration with a dictionary isn't the only way to setup this translation: you can also a closure to perform this translation in a more general way.

Then, we have a property alias which maps the server-side "description" property to the client-side "description_" property of Persons: because "description" is a predefined NSObject property we cannot simply override, we need to use an alias in our Swift model, hence the "description_" property above and the alias configuration: we wouldn't need such aliasing if the server model wasn't using a somehow reserved property name.

Let's now have a quick look at the code used to load the list of all characters in the master view:

func listPersons(completionHandler: ((PaginatedListWrapper!, NSError!) -> Void)) {
    let request = createRequest("\(personUrl)?pageSize=100", method: "GET")

    executeRequest(request, completionHandler: { (list:PaginatedListWrapper?, error:NSError?) -> Void in
        completionHandler(list, error)
    })
}

    // skipped code...

func createRequest(url:String, method:String) -> NSMutableURLRequest {
    var request = NSMutableURLRequest(URL: NSURL(string: url)!)
    request.HTTPMethod = method
    request.addValue("application/spearal", forHTTPHeaderField: "Content-Type")
    request.addValue("application/spearal", forHTTPHeaderField: "Accept")
    return request
}

func executeRequest<T>(request:NSURLRequest, completionHandler:((T?, NSError?) -> Void)) {
    let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, err) -> Void in
        var error:NSError? = err
        var value:T? = nil

        if error == nil {
            if (response as NSHTTPURLResponse).statusCode != 200 {
                error = NSError(domain: "Spearal", code: 1, userInfo: [
                    NSLocalizedDescriptionKey: "HTTP Status Code: \((response as NSHTTPURLResponse).statusCode)"
                ])
            }
            else if data.length > 0 {
                value = self.decode(data)
            }
        }

        dispatch_async(dispatch_get_main_queue()) {
            completionHandler(value, error)
        }
    })

    task.resume()
}

func decode<T>(data:NSData) -> T? {
    let printer = SpearalDefaultPrinter(SpearalPrinterStringOutput())

    let decoder:SpearalDecoder = self.factory.newDecoder(SpearalNSDataInput(data: data), printer: printer)
    let any = decoder.readAny()

    println("RESPONSE (data length: \(data.length) bytes)")
    println((printer.output as SpearalPrinterStringOutput).value)
    println("--")

    return any as? T
}

The listPersons function creates a HTTP GET request from the "https://examples-spearal.rhcloud.com/spring-angular/resources/persons?pageSize=100" url. Because we don't want to deal with pagination in this demo, we have chosen an arbitrary large page size of 100 to fetch all persons. The createRequest function configures the "Content-Type" and "Accept" HTTP headers to the "application/spearal" mime-type.

Then, the executeRequest function uses the NSURLSession to send the request and decode the response, taking care of unexpected errors. It then calls the given completionHandler that is responsible of dealing with the result of the listPersons call.

To get a clear and human-readable view of what was received from the server, the decode function is creating a printer and logging the content of the response in the XCode console. This extra code isn't required but this why you can see this kind of output in your console:

RESPONSE (data length: 3147 bytes)
#0 org.spearal.samples.springangular.pagination.PaginatedListWrapper {
    currentPage: 1,
    list: #1 [
        #2 org.spearal.samples.springangular.data.Person {
            description: "Konoha",
            id: 1,
            imageUrl: "http://img1.wikia.nocookie.net/__cb20140523045537/naruto/images/thumb/3/36/Naruto_Uzumaki.png/300px-Naruto_Uzumaki.png",
            name: "Uzumaki Naruto"
        },
    // etc.

The rest of the PersonService.swift class defines the same way other operations such as getPerson, savePerson and deletePerson. It also defines an encode function, which logs the content of sent data (if any) in the console.

Without going into details about how the whole application works, let's just say that a PersonService singleton is created in the AppDelegate.swift class at the start of the application and then used in the master and detail controllers through AppDelegate.instance().personService accesses.

Note: so far, so good, but we haven't seen yet the full power of Spearal: the main advantage of using Spearal instead of JSON is, like we have seen before, the very simple way of defining our client-side model, with no code required to deal with intermediate representations. Another benefit, however, is that the data size, without compression, can be slightly smaller.

Filtering unused properties

If you look again at your console, you will notice that the result of the listPersons call shows a list of persons with all their properties. Because we only display the name of each character in the master view of our iPhone application, we don't need to fetch the description and imageUrl fields. Spearal lets you filter these unused properties very easily. Open the SpearalIOSExample/SpearalIOSExample/PersonService.swift file, locate the listPersons function and uncomment the following line of code:

request.addValue("org.spearal.samples.springangular.data.Person#name", forHTTPHeaderField: "Spearal-PropertyFilter")

Stop and rerun the application. As soon it is launched, have a look at your console:

RESPONSE (data length: 567 bytes)
#0 org.spearal.samples.springangular.pagination.PaginatedListWrapper {
    currentPage: 1,
    list: #1 [
        #2 org.spearal.samples.springangular.data.Person {
            id: 1,
            name: "Uzumaki Naruto"
        },
    // etc.

With a single line of code, we have reduced the size of the response from 3147 to 567 bytes (about 6 times smaller) and the gain would be of course much more important with a more sophisticated data model. Notice that we didn't have to request explicitly the id property of Persons, but just their names: Spearal considers entity primary keys as unfilterable and the "org.spearal.samples.springangular.data.Person#name" filter is strictly equivalent to "org.spearal.samples.springangular.data.Person#id,name".

If you click on a Person name, you will however get the corresponding person with all its attributes because the PersonService.getPerson function is called before displaying the detail view: the filter is per-request based and is only applied during the PersonService.listPersons call.

Note: you won't find such filtering features with JSON popular libraries. Even if you manage to code something to get the same result, Spearal is offering a built-in and consistent way to deal with such partial objects. Person instances received after this filter configuration actually contain two undefined properties: you can check for definability with person._$isDefined("imageUrl"), which would return false here. Moreover, if such partial entity is sent back to the server for update - after changing the name of a person for example - the description and imageUrl won't be set to null in your database: the Spearal JPA module takes care of undefined properties, you don't face the risk of messing up your database with partial objects.

Dealing with outdated data models

A common issue with mobile applications (or, more generally, with any connected application installed on a client device) is that you may have to deal with several outdated versions before everybody has upgraded. If your server data model has evolved, usually by adding new properties, outdated clients must be able to work as before and the data these clients send to the server, even if the new properties are missing, shouldn't nullify any existing values.

With Spearal, you basically don't need to care about such issue: an outdated entity with missing properties is detected as a partial object by the server and only defined properties are used when updating your database. Without going into details about this feature, let's just say it is implemented in the JPA2 module of Spearal.

To give an example of such data model evolution, we have set up another OpenShift server here: https://examples-spearal.rhcloud.com/spring-angular-v2/index-spearal.html. If you browse to this url, you will notice a new column in the persons data grid: each Manga character can now have a worst enemy and this association can be circular (we are usually the worst enemy of our worst enemy). In addition to the new worst enemy association (and because it doesn't have to be that bad) we also have a new best friend association, which is lazily fetched. Our new person entity has now the following properties:

@Entity
public class Person implements Serializable {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String description;
    private String imageUrl;

    @ManyToOne(fetch=FetchType.LAZY)
    private Person bestFriend;
 
    @ManyToOne // eagerly fetched
    private Person worstEnemy;

    // skipped getters and setters...
}

Let's now connect our iPhone application to this upgraded server. Edit again the SpearalIOSExample/SpearalIOSExample/PersonService.swift file and do the following changes:

// private let personUrl = "https://examples-spearal.rhcloud.com/spring-angular/resources/persons"
private let personUrl = "https://examples-spearal.rhcloud.com/spring-angular-v2/resources/persons"

Restart the application. It just looks the same and the list of person's names is displayed as usual in the master view. Because we didn't remove the filter which asks for person's names only, we only received, just like before, the id and name properties of each person. Click on one of the characters (eg. Hatake Kakashi). This is what you should now see in your console:

RESPONSE (data length: 473 bytes)
#0 org.spearal.samples.springangular.data.Person {
    description: "Konoha",
    id: 2,
    imageUrl: "http://img1.wikia.nocookie.net/__cb20140616090940/naruto/images/thumb/b/b3/KakashiHatake.png/300px-KakashiHatake.png",
    name: "Hatake Kakashi",
    worstEnemy: #1 org.spearal.samples.springangular.data.Person {
        bestFriend: nil,
        description: "Missing-nin",
        id: 20,
        imageUrl: "http://img3.wikia.nocookie.net/__cb20100623204832/naruto/images/thumb/e/e8/Orochimaru2.jpg/300px-Orochimaru2.jpg",
        name: "Orochimaru",
        worstEnemy: @0
    }
}

Let's quickly explain what we are seeing. Hatake Kakashi has a worst enemy who is Orochimaru. Orochimaru, circularly, has Hatake Kakashi as its worst enemy: the "@0" notation stands for a reference to the object at "#0" (Hatake Kakashi). Then, Hatake Kakashi has a best friend in the database, which was lazily fetched and ignored by the serializer server-side. That's why there is no "bestFriend" property for Hatake Kakashi: uninitialized association are considered as undefined and just skipped. On the other hand, Orochimaru doesn't have a best friend, the association is set to null in the database, and its "bestFriend" property is, accordingly, present and set to nil in the serialized data.

It is worth noting that these extra properties aren't interfering with the deserialization process client-side, even if our client model isn't in sync with the server one: unknown properties are just skipped, they don't prevent from deserializing correct data.

If we modify the Hatake Kakashi name and click on the "Save" button, everything works as expected. There is no changes in the console except for the modified name and we can see that the "bestFriend" property of Hatake Kakashi is still missing: even if the client application is unaware of the best friend association, the existing association wasn't broken in the database during the update. However, if you look again at your console just before the last response, you will notice that nothing was sent for the best friend and worst enemy properties:

REQUEST (data length: 220 bytes)
#0 org.spearal.samples.springangular.data.Person {
    id: 2,
    name: "Hatake Kakashe",
    description: "Konoha",
    imageUrl: "http://img1.wikia.nocookie.net/__cb20140616090940/naruto/images/thumb/b/b3/KakashiHatake.png/300px-KakashiHatake.png"
}

Note: dealing correctly with outdated client models and preventing unwanted overrides in your database is a built-in feature of Spearal JPA2. While you can imagine several workarounds to achieve the same functionality with JSON, there is no ways - as far as I know - to get this result without taking great care of what you are doing client and server side (refer to this article to know more).

Moreover, JSON would simply fail with cyclic structures like the ones we are using here: while the JSON version of the application is working with the old model (see https://examples-spearal.rhcloud.com/spring-angular/index.html), it fails immediately with the new one (see https://examples-spearal.rhcloud.com/spring-angular-v2/index.html).

Sending differential updates

If you deal with entities that contain a large number of properties, it can be useful to have a simple way to send diffs instead of full objects for update. This can be achieved very easily with Spearal, thanks to its concept of partial objects.

To show a quick example of this feature, edit the SpearalIOSExample/SpearalIOSExample/DetailViewController.swift file and do the following change in the saveBtnClick function:

// let person = personFromUI()
let person = personDiffFromUI()

Rerun the application, edit one of our persons, change only its name (for example) and save it. This is what you can find in your console:

REQUEST (data length: 73 bytes)
#0 org.spearal.samples.springangular.data.Person {
    id: 2,
    name: "Hatake Kakashi"
}

As you can see, only the id and name properties were sent to server, the other fields being simply ignored. When the person is reloaded after the save operation, you will notice that other properties are still there: the differential update with just the id and the new name didn't nullify anything. Let's now have a look at the personFromUI and the alternate personDiffFromUI functions:

private func personFromUI() -> Person {
    let person = Person()

    person.id = self.person!.id
    person.name = self.nameText!.text
    person.description_ = self.descriptionText!.text
    person.imageUrl = self.imageUrlText!.text

    return person
}

private func personDiffFromUI() -> Person {
    let person = Person()

    person.id = self.person!.id

    if self.person!.name != self.nameText!.text {
        person.name = self.nameText!.text
    }
    if self.person!.description_ != self.descriptionText!.text {
        person.description_ = self.descriptionText!.text
    }
    if self.person!.imageUrl != self.imageUrlText!.text {
        person.imageUrl = self.imageUrlText!.text
    }

    return person
}

The personFromUI function creates a new person and populates its properties with values from the UI, regardless of whether they have been modified or not. On the contrary, however, the alternate personDiffFromUI function only sets properties that have been modified, leaving the others undefined. Spearal serialization is aware of such undefined properties and, accordingly, skips them when the entity is sent to the server for update.

Note: differential updates should be calculated by reflection or a kind of intelligent bindings instead of what we have done, property by property, in the code above. This is outside of the scope of this article. You could certainly do something similar with JSON, by copying in a dictionary only updated properties. However, it wouldn't be that simple to deal with these partial objects server-side. While Spearal automatically distinguishes between undefined and null properties, standard JSON libraries aren't offering the same feature: you must be very careful with properties that are null because they were missing and those that are null because they are actually set to null.

How to use Spearal iOS in your own projects?

Spearal iOS is based on the new Apple Swift language and building binary distributions of Swift frameworks isn't recommended yet (see this Apple blog post). The recommended way is to create a new XCode workspace, add a reference to the Spearal iOS source code and create your new iOS project alongside.

To get the Spearal iOS source code, you can download a specific release from Github here or clone it with this command:

$ git clone https://github.com/spearal/SpearalIOS.git

You can then checkout the tag you want to work with (eg. git checkout 0.2.0) or stay with the HEAD revision (be careful with the HEAD branch, some commits can be unfortunate). From your new XCode workspace, creating a reference to the Spearal framework is then just a matter of adding the SpearalIOS.xcodeproj file. From your application and in every Swift file where you want to use Spearal, just add this import:

import SpearalIOS

Conclusion and future work

With Spearal iOS, as we said at the beginning of this article, Spearal has reached its first major milestone: support HTML/JavaScript and popular mobile platforms with applications connected to a Java backend. While Spearal is still in an early development stage, we believe that it can be already used in many non-critical software with all the benefits we have exemplified in this article and the previous ones (AngularJS / JAX-RS and Android / Spring MVC).

In no particular order, future work will focus on:

  • Technical documentation,
  • Formal specification of the Spearal serialization format,
  • Support for other platforms (contributions are welcomed),
  • And - of course - improvements and bug fixes.

Feedback would be very appreciated, especially if you plan using or working with us on Spearal. Don't hesitate to comment on this article or to contact us in the Spearal forum!

Spearal website: http://spearal.io.
Follow us on Twitter: @Spearal.

Find your next Integration job at DZone Jobs. See jobs focused on integration, or create your profile and have the employers come to you!

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}