Programmatically add attributes to Core Data - iphone

I am developing a application that uses core data. In my application i would like every user to have the same database. So i want all of the devices to share the same core data objects.
In order to do that 'synchronization' I found a presentation on this answer that discussed this matter. In the presentation, it is suggested that I add attributes such as 'creationDate' and 'modificationDate' to every object in Core Data.
So to do that, i tried to subclass NSManagedObject (MyManagedObject) in order to add those properties to every object in core data. I also made every NSManagedObject subclass I had (generated automatically by the model) subclass MyManagedObject.
The problem is that the properties I added to the NSManagedObject subclass do not persist in the database. So when I close and reopen the application, 'creationDate' becomes (null).
Here's some of my code (header):
#interface MyManagedObject : NSManagedObject
#property (nonatomic, retain) NSDate * modificationDate;
#property (nonatomic, retain) NSDate * creationDate;
#end
And here's the .m file:
#implementation MyManagedObject
#synthesize modificationDate, creationDate;
-(void)willSave {
[super willSave];
self.modificationDate = [NSDate date];
}
-(void)awakeFromInsert {
[super awakeFromInsert];
[self setCreationDate:[NSDate date]];
[self setModificationDate:[NSDate date]];
}
Any help, directions or reading would be greatly appreciated. I have been struggling with this for a while now. I need a better way than adding these attributes to EVERY single entity in the model. Thanks in advance
PS: i intend to have a 'truth database' (a .sqlite file) in a server. The synchronization will be between the server and the iPhone. Such as suggested in the presentation

