8

I am using the EKEventStore in my app. I grab the default store and register for the EKEventStoreChangedNotification to be notified when changes are made to the calendar. However, when a change is made, the sender for the notification gets called several (5-10) times, sometimes with up to 15 seconds in between each call. This messes up my code and makes things much more difficult to work with. Is there anything I can do about this?

Thanks

iOS7 EDIT: It seems like as of the release of iOS7, this problem has disappeared. Now, regardless of the change made to the CalendarStore, only one EKEventStoreChangedNotification gets sent.

Kyle Rosenbluth
  • 1,672
  • 4
  • 22
  • 38

2 Answers2

17

This is a result of exactly how the notifications are dispatched and what each is actually notifying about. In my experience, you can expect to receive at least one notification for a change to an item (event, reminder, etc.) and at least one more for the resultant change to the containing calendar for that item.

Without seeing your code and knowing what changes are being made, I can't be too specific about an answer; however, in general, you have two options.

  1. Observe the changes more closely - maybe you can safely ignore some notifications if they concern events that don't pertain to your app, or if you've already handled a change for a particular item.
  2. Coalesce multiple changes into a single batch of handler code. Basically, when you receive the notification, instead of kicking off your response right away, start a timer that will run the response in a second or two. Then, if another notification comes in before the timer fires, you can cancel the timer and reset it. This way, you can batch up multiple notifications that come in a short time window and only respond to them once (when the timer eventually fires).

The latter solution is my preferred answer, and might look something like (temporarily ignoring threading concerns):

@property (strong) NSTimer *handlerTimer;

- (void)handleNotification:(NSNotification *)note {
    // This is the function that gets called on EKEventStoreChangedNotifications
    [self.handlerTimer invalidate];
    self.handlerTimer = [NSTimer timerWithTimeInterval:2.0
                                                target:self
                                              selector:@selector(respond)
                                              userInfo:nil
                                               repeats:NO];
    [[NSRunLoop mainRunLoop] addTimer:self.handlerTimer
                              forMode:NSDefaultRunLoopMode];
}

- (void)respond {
    [self.handlerTimer invalidate];
    NSLog(@"Here's where you actually respond to the event changes");
}
Tim
  • 59,527
  • 19
  • 156
  • 165
  • 1
    Just a quick thank you, and a feedback: with iOS 8.4.1 the behavior you noticed (at least 2 notifications fired for every change) is still very true and your n. 2 solution worked wonderfully for me; I'm not sure the solution n. 1 could be applied to EKEventStoreChangedNotification, though, since these notifications don't describe what changes occurred to the EKEventStore. So, solution n. 2 is the way to go! – cdf1982 Aug 21 '15 at 10:08
  • I have the same problem on iOS9 – Mohamed Saleh Nov 21 '15 at 20:42
0

I had this issue as well. After some debugging I realised that I was causing the additional EKEventStoreChangedNotification calls (for example, by creating or removing EKReminders). The EKEventStoreChangedNotification eventually gets triggered when I do an estore.commit().

I fixed this by declaring global variable like so:

// the estore.commit in another function causes a new EKEventStoreChangedNotification. In such cases I will set the ignoreEKEventStoreChangedNotification to true so that I DON'T trigger AGAIN some of the other actions.
var ignoreEKEventStoreChangedNotification = false 

And then doing this in AppDelegate.swift where I listen to EKEventStoreChangedNotification:

nc.addObserver(forName: NSNotification.Name(rawValue: "EKEventStoreChangedNotification"), object: estore, queue: updateQueue) {
            notification in

        if ignoreEKEventStoreChangedNotification {
            ignoreEKEventStoreChangedNotification = false
            return
        }

        // Rest of your code here.

}

In the function where I make changes to the estore I do this:

//
// lots of changes to estore here...
//
ignoreEKEventStoreChangedNotification = true        // the estore.commit causes a new EKEventStoreChangedNotification because I made changes. Ignore this EKEventStoreChangedNotification - I know that changes happened, I caused them!
try estore.commit()

It's not pretty with the global variable (especially if you're into functional programming) but it works.

Daniel
  • 3,758
  • 3
  • 22
  • 43