I encountered an issue trying to create a new event on iOS's Calendar application while using Swift.
This is what I have so far:
func addToCal(){
let eventStore = EKEventStore()
eventStore.requestAccessToEntityType(EKEntityTypeReminder) {
(granted: Bool, err: NSError!) in
if granted && !err {
var event:EKEvent = EKEvent(eventStore: eventStore)
event.title = self.eventTitle!.text
event.startDate = self.eventData.startDateObj
event.endDate = self.eventData.endDateObj
event.calendar = eventStore.defaultCalendarForNewEvents
eventStore.saveEvent(event, span: EKSpanThisEvent, error: nil)
println("Saved Event")
}
}
This is the error that I'm getting:
Error getting default calendar for new events: Error Domain=EKCADErrorDomain Code=1013 "The operation couldn’t be completed. (EKCADErrorDomain error 1013.)"
I checked my syntax and I'm pretty sure I got it right, so can anyone help me figure out where I'm going wrong?
Additional Information
iOS 8 Beta 5
Xcode 6 Beta 5
Issue occurs on iPad Mini w/ Retina Display (real device)
Update
Changing EKEntityTypeReminder to EKEntityTypeEvent (Sorry, my mistake) actually doesn't produce an error, but now the event doesn't even show up in Calendar. I checked the outputs of granted and err and I see false and nil, respectively.
I think your main problem was that you weren't committing the new event to be saved. The following code is what I use, and the prime difference is that my code includes a commit and allows my error to be changed from 'nil' if there is one. The conditional at the end just prints any reports of save errors or save successes to the Debug Area.
let eventStore = EKEventStore()
let event = EKEvent(eventStore: eventStore)
event.title = "Your Event Title Here" // Sets event's title
event.startDate = NSDate() // Sets event's start date
event.endDate = event.startDate.dateByAddingTimeInterval(20000) // Sets event's end date
event.calendar = eventStore.defaultCalendarForNewEvents // Selects default calendar
var saveError : NSError? = nil // Initially sets errors to nil
eventStore.saveEvent(event, span: EKSpanThisEvent, commit: true, error: &saveError) // Commits changes and allows saveEvent to change error from nil
//// Following checks for errors and prints result to Debug Area ////
if saveError != nil {
println("Saving event to Calendar failed with error: \(saveError!)")
} else {
println("Successfully saved '\(event.title)' to '\(event.calendar.title)' calendar.")
}
Hope this helps!
-Gorowski
Once access has been granted, you need to set the 'eventStore':
eventStore.requestAccess(to: .event) { (granted, error) in
if (granted) && (error == nil) {
//Set event store now access granted
eventStore = EKEventStore()
//Save calendar event here...
} else {
}
}
Related
I can detect workout started on backgroun with apple watch, with below code
let workoutevent = HKObjectType.workoutType()
if store.authorizationStatus(for: workoutevent) != HKAuthorizationStatus.notDetermined {
store.enableBackgroundDelivery(for: workoutevent, frequency: .immediate, withCompletion: { (worked, error) in
print(worked)
print(error)
print("workoutevent enableBackgroundDelivery")
guard worked else {
self.logger.error("Unable to set up background delivery from HealthKit: \(error!.localizedDescription)")
print("workoutevent unable to set up background ")
fatalError()
}
if error != nil {
print("workoutevent error is ")
print(error)
}
})
backgroundObserver3 =
HKObserverQuery(sampleType: workoutevent,
predicate: nil,
updateHandler: processUpdate3(query:completionHandler3:error:))
if let queryworkout = backgroundObserver3 {
print("Starting workoutevent333 the background observer query.\(queryworkout)")
store.execute(queryworkout)
}
}else{
print("not determined....")
}
whenever I started workout on apple watch, it goes to
processUpdate3
very well,
but what I need to know is to when user finish workout.
how can I detect it ?
func processUpdate3(query: HKObserverQuery,
completionHandler3: #escaping () -> Void,
error: Error?) {
print("come here when work out started ")
...........
}
I don't see it in your code. But somewhere you must have an HKWorkoutSession. My app is set up to track running and I configure the session to begin like so;
let configuration = HKWorkoutConfiguration()
configuration.activityType = .running
do {
// HKWorkoutSession is set up here.
session = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
workoutBuilder = session.associatedWorkoutBuilder()
} catch {
// handle errors
}
When the users taps the end workout button I call session.end()
Here is a link to the documentation.
This is my register for subscription method
public func RegisterForSubscription(completion: #escaping (Result<CKSubscription, Error>) -> Swift.Void) {
let subscription = CKQuerySubscription(recordType: Film.RecordType, predicate: NSPredicate(value: true), options: [.firesOnRecordCreation, .firesOnRecordDeletion, .firesOnRecordUpdate])
let notificationInfo = CKSubscription.NotificationInfo()
notificationInfo.shouldSendContentAvailable = true
subscription.notificationInfo = notificationInfo
database.save(subscription) { savedSubscription, error in
if let error = error {
print("Subscription error: \(error.localizedDescription)")
completion(.failure(error))
return
}
if let savedSubscription = savedSubscription {
completion(.success(savedSubscription))
return
}
}
}
Now all i care is .firesOnRecordCreation but for debugging purposes, i added all three options.
When I tried adding (save) a new record, it successfully added them to CloudKit. here's the method
public func save(film: Film, completion: #escaping (Result<CKRecord, Error>) -> Swift.Void) {
let filmRecord = film.toRecord()
let operation = CKModifyRecordsOperation(recordsToSave: [filmRecord], recordIDsToDelete: nil)
operation.qualityOfService = .userInitiated
operation.completionBlock = {
completion(.success(filmRecord))
}
database.add(operation)
}
and my .toRecord() method:
public func toRecord() -> CKRecord {
let record = self.record ?? CKRecord(recordType: Self.RecordType)
record[RecordKeys.id.rawValue] = id
record[RecordKeys.title.rawValue] = title
record[RecordKeys.filmDescription.rawValue] = filmDescription
record[RecordKeys.image.rawValue] = image
record[RecordKeys.director.rawValue] = director
record[RecordKeys.producer.rawValue] = producer
record[RecordKeys.releaseDate.rawValue] = releaseDate
record[RecordKeys.rtScore.rawValue] = rtScore
record[RecordKeys.imdbLink.rawValue] = imdbLink
record[RecordKeys.imdbScore.rawValue] = imdbScore
return record
}
Everything works. however, I wanted to register for subscription (silent notifications) to detect live changes when user add a new record (film).
The problem is, whenever i add via the method (calling the save method), it is not triggering the .firesOnRecordCreation option. However, the other two works. I updated one of the attributes via CloudKit dashboard and deleted them, it is called (on appDelegate's didReceiveRemoteNotification)
What am i doing wrong? When user added a new film from the method (not VIA dashboard) it should trigger it, but not in this case.
EDIT:
Adding via the dashboard TRIGGERS the susbcription. But NOT when using the method.
The subscription notification will not be received by the device that created, deleted or updated CKRecord.
Edit: Read here about handling subscription notifications.
You use multiple devices because a notification isn’t sent to the same device that originated the notification
I'm trying to add a calendar event to the macOS calendar from a Mac app I'm developing with Swift 5 in Xcode 11.2.1.
For starters, I have the Calendar entitlement checked in the App Sandbox.
I'm trying to prompt for permissions like this:
import EventKit
#IBAction func clickAddToCalendar(_ sender: NSButton){
let eventStore = EKEventStore()
eventStore.requestAccess(to: .event){ granted, error in
print(granted) // returns false
print("----")
print(error) // returns nil
}
}
...but granted is false every time and I never see a popup to grant permission. Is prompting for access like this necessary on macOS? It would seem so, because when I attempt to add a calendar event directly like this:
let event = EKEvent(eventStore: eventStore)
event.title = "My Event"
event.startDate = Date()
event.endDate = Date()
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
} catch let error as NSError {
print(error)
}
...I get an error:
serviceName = "com.apple.CalendarAgent.database"; ... returned error NSCocoaErrorDomain(134070) with userInfo dictionary { Problem = "request failed, insufficient permission" }
Has anyone does this that could give me some pointers? Thank you!
Do you have an info.plist item for Privacy - Calendars Usage Description?
required for EventKit see https://developer.apple.com/documentation/eventkit/accessing_the_event_store
I can save event into Calendar user
do {
try self.store.save(event, span: .thisEvent, commit: true)
self.addedEventId = event.eventIdentifier
print("\(self.addedEventId) has been added")
}
catch let error as NSError {
print("\(error.localizedDescription), userInfo: \(error.userInfo)")
}
it saved the event, and print eventIdentifier
61211649-CED1-4C74-A140-843AE0C38F44:8B7BDC14DD804F069D455E7B8F3534EF0
but when I try to delete the saved event using
if let eventToRemove = self.store.event(withIdentifier: self.addedEventId)
{
do {
try self.store.remove(eventToRemove, span: .thisEvent)
print("\(self.addedEventId) removed")
} catch let error as NSError {
print("\(error.userInfo)")
}
}
Seems I cannot find the event using eventIdentifier, the error I have from the console reads
Error getting event with identifier 61211649-CED1-4C74-A140-843AE0C38F44:8B7BDC14DD804F069D455E7B8F3534EF0: Error Domain=EKCADErrorDomain Code=1010 "(null)
After half day research, I finally found a way around is instead of using eventIdentifier, using calenderItemIdentifier
Trying to save logged in parse user's value, it only works for the first time but when i close the app and reopen it, it doesn't work again.
This is the save code I'm using which seems alright
PFUser.current()["about"] = textfield.text
PFUser.current().saveInBackground()
and this is the error i get when trying to save the objects to current user.
PFKeychainStore failed to set object for key 'currentUser', with error: -34018
or
cannot modify user objectIDxx
This started happening after i installed parse server instead of parse.com
Were you using "revocable sessions" before? If not, parse-server requires you to use them. You can check out the migration tutorial here.
You'll need to add this after you initialize parse:
[PFUser enableRevocableSessionInBackground]
And then you will need to re-login a user if you get an 'invalid session' error from parse.
// Swift
class ParseErrorHandlingController {
class func handleParseError(error: NSError) {
if error.domain != PFParseErrorDomain {
return
}
switch (error.code) {
case kPFErrorInvalidSessionToken:
handleInvalidSessionTokenError()
... // Other Parse API Errors that you want to explicitly handle.
}
private class func handleInvalidSessionTokenError() {
//--------------------------------------
// Option 1: Show a message asking the user to log out and log back in.
//--------------------------------------
// If the user needs to finish what they were doing, they have the opportunity to do so.
//
// let alertView = UIAlertView(
// title: "Invalid Session",
// message: "Session is no longer valid, please log out and log in again.",
// delegate: nil,
// cancelButtonTitle: "Not Now",
// otherButtonTitles: "OK"
// )
// alertView.show()
//--------------------------------------
// Option #2: Show login screen so user can re-authenticate.
//--------------------------------------
// You may want this if the logout button is inaccessible in the UI.
//
// let presentingViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
// let logInViewController = PFLogInViewController()
// presentingViewController?.presentViewController(logInViewController, animated: true, completion: nil)
}
}
// In all API requests, call the global error handler, e.g.
let query = PFQuery(className: "Object")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// Query Succeeded - continue your app logic here.
} else {
// Query Failed - handle an error.
ParseErrorHandlingController.handleParseError(error)
}
}