Introduction

La synchronisation de données en ligne est une pratique courante afin d’avoir un contenu mis à jour à chaque utilisation (applications d’informations, de news et autres).

Trouver un moyen simple d’embarquer ces données avant une synchronisation en ligne est intéressant, permettant une utilisation de l’application même si les données ne sont pas à jour.

Travaillant en Objective-C sur des applications mobiles pour iphone/ipad, nous allons voir comment utiliser Restkit à ces fins.

Pré requis

Il vous faudra Xcode à jour ainsi qu’un serveur web local afin de simuler un service web.

Restkit, Qu’est que c’est?

Restkit est un framework Cocoa permettant la communication avec des services web RESTfull sous iOS et Mac OS X.

Cette librairie permet entre autre d’interagir avec ces services web avec les requetes HTTP: GET POST PUT et DELETE. Plus interessant, elle permet de “mapper” des données avec Core Data, un framework de base de données utilisé dans Mac OS X et iOS.

Ce “mapping” peut être utilisé pour directement enregistrer les données d’un service web dans la base de donnée d’une application en passant par les entités de Core Data.

Enfin, Restkit permet de pré remplir la base de donnée de l’application mobile avec une base de données locale, ce qui peut permettre à une application d’avoir des données de base et de pouvoir continuer à synchroniser celles-ci auprès de services web.

Installation

La documentation de cette libraire est très bien faite, vous la trouverez ici: Restkit

L’installation de la libraire pour Xcode 4.X est tout aussi bien faite: Installation Restkit

Avant d’installé votre librairie, il est important que l’application que vous developpez intègre Core Data afin de simplifier la synchronisation de données.

 

 

Préparation de notre application mobile

Une fois la librairie installé, nous allons créé une entité dans le modèle de donnée de l’application. J’ai choisit l’entité suivante, Véhicule

  • idVehicule
  • couleur
  • nombreRoue

Nous allons générer le modèle de l’entité créé. Pour cela, il suffit de sélectionner l’entité dans le modèle, puis “Editor -> Create NSManagedObjectSubclass”.

L’objet créé, nous allons pouvoir travailler directement nos données.

Service web

Pour tester notre application, nous allons utiliser un petit script php afin de générer un service web renvoyant une liste de véhicule.

<?php header('Content-type: application/json'); $data=array('Vehicules' = array(
                                array(
                                    'idVehicule' => 1,
                                    'nombreRoue' => 4,
                                    'couleur' => 'vert'),
                                array(
                                    'idVehicule' => 2,
                                    'nombreRoue' => 8,
                                    'couleur' => 'rouge'),
                                array(
                                    'idVehicule' => 3,
                                    'nombreRoue' => 2,
                                    'couleur' => 'bleu')));

echo json_encode($data);

?>

Mapping et récupération de données

Passons au coeur du sujet, le mapping. Voici à quoi ressemble la fonction principale de mon AppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];

    // Creation d'un RKObjectManager local avec notre chemin
    RKObjectManager* objectManager = [RKObjectManager managerWithBaseURLString:@"http://localhost:8888/Synchronisator/"];

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Synchronisator" withExtension:@"momd"];

    // on indique dans quelle base de donnée les données seront sauvegardées

    objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"new-database.sqlite" usingSeedDatabaseName:nil managedObjectModel:[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] delegate:self];

    // on rend partage notre objetManager local
    [RKObjectManager setSharedManager:objectManager];

    // On cree un mapping avec la classe Vehicule
    RKManagedObjectMapping *vehiculeMapping = [RKManagedObjectMapping mappingForClass:[Vehicule class] inManagedObjectStore:objectManager.objectStore];

    // on indique les clés et les attributs liés
    [vehiculeMapping mapKeyPath:@"idVehicule" toAttribute:@"idVehicule"];
    [vehiculeMapping mapKeyPath:@"couleur" toAttribute:@"couleur"];
    [vehiculeMapping mapKeyPath:@"nombreRoue" toAttribute:@"nombreRoue"];

    // on indique sur quel attribut est la clé primaire
    [vehiculeMapping setPrimaryKeyAttribute:@"idVehicule"];

    // on ajoute notre mapping a l'objectManager
    [[RKObjectManager sharedManager].mappingProvider setMapping:vehiculeMapping forKeyPath:@"Vehicules"];
    [[RKObjectManager sharedManager].mappingProvider setSerializationMapping:[vehiculeMapping inverseMapping] forClass:[Vehicule class]];

    // on synchronise les données
    [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/index.php" delegate:self];

    [self.window makeKeyAndVisible];
    return YES;
}

