在iOS EventKit中自定义日历事件/警报?

问题描述:

我正在写我的第一个iOS应用程序(我厌倦了遗漏事件提醒,并希望有一个基于规则的提醒,例如对于某些日历和某些人,将响起不同的,更响亮的音调,10,5和事件发生前0分钟)在iOS EventKit中自定义日历事件/警报?

第一部分是获取访问日历,并感谢this伟大的演示,我有这样的覆盖。让我的iPhone上运行的应用程序,我可以看到我的日历事件。

(当然,第一部分是要弄清楚迅速,和一般的iOS基础,我还在工作)

第二部分是我想问问我花时间研究之前。我有两个任务,剩下要做的

  1. 无论是后台任务定期检查新/更新的活动,或以编程方式订阅某种事件总线的能力日历中的任意日历更新(新事件,事件的变化)在给定的时间

  2. 计划通知(我可能会使用这样的:How can I schedule local notification for the following scenario?

如何完成#1?

+0

关于您的问题“或以编程方式订阅某种事件总线的能力任何日历更新”您是否需要特定日历的事件更新,或者您需要iOS设备中所有可用日历的事件更新?我可否知道目的为什么你需要更新?我认为这可以清楚地表明你想要做什么。 –

+0

@JayeshSojitra伟大的问题,现在只需一个日历就足够了。虽然我想添加多个“规则”,例如“对于日历X,如果事件来自组织者Y,则在会议前使用声音Z,n0,n1,n2分钟” –

+0

哇,我非常感谢所有答案,但现在我遇到了一些问题。 ..我不知道如何排名他们,我将不得不尝试一切,可悲的是我只有3天...再次感谢大家... –

我不认为这是可能实现不改变设计。

应用程序通常不会在后台运行,除了Apple允许的特定情况(voip,音乐,位置等)外,您的应用程序将被暂停。有后台刷新API,但这不够可靠。

请注意,EKEventStoreChangedNotification通知仅适用于您的进程正在运行。如果您的流程死亡(由于内存压力,设备重启,用户查杀应用等),您将不会在应用启动时收到有关更改的通知,您仍然需要遍历会议并查找更改,所以你必须开发自己的机制来迭代EventKit数据库(可能在一定的时间范围内)。

如果是个人应用程序,可以使用一些技巧继续在后台运行,例如播放静音音乐文件,voip背景API,位置等。所有这些方法都可以让您的应用程序在后台运行,但会对电池寿命造成影响,并且在提交时不会得到Apple的批准。有一个一致的后台运行时间的一种方法是让推送通知服务设置一个无声推动来定期唤醒应用程序,并让它执行其逻辑。这有可能启动应用程序,以防它在后台死亡(但不是如果用户杀死它)。

+0

我喜欢云基础唤醒的想法。将研究它。对于背景抓取API的命中或错过,我很满意,例如如果它平均每天运行一次,我或多或少都可以。 –

+0

不幸的是,这不能保证。例如,如果在同步时电池电量不足,设备将不允许应用在后台同步。 –

要检查日历数据库更改有一个通知:

EKEventStoreChanged

发布时改变了日历数据库所做的,包括添加,删除和更改事件或提醒。没有描述个人的变化。当您收到此通知时,您应该重新提取您访问过的所有对象EKEventEKReminder,因为它们被视为陈旧。如果您正在编辑某个事件并且不希望重新提取它,除非这是绝对必要的,您可以调用它的刷新方法。如果该方法返回true,则不需要重新获取该事件。

添加观察员:

NotificationCenter.default.addObserver(self, selector: #selector(storeChanged), name: .EKEventStoreChanged, object: eventStore) 

和实施方法来处理通知

func storeChanged(_ notification: Notification) {} 
+0

不会当应用程序在后台工作时工作。 –

+0

@LeoNatan这取决于OP是否意味着*后台线程*或*应用程序在后台*不清楚。 – vadian

+1

几个问题 - 如果应用程序在后台,它不会醒来听到这些变化(但最终会听到它们)。如果应用程序死亡,它再次启动时不会听到这些更改。 –

应用程序可以在后台执行什么操作是有限制的。后台提取通常涉及从外部源获取信息,以便您的应用在用户返回后可以最新显示。

后台刷新

当启用时,iOS的监视使用模式来确定何时获取新的数据。您是而不是能够决定何时发布后台提取,并且不应该用于执行关键更新。在您的项目中,当系统认为适当的时候,应用程序可以获取新的日历事件。您可以实现后台取像这样:

  1. 检查框背景在您的应用程序的功能部分取

  2. 以下内容添加到application(_:didFinishLaunchingWithOptions:)

    UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum) 
    
  3. 最后,实现这个功能在你的AppDelegate

    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { 
        // Perform calendar updates here 
    } 
    

如果你可以妥协手动更新调度,后台刷新将是你最好的选择。目前,无法手动请求后台更新。

为了达到#1或者是后台任务定期检查新/更新的事件,或能以编程方式订阅某种事件总线的日历中的任意日历更新(新事件,事件的变化)

你可以做以下事情。

首先要检查新的/更新的日历事件,我们不需要运行任何后台任务。

我们可以使用.EKEventStoreChanged通知获取日历事件的更新,如viewWillAppear方法中所示。

NotificationCenter.default.addObserver(self, selector: #selector(eventStoreChanged:), name: .EKEventStoreChanged, object: eventStore) 

处理日历事件更改(新/更新)EKEventStore更改如下所示。

func eventStoreChanged(_ notification: Notification) { 
    let ekEventStore: EKEventStore? = notification.object 
    let now = Date() 
    let offsetComponents = DateComponents() 
    offsetComponents.day = 0 
    offsetComponents.month = 4 
    offsetComponents.year = 0 
    let endDate: Date? = Calendar.current.date(byAddingComponents: offsetComponents, to: now, options: []) 
    let ekEventStoreChangedObjectIDArray: [Any]? = (notification.userInfo?["EKEventStoreChangedObjectIDsUserInfoKey"] as? [Any]) 
    let predicate: NSPredicate? = ekEventStore?.predicateForEvents(withStartDate: now, endDate: endDate, calendars: nil) 
    // Loop through all events in range 
    ekEventStore?.enumerateEvents(matchingPredicate: predicate, usingBlock: {(_ ekEvent: EKEvent, _ stop: Bool) -> Void in 
     // Check this event against each ekObjectID in notification 
     (ekEventStoreChangedObjectIDArray as NSArray).enumerateObjects(usingBlock: {(_ ekEventStoreChangedObjectID: String, _ idx: Int, _ stop: Bool) -> Void in 
      let ekObjectID: NSObject? = (ekEvent as? NSManagedObject)?.objectID 
      if ekEventStoreChangedObjectID.isEqual(ekObjectID) { 
       // EKEvent object is the object which is changed. 
       stop = true 
      } 
     }) 
    }) 
} 

因此,每当有任何事件更改(添加/更新/删除),我们可以得到更新。

此外,当您创建任何事件时,您会从EKEvent对象获得eventIdentifier

let eventStore : EKEventStore = EKEventStore() 
eventStore.requestAccess(to: .event) { (granted, error) in 

    if (granted) && (error == nil) { 
     print("granted \(granted)") 
     print("error \(error)") 

     let event:EKEvent = EKEvent(eventStore: eventStore) 

     event.title = "Event" 
     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)") 
     } 
     print("Saved Event id : \(event.eventIdentifier)") 
    } 
    else{ 

     print("failed to save event with error : \(error) or access not granted") 
    } 
} 

