When saving an event in the regular calendar, the events are sometimes not saved. An error is displayed: "failed to save event with error: Error Domain = EKErrorDomain Code = 2" No start date has been set. "UserInfo = {NSLocalizedDescription = No start date has been set.}", Although the date is determined correctly
func saveTaskInCalendar() {
let eventStore : EKEventStore = EKEventStore()
eventStore.requestAccess(to: .event) { (granted, error) in
if (granted) && (error == nil) {
let event: EKEvent = EKEvent(eventStore: eventStore)
DispatchQueue.main.async {
event.title = "Test"
event.startDate = Date()
event.endDate = Date()
}
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
} catch let error as NSError {
print("failed to save event with error : \(error)")
}
}
else {
print("failed to save event with error : \(String(describing: error)) or access not granted")
}
}
}
How do I fix this error?
The problem is that you wrap setting the properties of the EKEvent in a dispatch async block, while you save the even outside the async block. This can cause the property setting to be executed after you've already tried saving the event.
Either remove the DispatchQueue.main.async call, since it shouldn't be necessary anyways or put the saving code inside the dispatch async as well.
let event: EKEvent = EKEvent(eventStore: eventStore)
event.title = "Test"
event.startDate = Date()
event.endDate = Date()
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
}
or
DispatchQueue.main.async {
let event: EKEvent = EKEvent(eventStore: eventStore)
event.title = "Test"
event.startDate = Date()
event.endDate = Date()
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
}
...
Related
Issue here is that while from API I get date in this form
Foundation.Date 2021-07-20 00:00:00 UTC
I successfully add hours, with I get start hour from API in form of "08:30:00" with gives me the value for start (but same issue about the end date) this way:
Foundation.Date 2021-07-20 08:30:00 UTC
but, when I get to calendar, the events are set to 10:30 AM, how could I correct the TIME ZONE?
I tested things like
let calendar = Calendar()
calendar.timezone = .current
but did not work
example from response
{
"data": {
"appointments": [
{
"date": "2021-07-20T00:00:00.000Z",
"time": {
"from": "08:30:00",
"to": "09:30:00"
}
}
]
}
}
my code
func onSynchronize(detailInfo: AppointmentDetail) {
let appointment = detailInfo
let fakeAddress = "fake address"
let fakeLocality = "Alderaan"
let store = EKEventStore()
store.requestAccess(to: .event) {(granted, error) in
if !granted { return }
let event = EKEvent(eventStore: store)
event.title = "my title"
guard let date0 = appointment.date else { return }
guard let hours = appointment.time else { return }
guard let host = appointment.host else { return }
if let componentsFrom = self.timeComponentsFromHourInString(from: hours.from) {
if let start = Calendar.current.date(byAdding: componentsFrom, to: date0, wrappingComponents: false) {
event.startDate = start
event.addAlarm(EKAlarm(absoluteDate: start.dayBefore))
event.addAlarm(EKAlarm(absoluteDate: start.halfHourBefore))
event.endDate = start.hourAfter
}
}
if let componentsTo = self.timeComponentsFromHourInString(from: hours.to) {
if let end = Calendar.current.date(byAdding: componentsTo, to: date0, wrappingComponents: false) {
event.endDate = end
}
}
event.location = fakeAddress.concats(fakeLocality, withSeparator: "-")
event.url = URL(string: BanksUtils.appLocalizeString(forKey: "url"))
if let managerDescription = host.managerDescription,
let manager = BanksUtils.localizeString(forKey: "handledBy").concats(managerDescription.capitalized),
let description = appointment.requestorDescription,
let reason = BanksUtils.localizeString(forKey: "reason").concats(description.capitalized) {
event.notes = manager + "\n" + reason
} else {
if let description = appointment.requestorDescription {
event.notes = BanksUtils.localizeString(forKey: "reason").concats(description.capitalized)
}
}
let structuredLocation = EKStructuredLocation(title: fakeAddress)
structuredLocation.geoLocation = self.location
event.structuredLocation = structuredLocation
event.calendar = store.defaultCalendarForNewEvents
do {
try store.save(event, span: .thisEvent, commit: true)
} catch {
Log.logger.error("Error: \(String(describing: error))")
}
guard let url = URL(string: "calshow:\(event.startDate.timeIntervalSinceReferenceDate)") else { return }
DispatchQueue.main.async {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
///Allows to get HH:MM:SS from an hour in form of a string
func timeComponentsFromHourInString(from string: String) -> DateComponents? {
let comps = string.components(separatedBy: ":")
guard comps.count == 3,
let hr = Int(comps[0]),
let mn = Int(comps[1]),
let sc = Int(comps[2])
else { return nil }
return DateComponents(hour: hr, minute: mn, second: sc)
}
I'm developing an OS X app with Swift.
If pressing the Add button and call the function "saveEvent()" on the screen for adding an event to the calendar, the error "unrecognized selector sent to instance" will appear.
It stops at eventStore.save and the event is not added.
How can I add an event?
import Cocoa
import EventKit
class ViewController: NSViewController {
let eventStore:EKEventStore = EKEventStore()
override func viewDidLoad() {
super.viewDidLoad()
let status = EKEventStore.authorizationStatus(for: EKEntityType.event)
if status == .authorized {
print("success")
}else if status == .notDetermined {
eventStore.requestAccess(to: EKEntityType.event) { (granted, error) in
if granted {
print("true")
}else {
print("false")
}
}
}
saveEvent()
}
func saveEvent() {
if EKEventStore.authorizationStatus(for: .event) == .authorized{
let event = EKEvent(eventStore: eventStore)
event.title = "sample"
event.startDate = Date()
event.endDate = Date().addingTimeInterval(2 * 60 * 60)
event.isAllDay = false
event.calendar = self.eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
}
catch {
print("Save is failed.")
}
}
}
}
I think what you can do is:
eventStore.requestAccess(to: .event, completion: { (granted, error) in
if (granted) && (error == nil) {
let event = EKEvent(eventStore: eventStore)
event.title = title
event.startDate = startDate
event.endDate = endDate
event.notes = note
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
} catch let e as NSError {
NSLog(e as! String)
return
}
}
})
I am trying to remove an event I saved in the calendar with an event id but its removing a different event and sometimes it doesn't remove anything.
I am saving the eventId in a realm database when event is created and reading it back when I want to delete but its not working.
I have tried running it on an actual device, using an array instead of a dictionary, changing the span to .futureEvents but still doesn't work
my code for creating event and saving to realm database
/// function exits in another class
func addEventToCalendar(userName: String, userDate: Date) {
let userDefaults = UserDefaults.standard
let eventStore: EKEventStore = EKEventStore()
eventStore.requestAccess(to: .event) { (granted, error) in
if (granted) && (error == nil) {
print("granted \(granted)")
print("error \(String(describing: error))")
let event: EKEvent = EKEvent(eventStore: eventStore)
event.title = "\(userName) Birthday"
event.startDate = userDate
event.endDate = userDate
event.notes = "Happy Birthday!"
event.isAllDay = true
event.calendar = eventStore.defaultCalendarForNewEvents
let ekrules: EKRecurrenceRule = EKRecurrenceRule.init(recurrenceWith: .yearly, interval: 1, end: nil)
event.recurrenceRules = [ekrules]
//event.addAlarm(EKAlarm(absoluteDate: event.startDate))
//sets alert 00:00 on day of event
event.addAlarm(EKAlarm(relativeOffset: 0))
do {
try eventStore.save(event, span: .thisEvent, commit: true)
} catch let error as NSError {
print("error: \(error)")
}
let eventId = event.eventIdentifier ?? "nil-id"
userDefaults.setValue(eventId, forKey: "eventId")
print(eventId)
} else {
print("error not granted: \(String(describing: error))")
}
}
}
//saving it in a view controller class
#IBAction func okBtnPressed(_ sender: UIButton) {
let eventId = UserDefaults.standard.string(forKey: "eventId") ?? "no-id"
//// saving data to device
let newItem = Item()
newItem.userImageName = String(describing: userImageUrl)
newItem.userName = uName
newItem.isYearPresent = uYearPresent
newItem.userDOB = uDOB
newItem.color = UIColor.init(randomFlatColorOf: .dark).hexValue()
newItem.daysRemaining = daysRemain
newItem.eventId = eventId
self.save(item: newItem)
}
The event id saves succesfully in the realm database.
function for removing the event from calendar
func removeEvent(id: String) {
let store = EKEventStore()
store.requestAccess(to: .event) { (granted, error) in
if !granted { return }
// checking if event exists
if let eventToRemove = store.event(withIdentifier: id) {
do {
print("removing: \(id)")
try store.remove(eventToRemove, span: .thisEvent, commit: true)
print("event removed sucessfully")
} catch let error as NSError {
print("error: \(error)")
}
} else {
print("event doesnt exist.")
}
}
}
This is how I remove it
var eventIDS = [Int: String]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// inserting evenIds to dictionary to access it in remove function
// I used an array but it gave me the same problem
if let item = itemsObject?[indexPath.row] {
eventIDS[indexPath.row] = item.eventId
}
}
// then I call remove function when swipe taps on cell
removeEvent(id: self.eventIDS[indexPath.row] ?? "")
Sometimes I get event removed successfully but it removes a different
event, sometimes I get the following errors
Error getting event with identifier 2BD633CA-BBEA-47CD-8410-40BCE6362A5C:98D9EAF2-D5EF-420F-B769-7F02B7795E54: Error Domain=EKCADErrorDomain Code=1010 "(null)"
event doesnt exist.
I figured it out. Because eventStore.requestAccess(to: .event) is asynchronous, I was saving the event id in the database before the id existed.
So I had to declare the function to accept a completion handler and return the value inside the completion handler.
//// adding events to calendar
func addEventToCalendar(userName: String, userDate: Date, completion: #escaping (String?)->()) {
let userDefaults = UserDefaults.standard
var eventId = ""
let eventStore: EKEventStore = EKEventStore()
eventStore.requestAccess(to: .event) { (granted, error) in
if (granted) && (error == nil) {
print("granted \(granted)")
print("error \(String(describing: error))")
let event: EKEvent = EKEvent(eventStore: eventStore)
event.title = "\(userName) \(NSLocalizedString("birthday", comment: "birthday"))"
event.startDate = userDate
event.endDate = userDate
event.notes = NSLocalizedString("happyBirthday", comment: "happyBirthday")
event.isAllDay = true
event.calendar = eventStore.defaultCalendarForNewEvents
let ekrules: EKRecurrenceRule = EKRecurrenceRule.init(recurrenceWith: .yearly, interval: 1, end: nil)
event.recurrenceRules = [ekrules]
//event.addAlarm(EKAlarm(absoluteDate: event.startDate))
//sets alert 00:00 on day of event
event.addAlarm(EKAlarm(relativeOffset: 0))
do {
try eventStore.save(event, span: .futureEvents, commit: true)
eventId = event.eventIdentifier ?? "no-Id"
print("Event has been saved with id \(String(describing: eventId))")
userDefaults.setValue(eventId, forKey: "eventId")
} catch let error as NSError {
print("error: \(error)")
}
completion(eventId)
} else {
print("error not granted: \(String(describing: error))")
completion(nil)
}
}
}
and then use it like so
addEventToCalendar(userName: uName, userDate: uDate) { (eventIdentifier) in
if let eventId = eventIdentifier {
print("Event add birthday id \(eventId)")
//// saving data to device
// run on main thread to avoid 'RLMException', reason: 'Realm accessed from incorrect thread.'
DispatchQueue.main.async {
let newItem = Item()
newItem.userImageName = String(describing: self.userImageUrl)
newItem.userName = uName
newItem.isYearPresent = uYearPresent
newItem.userDOB = uDOB
newItem.color = UIColor.init(randomFlatColorOf: .dark).hexValue()
newItem.daysRemaining = daysRemain
newItem.eventId = eventId
self.save(item: newItem)
// review app
self.review()
}
func editFutureEventToCalendar(id:String,title: String, description: String?,alarams :[EKAlarm],location:String,contacts:[GenParticipants],repeatString :String,timeZone:NSTimeZone, startDate: NSDate, endDate: NSDate , isAllDay :Bool,calendar:EKCalendar,StruLocation:EKStructuredLocation?)->Bool {
let eventStore = EventsManager.getEventStore()
let event = EventsManager.geteventID(id: id)
let ocdate = event.occurrenceDate
event.title = title
event.startDate = startDate as Date
event.endDate = endDate as Date
event.notes = description
event.calendar = calendar
// event.occurrenceDate = ocdate
event.timeZone = timeZone as TimeZone //timezoneNSTimeZone() as TimeZone //timeZone as TimeZone
event.isAllDay = isAllDay
if StruLocation != nil {
event.structuredLocation = StruLocation
}else {
event.structuredLocation = StruLocation
}
if repeatString != "None" {
if event.hasRecurrenceRules {
let rrrl = event.recurrenceRules
for i in rrrl! {
event.removeRecurrenceRule(i)
}
}
let rule = EventsManager.getRepeatValue(repeatString)//self.getRepeatValue(option: repeat)
event.addRecurrenceRule(rule!)
}else if repeatString == "None"{
if event.hasRecurrenceRules {
let rrrl = event.recurrenceRules
for i in rrrl! {
event.removeRecurrenceRule(i)
}
}
}
for item in alarams {
event.addAlarm(item)
}
// event.location = location
// let formatter = CNContactFormatter()
// formatter.style =
var attendees = [EKParticipant]()
// for item in contacts {
//
// let con = event.(contact: item)
// attendees.append(con!)
//
// }
do {
try eventStore.save(event, span: .futureEvents, commit: true)
//try eventStore.save(event, span: .thisEvent)
return true
} catch let e as NSError {
EZAlertController.alert(e.debugDescription)
}
return false
}
I have this function for editing all the future events in repeat series - if i edit the event in between the series of events its deleting all the previous events and editing future events .
i want it to edit all the events in series .
I am using eventKit not eventKitUI . i dont want eventkitUI.
How to edit this function to do the same or do i have to fetch the event differently as the identifier are same for all the events in repeating series?
i am using the great code from :
func addEventToCalendar(title: String, description: String?, startDate: NSDate, endDate: NSDate, completion: ((success: Bool, error: NSError?) -> Void)? = nil) {
let eventStore = EKEventStore()
eventStore.requestAccessToEntityType(.Event, completion: { (granted, error) in
if (granted) && (error == nil) {
let event = EKEvent(eventStore: eventStore)
event.title = title
event.allDay = true
event.startDate = startDate
event.endDate = endDate
event.notes = description
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.saveEvent(event, span: .ThisEvent)
} catch let e as NSError {
completion?(success: false, error: e)
return
}
completion?(success: true, error: nil)
} else {
completion?(success: false, error: error)
}
})
}
but how can i check if the event already exist?
For example you can check event on your calendar with the same settings as your event. You can use NSPredicate for this:
let startDate = yourStartDate
let endDate = yourEndDate
let predicate = eventStore.predicateForEventsWithStartDate(startDate, endDate: endDate, calendars: nil)
let existingEvents = eventStore.eventsMatchingPredicate(predicate)
for singleEvent in existingEvents {
if singleEvent.title == yourEventTitle && singleEvent.startDate == yourStartDate {
// Event exist
...
}
}