CoreData vous tente mais vous fait peur ? Vous trouvez le framework un peu dur à prendre en main ? Ou vous en avez marre d’écrire autant de ligne à chaque fois juste pour faire une simple récupération de vos données ?
Olivier Halligon (développeur de FoodReporter) vous offrira une découverte de MagicalRecord, le framework qui va drastiquement simplifier votre code CoreData, en apportant le Design Pattern ActiveRecord (comme utilisé en Ruby) sur Objective-C.
2. CoreData : rappels
• Framework Cocoa pour iOS et OSX
• Permet de gérer un graphe d’objets et sa persistance
• Gestion de relations
• Gestion des transactions, d’annulation…
• Gestion des futures (faulting)
• N’est pas une base de données relationnelle
• On peut l’utiliser en InMemory-only
• Peut faire persister les données dans une base SQLite mais aussi en XML
• On peut l’utiliser comme abstraction d’un WebService (via NSIncrementalStore)
5. Complexité de CoreData
• Beaucoup de classes à prendre en main quand on commence
• Dont pour la plupart le rôle semble abstrait de prime abord
• Même si ça vient avec la pratique, peu encourageant au début
• Beaucoup de lignes de code pour des opérations simples
• Une dizaine de lignes rien que pour récupérer un objet dans notre graphe
• Du coup répétitif pour des actions fréquentes
• Nécessité d’utiliser des NSString pour décrire les noms d’entités à récupérer
• Pas d’autocomplétion, pas de vérification à la compilation
• Nécessité de transtyper (caster) les résultats
7. Exemple de Requête
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html#//apple_ref/doc/uid/TP40002484-SW1
- (City*)findOrCreateCityWithName:(NSString*)cityName
{
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
// Find the city if it exists, create it if not
NSManagedObject* foundCity = nil;
NSEntityDescription* cityEntity = [NSEntityDescription entityForName:@"City" inManagedObjectContext:context];
NSFetchRequest* cityRequest = [[NSFetchRequest alloc] init];
[cityRequest setEntity:cityEntity];
NSPredicate* cityPredicate = [NSPredicate predicateWithFormat:@"name == %@", cityName];
[cityRequest setPredicate:cityPredicate];
[cityRequest setFetchLimit:1];
NSArray* cities = [context executeFetchRequest:cityRequest error:nil];
if (cities.count == 0)
{
foundCity = [[NSManagedObject alloc] initWithEntity:cityEntity insertIntoManagedObjectContext:context];
[foundCity setValue:cityName forKey:@"name"];
}
else
{
foundCity = [cities objectAtIndex:0];
}
return (City*)foundCity;
}
8. MagicalRecord
• ActiveRecord pour Objective-C
• Pattern bien connu des programmeurs Ruby
• Utiliser les objets du MDD directement : [Session findAll]
• Un framework «wrapper» pour faciliter votre code
• Requêtes implicites, lecture plus claire
• Plus facile à prendre en main pour débuter en CoreData
• Reste utile même quand vous n’êtes plus débutant pour avoir un code concis
• N’empêche pas de continuer à utiliser les méthodes du framework Apple
11. Comparatif
Nombre de lignes de code CoreData MagicalRecord
Configuration CoreData dans l’AppDelegate 70~90 1
logAllSessions 12 1
emptyGraphObject 17 3
newSessionWithSubject:date:summary:lecturer: 10 6
findOrCreateCityWithName: 24 7
findOrCreatePersonWithFirstName: 25 9
buildFetchedResultsController 24 1
findSimilarSessions 13 2
Total 230~250 65
Avant Après
70% de code
en moins !
12. Possibilités offertes par
MagicalRecord
• Catégories pour avoir des méthodes de commodité
• Récupérer tous les objets qui suivent un prédicat
• findAll, findAllSortedBy:ascending:, findAllByAttribute:withValue:orderBy:ascending:, findAllSortedBy:ascending:withPredicate:, …
• Compter le nombre d’entités
• countOfEntities, countOfEntitiesWithPredicate:
• Récupérer un seul objet
• findFirst, findFirstWithPredicate:, findFirstWithPredicate:sortedBy:ascending: findFirstByAttribute:withValue:
• Créer et supprimer des entités
• createEntity, deleteEntity, deleteAllMatchingPredicate:, truncateAll, …
• Construire des requêtes
• createFetchRequest, requestAll, requestAllWithPredicate:, requestAllWhere:isEqualTo:, …
• Utiliser les Fetch Results Controllers
• fetchAllSortedBy:ascending:withPredicate:groupBy:delegate:
13. Possibilités offertes par
MagicalRecord
• Faciliter la gestion des Contextes
• Toutes les méthodes de MR ont une variante avec et sans contexte
• Sans contexte précisé, MR utilise son defaultContext
• Un ManagedObjectContext par défaut, ainsi qu’un contexte par thread
• [NSManagedObjectContext defaultContext], [NSManagedObjectContext contextForCurrentThread]
• Passer un ManagedObject d’un thread à l’autre, d’un contexte à l’autre
• NSManagedObject
*objectOnThreadTwo
=
[objectOnThreadOne
inThreadContext];
• NSManagedObject
*objectInCtxTwo
=
[objectInCtxOne
inContext:otherContext];
• Création simple d’un contexte fils à la demande
• contextWithParent:, contextThatPushesChangesToDefaultContext
• Gestion des sauvegardes
• En cascade jusqu’au PersistentStore (ex: app en bkg) : saveToPersistentStoreWithCompletion:
• Sur un contexte local via une API avec des blocks : saveWithBlock:completion:, saveInBackgroundWithBlock:…
context
WithParent:
save:
14. Possibilités offertes par
MagicalRecord
• Simplifier la phase d’initialisation
• Initialisation en une ligne
• [MagicalRecord setupCoreDataStack];
• Très pratique pour les Tests Unitaires
• InMemoryStore : toujours partir sur une base vide, ne pas polluer la base de prod
• [NSBundle bundleForClass:self] pour fonctionner même en phase deTU
• [MagicalRecord setupCoreDataStackWithInMemoryStore];
• Possibilité de créer un NSManagedObjectModel unifié d’après les MOM du bundle
• defaultManagedObjectModel, mergedObjectModelFromMainBundle
• Support d’iCloud
• setupCoreDataStackWithiCloudContainer:localStoreNamed:
• setupCoreDataStackWithiCloudContainer:contentNameKey:localStoreNamed:cloudStorePathComponent:completion: