I'm using core data in Xcode 7 beta 6 and I just generated categories and managed object subclasses for each of my entities. The issue is that when I try to utilize the properties created from the attributes in my model, I get a "use of undeclared identifier" error. I was under the impression that I was supposed to put custom behavior in the managed object subclass that were generated, however I was not clear on how I could use the properties from the categories in the managed object subclass, so I placed the custom behavior in the categories as shown below. I feel like I'm merely missing an import statement, but I'm not sure. I understand I'm using beta software.
Core Data Model:
Thought+CoreDataProperties.h:
#import "Thought.h"
NS_ASSUME_NONNULL_BEGIN
#interface Thought (CoreDataProperties)
#property (nullable, nonatomic, retain) NSString *objectId;
#property (nullable, nonatomic, retain) id recordId;
#property (nullable, nonatomic, retain) Collection *parentCollection;
#property (nullable, nonatomic, retain) NSNumber *placement;
#property (nullable, nonatomic, retain) NSString *text;
#property (nullable, nonatomic, retain) NSString *extraText; // allows for extra description text to be set. Should be in smaller print than headline text and should only appear as an option in text != nil
#property (nullable, nonatomic, retain) NSSet<Photo *> *photos;
#property (nullable, nonatomic, retain) id location; // place a CLLocation here
#property (nullable, nonatomic, retain) id tags; // place an NSArray here
#property (nullable, nonatomic, retain) NSDate *creationDate;
#pragma mark - Initializers
/*!
#abstract this method converts a CKRecord into a Thought object
#discussion parentCollection will still be nil after this method executes
*/
-(nullable instancetype) initWithRecord: (nonnull CKRecord *) record;
/*!
#abstract this method converts a CKRecord into a Thought object. photos set is not populated
*/
-(nullable instancetype)initWithRecord: (nonnull CKRecord *) record collection: (nonnull Collection *) collection;
/*!
#abstract Creates a new Thought object with generic recordId, objectId, placement, and photos array
#discussion parentCollection will still be nil after this method executes
*/
-(nullable instancetype) init;
… other methods
#end
#interface Thought (CoreDataGeneratedAccessors)
- (void)addPhotosObject:(Photo *)value;
- (void)removePhotosObject:(Photo *)value;
- (void)addPhotos:(NSSet<Photo *> *)values;
- (void)removePhotos:(NSSet<Photo *> *)values;
#end
NS_ASSUME_NONNULL_END
Thought+CoreDataProperties.m:
#import "Thought+CoreDataProperties.h"
#implementation Thought (CoreDataProperties)
#dynamic creationDate;
#dynamic extraText;
#dynamic location;
#dynamic objectId;
#dynamic placement;
#dynamic recordId;
#dynamic tags;
#dynamic text;
#dynamic parentCollection;
#dynamic photos;
-(nullable instancetype) init {
self = [super init];
if (self) {
// THIS IS WHERE I GET MANY ERROR FOR USE OF UNDECLARED IDENTIFIER
_objectId = [IdentifierCreator createId];
_recordId = [[CKRecord alloc] initWithRecordType:THOUGHT_RECORD_TYPE zoneID:[[CKRecordZone alloc] initWithZoneName:ZONE_NAME].zoneID].recordID;
_photos = [NSArray new];
_placement = [NSNumber numberWithInt:0];
_creationDate = [NSDate date];
}
return self;
}
-(instancetype) initWithRecord:(nonnull CKRecord *)record {
self = [super init];
if (self) {
_objectId = [record objectForKey:OBJECT_ID_KEY];
_recordId = [record recordID];
_text = [record objectForKey:TEXT_KEY];
_extraText = [record objectForKey:EXTRA_TEXT_KEY];
_location = [record objectForKey:LOCATION_KEY];
_photos = [NSSet new];
_tags = [record objectForKey:TAGS_KEY];
_placement = [record objectForKey:PLACEMENT_KEY];
_creationDate = record.creationDate;
}
return self;
}
-(instancetype) initWithRecord:(CKRecord *)record collection:(Collection *)collection {
self = [self initWithRecord:record];
self.parentCollection = collection;
return self;
}
Thought.h:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Frameworks.h" // includes Frameworks I'm using and some string constants
#import "ForFundamentals.h" // includes mostly string constants
#import "Photo.h"
#import "Collection.h"
#class Collection, Photo;
NS_ASSUME_NONNULL_BEGIN
#interface Thought : NSManagedObject
// I think I should put method declarations here
#end
NS_ASSUME_NONNULL_END
#import "Thought+CoreDataProperties.h"
Thought.m:
#import "Thought.h"
#import "Collection.h"
#import "Photo.h"
#implementation Thought
// I think I should put method implementations here
#end
Subclasses of NSManagedObject do their initialization in awakeFromInsert or awakeFromFetch. Don't override init or implement initWith.... You have to wait until the object is instantiated, and alive within an NSManagedObjectContext, before you can set its properties.
Don't assign collection instances to your ivars corresponding to Core Data relationships (i.e. _photos, parentCollection. Core Data will do that for you when you insert or fetch the object.
Instead of your init methods, rethink your approach. Write a method insertInManagedObjectContext:withSKRecord. That method calls -insertNewObjectForEntityForName:#"Thought" inManagedObjectContext:foo, which returns an instance of Thought. Now, with that istance, set the objecgtID, recordID, etc–but with your accessors, not by directly banging the instance variables.
So it seems that although I can't use _name = #"string" syntax to set property values, I can use method syntax, like [self setName: #"string"]. This seems very strange to me. However, the method syntax does work in both the subclass and the category so I guess problem solved… for now.
UPDATE
I didn't understand #dynamic. This post helped clear it up. I can't use _propertyName because the accessor methods are dynamically created by core data.
Related
i successfully integrated core data in my JSQ project, for my JSQMessageData i use NSManagedObject i created called CDChatMessage
#interface CDChatMessage : NSManagedObject < JSQMessageData >
#end
at my JSQMessagesViewController i use NSfetchedresultsController,
it works fine for text messages but i can't figure out how to implement media messages.
JSQMessage.h have a property that represent the Media Data
#property (copy, nonatomic, readonly) id< JSQMessageMediaData > media;
but obviously i cant assassin property of type JSQMessageMediaData to my NSManagedObject,
anyone have a solution for using JSQMessageMediaData with Core Data ?
thanks.
Basically what I've done to solve this kind of issue is this:
Instead of using CoreData object which conforms to JSQMessageData I use something called viewModel.
A ViewModel is basically a normal NSObject which just unwraps all necessary information from the CoreData object and conforms to JSQMessageData protocol - providing text, senderId, and other information (and also media message if necessary)
#interface ChatMessageViewModel : NSObject <JSQMessageData>
#property (nonatomic, strong, readonly) CDChatMessage *chatMessage;
// main properties
#property (nonatomic, copy, readonly) NSString *text;
#property (nonatomic, copy, readonly) NSString *senderId;
#property (nonatomic, copy, readonly) NSString *watcherId;
...
#property (nonatomic, strong, readonly) JSQMessage *mediaMessage;
- (instancetype)initWithChatMessage:(CDChatMessage *)chatMessage;
#end
.m file could look like this:
#interface ChatMessageViewModel ()
#property (nonatomic, strong, readwrite) CDChatMessage *chatMessage;
// main properties
#property (nonatomic, copy, readwrite) NSString *text;
#property (nonatomic, copy, readwrite) NSString *senderId;
#property (nonatomic, copy, readwrite) NSString *watcherId;
...
#property (nonatomic, strong, readwrite) JSQMessage *mediaMessage;
#end
#implementation ChatMessageViewModel
- (instancetype)initWithChatMessage:(CDChatMessage *)chatMessage
if (self = [super init]) {
_chatMessage = chatMessage;
[self unpackViewModel];
}
return self;
}
- (void)unpackViewModel {
self.senderId = self.chatMessage.senderId;
self.text = self.chatMessage.senderId;
self.mediaMessage = [self unpackMediaData];
}
- (JSQMessage *)unpackMediaData {
// Here CDCustomPhotoMediaItem is a subclass of JSQPhotoMediaItem which just lets me create custom look of JSQ media item.
JSQPhotoMediaItem *photoItem = [[CDCustomPhotoMediaItem alloc] init];
return [JSQMessage messageWithSenderId:self.senderId displayName:#"" media:photoItem];
}
After I fetch data using NSFetchResultsController I just take all core data objects and turn them into immutable viewModels.
Then in cellForItemAtIndexPath I just call this:
cell.mediaView = [viewModel.media mediaView];
This approach creates nice immutable wrapper which contains only necessary chunk of information needed by the JSQ chat library. Also, you can easily write tests for such object. If you're using swift, you can use struct for this kind of purpose.
Hope my answer helps. Please ask if you need more detailed answer. ;-)
I'm just getting started with Core data, (and I'm also trying to use Magical Record). I'm creating a pretty simple Payment tracking app.
I would like to save a Payment object that has an array of Debtors. This is what my Payment object looks like
#class Debtor;
#interface Payment : NSObject
#property (strong, nonatomic) NSString *paymentAmountString;
#property (strong, nonatomic) NSString *titleString;
#property (strong, nonatomic) NSArray *debtorsArray;
#property (strong, nonatomic) NSDate *dueDate;
#property (strong, nonatomic) NSString *notesString;
#end
And the debtorsArray is an array of Debtor objects
#interface Debtor : NSObject
#property (strong, nonatomic) NSString *nameString;
#property (strong, nonatomic) NSString *amountOwedString;
How should I go about saving this object since it contains an array. Do I need to create two different Entities, with a relationship between Payment and Debtor? How exactly do I do this, and how would I ensure that they are fetched properly?
Create only one entity for Payment. You will have to use the 'Transformable' data type for your attribute debtorsArray within this entity.
Then implement the following methods in your Debtor class:
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.nameString forKey:#"nameString"];
[aCoder encodeObject:self.amountOwnedString forKey:#"amountOwnedString"];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if(self = [super init]){
self.nameString = [aDecoder decodeObjectForKey:#"nameString"];
self.amountOwnedString = [aDecoder decodeObjectForKey:#"amountOwnedString"];
}
return self;
}
Entity should be fetched normally like any other fetch query.
Hope this helps.
I am trying to create a very simple app that manipulates an iOS core data model I've named 'Person' with the attributes: 'age', 'firstName', and 'lastName'.
However it appears that as soon as I try to GET or Query this data model, iOS appears to be returning a nil value.
'NSInvalidArgumentException', reason: '+entityForName: nil is not a
legal NSManagedObjectContext parameter searching for entity name
'Person''
Here's what my view controller .m file looks like:
#import "bitcraft_v1ViewController.h"
#import <CoreLocation/CoreLocation.h>
#import "Person.h"
#interface bitcraft_v1ViewController ()
#end
#implementation bitcraft_v1ViewController
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
CLLocationManager *locationManager;
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
[locationManager startUpdatingLocation];
float latitude = locationManager.location.coordinate.latitude;
float longitude = locationManager.location.coordinate.longitude;
NSLog(#"This is latitude: %f", latitude);
NSLog(#"This is longitude: %f", longitude);
Person *newPerson = [NSEntityDescription
insertNewObjectForEntityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
}
My view controller header file looks like this:
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface bitcraft_v1ViewController : UIViewController
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
//- (void)saveContext;
#property (strong, nonatomic) IBOutlet UILabel *regionCurrentlyEntered;
#end
And finally I haven't touched Person.h and Person.m very much:
Person.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Person : NSManagedObject
#property (nonatomic, retain) NSNumber * age;
#property (nonatomic, retain) NSString * firstName;
#property (nonatomic, retain) NSString * lastName;
#end
Person.m
#import "Person.h"
#implementation Person
#dynamic age;
#dynamic firstName;
#dynamic lastName;
#end
I know that my view controller implementation file only declares the variable person but never actually stores a hardcoded first name, last name and age. But I can't seem to even get past this point.
Why is NSManagedObjectContext not able to find the entity Person in the Core Data? After finally declaring 'newPerson' Will I then be able to create "John Smith 42" and store this newPerson in the core data within the - (void)viewDidLoad?
I have a class which should hold some data:
the header:
#import <Foundation/Foundation.h>
#class Behandler, Tag;
#interface OeffnungsZeit2 : NSObject
#property (nonatomic, retain) NSNumber * offen_stunde;
#property (nonatomic, retain) NSNumber * offen_minute;
#property (nonatomic, retain) NSNumber * geschlossen_stunde;
#property (nonatomic, retain) NSNumber * geschlossen_minute;
#property (nonatomic, retain) Tag *tag;
#property (nonatomic, retain) Behandler *behandler;
-(void)setTag:(Tag *)tag;
-(Tag *)getTag;
-(void)setBehandler:(Behandler *)behandler;
-(Behandler *)getBehandler;
#end
the main file:
#import "OeffnungsZeit2.h"
#import "Behandler.h"
#import "Tag.h"
#implementation OeffnungsZeit2
#dynamic offen_stunde;
#dynamic offen_minute;
#dynamic geschlossen_stunde;
#dynamic geschlossen_minute;
-(void)setTag:(Tag *)tag{
self.tag = tag;
}
-(Tag*)getTag{
return self.tag;
}
-(void)setBehandler:(Behandler *)behandler{
self.behandler = behandler;
}
-(Behandler*)getBehandler{
return self.behandler;
}
#end
When I try to access it, somewhere in these lines:
NSArray *tage = [self fetchTageWithNummer:openDay];
Tag *tag = [tage objectAtIndex:0];
// create an OeffnungsZeit2 object and add it to our array
OeffnungsZeit2 *oeffnungsZeit2 = [[OeffnungsZeit2 alloc] init];
[oeffnungsZeit2 setTag:tag];
oeffnungsZeit2.offen_stunde = [NSNumber numberWithInt:[openHours intValue]];
oeffnungsZeit2.offen_minute = [NSNumber numberWithInt:[openMins intValue]];
oeffnungsZeit2.geschlossen_stunde = [NSNumber numberWithInt:[closeHours intValue]];
oeffnungsZeit2.geschlossen_minute = [NSNumber numberWithInt:[closeMins intValue]];
[self.oeffnungsZeiten addObject:oeffnungsZeit2];
I am getting an ECX_BAD_ACCESS error.
Can anyone hint me, why this is wrong? I am pretty new to iOS, and never wrote a class which holds data...
It looks like you are getting a stack overflow error when your getters/setters call themselves. For example, this code
-(void)setTag:(Tag *)tag{
self.tag = tag;
}
is infinite recursion, because self.tag = tag is another syntax for writing [self setTag:tag].
If you are using the latest Xcode, all you need to do is dropping the declarations and definitions of the getters and setters for tag and behandler that you have added manually. The compiler will generate the code that is equivalent, but has no infinite recursion.
Note: there are legitimate cases when you need to access the variable that "backs" an automatically generated property. By convention, the name of that variable is the name of the property prefixed with an underscore. You can override this in the #synthesize directive for the property.
I'm trying to add a field to an entity and whenever I try to access the new field I get
unrecognized selector sent to instance...
Everything else about the entity works. I'm able to add objects and I'm able to assign values to other fields in the object, but not to the new objects.
Of note, I also deleted the DerivedData directory and I deleted the .mom/.momd files to make sure the tables are being built correctly.
Any thoughts?
Locations.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Games, Players;
#interface Locations : NSManagedObject
#property (nonatomic, retain) NSNumber * defaultLoc;
#property (nonatomic, retain) NSString * locationAddr1;
#property (nonatomic, retain) NSString * locationAddr2;
#property (nonatomic, retain) NSString * locationCity;
#property (nonatomic, retain) NSNumber * locationID;
#property (nonatomic, retain) NSString * locationName;
#property (nonatomic, retain) NSString * locationState;
#property (nonatomic, retain) NSString * locationZip;
#property (nonatomic, retain) NSNumber * numberOfCourts;
#property (nonatomic, retain) NSNumber * defLoc;
#property (nonatomic, retain) NSSet *haveGames;
#property (nonatomic, retain) NSSet *havePlayers;
#end
#interface Locations (CoreDataGeneratedAccessors)
- (void)addHaveGamesObject:(Games *)value;
- (void)removeHaveGamesObject:(Games *)value;
- (void)addHaveGames:(NSSet *)values;
- (void)removeHaveGames:(NSSet *)values;
- (void)addHavePlayersObject:(Players *)value;
- (void)removeHavePlayersObject:(Players *)value;
- (void)addHavePlayers:(NSSet *)values;
- (void)removeHavePlayers:(NSSet *)values;
#end
Locations.m
#import "Locations.h"
#import "Games.h"
#import "Players.h"
#implementation Locations
#dynamic defaultLoc;
#dynamic locationAddr1;
#dynamic locationAddr2;
#dynamic locationCity;
#dynamic locationID;
#dynamic locationName;
#dynamic locationState;
#dynamic locationZip;
#dynamic numberOfCourts;
#dynamic defLoc;
#dynamic haveGames;
#dynamic havePlayers;
#end
Note: defLoc and defaultLoc are two fields that I added. These are the ones giving me problems.
Executing code:
- (IBAction)updateLocation:(UIStoryboardSegue *)segue
{
// If it is not "Edit" it is an "Add" and we need to insert a newobject.
if ([segueType1 isEqualToString:#"Add"])
{
NSManagedObjectContext *context = [[self fetchedResultsController] managedObjectContext];
location = [NSEntityDescription insertNewObjectForEntityForName:#"Locations" inManagedObjectContext:context];
}
location.locationName = lName;
location.locationAddr1 = lAddr1;
location.locationAddr2 = lAddr2;
location.locationCity = lCity;
location.locationState = lState;
location.locationZip = lZip;
location.numberOfCourts = lNumCourts;
location.defLoc = lNumCourts;
// location.defaultLoc = [NSNumber numberWithInt:1];
// DLog(#"ldefaultLocation = %#",ldefaultLocation);
location.defaultLoc = ldefaultLocation;
segueType1 = #"Add"; // Always reset back to Add so that segues work right
}
location.defLoc above is the command that gives the error...All other location statements work fine.
That can happen when you don't declare the new attribute in your NSManagedObject child. As soon as you add a new field in the core data model, remember to add the right property to the NSManagedObject class and declare it #dynamic in the implementation.