Passons à quelques explications:

  • RKObjectManager est la base de Restkit, c’est cet objet qui gère les connexions de Restkit.
  • RKManagedObjectStore indique où les données seront enregistrés, ici ça sera dans “new-database.sqlite”.
  • La methode setSharedManager permet de mettre à disposition notre RKObjectManager, il sera atteignable de partout avec la méthode sharedManager. Il faut savoir qu’on ne peut avoir qu’un RKObjectManager partagé, si nous voulons refaire un mapping en changeant l’url, il faudra le recréer.
  • RKManagedObjectMapping indique quelle classe sera liée et dans quel ObjectStore les données seront stockées.

    La déclaration du mapping se fait ensuite avec une clé (ou “keyPath”) correspondant au champ du JSON et son attribut correspondant à la variable de la classe liée.

    On peut aussi déclarer la clé primaire pour ce lien, celle ci sera retenu dans la base de donnée.

    On ajoute notre RKManagedObjectMapping à notre RKObjectManager partagé en indiquant la clé de celui-ci, on finit par le mapping inverse pour pouvoir sérialiser nos objets.

  • La méthode loadObjectAtResourcePath va lancer la synchronisation des données.

Comment apercevoir les données récupérées?

Pour voir nos objects, on va les afficher avec la methode didLoadObjects (généré par Restkit). Si les objects retournés sont de type “Vehicule” on pourra voir les infos correspondantes.

En cas d’erreur de connexion ou de synchronisation, on afficher l’erreur afin de la corriger.

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {

    if ([[objects objectAtIndex:0] isKindOfClass:[Vehicule class]]){

        for (Vehicule *vehicule in objects){
            NSLog(@"Vehicule: id: %d, nbr roue: %d, couleur: %@", [vehicule.idVehicule intValue], [vehicule.nombreRoue intValue], vehicule.couleur);
        }
    }
}

- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError *)error{

    NSLog(@"Error: %@", error);
    // Handle error
}

On obtient le résultat suivant:

Et la base de donnée dans tout ça?

Nos données une fois récupéré sont directement sauvegarder dans la base de donnée indiqué. Il faut aller l’ouvrir dans le dossier du Simulateur. On apperçoit aussi dos données.

Pré peupler sa base de donnée

Si vous voulez embarqué vos données pour une premiere utilisation sans avoir à la synchroniser directement, il va falloir pré peupler notre application avec une deuxième base de donnée.

Pour cela on va se servir de notre “new-database.sqlite” créé par le simulateur. Copier la ailleurs et modifier là.

Voici ma “last-database.sqlite”:

Ajouter cette nouvelle base de donnée à votre projet:

Il nous reste à remplacer une ligne de code:

objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"new-database.sqlite" usingSeedDatabaseName:nil managedObjectModel:[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] delegate:self];

Par une autre ligne de code:

// prepopulate database
    objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"new-database.sqlite" usingSeedDatabaseName:@"last-database.sqlite" managedObjectModel:[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] delegate:self];

Il ne reste qu’à compiler et on obtient la base de donnée suivante:

Conclusion

La librairie Restkit nous a permise sans trop de difficulté de récupérer simplement des données d’un service en ligne et de les lier à notre application afin de les sauvegarder via CoreData.

Vous savez maintenant aussi comment pré peupler une base de donnée pour éviter d’être (trop) dépendant d’un service web ou d’une connexion à internet.