Reputation: 5168
I have a very odd bug in my app. Attempting to save an event using saveEvent
causes the app to continue in one of 3 ways:
unrecognized selector sent to instance
error, where the offending selector is constraints:
and the object to which it's sent is always different and rather unpredictable (they are almost always private SDK classes)EXC_BAD_ACCESS
errorIn trying to debug this, I've stripped the app to just the view controller listing the events, with a button to add a new one. The first time I present the view controller to add an event, everything goes smoothly, but the second time I do this, it throws an error.
Here is the code I use:
self.event = EKEvent(eventStore: self.eventStore!)
self.event!.calendar = self.calendar!
self.event!.startDate = self.defaultStartDate()
self.event!.endDate = self.event!.startDate.dateByAddingTimeInterval(3600)
var error: NSError?
self.eventStore!.saveEvent(self.event!, span:EKSpanThisEvent, error: &error)
if let e = error {
println("Saving error: \(error)")
}
If the values for calendar
, startDate
or endDate
are invalid, I get a descriptive error with no crash, but here it crashes at the self.eventStore!.saveEvent()
. Any help is appreciated!
Edit
Turns out it was due to an extraneous call to self.eventStore.reset()
.
Upvotes: 1
Views: 1537
Reputation: 21520
After a long search I find the solution.
You have to save your events in background embedding code on a dispatch_async block.
enum UWCalendarError: Int {
case AlreadyExists
case Generic
case NotGranted
}
class Calendar {
static func saveEvent(title: String, startDate: NSDate, duration: NSTimeInterval, completion: (success: Bool, error: UWCalendarError?) -> Void) {
if Calendar.isEventAlreadyScheduled(title, startDate: startDate, duration: duration) {
completion(success: false, error: .AlreadyExists)
} else {
dispatch_async(dispatch_get_main_queue(),{
let store = EKEventStore()
store.requestAccessToEntityType(EKEntityTypeEvent) {(granted, error) in
if !granted {
completion(success: false, error: .NotGranted)
}
var event = EKEvent(eventStore: store)
event.title = title
event.startDate = startDate
event.endDate = event.startDate.dateByAddingTimeInterval(duration)
event.calendar = store.defaultCalendarForNewEvents
var err: NSError?
store.saveEvent(event, span: EKSpanThisEvent, commit: true, error: &err)
if err == nil {
completion(success: true, error: nil)
} else {
completion(success: false, error: .Generic)
}
}
})
}
}
static func isEventAlreadyScheduled(title: String, startDate: NSDate, duration: NSTimeInterval) -> Bool {
let endDate = startDate.dateByAddingTimeInterval(duration)
let eventStore = EKEventStore()
let predicate = eventStore.predicateForEventsWithStartDate(startDate, endDate: endDate, calendars: nil)
let events = eventStore.eventsMatchingPredicate(predicate)
if events == nil {
return false
}
for eventToCheck in events {
if eventToCheck.title == title {
return true
}
}
return false
}
}
Upvotes: 1