In my app, I create events in an EKCalendar. I fetch the events online, and in order to refresh the events, I want to first delete the calendar (if it exists), recreate it, and then put the new events in there.
To instantiate the calendar I use
- (EKCalendar *)calendar {
if (!_calendar) {
NSArray *calendars = [self.store calendarsForEntityType:EKEntityTypeEvent];
NSString *calendarTitle = #"MyCalendar";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title matches %#", calendarTitle];
NSArray *filtered = [calendars filteredArrayUsingPredicate:predicate];
if ([filtered count]) {
_calendar = [filtered firstObject];
} else {
_calendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:self.store];
_calendar.title = calendarTitle;
_calendar.source = self.store.defaultCalendarForNewEvents.source;
NSError *calendarErr = nil;
BOOL calendarSuccess = [self.store saveCalendar:_calendar commit:YES error:&calendarErr];
if (!calendarSuccess) {
NSLog(#"Calendar Error = %#", [calendarErr localizedDescription]);
}
}
}
return _calendar;
}
To delete the calendar, I use
-(IBAction)deleteCalendar{
NSError *error = nil;
[self.store removeCalendar:_calendar commit:YES error:&error];
}
Both methods work fine individually.
So, when I start the creation of events, I do the following:
[self deleteCalendar];//delete calendar and its events, in case it already exists
[self calendar];//create calendar
[self importEvents];//put events in calendar
Now, what I observe is the following:
On the first run of the app
a calendar is created
events are imported. (This is expected, and works just fine)
While the app is running, I trigger the above methods again with a button. With the following, for me puzzling, result:
the calendar is deleted (expected result)
NO calendar is created (WHY? that is my main question).The "if (!_calendar)" part of the method is considered FALSE, and nothing is executed.
The 'importEvents' method runs through its regular hoopla, without any apparent errors, although I would expect something like a 'no source' error.
Please advise.
UPDATE:
This could be an indicator of what is happening, but I still don't get it:
After a while, the events appear in a different calendar, i.e. not the calendar called 'myCalendar', but another, iCloud based calendar, apparently the one that at that point is the defaultCalendarForNewEvents. However, that also doesn't make any sense to me.
OK, so, what is happening:
I have deleted the Calendar from the store, effectively, but a reference to that calendar actually was still hanging around in my app.
I solved it as follows:
-(IBAction)deleteCalendar:(id)sender{
NSError *error = nil;
if(_calendar){
[self.store removeCalendar:_calendar commit:YES error:&error];
}
_calendar = nil;
}
I hope this is useful to someone
Related
I've developed an app that uses calendar using the eventkit.
The user can push a button and delete a specific event. Everything works just fine, except when it comes to recurrent events.
If the user itself created the recurrent events, it works as expected, the single event is deleted and the rest remains in calendar. But if the recurrent events is created by other and accepted by user and then try to delete one specific of those events, they all disappear from calendar. Why?
In this case iPad only uses one Exchange calendar, can the problem be Exchange specific?
/* Get the Exchange Calendar */
EKEventStore* store = [[EKEventStore alloc] init];
NSError* error = nil;
NSMutableArray* calendars = [[store calendarsForEntityType:EKEntityTypeEvent] mutableCopy];
NSMutableArray* cals = [[NSMutableArray alloc] init];
for (EKCalendar *cal in calendars) {
if (cal.type == EKCalendarTypeExchange) {
[cals addObject:cal];
break;
}
}
/* get the correct event, get Events with startDate & endDate and differ with eventId */
// (tappedEvent = event to remove)
NSPredicate *predicate = [store predicateForEventsWithStartDate:[tappedEvent valueForKey:#"from"] endDate:[tappedEvent valueForKey:#"to"] calendars:cals];
NSArray *theEvents=[store eventsMatchingPredicate:predicate];
NSString *recurrenceEventIdentifier = [tappedEvent valueForKey:#"appID"];
for(EKEvent * theEvent in theEvents){
if([theEvent.eventIdentifier isEqualToString: recurrenceEventIdentifier] && ![store removeEvent:theEvent span:EKSpanThisEvent error:&error]){
NSLog(#"Error in removing event: %#",error);
}
}
[store commit:&error];
if(error){
...
}
I am working on my first iPhone App: a simple app showing the heartRate results from HealthKit in a nice way. My first step is to show the results as a raw text. But unfortunately I'm getting an exception at the following line, telling me: "thread 1 signal SIGABRT". Does someone know, what I did wrong and hint me in a direction?
double usersBeatsPerMinute = [quantity doubleValueForUnit:[HKUnit countUnit]];
The rest of the code looks like this:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// Set up an HKHealthStore, asking the user for read/write permissions. The profile view controller is the
// first view controller that's shown to the user, so we'll ask for all of the desired HealthKit permissions now.
// In your own app, you should consider requesting permissions the first time a user wants to interact with
// HealthKit data.
if ([HKHealthStore isHealthDataAvailable]) {
NSSet *writeDataTypes = [self dataTypesToWrite];
NSSet *readDataTypes = [self dataTypesToRead];
[self.healthStore requestAuthorizationToShareTypes:writeDataTypes readTypes:readDataTypes completion:^(BOOL success, NSError *error) {
if (!success) {
NSLog(#"You didn't allow HealthKit to access these read/write data types. In your app, try to handle this error gracefully when a user decides not to provide access. The error was: %#. If you're using a simulator, try it on a device.", error);
return;
}
}];
}
HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
// Since we are interested in retrieving the user's latest sample
// we sort the samples in descending order by end date
// and set the limit to 1
// We are not filtering the data, and so the predicate is set to nil.
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
// construct the query & since we are not filtering the data the predicate is set to nil
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:weightType predicate:nil limit:1 sortDescriptors:#[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
// if there is a data point, dispatch to the main queue
if (results) {
dispatch_async(dispatch_get_main_queue(), ^{
HKQuantitySample *quantitySample = results.firstObject;
// pull out the quantity from the sample
HKQuantity *quantity = quantitySample.quantity;
double usersBeatsPerMinute = [quantity doubleValueForUnit:[HKUnit countUnit]];
_HeartRateResults.text = [NSString stringWithFormat:#"%# lbs", [NSNumberFormatter localizedStringFromNumber:#(usersBeatsPerMinute) numberStyle:NSNumberFormatterNoStyle]];
});
}
}];
// do not forget to execute the query after its constructed
[_healthStore executeQuery:query];}
There was a comment in the documentation ("These samples use count/time units") I didn't quite understand, so I did a little searching and tried it out and was able to get a value I manually put into the Health app using this:
double rate = [mostRecentQuantity doubleValueForUnit:[[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]];
I haven't seen unitDividedByUnit before. Here's the article I pulled it from.
I have a recurring event in calendar. I'm delete a single event using this code [store removeEvent:event span:EKSpanThisEvent commit:YES error:&errorThis]; and this methods returns true but the event is not deleted from the calendar.
On EKCalendarItem Class reference using the property calendarItemExternalIdentifier you find this
Recurring event identifiers are the same for all occurrences. If you wish to differentiate between occurrences, you may want to use the start date
So you want delete only a recurrence you have to do something like this:
NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:calendars];
NSArray *theEvents = [eventStore eventsMatchingPredicate:predicate];
NSString *recurrenceEventIdentifier;
for(EKEvent * theEvent in theEvents)
{
if([theEvent.eventIdentifier isEqualToString: recurrenceEventIdentifier]
&& ![eventStore removeEvent:theEvent span:EKSpanThisEvent error:&error])
{
NSLog(#"Error in removing event: %#",error);
}
}
Your method instead, deletes only the first occurrence. If you want delete all recurring events just change "span" parameter in EKSpanFutureEvents.
EDIT: Now only deletes the matching recurrent event, not everything.
Please make sure you have only one instance of EKEventStore in singleton pattern in your app :
static EKEventStore *eventStore = nil;
+ (EKEventStore *)getEventStoreInstance
{
if (eventStore == nil){
#synchronized(self){
if (eventStore == nil){
eventStore = [[EKEventStore alloc] init];
}
}
}
return(eventStore);
}
I am trying my hand at some very basic implementation of MagicalRecord to get the hang of it and run into the following.
When I save an entry and then fetch entries of that type it will come up with the entry I just saved. However, when I save the entry, close the app, start it again, and then fetch, it comes up empty.
Code for saving:
- (void)createTestTask{
NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];
Task *task = [Task createInContext:localContext];
task.tName = #"First Task";
task.tDescription = #"First Task created with MagicalRecord. Huzzah!";
NSError *error;
[localContext save:&error];
if (error != Nil) {
NSLog(#"%#", error.description);
}
}
Code for fetching: (all I want to know here if anything is actually saved)
- (void) fetchTasks{
NSArray *tasks = [Task findAll];
NSLog(#"Found %d tasks", [tasks count]);
}
I am sure I am missing something here, but not anything I can seem to find on stackoverflow or in the Tutorials I looked at.
Any help is welcome.
I have to ask the obvious "Is it plugged in" question: Did you initialize the Core Data Stack with one of the +[MagicalRecord setupCoreDataStack] methods?
Did your stack initialize properly? That is, is your store and model compatible? When they aren't, MagicalRecord (more appropriately, Core Data) will set up the whole stack without the Persistent Store. This is annoying because it looks like everything is fine until it cannot save to the store...because there is no store. MagicalRecord has a +[MagicalRecord currentStack] method that you can use to examine the current state of the stack. Try that in the debugger after you've set up your stack.
Assuming you did that, the other thing to check is the error log. If you use
[localContext MR_saveToPersistentStoreAndWait];
Any errors should be logged to the console. Generally when you don't see data on a subsequent run of your app, it's because data was not saved when you thought you called save. And the save, in turn, does not happen because your data did not validate correctly. A common example is if you have a required property, and it's still nil at the time you call save. "Normal" core data does not log these problems at all, so you might think it worked, when, in fact, the save operation failed. MagicalRecord, on the other hand, will capture all those errors and log them to the console at least telling you what's going on with your data.
When i have started with magical record I was also facing this problem, problem is context which you are using to save data. here is my code which might help you
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
NSArray *userInfoArray = [UserBasicInfo findByAttribute:#"userId" withValue:[NSNumber numberWithInt:loggedInUserId] inContext:localContext];
UserBasicInfo* userInfo;
if ([userInfoArray count]) {
userInfo = [userInfoArray objectAtIndex:0];
} else {
userInfo = [UserBasicInfo createInContext:localContext];
}
userInfo.activeUser = [NSNumber numberWithBool:YES];
userInfo.firstName = self.graphUser[#"first_name"];
userInfo.lastName = self.graphUser[#"last_name"];
userInfo.userId = #([jsonObject[#"UserId"] intValue]);
userInfo.networkUserId = #([jsonObject[#"NetworkUserId"] longLongValue]);
userInfo.userPoint = #([jsonObject[#"PointsEarned"] floatValue]);
userInfo.imageUrl = jsonObject[#"Picturelist"][0][#"PictureUrL"];
userInfo.imageUrlArray = [NSKeyedArchiver archivedDataWithRootObject:jsonObject[#"Picturelist"]];
} completion:^(BOOL success, NSError *error) {
}];
Use this when your done
[[NSManagedObjectContext MR_defaultContext]saveToPersistentStoreAndWait];
I'm creating a simple note application and I want to implement Reminders. The user would type a note, tap a button and it would set up a reminder in the Reminders app using the text. Is this possible, and if so, how do I do it? I have seen Apple's documentation on EventKit and EKReminders but it has been no help at all.
From the "Calendars and Reminders Programming Guide"? Specifically "Reading and Writing Reminders"
You can create reminders using the reminderWithEventStore: class method. The title and calendar properties are required. The calendar for a reminder is the list with which it is grouped.
Before you create a reminder, ask for permission:
In the .h:
#interface RemindMeViewController : UIViewController
{
EKEventStore *store;
}
and the .m, when you are going to need access to Reminders:
store = [[EKEventStore alloc] init];
[store requestAccessToEntityType:EKEntityTypeReminder
completion:^(BOOL granted, NSError *error) {
// Handle not being granted permission
}];
To actually add the reminder. This happens asynchronously, so if you try to add a reminder immediately after this, it will fail (crashes the app in my experience).
- (IBAction)addReminder:(id)sender
{
EKReminder *reminder = [EKReminder reminderWithEventStore:store];
[reminder setTitle:#"Buy Bread"];
EKCalendar *defaultReminderList = [store defaultCalendarForNewReminders];
[reminder setCalendar:defaultReminderList];
NSError *error = nil;
BOOL success = [store saveReminder:reminder
commit:YES
error:&error];
if (!success) {
NSLog(#"Error saving reminder: %#", [error localizedDescription]);
}
}