RestKit Mapping Invalid parameter not satisfying: managedObjectStore - ios

Possible duplication : RestKit Core Data 'managedObjectStore is nil'
Could somebody explain me why when I run my application I get error like :
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: managedObjectStore'
*** First throw call stack
It happened to me only in my MainViewController when I'm calling the following method:
-(void)loadLocationsOfWearers{
RKManagedObjectStore *store = [[DateModel sharedDataModel] objectStore];
NSIndexSet *statusCodeSet= RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKMapping *mapping = [MappingProvider watchesMapping];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:nil keyPath:#"watch" statusCodes:statusCodeSet];
NSURL *url = [NSURL URLWithString:kSERVER_ADDR];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
RKManagedObjectRequestOperation *operation =[[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
operation.managedObjectCache = store.managedObjectCache;
operation.managedObjectContext = store.mainQueueManagedObjectContext;
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
_wearerList = mappingResult.array;
NSLog(#"Results:\n %#",mappingResult.dictionary);
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"ERROR: %#", error);
NSLog(#"Response: %#", operation.HTTPRequestOperation.responseString);
}];
[operation start];
}
When I'm calling this method in other ViewControllers everything works fine.
It looks like my managedObjectStore is nil, but I don't know what is the reason for that...
Please take a look at my DateModel code and share with me your ideas:
#implementation DateModel
+ (id)sharedDataModel
{
static DateModel *__sharedDataModel = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__sharedDataModel = [[DateModel alloc] init];
});
return __sharedDataModel;
}
- (NSManagedObjectModel *)managedObjectModel
{
return [NSManagedObjectModel mergedModelFromBundles:nil];
}
- (id)optionsForSqliteStore
{
return #{
NSInferMappingModelAutomaticallyOption: #YES,
NSMigratePersistentStoresAutomaticallyOption: #YES
};
}
- (void)setup
{
self.objectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"Data.sqlite"];
NSLog(#"Setting up store at %#", path);
NSError *error;
[self.objectStore addSQLitePersistentStoreAtPath:path
fromSeedDatabaseAtPath:nil
withConfiguration:nil
options:[self optionsForSqliteStore]
error:&error];
[self.objectStore createManagedObjectContexts];
self.objectStore.managedObjectCache =[[RKInMemoryManagedObjectCache alloc]initWithManagedObjectContext:self.objectStore.persistentStoreManagedObjectContext];
[RKManagedObjectStore setDefaultStore:self.objectStore];
}
In my AppDelegate method I just simply setup my store.
-(void)setupCoreData
{
[[DateModel sharedDataModel]setup];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self setupCoreData];
}
When I run my application I'm getting error like on the screen below:
+(RKMapping *)watchesMapping
{
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Watches" inManagedObjectStore:[[DateModel sharedDataModel]objectStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"watch_id",
#"altitude":#"altitude",
#"battery_life":#"battery_life",
#"button_press_time":#"button_press_time",
#"charging_status":#"charging_status",
#"gmaps":#"gmaps",
#"id_addr":#"id_addr",
#"last_keep_alive":#"last_keep_alive",
#"last_update_time":#"last_update_time",
#"latitude":#"latitude",
#"longitude":#"longitude",
#"location":#"location",
#"network":#"network",
#"phonewatchno":#"phonewatchno",
#"rssi":#"rssi",
#"short_imei":#"short_imei",
#"updated_at":#"updated_at",
#"voltage":#"voltage",
#"waerer_id":#"wearer_id",
#"updated_at":#"updated_at",
#"token":#"token"
}
];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"alerts" toKeyPath:#"alerts" withMapping:[MappingProvider alertsMapping]]];
return mapping;
}

loadLocationsOfWearers is being called too soon (before setupCoreData is called). Call it later, or call setupCoreData when the data model singleton is created.

Related

iOS app requires deleting to fetch data