并使用以下方法获取事件。

let event:EKEvent = eventStore?.event(withIdentifier: eventIdentifier) 

请让我知道如果您需要任何更多的澄清。

,此时无法为iOS日历构建专业和功能性的后台任务(Apple Swift 3.1版)。

iOS的背景状态是非常有限的(有很多原因,以保护电池寿命等)和“日历背景模式”尚不存在在实际的时刻(你可以去请看官方提供的Background modes for apps官方Apple文档,表3-1)。以永远呈现我们有一个移动设备的所有物理限制,而不是一个Linux服务器,但对于日历我们也是不幸的。

换言之,系统不会在后台状态中向您的应用程序传达新的日历更改,您将永远无法使用iOS官方应用程序日历获得可靠的提醒。

您可以使用EKEventStoreChanged通知进行后台任务,但是当您的应用处于“暂停状态”时会发生什么?您总是会通过背景状态与您的应用程序通话,如您所见,没有选择可以获取日历更改。

没有办法,如果你还做了一个自定义日历应用程序(所以不使用官方日历应用程序),因为你必须始终陪在后台状态到外部服务器(远程通知可能是一个想法。):假设你没有连接/互联网,所以你的推送通知迟到了。在这种情况下,您的提醒并不知道您在此期间有特定的约会,您可能会被警告过晚,这可能意味着完全灾难。