Created
February 15, 2013 16:55
-
-
Save seanhess/4961693 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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