If you create MyManagedObject in the data model design tool and also register it as the parent of other entities inside that tool, this should almost work. (I suspect you didn't do that because of the #synthesize statement...those would usually be #dynamic.)
The one other problem to fix is that you have to test whether you've already changed an object's property (self.modificationDate in your case) before changing it again or else willSave continues to get called. A BOOL value that gets set after the first change and cleared in didSave is a simple thing to test.

Related

How to add an object to a NSMutableArray that is stored in a CoreData iOS database?

I have an NSManagedObject subclass where absences is an NSMutableArray
#interface Record : NSManagedObject
#property (nonatomic, retain) id absences;
#end
I want to be able to add items to the absences array; however, if I do [myRecord.absences addObject:SomeObj the record does not save properly. It almost appears that the NSManagedObject does not know that I updated the absences array.
Nevertheless, if I add SomeObj to some localAray, then set myRecord.absences = localArray, the NSManagedObject saves correctly.
Can someone explain this behaviour and how I might avoid it...thanks
You're exactly right, in the first case you're changing an object outside of NSManagedObject field of view. To solve this, Apple doc says the following
For mutable values, you should either transfer ownership of the value to Core Data, or implement custom accessor methods to always perform a copy.
So declaring your property with (copy) should suffice.

Property not found on object of type

I have a simple question:
I have a NSManagedObject subclass that I've been using for some time and it was working perfectly fine.
Today I added a new property to it with the corresponding iVar. The property is a simple (nonatomic,retain) NSString *.
And as normal i added #synthesize to the property to generate setter and getter functions.
However, there is no way i can use the newly defined property!!! I keep getting "property not found on object of type" error and my build fails.
Here are a few chunks of code that will clear things out:
//import the core data header first CoreData/CoreData.h
#interface Contact : NSManagedObject
{
NSString *contactID;
NSString *firstName;
NSString *myDevMod;
}
#property (nonatomic,retain) NSString *contactID;
#property (nonatomic,retain) NSString *firstName;
#property (nonatomic,retain) NSString *myDevMod;
#end
and the corresponding .m implementation:
#import "Contact.h"
#implementation Contact
#synthesize contactID, firstName, myDevMod;
and the code that uses the Contact class:
#import "Contact.h" //at the start
Contact *aContact = [[Contact alloc] init];
aContact.contactID = someId; //works perfectly fine
aContact.firstName = someName; //works perfectly fine
aContact.myDevMod = #""; //THIS IS WHERE THE ERROR OCCURS!!
[aContact doSomethingHere];
[aContact release];
What do you think could be the error??
Thanks in advance for your support.
Weirdly the site won't let me add a comment so :
comment:
Sounds odd. First thing I'd try is removing the #property lines and #synthesize line - so the getters and setters are created automagically. Also try: aContact.myDevMod = someName; to see if that actually works - might shed some light.
I had exact the same problem last night. It took me about 5 hours to fix that. My first attempts were to create the model classes again with menue Editor -> Create NSManagedObject Subclass...
But that didn´t help. Even if i had deleted the classes beforehand Xcode did not recognize the properties. I´ve deleted the model as well and rebuild it with same name. And created the NSManagedObject Subclasses again. Didn't help. Then i´ve deleted the model and the MOS again and build it up from scratch! But with different names! Took me again a long time to change my code but after that everthing seemed to be normal. Now i can add, change and delete attributes in the model and the properties NSManagedObject Subclasses and Xcode recognizes them again.
But boy, it almost drove me nuts!!! And i still don't know what happend!
My experience is that it isn't the model or the objects that need to be recreated.
Most of the time you can recreate the file you are working in. Make a new NSView of what every .h and .m file and copy the info over to the new file, and it will work.

Using a single view for multiple NSManagedObjects

So I have a couple properties that are common across several of my Core Data entities. Notes, URL, etc.
I have a single view controller for editing this information, and I pass the Entity to it as a NSManagedObject
#property (nonatomic, retain) NSManagedObject *editedObject;
#property (nonatomic, retain) Thing *thing;
#property (nonatomic, assign, getter=isEditingThing) BOOL editingThing;
And to actually get the object in a state I could make changes I would
if([editedObject isKindOfClass:[Thing class]]) {
thing = (Thing *)editedObject;
editingThing = YES;
}
That way I can set the values on thing and save the managedObjectContext like normal.
However, this is crashing my application, but only if I enter and exit the View Controller for editing the field over and over again.
Which brings me to my question: What's the best way to handle using a single view controller to edit a common field on more than one Core Data Entity? Should I just set the Boolean and populate the correct entity property when I call the View Controller?
I imagine your thing property is not getting retained because you're not using your accessor to set it. Change:
thing = (Thing *)editedObject;
to:
[self setThing:editedObject];
As to your bigger question, I think you need to provide more context. What do you mean by "edit a common field on more than one Core Data Entity"?
If both entities have a property called notes then change your property:
#property (nonatomic, retain) id managedObject;
Then when you go to set it, you can just set it without caring what the object is:
[[self managedObject] setNotes:...];
As long as whatever you set managedObject to responds to the property notes it will work just fine. If not, it will error out and you can test that easily.
You could go a more paranoid route and test if it responds first:
if ([[self managedObject] respondsToSelector:#selector(notes)]) {
but that is over protective since the only thing you would want to do that point is crash anyway.

Editing and iPhone SDK Framework?

I am working with MapKit and want to be able to add a (NSString *)itemTag value to each of my annotations. I have created myAnnotiation.m and myAnnotation.h
I tried adding itemTag to the myAnnotation.h/m but when I try to access currentAnnotation.itemTag within my main code, it says "itemID not found in protocols" - so I went to the MapKit.Framework and into MKAnnotation.h. I added (NSString *)itemID, but when I save the .h file in the Framework, it changes the file's icon and doesn't appear to by jiving with everything else.
Any help or links to help would be greatly appreciated. I am not even sure if I'm on the right path here, but Googling "modify iphone sdk framework" does not turn up much.
Why are you trying to modify the framework? You should be defining itemID as a property or instance variable (or both) in myAnnotation.h. You say that currentAnnotation.itemTag
didn't work; for that to work, you need to have itemTag defined as a property of whatever class currentAnnotation belongs to.
Changing the header file for the framework won't recompile it, so you won't be able to get that to work.
EDIT: Here's an example.
In MyAnnotation.h:
#interface MyAnnotation : NSObject <MKAnnotation> {
NSString *itemID;
// Other instance variables
}
#property (nonatomic, retain) NSString *itemID;
// Class and instance methods.
#end
In MyAnnotation.m:
#implementation MyAnnotation
#synthesize itemID;
// Your code here.
#end
The #property call defines the property and the #synthesize call will create setters and getters for you (methods to set and retrieve the value of itemID). In MyAnnotation.m, you can use self.itemID or [self itemID] to get the value of itemID, and you can use self.itemID = #"something" or [self setItemID:#"Something"] to set the value.
EDIT 2:
When you get currentAnnotation, if the compiler doesn't know that the annotation is an instance of your MyAnnotation class, it won't know about itemID. So, first ensure that you've included this line at the beginning of your .m file:
#import MyAnnotation.h
That wil ensure that the compiler knows about the class. When you use currentAnnotation, you cast it as an instance of MyAnnotation like so:
(MyAnnotation*)currentAnnotation
That should quiet down the warnings.

How to share an array between two classes?

I want an array that gets data from one class to be avaliable to me in another class with the same data.
If we declare an array on the applicationDelegate class.
Then declare an object of applicationDelegate in both classes.
And assign the array into appDelegate.array from one class, will i be able get the array across the classes?
I'm with Mike. Leave the App Delegate out of it.
You're in control of when and how your objects are instantiated. If it's important for more than one class to have access to the same data, hand off the data, or a means of getting at your data, as you create instances of the dependent class.
An example, given classes WVParent and WVChild.
WVParent has a property, someArray, which is the array your other objects need:
#interface WVParent : NSObject {
NSArray *someArray
}
#property (nonatomic, retain) NSArray *someArray;
#end
Then you have WVChild, which itself has a property called parentObject:
#class WVParent;
#interface WVChild : NSObject {
WVParent *parentObject;
}
#property (nonatomic, assign) WVParent *parentObject;
#end
Assuming the parent is creating the child instance, you'd allocate it and assign the parent:
WVChild *child = [[WVChild alloc] init];
child.parentObject = self;
Then the child instance can access someArray by:
self.parentObject.someArray
This is just one option. You could also just pass the array itself to the child, assuming the data is static and unlikely to change over the course of the application session.
This way, instead of having a source of data living somewhere in the App Delegate, it lives within a class more appropriately responsible for its creation, maintenance and vending. If you've got a class that pops into existence and can only reach the rest of your app by getting the app delegate, you might want to put a little more thought into your architecture.
More on dependency injection here:
http://en.wikipedia.org/wiki/Dependency_injection
Yes, you could declare a member/property on your applicationDelegate class, although you should try to follow the Single Responsibility Principle and make sure you don't end up stuffing lots of miscellaneous shared stuff in your app delegate (which happens a lot with iPhone code).
Another alternative would be to inject the array into the objects' constructors when you create them.
It's hard to know the best solution in terms of design without knowing what this data is and where it really belongs.