SUBJECT: Core Data, can’t retrieve/set to my properties from VC’s. Most common mistake?
Searched through this site but could not quite get the answered I need (many good tips though), so thought I post this question in hope it will resolve my issue I’ve had for a couple of weeks now! Yes, very frustrating, you probably know the feeling! So ANY help would be great – thank’s! 🙂
Oveview:
iOS 5.1 project. Got Core Data working (tested in main.h/NSLog) but I’m having trouble retrieving and setting Entities properties (data) from other view controllers. Xcode recognizes my singelton “AppContent” found in the AppDelegate from other view controllers, but not the entity name and it’s properties.
What is the most common mistake regarding this?
I get a feeling of that I’ve just missed to importing some file in the right place etc…
Some more details;
FYI: I’m trying to use a recommended method by Matt Campell, that creates a singleton in the AppDelegate that can be used all over the app to work with the managedObjectContext from any view controllers, and retrieve and save data to Core Data and it’s entities and it’s respective properties. This is done by importing the following two files to the app;
appContent.h
#import <Foundation/Foundation.h>
#import "Contest.h" // Root Entity in CD model
#import "Player.h"
@interface AppContent : NSObject
+(AppContent *)sharedContent;
@property(strong, readonly) id rootObject;
-(void)save;
-(void)rollback;
@end
appContent.m
#import "AppContent.h"
#import <CoreData/CoreData.h>
@interface AppContent()
-(NSURL *)dataStoreURL;
@property (nonatomic, strong, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, strong, readonly) NSManagedObjectContext *managedObjectContext;
@end
@implementation AppContent
NSManagedObjectModel *_managedObjectModel;
NSPersistentStoreCoordinator *_persistentStoreCoordinator;
NSManagedObjectContext *_managedObjectContext;
id _rootObject;
static AppContent *singletonInstance = nil;
+ (AppContent *)sharedContent{
@synchronized(self){
if (singletonInstance == nil)
singletonInstance = [[self alloc] init];
return(singletonInstance);
}
}
- (NSURL *)dataStoreURL {
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
return [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"DataStore.sql"]];
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel) {
return _managedObjectModel;
}
_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[self dataStoreURL]
options:nil
error:&error]) {
NSLog(@"Unresolved Core Data error with persistentStoreCoordinator: %@, %@", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext) {
return _managedObjectContext;
}
if ([self persistentStoreCoordinator]) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
}
return _managedObjectContext;
}
-(id)rootObject{
if(_rootObject)
return _rootObject;
// #warning Replace [CHANGE] with your root object entity name
NSString *entityName = @"Contest";
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName
inManagedObjectContext:context];
request.entity = entity;
NSArray *listOfObjects = [context executeFetchRequest:request
error:nil];
if([listOfObjects count] == 1){
_rootObject = [listOfObjects lastObject];
return _rootObject;
}
_rootObject = [NSEntityDescription insertNewObjectForEntityForName:entityName
inManagedObjectContext:context];
// Adding some testdata (only first time...)
// Contest
Contest *c = _rootObject;
c.name = @"Big Game 1";
// Players
Player *p1 = (Player *) [NSEntityDescription insertNewObjectForEntityForName:@"Player"
inManagedObjectContext:context];
p1.name = @"Player One";
[c addPlayersObject:p1];
Player *p2 = (Player *) [NSEntityDescription insertNewObjectForEntityForName:@"Player"
inManagedObjectContext:context];
p2.name = @"Player Two";
[c addPlayersObject:p2];
[self save];
return _rootObject;
}
-(void)save{
NSError *error = nil;
NSManagedObjectContext *context = [self managedObjectContext];
if([context hasChanges])
[context save:&error];
if(error)
NSLog(@"Warning: Error saving to data store. %@", error);
}
-(void)rollback{
NSManagedObjectContext *context = [self managedObjectContext];
if([context hasChanges])
[context rollback];
}
@end
Here are the model files (created by CD model editor);
Contest.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class Player;
@interface Contest : NSManagedObject
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSSet *players;
@end
@interface Contest (CoreDataGeneratedAccessors)
- (void)addPlayersObject:(Player *)value;
- (void)removePlayersObject:(Player *)value;
- (void)addPlayers:(NSSet *)values;
- (void)removePlayers:(NSSet *)values;
@end
Contest.m
#import "Contest.h"
#import "Player.h"
@implementation Contest
@dynamic name;
@dynamic players;
@end
Player.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class Contest;
@interface Player : NSManagedObject
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) Contest *contests;
@end
Player.m
#import "Player.h"
#import "Contest.h"
@implementation Player
@dynamic name;
@dynamic contests;
@end
Here is a view controller
Trying to get hold of the Core Data Entities and it properties here, but Xcode see’s the appContent but don’t recognize it and when building it gives error “Can’t find property player”…
contestPlayerVC.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "AppContent.h"
@interface contestPlayerVC : UIViewController
@property (strong, nonatomic) IBOutlet UITextField *playerNameField;
@property (strong, nonatomic) IBOutlet UITextField *playerMailField;
@property (strong, nonatomic) IBOutlet UIButton *playerSaveButton;
@property (weak, nonatomic) AppContent *content;
// Test
// @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
- (IBAction)playerChooseImage:(UIButton *)sender;
- (IBAction)dismissModal:(UIButton *)sender;
- (IBAction)hideKeyboard:(id)sender;
- (IBAction)playerSave:(UIButton *)sender;
@end
contestPlayerVC.m
Not showing hole file since problem is in the method viewDidLoad…
#import "contestPlayerVC.h"
@interface contestPlayerVC ()
@end
@implementation contestPlayerVC
@synthesize playerNameField, playerMailField, playerSaveButton, content;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// Call 1 - seems to work ok
self.content = [AppContent sharedContent];
////// Call 2 - gives error, can't find property "player" - WHY?
// Trying to set textfield to CD Entity "Player" and it's property "name"
self.playerNameField.text = self.content.player.name;
NSLog(@"Player is: %@", self.content.player.name);
// Call 3 - works ok
NSLog(@"Content is: %@", self.content.description);
}
...
Any tips or suggestions? Thanks! 🙂
Btw: Sorry about the long post, I’m not sure about how else describe my problem well enough for anyone to understand it. If you read it all – I’m impressed and I’ll be real happy to if you help me solve this. Probably some basic thing I totally missed. Thank’s 😉
@user1578933 Ok, let’s back up a second and think about what each piece is responsible for:
In your app, everything starts with Contest. In your AppContent implementation you have code that generates that property rootObject and this is where the Contest object is created for the first time.
NOTE You have that defined as id because you are copying this code directly for our program, but you could also have defined rootObject as type Contest like this:
This your point of entry to your object graph. The idea is that you will access AppContent (using the Singleton pattern) and then retrieve the Contest object. The Contest object will have the NSSet object collection that contains references to each player in the collection.
Here’s an example of how you would use this in your view controller:
This is essentially how you would add this content to your view controller; for a table view controller you would use the index path row property to find out what player goes in what table view cell (see cellForRowAtIndexPath).
@user1578933 – after reading this post and also the ones you left on Mobile App Mastery Institute it seems like you are missing some understanding of the object graph and how all these object relationships are organized in general. I’ve recently added some additional content about how to use and think about the Objective-C object graph here. Also, I would carefully look over the sections on Objective-C, Table Views and Core Data again.