[iOS] RestKit + CoreData

lundi 31 août 2015

Voici un code qui permet de charger le contenu d’un fichier JSON sur un serveur et qui l’enregistre dans une base de donnée CoreData du téléphone.

Pour ce tutoriel, il faut installer Restkit via cocoapods. Il faut aussi ajouter une db Coredata via File > New File > Data Model.
Ensuite, créé vos entités en vous basant sur le json générer , car on fera le mapping entre les différents attributs de votre fichier JSon et des objets en Objective C de type core Data.

Voici l’exemple du JSON

[
    {
        "id": "1",
        "titre": "Exemple de titre 1",
        "sous_titre": "Exemple de sous titre 1",
        "url": "http://www.google.be",
        "nom_image": "image1",
        "type" : "url"
    },	
    {

        "id": "2",
        "titre": "Exemple de titre 2",
        "sous_titre": "Exemple de sous titre 1",
        "url": "http://www.google.com",
        "nom_image": "image2",
        "type" : "url"
    },
    ....
]

Une fois vos entités CoreData déterminées, Xcode peut générer des objets à partir du modèle: File > New File > NSManagedObject subclass, on sélectionne notre data model et ensuite nos entités à générer.

Voici un exemple de fichier généré:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>


@interface Pictures : NSManagedObject

@property (nonatomic, retain) NSNumber * id;
@property (nonatomic, retain) NSString * type;
@property (nonatomic, retain) NSString * nom_image;
@property (nonatomic, retain) NSString * url;
@property (nonatomic, retain) NSString * sous_titre;
@property (nonatomic, retain) NSString * titre;

@end

Dans AppDelegate.m
#import <RestKit/CoreData.h>
#import <RestKit/RestKit.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    //Initialize RESTKIT
    NSURL *baseURL = [NSURL URLWithString:@"http://xxxx.net"];
    RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
    
    // Initialize managed object model from bundle
    NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    // Initialize managed object store
    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
    objectManager.managedObjectStore = managedObjectStore;
    
    // Complete Core Data stack initialization
    [managedObjectStore createPersistentStoreCoordinator];
    NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"PicturesDB.sqlite"];
    NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"RKSeedDatabase" ofType:@"sqlite"];
    NSError *error;
    NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
    NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
    
    // Create the managed object contexts
    [managedObjectStore createManagedObjectContexts];
    
    // Configure a managed object cache to ensure we do not create duplicate objects
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    
    RKEntityMapping *picturesMapping = [RKEntityMapping mappingForEntityForName:@"Pictures" inManagedObjectStore:managedObjectStore];
    picturesMapping.identificationAttributes = @[ @"id" ];
    [picturesMapping addAttributeMappingsFromArray:@[@"id", @"type", @"nom_image", @"url", @"sous_titre", @"titre" ]];

    RKResponseDescriptor *picturesListResponseDescriptor =
    [RKResponseDescriptor responseDescriptorWithMapping:picturesMapping
                                                 method:RKRequestMethodGET
                                            pathPattern:@"/appli/json/cloud.json"
                                                keyPath:nil
                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
     ];
    
    [objectManager addResponseDescriptor:picturesListResponseDescriptor];

    // Enable Activity Indicator Spinner
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    return YES;
}

Dans cette fonction, on initialise nos objets CoreData et Restkit.
Il est important de définir une ResponseDescriptor et si vous créer un objet de type cache c’est très utile pou éviter d’avoir des doublons et par conséquent à chaque appel devoir vider votre entité avant de devoir l’alimenter.

Pour finir voici les appels dans votre vue:

#pragma mark - RESTKit

- (void)requestData {
    
    NSString *requestPath = @"/appli/json/cloud.json";
    
    [[RKObjectManager sharedManager]
     getObjectsAtPath:requestPath
     parameters:nil
     success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
         //pictures have been saved in core data by now
         [self fetchPicturesFromContext];
     }
     failure: ^(RKObjectRequestOperation *operation, NSError *error) {
         RKLogError(@"Load failed with error: %@", error);
     }
     ];
    
}

- (void)fetchPicturesFromContext {
    
    NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Pictures"];
    
//    NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES];
//    fetchRequest.sortDescriptors = @[descriptor];
    
    NSError *error = nil;
    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
    
    Pictures * pic = [fetchedObjects firstObject];
    
    NSLog(@"PICTURES NB: %lu",[fetchedObjects count]);
    NSLog(@"FIRST PICTURE: %@",pic.titre);
}

Et enfin dans le viewDidLoad.m

- (void)viewDidLoad {
    [super viewDidLoad];
    [self requestData];
}

Pour cet article, je me suis basé sur ces deux tutoriels: https://medium.com/ios-os-x-development/restkit-tutorial-how-to-fetch-data-from-an-api-into-core-data-9326af750e10
http://code.tutsplus.com/tutorials/ios-sdk-advanced-restkit-development–mobile-5916

Si jamais vous rencontrer des erreurs avec RestKit, notamment des soucis avec le mapping et vous ne savez pas trop d’où cela peut venir, je vous conseilles de configurer des logs plus poussé via les deux commandes suivantes:

RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelDebug);

Tags: CoreData , Json , RestKit