I have added events to calendar using EventKit and I can fetch all events from calendar, but I need only my app's event from calendar. So now my question is, how can I fetch events which are related to my app.
func getEvents() -> [EKEvent] {
var allEvents: [EKEvent] = []
// calendars
let calendars = self.eventStore.calendars(for: .event)
// iterate over all selected calendars
for (_, calendar) in calendars.enumerated() where isCalendarSelected(calendar.calendarIdentifier) {
// predicate for today (start to end)
let predicate = self.eventStore.predicateForEvents(withStart: self.initialDates.first!, end: self.initialDates.last!, calendars: [calendar])
let matchingEvents = self.eventStore.events(matching: predicate)
// iterate through events
for event in matchingEvents {
allEvents.append(event)
}
}
return allEvents
}
this code will gvie you all events and then you have to filter it by title or something which can be identify that this event is from your app :)
Related
A new stored property created for exisiting class using extension. While getting value its always returning nil value. The below code is what i tried to add new stored property.
var IdentifiableIdKey = "kIdentifiableIdKey"
extension EKEvent {
public var customId: Int {
get {
return (objc_getAssociatedObject(self, &IdentifiableIdKey) as? Int) ?? 0
}
set {
print("\(newValue)")
objc_setAssociatedObject(self, &IdentifiableIdKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Utilisation
let eventStore = EKEventStore()
let calendars = eventStore.calendars(for: .event)
for calendar in calendars {
if calendar.title == "Events" {
let oneMonthAgo = NSDate(timeIntervalSinceNow: -30*24*3600)
let oneMonthAfter = NSDate(timeIntervalSinceNow: +30*24*3600)
let predicate = eventStore.predicateForEvents(withStart: oneMonthAgo as Date, end: oneMonthAfter as Date, calendars: [calendar])
let events = eventStore.events(matching: predicate)
for event in events {
print("evnet id \(event.customId)")
}
}
}
Somebody help me to find the mistake I did. Thanks in advance.
The fact is that objc_getAssociatedObject/objc_setAssociatedObject assign a value to an object instance so it can be alive with this instance only. Since your custom property is not serialised by EventKit it's naturally that new obtained instances of events after the request have no associated objects.
Extensions in Swift can add computed instance properties and computed type properties only https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
It's called objc_setAssociatedObject. See the "Object" at the end? An Int is not an object. Turn your newValue into an NSNumber so you have an object. So that is definitely not going to work.
If your class is not an #objc class that might be a problem.
I've created a function in swift that checks through all the Calendars currently saved on an iOS device, then creates a new one if a calendar with the specific title "TESTCAL" (in this case) doesn't exist.
func checkCal(){
print("checkCal")
let calendars = eventStore.calendars(for: EKEntityType.event) as [EKCalendar]
var exists = false
for calendar in calendars {
if calendar.title == "TESTCAL"{
exists = true
print("FOUND CAL: TESTCAL")
print(calendar.title)
print(calendar.calendarIdentifier)
self.calIdent = calendar.calendarIdentifier
} else{
print("NO CAL: ", calendar.title)
}
}
if exists == false {
let newCal = EKCalendar(for: EKEntityType.event, eventStore: eventStore)
newCal.title = "TESTCAL"
newCal.source = eventStore.defaultCalendarForNewEvents?.source
_ = try? eventStore.saveCalendar(newCal, commit: true)
print("CAL CREATED: ", newCal.title)
print("With Ident: ", newCal.calendarIdentifier)
self.calIdent = newCal.calendarIdentifier
}
addToCal()
}
Then when I come to add the event later, in the following way
func addToCal()
{
let eventVC = EKEventEditViewController()
eventVC.editViewDelegate = self
eventVC.eventStore = EKEventStore()
let date = self.passedDate
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yy"
let dateForCal = dateFormatter.date(from: date)
let event = EKEvent(eventStore: eventVC.eventStore)
event.title = self.passedTitle
event.notes = self.passedDetail
event.startDate = dateForCal
event.endDate = dateForCal
event.isAllDay = true
event.url = URL(string: passedWebLink) }
The event gets added to the iOS default calendar.
I've tried adding the following code to the addCal() function:
event.calendar = eventStore.calendar(withIdentifier: self.calIdent)
and
let calendars = eventStore.calendars(for: EKEntityType.event) as [EKCalendar]
for calendar in calendars {
if calendar.title == "TESTCAL"{
event.calendar = calendar
print("YES")
} else{
print("NO")
}
}
Both of these give me error message in the AppDelegate and cause a crash:
[EventKit] Error getting shared calendar invitaions for entity types 3 from daemon: Error Domain=EKCADErrorDomain Code 1014 "(null)
Any ideas?
I see more possible causes for the issue you are experiencing:
The TESTCAL calendar is not created successfully. You can validate the calendar creation by catching the possible error thrown on saving the calendar:
do {
try eventStore.saveCalendar(newCal, commit: true)
} catch {
print(error)
}
How the event saving is happening is not clear from the code you posted.
If you are presenting the EKEventEditViewController instance for the user to
complete the event creation, you are missing setting the event on the view
controller in the addToCal method:
eventVC.event = event
If you want to save the event directly, then you don't need to use
EKEventEditViewController, but instead call save on the eventStore:
do {
try eventStore.save(event, span: .thisEvent)
} catch {
print(error)
}
For shared calendars, an additional privacy key for contacts accessing is needed in the .plist file of your target, namely NSContactsUsageDescription. Without this key, accessing shared calendars causes a crash.
With everything from the above checked/taken care of, your solution of setting the calendar on the event should work:
event.calendar = eventStore.calendar(withIdentifier: self.calIdent)
Also, make sure to have in place the code for requesting event kit store access from the user before any calendar/event manipulation. For more info on requesting access, check https://developer.apple.com/documentation/eventkit/ekeventstore/1507547-requestaccess
I am kinda new to Swift, but really interested.
I would like to get all the events I stored in a Calendar called "Work" and show them in a tableView.
I was looking for questions like this, but the code shown there seems to be kinda old and not really working. How do I do that? The tableView should be able to show the Title, start and end-Date. Is it possible to get like all Titles in an Array of Strings. Same with the start and end?
Would be awesome to get some tips!
Update:
I declared the variables outside the class.
Now I tried a code that looks like this, thanks to an answer I got here, but I don't get the cells to display anything?! And Yes I already created a testEvent in my Work calendar on the simulator.
override func viewDidAppear(animated: Bool) {
let eventStore = EKEventStore()
switch EKEventStore.authorizationStatusForEntityType(.Event) {
case .Authorized:
readEvents()
case .Denied:
print("Access denied")
case .NotDetermined:
eventStore.requestAccessToEntityType(.Event, completion: { (granted: Bool, NSError) -> Void in
if granted {
self.readEvents()
}else{
print("Access denied")
}
})
default:
print("Case Default")
}
self.tableView.reloadData()
}
func readEvents() {
let eventStore = EKEventStore()
let calendars = eventStore.calendarsForEntityType(.Event)
for calendar in calendars {
if calendar.source.title == "Work" {
let oneMonthAgo = NSDate(timeIntervalSinceNow: -30*24*3600)
let oneMonthAfter = NSDate(timeIntervalSinceNow: +30*24*3600)
let predicate = eventStore.predicateForEventsWithStartDate(oneMonthAgo, endDate: oneMonthAfter, calendars: [calendar])
var events = eventStore.eventsMatchingPredicate(predicate)
for event in events {
titles.append(event.title)
startDates.append(event.startDate)
endDates.append(event.endDate)
}
}
}
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return titles.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel!.text = titles[indexPath.row]
cell.detailTextLabel!.text = "From: \(startDates[indexPath.row]) Until: \(endDates[indexPath.row])"
// Configure the cell...
return cell
}
You can try something like this
import EventKit
var titles : [String] = []
var startDates : [Date] = []
var endDates : [Date] = []
var store = EKEventStore()
let calendars = store.calendars(for: .event)
for calendar in calendars {
if calendar.title == "Work" {
let oneMonthAgo = Date(timeIntervalSinceNow: -30*24*3600)
let oneMonthAfter = Date(timeIntervalSinceNow: 30*24*3600)
let predicate = store.predicateForEvents(withStart: oneMonthAgo, end: oneMonthAfter, calendars: [calendar])
let events = store.events(matching: predicate)
for event in events {
titles.append(event.title)
startDates.append(event.startDate)
endDates.append(event.endDate)
}
}
}
(Updated with Swift 5 APIs)
func fetchEventsFromCalendar() -> Void {
let status = EKEventStore.authorizationStatus(for: EKEntityType.event)
switch (status) {
case .notDetermined:
requestAccessToCalendar()
case .authorized:
self.fetchEventsFromCalendar(calendarTitle: "Calendar")
break
case .restricted, .denied: break
}
}
func requestAccessToCalendar() {
eventStore.requestAccess(to: EKEntityType.event) { (accessGranted, error) in
self.fetchEventsFromCalendar(calendarTitle: "Calendar")
}
}
// MARK: Fetech Events from Calendar
func fetchEventsFromCalendar(calendarTitle: String) -> Void {
//PGAEventsCalendar
for calendar:EKCalendar in calendars! {
if calendar.title == calendarTitle {
let selectedCalendar = calendar
let startDate = NSDate(timeIntervalSinceNow: -60*60*24*180)
let endDate = NSDate(timeIntervalSinceNow: 60*60*24*180)
let predicate = eventStore.predicateForEvents(withStart: startDate as Date, end: endDate as Date, calendars: [selectedCalendar])
addedEvents = eventStore.events(matching: predicate) as [EKEvent]
print("addedEvents : \(addedEvents)")
}
}
}
Make sure to give the proper title of the calendar, because different calendars have different list of events, In my case calendar with title "Calendar" was having the required events list, before giving check for this:
if calendar.title == calendarTitle {
My first list was getting overrides by list from other calendar, so I used to get only few events which were not relevant to me.
Swift 3.0
let eventStore = EKEventStore()
let calendars = eventStore.calendars(for: .event)
for calendar in calendars {
if calendar.title == "Work" {
let oneMonthAgo = NSDate(timeIntervalSinceNow: -30*24*3600)
let oneMonthAfter = NSDate(timeIntervalSinceNow: +30*24*3600)
let predicate = eventStore.predicateForEvents(withStart: oneMonthAgo as Date, end: oneMonthAfter as Date, calendars: [calendar])
let events = eventStore.events(matching: predicate)
for event in events {
titles.append(event.title)
startDates.append(event.startDate as NSDate)
endDates.append(event.endDate as NSDate)
}
}
}
Active calories, stand hours and workouts are saved in healthkit, but it seems that exercise data is stored only in Activity app but not in healthkit. Are there any way to access this information?
As of iOS 9.3, you can read each of the activity rings via the new HKActivitySummaryQuery which will return an HKActivitySummary object containing details of each ring. The sample code from Apple is as follows:
// Create the date components for the predicate
guard let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) else {
fatalError("*** This should never fail. ***")
}
let endDate = NSDate()
guard let startDate = calendar.dateByAddingUnit(.Day, value: -7, toDate: endDate, options: []) else {
fatalError("*** unable to calculate the start date ***")
}
let startDateComponents = calendar.components(units, fromDate: startDate)
startDateComponents.calendar = calendar
let endDateComponents = calendar.components(units, fromDate:endDate)
endDateComponents.calendar = calendar
let startDateComponents = calendar.components(units, fromDate: startDate)
// Create the predicate for the query
let summariesWithinRange = HKQuery.predicateForActivitySummariesBetweenStartDateComponents(startDateComponents, endDateComponents: endDateComponents)
// Build the query
let query = HKActivitySummaryQuery(predicate: summariesWithinRange) { (query, summaries, error) -> Void in
guard let activitySummaries = summaries else {
guard let queryError = error else {
fatalError("*** Did not return a valid error object. ***")
}
// Handle the error here...
return
}
// Do something with the summaries here...
if let summary = summaries?.first {
NSLog("Exercise: \(summary.appleExerciseTime)")
}
}
// Run the query
store.executeQuery(query)
The piece you'll be interested in is the appleExerciseTime property of the HKActivitySummary.
Please note that this code doesn't include the authorisation request you'll need to make to be able to read the activity summary which is of HKObjectType.activitySummaryType(). It is not possible to write HKActivitySummary to HealthKit so the app will crash if you request write permissions.
The green Exercise ring data is unfortunately not accessible to apps. You should file a radar with Apple to ask for it to be exposed by HealthKit.
I use the following function to retrieve my calendar:
func retrieveCalendar() -> EKCalendar? {
appDelegate = UIApplication.sharedApplication().delegate
as? AppDelegate
var myCalendar: EKCalendar?
let calendars = appDelegate!.eventStore!.calendarsForEntityType(EKEntityTypeReminder) as! [EKCalendar]
let filteredCalendars = calendars.filter {$0.title == "MedicalCalendar"}
if filteredCalendars.isEmpty {
println("could not find reminder calendar 'MedicalCalendar'")
return nil
} else {
myCalendar = filteredCalendars[0]
return myCalendar!
}
}
However, anytime I add new events to the calendar I'd like to check if they already exist there. I figured out that the easiest approach would be to delete all reminders and load new ones again. I tried:
self.retrieveCalendar()?.reset()
But it does not work. How can I remove reminders from calendar?(either one at a time or all of them at once)
To check reminders you have to call the method fetchRemindersMatchingPredicate() in conjunction with predicateForRemindersInCalendars or predicateForIncompleteRemindersWithDueDateStarting:ending:calendars: or predicateForCompletedRemindersWithCompletionDateStarting:ending:calendars:
For example if you want to delete all expired reminders in the past until now, use something like this
assumed properties:
var calendar : EKCalendar // current calendar
let eventStore : EKEventStore // current event store
code
func removeExpiredReminders() {
let pastPredicate = eventStore.predicateForIncompleteRemindersWithDueDateStarting(nil, ending:NSDate(), calendars:[calendar])
eventStore.fetchRemindersMatchingPredicate(pastPredicate) { foundReminders in
let remindersToDelete = !foundReminders.isEmpty
for reminder in foundReminders as! [EKReminder] {
self.eventStore.removeReminder(reminder, commit: false, error: nil)
}
if remindersToDelete {
self.eventStore.commit(nil)
}
}
}
in the loop you can check for further conditions