2017-04-13 75 views
12

我正在寫我的第一個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天...再次感謝大家... –

回答

6

我不認爲這是可能實現不改變設計。

應用程序通常不會在後臺運行,除了Apple允許的特定情況(voip,音樂,位置等)外,您的應用程序將被暫停。有後臺刷新API,但這不夠可靠。

請注意,EKEventStoreChangedNotification通知僅適用於您的進程正在運行。如果您的流程死亡(由於內存壓力,設備重啓,用戶查殺應用等),您將不會在應用啓動時收到有關更改的通知,您仍然需要遍歷會議並查找更改,所以你必須開發自己的機制來迭代EventKit數據庫(可能在一定的時間範圍內)。

如果是個人應用程序,可以使用一些技巧繼續在後臺運行,例如播放靜音音樂文件,voip背景API,位置等。所有這些方法都可以讓您的應用程序在後臺運行,但會對電池壽命造成影響,並且在提交時不會得到Apple的批准。有一個一致的後臺運行時間的一種方法是讓推送通知服務設置一個無聲推動來定期喚醒應用程序,並讓它執行其邏輯。這有可能啓動應用程序,以防它在後臺死亡(但不是如果用戶殺死它)。

+0

我喜歡雲基礎喚醒的想法。將研究它。對於背景抓取API的命中或錯過,我很滿意,例如如果它平均每天運行一次,我或多或少都可以。 –

+0

不幸的是,這不能保證。例如,如果在同步時電池電量不足,設備將不允許應用在後臺同步。 –

2

要檢查日曆數據庫更改有一個通知:

EKEventStoreChanged

發佈時改變了日曆數據庫所做的,包括添加,刪除和更改事件或提醒。沒有描述個人的變化。當您收到此通知時,您應該重新提取您訪問過的所有對象EKEventEKReminder,因爲它們被視爲陳舊。如果您正在編輯某個事件並且不希望重新提取它,除非這是絕對必要的,您可以調用它的刷新方法。如果該方法返回true,則不需要重新獲取該事件。

添加觀察員:

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

和實施方法來處理通知

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

不會當應用程序在後臺工作時工作。 –

+0

@LeoNatan這取決於OP是否意味着*後臺線程*或*應用程序在後臺*不清楚。 – vadian

+1

幾個問題 - 如果應用程序在後臺,它不會醒來聽到這些變化(但最終會聽到它們)。如果應用程序死亡,它再次啓動時不會聽到這些更改。 –

3

應用程序可以在後臺執行什麼操作是有限制的。後臺提取通常涉及從外部源獲取信息,以便您的應用在用戶返回後可以最新顯示。

後臺刷新

當啓用時,iOS的監視使用模式來確定何時獲取新的數據。您是而不是能夠決定何時發佈後臺提取,並且不應該用於執行關鍵更新。在您的項目中,當系統認爲適當的時候,應用程序可以獲取新的日曆事件。您可以實現後臺取像這樣:

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

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

    UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum) 
    
  3. 最後,實現這個功能在你的AppDelegate

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

如果你可以妥協手動更新調度,後臺刷新將是你最好的選擇。目前,無法手動請求後臺更新。

2

爲了達到#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) 

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

2

,此時無法爲iOS日曆構建專業和功能性的後臺任務(Apple Swift 3.1版)。

iOS的背景狀態是非常有限的(有很多原因,以保護電池壽命等)和「日曆背景模式」尚不存在在實際的時刻(你可以去請看官方提供的Background modes for apps官方Apple文檔,表3-1)。以永遠呈現我們有一個移動設備的所有物理限制,而不是一個Linux服務器,但對於日曆我們也是不幸的。

換言之,系統不會在後臺狀態中向您的應用程序傳達新的日曆更改,您將永遠無法使用iOS官方應用程序日曆獲得可靠的提醒。

您可以使用EKEventStoreChanged通知進行後臺任務,但是當您的應用處於「暫停狀態」時會發生什麼?您總是會通過背景狀態與您的應用程序通話,如您所見,沒有選擇可以獲取日曆更改。

沒有辦法,如果你還做了一個自定義日曆應用程序(所以不使用官方日曆應用程序),因爲你必須始終陪在後臺狀態到外部服務器(遠程通知可能是一個想法。):假設你沒有連接/互聯網,所以你的推送通知遲到了。在這種情況下,您的提醒並不知道您在此期間有特定的約會,您可能會被警告過晚,這可能意味着完全災難。