Skip to content

Instantly share code, notes, and snippets.

@seanhess
Created February 15, 2013 16:55
Show Gist options
  • Save seanhess/4961693 to your computer and use it in GitHub Desktop.
Save seanhess/4961693 to your computer and use it in GitHub Desktop.
//
// BookService.h
// Libros
//
// Created by Sean Hess on 1/10/13.
// Copyright (c) 2013 Sean Hess. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Book.h"
@interface BookService : NSObject
+(BookService*)shared;
-(void)loadStore;
-(NSFetchRequest*)allBooks;
-(NSFetchRequest*)popular;
-(NSPredicate*)searchForText:(NSString*)text;
-(NSPredicate*)filterByType:(BookFilter)filter;
@end
//
// BookService.m
// Libros
//
// Created by Sean Hess on 1/10/13.
// Copyright (c) 2013 Sean Hess. All rights reserved.
//
#import "BookService.h"
#import "Book.h"
#import <RestKit/RestKit.h>
#import "ObjectStore.h"
#import "NSObject+Reflection.h"
@interface BookService ()
@end
@implementation BookService
+ (BookService *)shared
{
static BookService *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[BookService alloc] init];
[instance addMappings];
});
return instance;
}
- (void)addMappings {
RKEntityMapping *bookMapping = [ObjectStore.shared mappingForEntityForName:@"Book"];
[bookMapping setIdentificationAttributes:@[@"bookId"]];
NSMutableArray * propertyNames = [NSMutableArray arrayWithArray:[_Book propertyNames]];
NSIndexSet * indices = [propertyNames indexesOfObjectsPassingTest:^(NSString * name, NSUInteger idx, BOOL * stop) {
return [name isEqualToString:@"descriptionText"];
}];
[propertyNames removeObjectsAtIndexes:indices];
[bookMapping addAttributeMappingsFromArray:propertyNames];
[bookMapping addAttributeMappingsFromDictionary:@{@"description": @"descriptionText"}];
RKResponseDescriptor * responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:bookMapping pathPattern:@"/books" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[ObjectStore.shared addResponseDescriptor:responseDescriptor];
[ObjectStore.shared syncWithFetchRequest:self.allBooks forPath:@"/books"];
}
// So you can compose with compoundPredicates. Wahoo.
// Or just let the dumb view controllers do whatever they want. it's not THAT bad
// not that great either
// You can execute fetch requests right on the context
// OR you can make a fetched results controller and give it a fetch request
-(void)loadStore {
[[ObjectStore shared].objectManager getObjectsAtPath:@"/books" parameters:nil success:^(RKObjectRequestOperation * operation, RKMappingResult *mappingResult) {
} failure: ^(RKObjectRequestOperation * operation, NSError * error) {
NSLog(@"FAILURE %@", error);
}];
}
// has the sort descriptor built in
-(NSFetchRequest*)allBooks {
NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Book"];
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES];
fetchRequest.sortDescriptors = @[descriptor];
return fetchRequest;
}
-(NSFetchRequest*)popular {
NSFetchRequest * fetchRequest = [self allBooks];
return fetchRequest;
}
-(NSPredicate*)searchForText:(NSString*)text {
return [NSPredicate predicateWithFormat:@"title BEGINSWITH[c] %@", [text lowercaseString]];
}
-(NSPredicate*)filterByType:(BookFilter)filter {
if (filter == BookFilterHasAudio) return [NSPredicate predicateWithFormat:@"audioFiles > 0"];
else if (filter == BookFilterHasText) return [NSPredicate predicateWithFormat:@"textFiles > 0"];
else return nil;
}
@end
//
// ObjectStore.h
// Libros
//
// Created by Sean Hess on 1/11/13.
// Copyright (c) 2013 Sean Hess. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#import "User.h"
@interface ObjectStore : NSObject
@property (strong, nonatomic) RKObjectManager* objectManager;
@property (strong, nonatomic) RKManagedObjectStore * objectStore;
@property (readonly, nonatomic) NSManagedObjectContext * context;
// use [UserService main] instead of calling this
@property (strong, nonatomic) User* mainUser;
+ (ObjectStore*)shared;
- (void)saveContext;
- (RKEntityMapping*)mappingForEntityForName:(NSString*)entityName;
- (void)addResponseDescriptor:(RKResponseDescriptor*)descriptor;
// Simple way to sync objects whose urls have no parameters
// Will delete anything not matched
- (void)syncWithFetchRequest:(NSFetchRequest*)request forPath:(NSString*)path;
@end
//
// ObjectStore.m
// Libros
//
// Created by Sean Hess on 1/11/13.
// Copyright (c) 2013 Sean Hess. All rights reserved.
//
// I need to store some stuff
// it needs to know the application directory
// so a singleton with some initialization
#import "ObjectStore.h"
#import "Settings.h"
@interface ObjectStore()
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@end
@implementation ObjectStore
@synthesize managedObjectModel = _managedObjectModel;
+ (ObjectStore *)shared
{
static ObjectStore *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ObjectStore alloc] init];
[sharedInstance initObjectManager];
});
return sharedInstance;
}
- (void)initObjectManager {
NSLog(@"ENDPOINT = %@", API_ENDPOINT);
// Core Data Example
// Initialize RestKIT
self.objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:API_ENDPOINT]];
self.objectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:self.managedObjectModel];
self.objectManager.managedObjectStore = self.objectStore;
// Other Initialization (move this to app delegate)
[self.objectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Libros.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [self.objectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
[self.objectStore createManagedObjectContexts];
self.objectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:self.context];
}
// some demeters law, don't access the store and stuff, just add them here.
// See BookService for usage
- (RKEntityMapping *)mappingForEntityForName:(NSString *)entityName {
return [RKEntityMapping mappingForEntityForName:entityName inManagedObjectStore:self.objectStore];
}
- (void)addResponseDescriptor:(RKResponseDescriptor *)descriptor {
[self.objectManager addResponseDescriptor:descriptor];
}
- (void)syncWithFetchRequest:(NSFetchRequest *)request forPath:(NSString *)path {
[self.objectManager addFetchRequestBlock:^(NSURL* url) {
RKPathMatcher * matcher = [RKPathMatcher pathMatcherWithPattern:@"/books"];
NSFetchRequest * matchingRequest = nil;
BOOL match = [matcher matchesPath:[url relativePath] tokenizeQueryStrings:NO parsedArguments:nil];
if (match) {
matchingRequest = request;
}
return matchingRequest;
}];
}
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Libros" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSManagedObjectContext *)context {
// the mainQueueManagedObjectContext is a child of this one, but it doesn't persist. I'm not exactly sure what it is for.
// either way, we need to use this one if we want things to save
return self.objectStore.persistentStoreManagedObjectContext;
}
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.context;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment