Properly subclassing the EKEvent Class - iphone

I have been having a bit of trouble sublclassing the EKEvent Class. The scenario is this, I am pulling all my events from an external database using a webservice, so all the events come with an ID. I then want to put these events into the device calendar and retrieve them later. The problem is, when I retrieve the event I need it to have the same id as the event on the server so I can do a quick look up to get additional info on the event.
I am aware that the identifier property of EKEvent is read only, hence the reason I want to create a subclass of the class where I can add an additional property called something like myid and store the id of the event (the one from the server) with it in the eventstore. I have tried to create a subclass and everything seems to work fine and compiles, but on runtime I get an error when I try to set the extra eventid proporty that I add in the subclass, the error message is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[EKEvent setEventId:]: unrecognized selector sent to instance 0x83c0770'
This is some test code I use to create the event from my EKEvent subclass:
SectureEvent *myEvent = (SectureEvent*)[EKEvent eventWithEventStore:eventDB];
myEvent.title = self.evento;
myEvent.startDate = [[NSDate alloc] init];
myEvent.startDate = [NSDate date];
myEvent.endDate = [[NSDate alloc] init];
myEvent.endDate = [[NSDate alloc] init];
myEvent.allDay = YES;
myEvent.eventId = self.eventId;
The error occurs on the last line myEvent.eventId = self.eventId;
and app crashes. So my question essentailly if I can effectively subclass the EKEvent class and if so what am I doing wrong here?
Thanks in advance!

EKEvent is not meant to be subclassed. Event Kit objects are used to represent database records. Creating a subclass of EKEvent will not magically insert new fields in the Event Kit database, nor will casting a EKEvent to something else magically change that object' class.
The only way to store extra fields into the database is to have a direct access to that database, which Apple reserves for themselves.
As you cannot add new fields to the Event Kit database, you can either use the existing fields (for example, add the event ID in the notes of the event) or extend it with a second database managed by your application.
Just create a SQLite database (or a Property List file, or whatever format you want) that associate your event IDs to EKEvent identifiers.

Creating an EKEvent and casting it as a SectureEvent is not the same as creating a SectureEvent.
Try this:
SectureEvent *myEvent = [SectureEvent eventWithEventStore:eventDB];

Related

Entity is still not key value coding-compliant for the key, even after rebooting my dev Mac

if (self.temp)
{
NSDateFormatter *form = [[NSDateFormatter alloc] init];
[form setDateFormat:#"dd/MM/yyyy"];
dt1 = [form stringFromDate:[datePicker date]];
[self.temp setValue:dt1 forKey:#"dateOne"];
}
else
{
NSDateFormatter *form = [[NSDateFormatter alloc] init];
[form setDateFormat:#"dd/MM/yyyy"];``
dt1 = [form stringFromDate:[datePicker date]];
NSManagedObject *newGoalText = [NSEntityDescription insertNewObjectForEntityForName:#"Goal" inManagedObjectContext:context];
[newGoalText setValue:dt1 forKey:#"dateOne"];
}
This is the code I have written to save the date as a string into my core data model. And I have created a single entity. Goal 'Goal' contain attributes 'goalText(String)', 'dateStr(string)' and 'dateOne(String)'.
When the data is going to be edited the if condition works otherwise else condition works. That is the flow.
So when I execute this code I am getting the error as "[ setValue:forUndefinedKey:]: the entity Goal is not key value coding-compliant for the key "dateOne".'". The first two attributes are successfully saved and fetched though.. And one more thing is that the third attribute is set from a different view.
I have rebooted the Mac as per the answer in this other question,but no result is getting.
I have solved. At one stage I deleted the name.xcdatamodel and created a new one with the same name. But the old one some how remains in my project. Deleting that old one solved my problem. Thanks for your help
Not sure it will fix your pb or not, but last time i met the issue was because a messed up in the bundle. (Happened when i added a property on the coredata model, and regenerate the class from the model) the old model was still being used.
pretty much the same thing as explained in the question:
Entity is not key value coding-compliant for the key
If not yet done try to clean the the derived data of your project from the organizer. This is what worked for me.
May be this is late post. But hope it will help some one.
I got this error recently. For me while changing Xcdatamodel I was missing with the key. I added the key in entity. Then it's working fine.

iPhone calendar EKEvent

I am currently trying to create an iPhone calendar app. In order to make sure it syncs with the existing iPhone calendars, I am using the EKEvent toolkit.
However, the events I will be storing will have more properties than the ones EKEvent allows for- e.g., my events will not just have title, details and the few other categories that are allowed for; they will also have themes, priorities...
Thus, when I load the EKEventStore every time my calendar starts up, this information will not be contained in the EKEvents that are loaded.
How can I associate this information to the existing EKEvents so that whenever my calendar is loaded, these additional properties are also loaded?
I would use the eventIdentifier but the iPhone documentation says that "If the calendar of an event changes, its identifier most likely changes as well." If I am reading this correctly, this means that I cannot consistently use eventIdentifier to identify an event..
I would use the notes property and set a string which you can parse later.
Since EKCalendarItem is EKEvent's superclass some of the properties of EKEvent are inherited from EKCalendarItem. (documentation here)
However, you can still 'set' notes on an EKEvent, even though notes is not a property of EKEvent anymore. (go figure)
So, from your question, I might set an integer for each of your additional (custom) properties, like so..
In the view controller code that creates the event:
- set an integer for each of you custom options. (priority, theme, etc.)
int priority = 0;
EKEvent *newEvent = [EKEvent eventWithEventStore:yourEventStore];
[newEvent setCalendar:yourCalendar];
if (priority == 0) {
newEvent.notes = #"0"
}
newEvent.title = #"YourTitle";
newEvent.startDate = yourStartDate;
newEvent.endDate = yourEndDate;
[youreventStore saveEvent:newEvent span:EKSpanThisEvent commit:YES error:nil];
Then if you want to check/convert the custom property, just check the notes of the event whenever you fetch them either using characterAtIndex or make a subString from the notes and compare that to another string.

Changing Core Data Items crashes App

I am running into another problem with my Iphone App which I just can't solve myself.
I implemented a kind of organizer functionality in my latest app.
There one can create appointments which are displayed in a tableview and persisted in a CoreDataStore. I use 4 classes:
an overview where appointments are displayed
a view with textfields to put in Values for place and name of appointment (create/edit view)
a view with DatePicker to define start- and enddate
a controller which handles creation and deletion of items using this methods:
The code:
-(void)createAppointmentObjectWithDate:(NSDate *)
appointmentDate name:(NSString *)appointmentName
description:(NSString *)appointmentDescription
eDate:(NSDate *)appointmentEndDate
{
NSManagedObjectContext *managedObjectContext = [[CoreDataManager sharedManager] managedObjectContext];
AppointmentObject *newAppointmentObject = [NSEntityDescription insertNewObjectForEntityForName:AppointmentObjectEntityName
inManagedObjectContext:managedObjectContext];
newAppointmentObject.appointmentName = appointmentName;
newAppointmentObject.appointmentDescription = appointmentDescription;
newAppointmentObject.appointmentDate = [appointmentDate earlierDate:appointmentEndDate];
newAppointmentObject.appointmentEndDate = [appointmentEndDate laterDate:appointmentDate];
}
-(void)deleteAppointmentObject:(AppointmentObject *)appointmentObject triggeredByUser:(BOOL)byUser{
NSManagedObjectContext *managedObjectContext = [[CoreDataManager sharedManager] managedObjectContext];
[managedObjectContext deleteObject:appointmentObject];
}
But all kind of crazy stuff is happening which makes my app crash with "SICBART" message:
2010-10-13 17:35:04.630 didacta[109:307] Serious application error. Exception was caught during Core Data change processing.
This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.
-[CALayer controllerWillChangeContent:]: unrecognized selector sent to instance 0x19f150 with userInfo (null)
2010-10-13 17:35:05.118 didacta[109:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CALayer controllerWillChangeContent:]: unrecognized selector sent to instance 0x19f150'
errors appear while doing this:
creating new Appointment and pressing "Done" (should trigger creation and pop overview)
changing appointments and pressing "Done" (should send changes and pop overview)
tapping on an appointment in overview ( should pop create/edit view and hand over values)
deleting an item
sometimes I can even delete an appointment but then the order of the items in the tableview is somehow gotten twisted so the index of the tableview isn't pointing to the index of the appointment anymore.
Right.
-[CALayer controllerWillChangeContent:]: unrecognized selector sent to instance 0x19f150 with userInfo (null)
That's your error. You have an NSFetchedResultsController whose delegate is a CALayer. This sounds like the original delegate was deallocated and a CALayer was allocated using the same region of memory. The fix is to find the offending -dealloc and add something like self.myFetchedResultsController.delegate = nil; self.myFetchedResultsController = nil; assuming you're using properties.
You can sometimes help debugging things like this by enabling zombies (go to Project → Edit Current Executable or so, select Environment, add the NSZombieEnabled environment variable, and set its value to "YES" or so; uncheck the checkbox when you've finished debugging). Zombies cause an exception when a message is sent to a deallocated object. (Zombies are not deallocated by default, so your application will effectively leak; remember to uncheck the checkbox!).
"unrecognized selector" makes it sound like maybe your data model doesn't contain some of the Entity attributes that you're trying to use. For example, maybe you're trying to set an attribute of the object that doesn't exist.
Try creating a breakpoint at newAppointmentObject.appointmentName = appointmentName; and step through it to see at what point the error occurs.

ExecuteFetchRequest intermittently throwing exception with same parameters. "not key value coding-compliant for the key"

EDIT Thanks to Matt's post I now understand that I should not be trying to access 'started' as an array. However, if that is the case, I would like to know why this code appears to be working in other places. It still seems to me that this should be "one-or-the-other." It should work or it shouldn't.
ORIGINAL
I'm using the same fetch request in various parts of my code to find the most recent game:
Game *lastGame = [[[CoreDataAccess managedObjectContext] fetchObjectsForEntityName:#"Game" withPredicate:#"started == started.#max"] anyObject];
'Game' is an NSManagedObject and 'started' is a date attribute. 'started' is set exactly once per object in awakeFromInsert. It is never changed after that. Game is never directly instantiated, but it has three subclasses. I have tried making Game both abstract and concrete, but neither has any effect on this problem.
I'm using an NSManagedObjectContext category to perform the fetch, as shown on cocoa with love here http://cocoawithlove.com/2008/03/core-data-one-line-fetch.html.
The error I am getting is the following:
Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. [<__NSDate 0xebb1130> valueForUndefinedKey:]: this class is not key value coding-compliant for the key #max. with userInfo {
NSTargetObjectUserInfoKey = "2010-11-06 11:16:53 GMT";
NSUnknownUserInfoKey = "#max";
}
It looks to me like the predicate might be trying to apply #max to a single NSDate, instead of all 'started' attributes in all games. I'm not sure though. I'm not very good with predicates, and it took me a lot of trial and error to make this one. I don't understand how the exact same fetch can have errors in different places, though.
The fetch is not part of an NSFetchedResultsController, but I am using a fetchedResultsController in the class where I'm getting the error. For example:
- (void)configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath{
Game *game = [self.frc objectAtIndexPath:indexPath];
Game *lastGame = [[[CoreDataAccess managedObjectContext] fetchObjectsForEntityName:#"Game" withPredicate:#"started == started.#max"] anyObject]; // Sometimes we get past this line, sometimes we don't...
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"EEE, MMM d, yyyy h:mm a"];
if (game != lastGame)
cell.detailTextLabel.text = [format stringFromDate:game.started];
else
cell.detailTextLabel.text = #"In Progress";
[format release];
...
}
and also here:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
Game *lastGame = [[[CoreDataAccess managedObjectContext] fetchObjectsForEntityName:#"Game" withPredicate:#"started == started.#max"] anyObject];
if (lastGame == [frc objectAtIndexPath:indexPath])
return NO;
return YES;
}
This exact fetch is performed several times in several places, for example on startup, but it only crashes in one class. As I said it is intermittent, but it seems to be sometime after I create a new Game object. The game is created in one tab, and the code above is from a second tab which shows history.
I have seen a similar error here. In that case the problem was solved by restarting the computer, which finally allowed XCode to realize that the attribute had been deleted from the model. I have tried that, and I'm still experiencing the problem. I have also tried deleting and recreating the 'started' attribute in the model. I have also read the Core Data Troubleshooting Guide, but was unable to find any help there either.
Predicates are applied to one source object at a time. If your source object does not have an array property, you can't use an array operator.
In your case, the predicate says:
Look at a given "Game". If its own
"started" property is equal to its own
"started" property with the #max KVC
array operator applied, then this
predicate will be true.
This is not what you want. The only situation where you'd use a KVC array operator in a predicate is where a property on an object is an array. e.g.
Fetch every "Season" where the "games.#max.homeTeamScore > 50" (i.e. seasons where a home team scored more than 50 in a game). This would work because the "games" property on a "Season" would be an array, so games.homeTeamScore would also be an array.
However, the "started" property on a single Game is not an array. The array you want to operate on is actually the array of all games, which is not a property of a game.
The only twisted way you could access the array of all games would be to fetch the array of all games first, then apply the array operator outside the predicate and then on the inside of the predicate only apply the equality test.
i.e. fetch all games first, then refetch with:
fetchObjectsForEntityName:#"Game" withPredicate:#"started == %#", [allGames valueForKey:#"#max.started"]
But this is not the smart thing to do either.
Ultimately, the correct way to fetch the game with the latest starting date as you're trying to do, can't be done with the single line fetch method.
You'll need to create a variant of the method that allows you to setSortDescriptors: on the fetch request (to sort by "started", descending) and then setFetchLimit:1 on the fetch request to only get first result.

could not locate an NSManagedObjectModel for entity name

This is the code for toggleAddProject method, the Core Data code is almost the same as found in Apple's CoreDataBooks sample, however when I click the add button the app crashes with entityForName: could not locate an NSManagedObjectModel for entity name 'Project' on the line starting with newProjectController.project
-(IBAction)toggleAddProject
{
NewProjectViewController *newProjectController = [[[NewProjectViewController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];
// Create a new managed object context for the new project -- set its persistent store coordinator to the same as that from the fetched results controller's context.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addingManagedObjectContext = addingContext;
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
newProjectController.project = (Project *)[NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:addingContext];
[addingContext release];
UINavigationController *addNewNavigationController = [[UINavigationController alloc] initWithRootViewController:newProjectController];
[self.navigationController presentModalViewController:addNewNavigationController animated:YES];
[addNewNavigationController release];
}
Everything has been synthesized, the Project entity exists. I can't figure out why it crashes. Most people seem to be able to fix this error by inserting the following code either in the method itself, or in viewDidLoad:
if (managedObjectContext == nil)
{
managedObjectContext = [(CoreDataBooksAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
When modified for my app delegate it makes no difference. Thanks for any help.
This error has only a few possible sources:
Typo in the Entity name.
Nil managed object context object.
Failure to add the model containing the entity to the persistent store the context uses.
Failure to add the correct persistent store to the context itself.
I had this problem when I had several different NSManagedObjectContexts. The quick way to debug it was to inspect the different connection bits and make sure my entity was listed before calling the context.
NSLog(#"Context: %#",context);
NSLog(#"PS Coord : %#",context.persistentStoreCoordinator);
NSLog(#"MOM : %#", context.persistentStoreCoordinator.managedObjectModel);
NSLog(#"Entities : %#",[[context.persistentStoreCoordinator.managedObjectModel entities] valueForKey:#"name"]);
Use the debugger and confirm that your model is not nil. That is the most common cause of this error. If it is not nil then look for a typo in the entity name.
The Apple docs give some good information on debugging the error entityForName: could not locate an NSManagedObjectModel for entity name 'Foo'.
Look at this section of the Core Data Programming Guide.
Ok I ran across this issue as well and I solved it thusly. The original code was given as:
Event *event = (Event *)[NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:managedObjectContext];
While the code is concise it seems like the debugger can't display more detailed information about where the error is since you are both creating and configuring a new instance of the 'Event' entity (or whatever your Entity is named).
Instead I broke out this into three lines and the debugger displayed a lot more information:
Event *event = [[NSManagedObject alloc] init];
NSManagedObjectContext *moc = [self managedObjectContext];
event = [NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:moc];
I found I had not set the correct Type for one of the attributes and I had a typo in my code, all of which the debugger pointed out.
During my development, I could not find Entities that I added later on.
What worked for me: (Basically a sanity-tip)
Uninstall the App EVERY TIME you change the Data Model!
The Data Model is cached by Core Data between installs, to make sure the integrity stays in tact. Delete the App from the simulator / iPhone to be able to test your changes.
PS: does anyone know how to do that automatically?
TechZen is spot on...in my case it was #4. Walk through the steps in the following link and this should help you add the appropriate CoreData methods into an existing project and get everything set up correctly so you don't run into the error you're having.
Adding Core Data To Existing iPhone Projects