i'm looking for some information regarding this problem.
I have an iOS project which fetches data from a remote server using REST-Kit. If I open the iOS simulator, delete the app, and then build and run, the tableview appears as expected. Left Image
However if I then rebuild and run without deleting the app within the simulator first, there are no thrown errors, but the cells in the tableview are empty. Right Image
Has anybody had this issue before? How do I get the data to appear correctly without first deleting the app! Thanks for reading!
Code from app delegate to initialise core data and define mappings between JSON and core data.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(#"App started");
// Initialise RestKit
NSURL *baseURL = [NSURL URLWithString:#"http://*.*.*.*/"];
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];
NSError *error = nil;
BOOL success = RKEnsureDirectoryExistsAtPath(RKApplicationDataDirectory(), &error);
if(! success){
RKLogError(#"Failed to create application directory at path '%#': %#", RKApplicationDataDirectory(), error);
}
[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
// Complete core data store initialization
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"EventsDB.sqlite"];
NSString *seedPath = [[NSBundle mainBundle] pathForResource:#"RKSeedDatabase" ofType:#"sqlite"];
NSPersistentStore *persistantStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
NSAssert(persistantStore, #"Failed to add persistent with error %#", error);
objectManager.managedObjectStore = managedObjectStore;
// Create the managed object contexts
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache to ensure we don't create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
// If a data request fails we should send an NSError message
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"errorMessage"]];
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping pathPattern:nil keyPath:#"error" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
[objectManager addResponseDescriptorsFromArray:#[errorDescriptor]];
// Map event
RKEntityMapping *eventMapping = [RKEntityMapping mappingForEntityForName:#"Event" inManagedObjectStore:managedObjectStore];
eventMapping.identificationAttributes = #[#"eventId"];
[eventMapping addAttributeMappingsFromDictionary:#{//#"JSON":#"MODEL"
#"_id":#"eventId",
#"name":#"name",
#"venue":#"venue",
#"drink_prices":#"prices",
#"popularity":#"popularity",
#"genre":#"genre",
#"promotionQuote":#"promotionQuote",
#"dress_code":#"dressCode",
#"opening_time":#"openingTime",
#"closing_time":#"closingTime",
#"critic_review":#"criticReview",
#"critic_rating":#"criticRating",
#"image":#"imageURL",
#"published":#"published"
}];
// Map drink deals
RKEntityMapping *dealMapping = [RKEntityMapping mappingForEntityForName:#"Deal" inManagedObjectStore:managedObjectStore];
dealMapping.identificationAttributes = #[#"dealId"];
[dealMapping addAttributeMappingsFromDictionary:#{ //#"JSON":#"MODEL"
#"_id":#"dealId",
#"description":#"dealDescription",
#"price":#"price"
}];
// Map entries
RKEntityMapping *entryMapping = [RKEntityMapping mappingForEntityForName:#"Entry" inManagedObjectStore:managedObjectStore];
entryMapping.identificationAttributes = #[#"entryId"];
[entryMapping addAttributeMappingsFromDictionary:#{ //#"JSON":#"MODEL"
#"_id":#"entryId",
#"type":#"type",
#"price":#"price"
}];
// Map guestliest -- reviews, city and university come off a guestlisted user
RKEntityMapping *guestlistMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:managedObjectStore];
guestlistMapping.identificationAttributes = #[#"userId"];
[guestlistMapping addAttributeMappingsFromDictionary:#{//#"JSON":#"MODEL"
#"_id":#"userId",
#"name":#"name",
}];
// Map vip extras
RKEntityMapping *vipMapping = [RKEntityMapping mappingForEntityForName:#"VIP" inManagedObjectStore:managedObjectStore];
vipMapping.identificationAttributes = #[#"vipId"];
[vipMapping addAttributeMappingsFromDictionary:#{ //#"JSON":#"MODEL"
#"_id":#"vipId",
#"type":#"type",
#"available":#"available"
}];
// EventList Mapping
RKEntityMapping *eventListMapping = [RKEntityMapping mappingForEntityForName:#"EventList" inManagedObjectStore:managedObjectStore];
// Register relationships
[eventListMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"events" toKeyPath:#"events" withMapping:eventMapping]];
[eventMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"drink_deals" toKeyPath:#"deals" withMapping:dealMapping]];
[eventMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"entry" toKeyPath:#"entries" withMapping:entryMapping]];
[eventMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"guestlist" toKeyPath:#"guestlist" withMapping:guestlistMapping]];
[eventMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"vipExtras" toKeyPath:#"vipExtras" withMapping:vipMapping]];
// Regiseter event mapping with the provider. Do i have to do this for all 4 event maps?
RKResponseDescriptor *eventListResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:eventListMapping
method:RKRequestMethodGET
pathPattern:#"api/event.json"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:eventListResponseDescriptor];
NSLog(#"objMan responseDescriptors->%#", objectManager.responseDescriptors);
// enable activity indicator spinner
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
// Map
return YES;
}
Code to fetch data
-(void)requestData {
NSLog(#"requesting data");
NSString *requestPath = #"api/event.json";
[[RKObjectManager sharedManager]
getObjectsAtPath:requestPath
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// events have been saved in core data by now
NSLog(#"Success");
[self fetchEventsFromContext];
}
failure: ^(RKObjectRequestOperation *operation, NSError *error){
RKLogError(#"Load failed with error: %#", error);
NSLog(#"Fail");
}
];
}
-(void)fetchEventsFromContext {
NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"EventList"];
if(!context) exit(-1);
if(!fetchRequest) exit(-1);
//NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"popularity" ascending:YES];
fetchRequest.sortDescriptors = nil;//#[descriptor];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
EventList *eventList = [fetchedObjects firstObject];
self.events = [eventList.events allObjects];
if(!self.events){NSLog(#"No events");}
[self.tableView reloadData];
NSLog(#"reloading data");
}
**Code to render table cells **
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"rendering cells");
MasterTableCellTableViewCell *cell = (MasterTableCellTableViewCell *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:#"MasterTableCell" ];
if(!cell)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MasterTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSLog(#"Got here");
NSLog(#"self.events[indexPath.row]->%#", self.events[indexPath.row]);
tableView.rowHeight = 160;
Event *event = self.events[indexPath.row];
NSString *imageURL = event.imageURL;
[cell.backgroundImageView sd_setImageWithURL:[NSURL URLWithString:imageURL]];
cell.eventLocationLabel.text = event.venue;
cell.eventNameLabel.text = event.name;
cell.criticRatingLabel.text = #"Critic Rating";
// cell.criticRatingValue. = event.criticRating; // How to display a number?
cell.userRatingLabel.text = #"User Rating";
cell.userRatingValue.text = #"X";
NSLog(#"Finished assigning cell deets");
return cell;
}

Working with RestKit to simultaneously in transient and persistent objects(core data)

I'm working on RestKit to store data in Core Data I have done storing them on transient objects. How do I save them parallely in Core Data.
Should I assign PersistentStore to RKObjectManager or NSManagedObjectContext to RKObjectManager.
I have gone through many tutorials each one conflicts from other.
Suggest some clear Tutorial to work on.
After going through github tutorial i have written the following code for mapping data to coredata and transient objects.But it is not working
//assigning the model
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Sample" ofType:#"momd"]];
NSLog(#"sql:%#",[RKApplicationDataDirectory() stringByAppendingPathComponent:#"Sample![enter image description here][1].sqlite"]);
objectManager=[[RKObjectManager alloc]init];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
NSError *error;
[managedObjectStore addSQLitePersistentStoreAtPath:[RKApplicationDataDirectory() stringByAppendingPathComponent:#"FourthEstateApp.sqlite"] fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSLog(#"sql:%#",[RKApplicationDataDirectory() stringByAppendingPathComponent:#"FourthEstateApp.sqlite"]);
[managedObjectStore createManagedObjectContexts];
//creating mapping
storyDataMapping=[RKEntityMapping mappingForEntityForName:#"Story" inManagedObjectStore:managedObjectStore];
[storyDataMapping addAttributeMappingsFromArray:[NSArray arrayWithObjects:
#"shares",
#"views",
#"weight",
#"story_url",
#"other_articles",
#"color",
#"public_url",
#"images_urls", nil
]
];
articleDataMapping=[RKEntityMapping mappingForEntityForName:#"Article" inManagedObjectStore:managedObjectStore];
[articleDataMapping addAttributeMappingsFromArray:[
NSArray arrayWithObjects:#"location", #"original_url",
#"publication_date_time", #"title",
#"article_url", #"source_name",
#"source_twitter_id", nil
]
];
[storyDataMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"main_article"
t toKeyPath:#"main_article"
withMapping:articleDataMapping]];
responseDescriptorForStoryData = [RKResponseDescriptor responseDescriptorWithMapping:storyDataMapping method:RKRequestMethodGET
pathPattern:Nil keyPath:nil statusCodes:
RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptorsFromArray:#[responseDescriptorForStoryData]];
NSURL *URL = [NSURL URLWithString:categoryURL];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
RKManagedObjectRequestOperation *objectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[responseDescriptorForStoryData]];
[objectRequestOperation
setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Load collection of Articles: %#", mappingResult);
for (Story *s in mappingResult.array) {
NSLog(#"storymain article is:%#", s.main_article.title);
[stories addObject:s];
}
//on success fetching the data from coredata
NSManagedObjectContext *managedObjectContext =[[NSManagedObjectContext alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Story" inManagedObjectContext:managedObjectStore.mainQueueManagedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
NSLog(#"results from cd:%# error:%#",mutableFetchResults,error);
[[NSNotificationCenter defaultCenter] postNotificationName:#"success" object:stories];
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
//RKLogError(#"Operation failed with error: %#", error);
[[NSNotificationCenter defaultCenter] postNotificationName:#"failure" object:error];
NSLog(#"Operation failed with error: %#", error);
}
];
Enter code here
[objectManager enqueueObjectRequestOperation:objectRequestOperation];
[objectRequestOperation start];
successblock is executed
when I am trying to fetch the data following error is occured
CoreData: error: Failed to call designated initializer on NSManagedObject class 'Story'

POST with RestKit 0.20 - NSManagedObjectContext issue

I'm trying to make a POST request with RestKit, but when I call the postObject method of my RKObjectManager I get the following error :
Can only use -performBlockAndWait: on an NSManagedObjectContext that was created with a queue.
This error came when the method performBlockAndWait is called by the RKManagedObjectRequestOperation class.
Here is my code :
RKObjectMapping *responseMapping = [RKObjectMapping requestMapping];
[responseMapping addAttributeMappingsFromDictionary:#{
// ...
}];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
NSString *pathPattern = #"myPath";
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping method:RKRequestMethodPOST pathPattern:pathPattern keyPath:nil statusCodes:statusCodes];
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
[requestMapping addAttributeMappingsFromDictionary:#{
// ...
}];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[MyClass class] rootKeyPath:#"" method:RKRequestMethodPOST];
NSURL *baseURL = [NSURL URLWithString:#"http://192.168.1.1:8080"];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:baseURL];
[manager addRequestDescriptor:requestDescriptor];
[manager addResponseDescriptor:responseDescriptor];
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
manager.managedObjectStore = managedObjectStore;
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"mySqliteDatabaseFile"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
[managedObjectStore createManagedObjectContexts];
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
// POST to create
#try {
[manager postObject:visitReport path:#"myPath" parameters:nil success:^( RKObjectRequestOperation *operation , RKMappingResult *mappingResult ) {
NSLog(#"Success");
} failure:^( RKObjectRequestOperation *operation , NSError *error ){
NSLog(#"Failure - %#",error);
}];
}
#catch (NSException *exception) {
NSLog(#"error - %#", exception);
}
I don't understand the problem with my context, any help is welcomed.
Thank you.
Ok finally I find the problem :
The object visitReport I tried to POST is not from the NSManagedObjectContext expected by RestKit.
To solve, I create a new object :
MyObject *visitReportContext = [NSEntityDescription insertNewObjectForEntityForName:#"tableName"
inManagedObjectContext:objectStore.mainQueueManagedObjectContext];
visitReportContext.Id = visitReport.Id
visitReportContext.title = visitReport.title
// etc..
Then, when I POST my new object, it works. But it's not very beautiful... How can I deal with my old object visitReport without create a new one in this "RestKit context" ?

iOS RestKit issue: Invalid parameter not satisfying: responseDescriptors

I am trying to use RestKit to retrieve a listing of events and I keep getting this:
2013-05-20 10:52:56.708 EventApp[3380:c07] I restkit:RKLog.m:34 RestKit logging initialized...
2013-05-20 10:52:56.773 EventApp[3380:c07] *** Assertion failure in -[RKObjectRequestOperation initWithRequest:responseDescriptors:], /Users/mitchell/Desktop/eventapp/take2/EventApp/Pods/RestKit/Code/Network/RKObjectRequestOperation.m:158
2013-05-20 10:52:56.774 EventApp[3380:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: responseDescriptors'
I have been scratching my head for days on this one. As I have a fair amount of gaps in my iOS dev skills(about one project every year) it would greatly help if someone can just lead me in the right direction here using some laymen terms.
Please consider that I am looking to use enqueueObjectRequestOperation specifically for batching multiple requests. I have just pieced together bits of my code for translation here.
Here is what my datamodel looks like:
Here is the what the JSON file looks like:
[{
"id":1,
"farm_id":1,
"all_day": "NO",
"from": "2013-05-08T18:45:38Z",
"to": "2013-05-08T18:45:38Z",
"name": "event 1",
"desc": "some description",
"photo": "some.png",
"price": "price"
}]
Here is my code:
NSManagedObjectContext *context;
RKObjectManager *objectManager;
if (self.eventContext == nil) {
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"FarmApp" ofType:#"momd"]];
RKEntityMapping *entityMapping;
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *eventPersistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(eventPersistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
// Configure the object manager
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://sandbox.bm.com"]];
[RKObjectManager setSharedManager:objectManager];
[objectManager setRequestSerializationMIMEType:#"application/json"];
[objectManager setAcceptHeaderWithMIMEType:#"text/plain"];
objectManager.managedObjectStore = managedObjectStore;
entityMapping = [RKEntityMapping mappingForEntityForName:#"Event" inManagedObjectStore:managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:#{
#"id": #"eventID",
#"farm_id": #"farm",
#"all_day": #"allDay",
#"from": #"from",
#"to": #"to",
#"name": #"name",
#"desc": #"desc",
#"photo": #"photo",
#"price": #"price"
}];
RKResponseDescriptor *successDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:successDescriptor];
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:#"errors" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
[objectManager addResponseDescriptor:errorDescriptor];
self.eventContext = managedObjectStore.mainQueueManagedObjectContext;
}
context = self.eventContext;
NSString* url = [NSString stringWithFormat:#"http://sandbox.bm.com/farmapp/%#.json", #"events"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:[objectManager responseDescriptors]];
[operation setCompletionBlockWithSuccess:nil failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Loaded this error: %#", [error localizedDescription]);
}];
NSArray *operations = [NSArray arrayWithObjects:operation, nil];
for (int i = 0; i < [operations count]; i++ ) {
[[RKObjectManager sharedManager] enqueueObjectRequestOperation:[operations objectAtIndex:i]];
}
Can someone out there help me?
Here is the final solution
if (self.eventContext == nil) {
NSManagedObjectContext *context;
RKObjectManager *objectManager;
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"FarmApp" ofType:#"momd"]];
RKEntityMapping *entityMapping;
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *eventPersistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(eventPersistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
// Configure the object manager
objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://sandbox.bm.com"]];
[RKObjectManager setSharedManager:objectManager];
[objectManager setRequestSerializationMIMEType:#"application/json"];
[objectManager setAcceptHeaderWithMIMEType:#"text/plain"];
objectManager.managedObjectStore = managedObjectStore;
entityMapping = [RKEntityMapping mappingForEntityForName:#"Event" inManagedObjectStore:managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:#{
#"id": #"eventID",
//#"farm_id": #"farm",-->cannot create relationship this way
#"farm_id" : #"farmID",//farmID attribute needs to be added to Event's model
#"all_day": #"allDay",
#"from": #"from",
#"to": #"to",
#"name": #"name",
#"desc": #"desc",
#"photo": #"photo",
#"price": #"price"
}];
[entityMapping addConnectionForRelationship:#"farm" connectedBy:#"farmID"];
RKResponseDescriptor *successDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:#"events" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:successDescriptor];
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:#"errors" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
[objectManager addResponseDescriptor:errorDescriptor];
self.eventContext = managedObjectStore.mainQueueManagedObjectContext;
context = self.eventContext;
NSString* url = [NSString stringWithFormat:#"http://sandbox.bm.com/farmapp/%#.json", #"events"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
RKManagedObjectRequestOperation *operation = [objectManager managedObjectRequestOperationWithRequest:request managedObjectContext:managedObjectStore.mainQueueManagedObjectContext success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
NSArray *operations = [NSArray arrayWithObjects:operation, nil];
for (int i = 0; i < [operations count]; i++ ) {
[[RKObjectManager sharedManager] enqueueObjectRequestOperation:[operations objectAtIndex:i]];
}
}
As #JoelH. suggests in one of his comments, you need to use RKManagedObjectRequestOperation instead of RKObjectRequestOperation.
For example :
RKManagedObjectRequestOperation *operation = [objectmanager managedObjectRequestOperationWithRequest:request managedObjectContext:managedObjectStore.mainQueueManagedObjectContext success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
Besides, I think the way you are mapping
#"farm_id": #"farm"
is not correct.
If you want to build the relationship between Event and Farm, you need to use one of these methods :
relationshipMappingFromKeyPath:toKeyPath:withMapping:
or addConnectionForRelationship:connectedBy:
If the Farm object already exists and you only want to map the new Event, I would go for addConnectionForRelationship:connectedBy:
For example :
RKEntityMapping* eventMapping = [RKEntityMapping mappingForEntityForName:#"Event" inManagedObjectStore:managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:#{
#"id": #"eventID",
//#"farm_id": #"farm",-->cannot create relationship this way
#"farm_id" : #"farmID",//farmID attribute needs to be added to Event's model
#"all_day": #"allDay",
#"from": #"from",
#"to": #"to",
#"name": #"name",
#"desc": #"desc",
#"photo": #"photo",
#"price": #"price"
}];
[eventMapping addConnectionForRelationship:#"farm" connectedBy:#"farmID"];
It would also require to add a farmID attribute in your Event model, as RestKit does not allow relationship connection without intermediary attributes yet.
The RestKit docs (Mapping Without KVC) seem to imply that if you don't have a KVC label on the top level(e.g. events:[]), a pathPattern: is required for the parser to know which mapping to use. Since both your keyPath: and pathPattern: are nil for your successDescriptor, I have two suggestions:
If changing the JSON structure is possible, change the top level to { events:[...] } and change keyPath:nil to keyPath:#"events" to reflect this change.
If it's not possible, change pathPattern:nil to pathPattern:#"/farmapp" Notice, I set pathPattern: to match your resource URL. This second suggestion might not work if you have other types of resources branching from that URL as well.
Also, I noticed that your errorDescriptor uses the same mapping(entityMapping) as your successDescriptor. I don't know if that's what you intended, but if your error response is supposed to be some different error object, I suggest changing that.
Hope this helps!

RestKit: How to batch multiple requests and get a response once they finish?

I just found out RestKit and it will be an important part of the app I'm doing. At the time, I was able to integrate it with the core data, but have not figured out the best way to send multiple GET requests.
What I need to do is:
Get data from the following addresses:
http://url.com/api/banner/
http://url.com/api/category/
http://url.com/api/link/
The URL will always be in the following format: http://url.com/api/SOMETHING/
Once all requests are finished, I would like to run a code (such as calling a new view controller). What would be the best way to do this?
At the moment, this is the code I'm using:
- (id)init
{
self = [super init];
if (self) {
[self setupConnector];
[self setupDatabase];
[self setupMappings];
[self sendRequests];
}
return self;
}
- (void)setupConnector
{
// Initialize RestKIT
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://baseURL"]];
self.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[[NLCoreData shared] managedObjectModel]];
objectManager.managedObjectStore = self.managedObjectStore;
}
- (void)setupDatabase
{
NSString *storePath = [[NLCoreData shared] storePath];
NSError *error = nil;
NSPersistentStore *persistentStore = [self.managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
[self.managedObjectStore createManagedObjectContexts];
self.managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext];
}
- (void)setupMappings
{
RKObjectManager *objectManager = [RKObjectManager sharedManager];
// Mappings
// banner
RKEntityMapping *bannerMapping = [RKEntityMapping mappingForEntityForName:#"Banner" inManagedObjectStore:self.managedObjectStore];
[bannerMapping addAttributeMappingsFromDictionary:#{
#"title": #"title",
#"id": #"bannerID",
#"created_at": #"created_at",
#"image": #"image",
#"resource_uri": #"resource_uri",
#"updated_at": #"updated_at",
#"url": #"url"
}];
bannerMapping.identificationAttributes = #[ #"bannerID" ];
RKResponseDescriptor *bannerDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:bannerMapping
pathPattern:#"/api/v1/banner/"
keyPath:#"objects"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:bannerDescriptor];
// category
RKEntityMapping *categoryMapping = [RKEntityMapping mappingForEntityForName:#"Category" inManagedObjectStore:self.managedObjectStore];
[categoryMapping addAttributeMappingsFromDictionary:#{
#"name": #"name",
#"id": #"categoryID",
#"created_at": #"created_at",
#"resource_uri": #"resource_uri",
#"updated_at": #"updated_at",
#"active": #"active"
}];
categoryMapping.identificationAttributes = #[ #"categoryID" ];
RKResponseDescriptor *categoryDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:categoryMapping
pathPattern:#"/api/v1/category/"
keyPath:#"objects"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:categoryDescriptor];
}
- (void)sendRequests
{
RKObjectManager *objectManager = [RKObjectManager sharedManager];
// Send Request
[objectManager getObjectsAtPath:#"/api/v1/banner/" parameters:nil success:^(RKObjectRequestOperation * operation, RKMappingResult *mappingResult) {
NSLog(#"SUCCESS: %#", mappingResult.array);
} failure: ^(RKObjectRequestOperation * operation, NSError * error) {
NSLog(#"FAILURE %#", error);
}];
// category
[objectManager getObjectsAtPath:#"/api/v1/category/" parameters:nil success:^(RKObjectRequestOperation * operation, RKMappingResult *mappingResult) {
NSLog(#"SUCCESS: %#", mappingResult.array);
} failure: ^(RKObjectRequestOperation * operation, NSError * error) {
NSLog(#"FAILURE %#", error);
}];
}
Any tips?
The RestKit solution would be this: instead of using the convenience method ObjectManager::getObjectsAtPath you will have to init all of your RKObjectRequestOperations manually and then use ObjectManager::enqueueBatchOfObjectRequestOperations:progress:completion: method to enqueue them.
Alternatively, and I think this is actually easier and cleaner solution, use dispatch groups as described in the accepted answer to this question.
NSURL *url1 = [NSURL URLWithString:#"http://baseURL.domain/api/banner/"];
NSMutableURLRequest *request2 = [NSMutableURLRequest requestWithURL:url1];
RKObjectRequestOperation *objectRequestOperation1 = [[RKObjectRequestOperation alloc] initWithRequest:request2 responseDescriptors:#[ ResponseDescriptor ]];
NSURL *url2 = [NSURL URLWithString:#"http://baseURL.domain/api/category/"];
NSMutableURLRequest *request2 = [NSMutableURLRequest requestWithURL:url2];
RKObjectRequestOperation *objectRequestOperation2 = [[RKObjectRequestOperation alloc] initWithRequest:request2 responseDescriptors:#[ ResponseDescriptor ]];
NSArray *requestArray = [NSArray arrayWithObjects:objectRequestOperation,objectRequestOperation1,objectRequestOperation2, nil];
[[RKObjectManager sharedManager] enqueueBatchOfObjectRequestOperations:requestArray progress:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
//
// Handle process indicator
//
NSLog(#"%lu",(unsigned long)totalNumberOfOperations);
} completion:^(NSArray *operations) {
//
// Remove blocking dialog, do next tasks
?

Resources