about concurrency with core data and relationship - core-data

i want to insert data on secondary thread and then track changes in main thread.
I have two Entitys,and they was set Inverse.
#interface Entity1 : NSManagedObject
#property (nonatomic, retain) NSString * data;
#property (nonatomic, retain) Entity2 * entity2;
#end
#interface Entity2 : NSManagedObject
#property (nonatomic, retain) Entity1 * entity1;
#end
I register context save notificaton in main thread.
//this managedObjectContext run in main thread
-(NSManagedObjectContext *)managedObjectContext_mainThread {
......
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
return managedObjectContext_mainThread ;
}
//pass notification
- (void)contextDidSave:(NSNotification *)notification
{
......
[managedObjectContext_mainThread
mergeChangesFromContextDidSaveNotification:notification];
}
fetch from coredata,it will run in main thread
-(NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController == nil) {
NSManagedObjectContext *moc = [self managedObjectContext_mainThread];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity2"
inManagedObjectContext:moc];
NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:#"entity1"
ascending:YES];
.....
}
return fetchedResultsController;
}
//NSFetchedResultsControllerDelegate, in this functions updata my UI
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
NSLog(#"controllerDidChangeContent start!");
}
this is the app start.
-(void)loadView {
myQueue = dispatch_queue_create("myQueue", NULL);
// this context is managedObjectContext_mainThread and run in main thread
NSArray *results = [self fetchedResultsController];
//insert Data oparation in managedObjectContext_otherThread and myQueueu
dispatch_async(myQueue, ^{
......
Entity1 *entity1 =
[NSEntityDescription insertNewObjectForEntityForName:#"Entity1"
inManagedObjectContext:managedObjectContext_otherThread];
Entity2 *entity2 =
[NSEntityDescription
insertNewObjectForEntityForName:#"Entity2"
inManagedObjectContext:managedObjectContext_otherThread];
entity1.data = #"myData";
entity1.entity2 = entity2;
[[self managedObjectContext_otherThread] save:nil];
});
}
when i build i got an error
-[Entity1 compare:]: unrecognized selector sent to instance 0x4d3ec90
and the error occur in NSFetchedResultsController handle context notification,this is the call stack:
__exceptionPreprocess + 185
objc_exception_throw + 47
-[NSObject(NSObject) doesNotRecognizeSelector:] + 187
___forwarding___ + 966
CF_forwarding_prep_0 + 50
_NSCompareObject + 76
+[NSFetchedResultsController(PrivateMethods)
_insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 286
-[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 402
-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 1804
if i don't fetch the Entity2 but Entity1 in fetchedResultsController,my app run ok.but I want to fetch entity2,and then use entity2.entity1.data to access entity1.who can help me.

I have found my mistake, I used the relationship to be sort descriptor in fetchrequest.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity2"
inManagedObjectContext:moc];
NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:#"entity1"
ascending:YES];
if I use other attribute to be sortdescriptor, the app will be OK.

Related

Child context changes are propagated to other child context (same hierarchy level) without merge

I am implementing CoreData stack according to
https://stackoverflow.com/a/24663533 (option A from image) but it works in an unexpected way.
I have rootContext (NSPrivateQueueConcurrencyType), it has 2 children: uiContext (NSMainQueueConcurrencyType) for objects fetching and syncContext (NSPrivateQueueConcurrencyType) for asynchronous data editing.
As I thought, when I save something in syncContext in performBlock (background queue), changes will be propagated to rootContext, but uiContext will not be changed until I observe NSManagedObjectContextDidSaveNotification and merge changes from notification. But changes are reflected immediately after syncContext save.
My first question is: why is uiContext updated without manual merge?
My second question: why is rootContext modified on background (not on main thread) after syncContext save? Some time ago I asked question about "CoreData could not fulfill a fault" problem with MagicalRecord 'CoreData could not fulfill a fault' error with MagicalRecord but I didn't receive answer, so I decided to find solution without external libraries.
It seems, that main thread is reading object properties and the same object is deleted on background whilst operators on main thread still don't return control.
Here is my source code:
#import <CoreData/CoreData.h>
#import "DataLayer.h"
#import "Person.h"
#interface DataLayer ()
#property (nonatomic, strong) NSManagedObjectModel *model;
#property (nonatomic, strong) NSPersistentStoreCoordinator *coordinator;
#property (nonatomic, strong) NSManagedObjectContext *rootContext;
#property (nonatomic, strong) NSManagedObjectContext *uiContext;
#property (nonatomic, strong) NSManagedObjectContext *syncContext;
#end
#implementation DataLayer
+ (void)load
{
[self instance];
}
+ (DataLayer *)instance
{
static DataLayer *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[DataLayer alloc] init];
});
return instance;
}
- (instancetype)init
{
self = [super init];
if (self) {
[self initModel];
[self initCoordinator];
[self initContexts];
[self observeContextSaveNotification];
[self startTesting];
}
return self;
}
- (void)initModel
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Model" withExtension:#"momd"];
self.model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
}
- (void)initCoordinator
{
NSURL *directory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [directory URLByAppendingPathComponent:#"Model.sqlite"];
NSError *error = nil;
self.coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
if (![self.coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
- (void)initContexts
{
self.rootContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.rootContext.persistentStoreCoordinator = self.coordinator;
self.uiContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.uiContext.parentContext = self.rootContext;
self.syncContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.syncContext.parentContext = self.rootContext;
}
- (void)observeContextSaveNotification
{
// [[NSNotificationCenter defaultCenter] addObserver:self
// selector:#selector(onManagedObjectContextDidSaveNotification:)
// name:NSManagedObjectContextDidSaveNotification
// object:nil];
}
- (void)onManagedObjectContextDidSaveNotification:(NSNotification *)notification
{
// NSManagedObjectContext *context = notification.object;
// if (context != self.uiContext) {
// [self.uiContext mergeChangesFromContextDidSaveNotification:notification];
// }
}
- (void)startTesting
{
NSArray *personsBeforeSave = [self fetchEntities:#"Person" fromContext:self.uiContext];
NSLog(#"Before save: %i persons in syncContext", [personsBeforeSave count]); // Before save: 0 persons in syncContext
[self.syncContext performBlock:^{
Person *person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.syncContext];
person.firstName = #"Alexander";
NSError *error = nil;
[self.syncContext save:&error];
if (error) {
NSLog(#"Error during save: %#", error);
}
NSArray *personsAfterSaveFromBackground = [self fetchEntities:#"Person" fromContext:self.rootContext];
NSLog(#"After save from background: %i persons in rootContext", [personsAfterSaveFromBackground count]); // After save from background: 1 persons in rootContext
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *personsAfterSaveFromMain = [self fetchEntities:#"Person" fromContext:self.uiContext];
NSLog(#"After save from main: %i persons in uiContext", [personsAfterSaveFromMain count]); // After save from main: 1 persons in uiContext
});
}];
}
- (NSArray *)fetchEntities:(NSString *)entity fromContext:(NSManagedObjectContext *)context
{
NSError *error = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity];
NSArray *result = [context executeFetchRequest:request error:&error];
if (error) {
NSLog(#"Error during fetch %#: %#", entity, error);
return nil;
}
return result;
}
#end
They are not being merged into the UI context. You are manually fetching them.
When you save in the syncContext, the data gets pushed up into the rootContext. The data is NOT merged into the uiContext. However, when you perform the fetch, the fetch pulls data down from the parent context.
You can get the objects in a context with registeredObjects.

bool value does NOT get stored in core data stack

I'm trying to simply toggle a BOOL Attribute in my core data stack and also use that coredata bool to set the "state" of a UISwitch in viewDidLoad in my mainView.
following scenario:
Switch is off, user hits edit(save) UIButton, --> that toggles the settingSysTimeOverride Attribute of entity Setting from #NO to #YES
next time app launches, in viewDidLoad, I fetch the core data stack and look at status of settingSysTimeOverride and use that to
set the "state" of my UISwitch.
But for some reason, my value does NOT get stored to core data stack.
The whole project is attached, also here is my code.
MainViewController.m
#import "MainViewController.h"
#import "AppDelegate.h"
#interface MainViewController ()
#property (nonatomic, strong)NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong)NSFetchedResultsController *fetchedResultsController;
#property (strong, nonatomic) NSMutableArray *settingsArray;
#end
#implementation MainViewController
#synthesize editSetting;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (NSManagedObjectContext *)managedObjectContext
{
return [(AppDelegate*)[[UIApplication sharedApplication]delegate]managedObjectContext];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Setting" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"settingSysTimeOverride == %#", editSetting];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"ERROR! %# - %#", [error localizedDescription], [error userInfo]);
} else {
NSLog(#"FETCH SUCCESSFUL");
}
if ((fetchedObjects.count) >0) {
// list array contents
for (int i=0; i < (fetchedObjects.count); i++) {
// output for debug purpose
NSLog(#"Array-index [%i]: %#", i, fetchedObjects[i]);
}
}
// check settingSysTimeOverride and set UISwitch-State
if (editSetting.settingSysTimeOverride.boolValue == 0) {
// turn switch to OFF - Position
_overrideSysTimeSwitch.on = NO;
} else {
// turn switch to ON - Position
_overrideSysTimeSwitch.on = YES;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)editSave:(UIBarButtonItem *)sender
{
// toggle BOOL status of settingSysTimeOverride here
//
}
- (IBAction)overrideSysTime:(UISwitch *)sender
{
}
- (IBAction)timeFormat:(UISwitch *)sender {
}
#end
MainViewController.h
#import <UIKit/UIKit.h>
#import "Setting.h"
#interface MainViewController : UIViewController <NSFetchedResultsControllerDelegate>
#property (nonatomic, strong)Setting *editSetting;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *editSaveButton
#property (strong, nonatomic) IBOutlet UILabel *overrideSysTimeLabel;
#property (strong, nonatomic) IBOutlet UILabel *timeFormatLabel;
#property (strong, nonatomic) IBOutlet UISwitch *overrideSysTimeSwitch;
#property (strong, nonatomic) IBOutlet UISwitch *timeFormatSwitch;
- (IBAction)editSave:(UIBarButtonItem *)sender;
- (IBAction)overrideSysTime:(UISwitch *)sender;
- (IBAction)timeFormat:(UISwitch *)sender;
#end
AppDelegate.h
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
#end
Setting.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Setting : NSManagedObject
#property (nonatomic, retain) NSString * settingName;
#property (nonatomic, retain) NSNumber * settingSysTimeOverride;
#property (nonatomic, retain) NSNumber * settingTimeFormat;
#end
Setting.m
#import "Setting.h"
#implementation Setting
#dynamic settingName;
#dynamic settingSysTimeOverride;
#dynamic settingTimeFormat;
#end
Can someone please help me with this???
here is what I was doing wrong, I hope it's going to help others in the future to avoid running in the same issues:
I forgot to point my NSManagedObject at the beginning of my fetched array.
NSManagedObject *matches;
matches=[objects objectAtIndex:(0)];
The code in one piece would look like this:
NSManagedObjectContext *context;
NSEntityDescription *entityDesc;
NSFetchRequest *request;
NSPredicate *pred;
NSManagedObject *matches;
NSArray *objects;
NSUInteger elementsInArray;
context = [self managedObjectContext];
entityDesc = [NSEntityDescription entityForName:#"Setting" inManagedObjectContext:context];
pred = [NSPredicate predicateWithFormat:#"(settingName = %#)", #"User-Settings"];
request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
[request setPredicate:pred];
matches = nil;
NSError *errorFS = nil;
if (!(objects = [context executeFetchRequest:request error:&errorFS])) {
// fetch failed
NSLog(#"Fetch failed: %# - %#", [errorFS localizedDescription], [errorFS userInfo]);
} else {
// fetch succeeded
NSLog(#"Fetch Succeeded");
}
if ([objects count] == 0)
{ // does not exist -> error?
} else
{
matches=[objects objectAtIndex:(0)]; // <- HERE WAS THE ERROR !!!!!
// # system time override switch
if([[matches valueForKey:#"settingSysTimeOverride"] boolValue]) {
_sysTimeOverrideSwitch.on = YES;
} else {
_sysTimeOverrideSwitch.on = NO;
}
}

CoreData: Error by decode data from Context/Store NSData -> NSArray

i'm new in iOS-Development area and trying now to update an existing App to iOS 6.1 from iOS4.
get Data from Store to show in NewsPageView : UIViewController
NewsPageView.m
#import "NewsPageView.h"
#interface NewsPageView ()
#end
#implementation NewsPageView
#synthesize newsDetailsView,
newsTableView,
newsHeaderText
;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
/*
* Get managedObjectContext and List of News over AppDelegate
*/
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
//Setting self.managedObjectContext
self.managedObjectContext = appDelegate.managedObjectContext;
// Fetching Records from the data base to show in the table
self.listOfNews = [[appDelegate getDataFromStore:self]copy];
//
NSLog(#"Data from Store in NewsPageView: %#",self.listOfNews.description);
}
try to set/write data into Context
-(void) setDataToStore
{
NSLog(#"Trying to write News in Store");
News *newNews = [NSEntityDescription insertNewObjectForEntityForName:#"News" inManagedObjectContext:self.managedObjectContext];
newNews.newsId = [[NSNumber alloc]initWithInt:508];
newNews.title = #"Some Title";
newNews.content = #"Some content";
newNews.detailsUrl = #"www.someURL.de";
newNews.date = [[NSDate alloc]init];
newNews.dateTs = [[NSNumber alloc]initWithInt:138114787];
newNews.primaryCat = [[NSNumber alloc]initWithInt:0];
//make Categorien...
NewsCategorien *cat1 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat1.catId = [[NSNumber alloc]initWithInt:1];
cat1.name = #"Windows";
cat1.news = newNews;
NewsCategorien *cat2 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat2.catId = [[NSNumber alloc]initWithInt:2];
cat2.name = #"Linux";
cat2.news = newNews;
NewsCategorien *cat3 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat3.catId = [[NSNumber alloc]initWithInt:3];
cat3.name = #"Apple";
cat3.news = newNews;
NewsCategorien *cat5 = [NSEntityDescription insertNewObjectForEntityForName:#"NewsCategorien" inManagedObjectContext:self.managedObjectContext];
cat5.catId = [[NSNumber alloc]initWithInt:5];
cat5.name = #"Smartphone";
cat5.news = newNews;
newNews.isNews = [NSNumber numberWithBool:YES];
NSArray *catListe = [NSArray arrayWithObjects:cat1,cat2,cat3,cat5, nil];
// //saving cat by converting Cat. objects
// newNews.categories = catListe;
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSLog(#"Saving ok!");
}
try to get Data from Context
- (NSArray *)getDataFromStore:(id)sender
{
// initializing NSFetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSArray *fetchedData;
//get Data für NewsTable View
if([sender isKindOfClass:[NewsPageView class]]){
//Setting Entity to be Queried
fetchRequest.entity = [NSEntityDescription entityForName:#"News" inManagedObjectContext:self.managedObjectContext];
NSError* error;
//Point of Error by debugging!!!
//return list of News
fetchedData = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
}
//print fetched Data
for (News *news in fetchedData) {
NSLog(#"News:%#", news.description);
}
// Returning Fetched News
return fetchedData;
}
Classes
News.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class NewsCategorien;
#interface News : NSManagedObject <NSCoding>
#property (nonatomic, retain) id categories;
#property (nonatomic, retain) NSString * content;
#property (nonatomic, retain) NSDate * date;
#property (nonatomic, retain) NSNumber * dateTs;
#property (nonatomic, retain) NSString * detailsUrl;
#property (nonatomic, retain) NSNumber * isNews;
#property (nonatomic, retain) NSNumber * newsId;
#property (nonatomic, retain) NSNumber * primaryCat;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSSet *categorie;
//have to be implement...
//to work with NSKeyedArchiver by serialising Objects
- (void)encodeWithCoder:(NSCoder *)aCoder;
//to work with NSKeyedUnarchiver by deserialising Objects
- (id)initWithCoder:(NSCoder *)aDecoder;
#end
#interface News (CoreDataGeneratedAccessors)
- (void)addCategorieObject:(NewsCategorien *)value;
- (void)removeCategorieObject:(NewsCategorien *)value;
- (void)addCategorie:(NSSet *)values;
- (void)removeCategorie:(NSSet *)values;
#end
/*
* News Transformer NSArray -> NSData and reverse
* Model: News should have Attribut type of Transformable and have name NewsTrasformer
*/
#interface NewsTransformer : NSValueTransformer
#end
News.m
#import "News.h"
#import "NewsCategorien.h"
#implementation News
#dynamic categories;
#dynamic content;
#dynamic date;
#dynamic dateTs;
#dynamic detailsUrl;
#dynamic isNews;
#dynamic newsId;
#dynamic primaryCat;
#dynamic title;
#dynamic categorie;
//tell the archiver how to transform the serialized representation into a new object -> deserialize Objects!
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.categories = [decoder decodeObjectForKey:#"categories"];
self.content = [decoder decodeObjectForKey:#"content"];
self.date = [decoder decodeObjectForKey:#"date"];
self.dateTs = [decoder decodeObjectForKey:#"dateTs"];
self.detailsUrl = [decoder decodeObjectForKey:#"detailsUrl"];
self.isNews = [decoder decodeObjectForKey:#"isNews"];
self.newsId = [decoder decodeObjectForKey:#"newsId"];
self.primaryCat = [decoder decodeObjectForKey:#"primaryCat"];
self.title = [decoder decodeObjectForKey:#"title"];
self.categorie = [decoder decodeObjectForKey:#"categorie"];
}
NSLog(#"<<<< ---- Deserialize Object News (Init With Code) --->");
return self;
}
//tell the archiver how to serialize your object into bytes -> serialize Objects!
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.categories forKey:#"categories"];
[encoder encodeObject:self.content forKey:#"content"];
[encoder encodeObject:self.date forKey:#"date"];
[encoder encodeObject:self.dateTs forKey:#"dateTs"];
[encoder encodeObject:self.detailsUrl forKey:#"detailsUrl"];
[encoder encodeObject:self.isNews forKey:#"isNews"];
[encoder encodeObject:self.newsId forKey:#"newsId"];
[encoder encodeObject:self.primaryCat forKey:#"primaryCat"];
[encoder encodeObject:self.title forKey:#"title"];
[encoder encodeObject:self.categorie forKey:#"categorie"];
NSLog(#"<<<< ---- Serialize Object News (encode With Coder) --->");
}
#end
//
#implementation NewsTransformer
//Return the Class for Forwarding Transformation
+ (Class)transformedValueClass
{
return [NSArray class];
}
+ (BOOL)allowsReverseTransformation
{
return YES;
}
// converts the NSArray into NSData using NSKeyedArchiver
- (id)transformedValue:(id)value
{
NSLog(#"Transformer: NSArray -> NSData");
return [NSKeyedArchiver archivedDataWithRootObject:value];
}
// by default raises an exception if +allowsReverseTransformation returns NO and otherwise invokes transformedValue:
//converts NSData to an NSArray using NSKeyedUnarchiver
- (id)reverseTransformedValue:(id)value
{
NSLog(#"Transformer: NSData -> NSArray");
return [NSKeyedUnarchiver unarchiveObjectWithData:value];
}
#end
NewsCategorien.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class News;
#interface NewsCategorien : NSManagedObject <NSCoding>
#property (nonatomic, retain) NSNumber * catId;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) News *news;
//for serializing of objects have to be implement...
- (void)encodeWithCoder:(NSCoder *)aCoder;
//for serializing of objects have to be implement...
- (id)initWithCoder:(NSCoder *)aDecoder;
#end
NewsCategorien.m
#import "NewsCategorien.h"
#import "News.h"
#implementation NewsCategorien
#dynamic catId;
#dynamic name;
#dynamic news;
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.catId = [decoder decodeObjectForKey:#"catId"];
self.name = [decoder decodeObjectForKey:#"name"];
self.news = [decoder decodeObjectForKey:#"news"];
}
NSLog(#"<<<< ---- Deserialize Object Categorie ---> : %#",self.description);
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.catId forKey:#"catId"];
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.news forKey:#"news"];
NSLog(#"<<<< ---- Serialize Object Categorie ---> %#",self.description);
}
#end
Model
News
Attribute: categorien Type: Transformable Name: NewsTransformer
Relationchip one-to-more
categorien Des. NewsCategorien Inverse news
Categorien
Att. catId String
Att. name Interger16
My problem:
1. writing data to the context - OK
2.when i'm trying to get data from the context(NSData->NSArray) i get following error:
Error
CoreData: error: Failed to call designated initializer on NSManagedObject class 'NewsCategorien'
2013-11-20 16:03:19.204 securityNews[19458:c07] -[NewsCategorien setCatId:]: unrecognized selector sent to instance 0x8173740
2013-11-20 16:03:19.205 securityNews[19458:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NewsCategorien setCatId:]: unrecognized selector sent to instance 0x8173740'
any idea what i doing wrong??
The first error is almost certainly due to your implementation of initWithCoder:, specifically this line:
if (self = [super init]) {
You can't create a managed object using init. You must use the designated initializer initWithEntity:insertIntoManagedObjectContext:. Or you can use NSEntityDescription's method insertNewObjectForEntityForName:inManagedObjectContext:. But using init is right out, and doing so will cause a Failed to call designated initializer error.
This means that your initWithCoder needs to be able to find a managed object context. How you do that depends on your app's structure and where you are creating contexts. If your app delegate has a context object and that's the one you want to use, you could look it up from there. If it's some other context, you'll have to get it from somewhere else. You have a couple of versions of initWithCoder and they both have this problem.
The second error is a side-effect of the first error. In initWithCoder you do this:
if (self = [super init]) {
self.catId = [decoder decodeObjectForKey:#"catId"];
....
But since [super init] doesn't work, you're getting back a bogus object that doesn't know what catId is. Then you try to set catId and it fails. Fix the first error and the second error will disappear.

How can I solve the error: entity GroceryItems is not key value coding-compliant for the key "hasLocations"

What follows is everything I can possibly think of as background to try and identify the source of the error. I will, of course, provide any other information that may be helpful. Thanks in advance for your help.
I am new to Core Data, and I have an entity named GroceryItem. It has a to-many relationship named hasLocations, which I am trying to query with the following code:
itemObject = (GroceryItem *)[GroceryItem itemNameToObject:itemName];
if (itemObject != nil)
{
NSLog(#"%#", [NSString stringWithFormat:#"initAndFillItemLocationsTable got an item named %#", itemObject.name]);
}
if (itemLocations != nil)
{
[itemLocations removeAllObjects];
}
if (itemObject != nil)
{
NSLog(#"Before mutable set assignment");
NSMutableSet *mutableLocationsSet = [itemObject mutableSetValueForKeyPath:#"hasLocations"];
I see the "Before mutable set assignment" message in the output, followed by
2013-01-23 03:27:14.898 Grocery Manager[6431:11603] initAndFillItemLocationsTable got an item named Cream Cheese
2013-01-23 03:27:14.899 Grocery Manager[6431:11603] Before mutable set assignment
2013-01-23 03:27:14.901 Grocery Manager[6431:11603] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: the entity GroceryItems is not key value coding-compliant for the key "hasLocations".'
* First throw call stack:
(0x15eb012 0x1410e7e 0x1673fb1 0x11c304 0xe298db 0xb8374 0xe298db 0xec3180 0xec31de 0x17450 0xaa5c 0x439817 0x439882 0x439b2a 0x450ef5 0x450fdb 0x451286 0x451381 0x451eab 0x4524a3 0x452098 0x7adda3 0x79fad9 0x79fb54 0x407899 0x407b3d 0xe0ee83 0x15aa376 0x15a9e06 0x1591a82 0x1590f44 0x1590e1b 0x24377e3 0x2437668 0x35865c 0x27bd 0x26e5)
libc++abi.dylib: terminate called throwing an exception
The contents of the GroceryItem class definition are:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface GroceryItem : NSManagedObject
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSSet *hasLocations;
#property (nonatomic, retain) NSSet *containedInIngredients;
#end
#interface GroceryItem (CoreDataGeneratedAccessors)
- (void)addHasLocationsObject:(NSManagedObject *)value;
- (void)removeHasLocationsObject:(NSManagedObject *)value;
- (void)addHasLocations:(NSSet *)values;
- (void)removeHasLocations:(NSSet *)values;
- (void)addContainedInIngredientsObject:(NSManagedObject *)value;
- (void)removeContainedInIngredientsObject:(NSManagedObject *)value;
- (void)addContainedInIngredients:(NSSet *)values;
- (void)removeContainedInIngredients:(NSSet *)values;
+(GroceryItem *)itemNameToObject:(NSString *)itemName;
I have verified in the database editor that the class GroceryItem is assigned to the entity GroceryItem.
I read the Key-Value Coding Programming Guide and implemented the following code in GroceryItem.m:
- (void)addHasLocationsObject:(NSManagedObject *)value
{
[self.hasLocations setByAddingObject:value];
}
- (void)removeHasLocationsObject:(NSManagedObject *)value
{
NSMutableSet *mutable = [NSMutableSet setWithSet:self.hasLocations];
[mutable removeObject:value];
self.hasLocations = mutable;
}
- (void)addHasLocations:(NSSet *)values
{
[self.hasLocations setByAddingObjectsFromSet:values];
}
- (void)removeHasLocations:(NSSet *)values
{
NSMutableSet *mutable = [NSMutableSet setWithSet:self.hasLocations];
for (id obj in [mutable allObjects])
{
if ([values containsObject:obj])
{
[mutable removeObject: obj];
}
}
self.hasLocations = mutable;
}
- (NSUInteger)countOfHasLocations
{
return [self.hasLocations count];
}
- (NSEnumerator *)enumeratorOfHasLocations
{
return [self.hasLocations objectEnumerator];
}
- (GroceryLocation *)memberOfHasLocations:(GroceryLocation *)anObject
{
return [self.hasLocations member:anObject];
}
The static itemNameToObject function looks like this:
+(GroceryItem *)itemNameToObject:(NSString *)itemName
{
GroceryItem *groceryItem;
groceryItem = nil;
if (itemName != nil)
{
GroceryManagerAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"GroceryItems" inManagedObjectContext:context]];
[request setPredicate:[NSPredicate predicateWithFormat:#"name == %#", itemName]];
NSError *error;
NSArray *groceryItemObjects = [context executeFetchRequest:request error:&error];
NSInteger countOfGroceryItems = [groceryItemObjects count];
if (countOfGroceryItems == 1)
{
groceryItem = (GroceryItem *)[groceryItemObjects objectAtIndex:0];
}
}
return groceryItem;
}
(From the comments:) The crash is caused by the fact that the entity is declared as "GroceryItems", but "hasLocations" is a property of the class "GroceryItem". The fetch request returns an array of "GroceryItems" objects, which do not respond to the "hasLocations" method.
Also it is not normally necessary to implement the Core Data accessor methods, they are dynamically created at runtime.

Core Data - One to Many relationship with object(one) and UIImages(many), storing and fetching

I aim to build a app with the object of:
- Request(contains an unique id, customer name, phone, email, a brief summary of request info, a list of images(store in NSMutable Array)
My Core data contains:
Request
Image
For my request object, I have:
#import <Foundation/Foundation.h>
#interface BAPRequest : NSObject
#property (nonatomic, retain) NSString *requestID;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *email;
#property (nonatomic, retain) NSString *phone;
#property (nonatomic, retain) NSString *detail;
#property (nonatomic, retain) NSMutableArray *images;
#property (nonatomic, retain) NSDate *requestDate;
#property (nonatomic) BOOL isSent;
- (void)fetchImages;
#end
And the .m File:
#import "BAPAppDelegate.h"
#import "BAPRequest.h"
#implementation BAPRequest
#synthesize name, email, phone, detail;
#synthesize images;
#synthesize requestDate;
#synthesize isSent;
#synthesize requestID;
- (id)init{
self = [super init];
if(self != nil){
self.isSent = false;
self.requestID = [[NSProcessInfo processInfo] globallyUniqueString];
self.images = [[NSMutableArray alloc] init];
}
return self;
}
- (void)fetchImages{
//get delegation
BAPAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSError *error;
//init request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//load the entity description
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Image"
inManagedObjectContext:context];
//set the request query
[fetchRequest setEntity:entityDescription];
//set predicate to request to ask select specific entity with the matching line num
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(requestId == %#)", self.requestID];
[fetchRequest setPredicate:pred];
//exec the command to find the object(table) from core database
NSArray * imageList = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *imageObject in imageList){
[self.images addObject: [UIImage imageWithData:[imageObject valueForKey:#"content"]]];
}
}
#end
To fetch all the requests, I also do:
- (void)removeRequest:(BAPRequest*)request{
//get delegation
BAPAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSError *error;
//init request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//load the entity description
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Request"
inManagedObjectContext:context];
//set the request query
[fetchRequest setEntity:entityDescription];
//set predicate to request to ask select specific entity with the matching line num
NSLog(#"request id is %#", request.requestID);
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(requestId LIKE %#)", request.requestID];
[fetchRequest setPredicate:pred];
//exec the command to find the object(table) from core database
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
//if this object doesn't exist in the database, it must be something wrong
if (objects == nil) {
NSLog(#"There was an error !");
NSLog(#"Error Info: %#", error);
}
//if object exist in database, take the first search object, and deletes it
if ([objects count] > 0){
NSManagedObject *theRequest = [objects objectAtIndex:0];
[context deleteObject:theRequest];
}
[context save:&error];
/*
//delete existing images relates to this request first
entityDescription = [NSEntityDescription entityForName:#"Image"
inManagedObjectContext:context];
pred = [NSPredicate predicateWithFormat:#"(self.request == %#)", request];
[fetchRequest setPredicate:pred];
NSArray * imageList = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *removeImageObject in imageList){
[context deleteObject:removeImageObject];
}
*/
}
#pragma mark - Convert Core Data Object to Request Object
#pragma mark - Core Data Methods
- (NSMutableArray *)getRequests{
//get app delegate
BAPAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
//get object context that's created for us
NSManagedObjectContext *context = [appDelegate managedObjectContext];
//define the entities we want load to description
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Request"
inManagedObjectContext:context];
//init request and set search query to the request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entityDescription];
NSError *error;
//get objects(table) though request
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
if (objects == nil){
NSLog(#"There was an error !");
//error handling there
NSLog(#"Error Info: %#", error);
}
NSMutableArray *requests = [[NSMutableArray alloc] init];
for (NSManagedObject *requestObj in objects){
BAPRequest *request = [[BAPRequest alloc] init];
request.requestID = [requestObj valueForKey:#"requestId"];
request.name = [requestObj valueForKey:#"name"];
request.email = [requestObj valueForKey:#"email"];
request.phone = [requestObj valueForKey:#"phone"];
request.detail = [requestObj valueForKey:#"detail"];
request.requestDate = [requestObj valueForKey:#"requestDate"];
request.isSent = [[requestObj valueForKey:#"isSent"] boolValue];
[request fetchImages];
[requests addObject: requestObj];
}
return requests; //return the requests
}
- (void)saveRequest:(BAPRequest *)request{
request.requestDate = [NSDate date];
//get delegation
BAPAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSError *error;
BAPRequest *theRequest = [NSEntityDescription insertNewObjectForEntityForName:#"Request"
inManagedObjectContext:context];
//set the request obejct basics
[theRequest setValue:request.requestID forKey:#"requestId"];
[theRequest setValue:request.name forKey:#"name"];
[theRequest setValue:request.email forKey:#"email"];
[theRequest setValue:request.phone forKey:#"phone"];
[theRequest setValue:request.detail forKey:#"detail"];
[theRequest setValue:request.requestDate forKey:#"requestDate"];
[theRequest setValue:[NSNumber numberWithBool:request.isSent ] forKey:#"isSent"];
//also save its requests
for (UIImage *img in request.images) {
//init image object
UIImage *newImage=[NSEntityDescription insertNewObjectForEntityForName:#"Image" inManagedObjectContext:context];
[newImage setValue:request.requestID forKey:#"requestId"];
[newImage setValue:UIImagePNGRepresentation(img) forKey:#"content"];
}
[context save:&error]; //save the object
}
#end
My Issue is:
I guess it just doesn't fetch the images at all. Even they fetched the images in the getRequests method, when I try to pass the request to somewhere else, it still get lots issues:
boolean not loading properly
images are nil(even it was loaded when fetch)
and the reason is(I can be wrong) that when I store these request objects(BAPRequest*) into a NSArray and pass it to a table view, when I load them from array (e.g [myArray objectAtIndex:0]), they are still NSManagedObject. I tried to give a class name in the core data entity option to make the Request(Core Data) Object Class of BAPRequest, but it keeps saying I have to subclass NSManagedObject, where subclassing NSManagedObject is not possible:
I can't do:
BAPRequest: NSManagedObject
XCode doesn't likes it.
Look at mogenerator. Here's a tutorial on how to use it: http://raptureinvenice.com/getting-started-with-mogenerator/ . It will save you a lot of trouble.

Resources