iOS—EventKit實(shí)現(xiàn)app日程同步到本地日歷

最近在做日歷同步的需求,趁著已經(jīng)提測,整理一些坑坑洼洼的地方。

和產(chǎn)品經(jīng)理一起研究了一下市面上該功能的實(shí)現(xiàn),絕大多數(shù)都是把本地手機(jī)日歷的日程單向同步到自己app中去。而我們產(chǎn)品經(jīng)理反其道而行,同步依然是單向的,但是是要從app的日程業(yè)務(wù)中把數(shù)據(jù)同步到日歷中去。其實(shí)這樣的需求對于我們本身的業(yè)務(wù)來講是說得通的,而且不需要通過push消息就能讓用戶自己去定義提醒的時(shí)間。但是在預(yù)研階段就發(fā)現(xiàn)了問題,所以正式開發(fā)以前,給她們總結(jié)了一些解決思路和不可避免的問題。


問題一:EKEvent對象的eventIdentifier屬性是只讀的

@property(null_unspecified, nonatomic, readonly) NSString *eventIdentifier;

看到readonly我瞬間明白了其他APP為什么是手機(jī)本地日歷單向同步到應(yīng)用中,因?yàn)闃I(yè)務(wù)對象的唯一標(biāo)識不能和本地的日歷建立有效的連接。這樣就會(huì)導(dǎo)致如果我們對APP內(nèi)日程的數(shù)據(jù)進(jìn)行的更改或者刪除操作,本地日歷中的日程就沒有辦法同步更新,因?yàn)槠ヅ洳簧蟸

解決方案:將我們的日程id帶入到本地日程中去。

eventIdentifier用不了了,只能找其他屬性,并且是string類型,最后決定用url,當(dāng)然做了一些其他處理。不過問題依然是有的,比如該屬性是暴露給用戶的,用戶可以自己去編輯。那么就會(huì)導(dǎo)致有新的編輯過的數(shù)據(jù)過來以后,我只能把該日程處理成新增日程。


問題二:獲取本地日歷中的日程數(shù)據(jù)數(shù)據(jù)量可能會(huì)很大,導(dǎo)致與服務(wù)端返回的新數(shù)據(jù)進(jìn)行匹配的時(shí)候雙重for循環(huán)影響效率(雖然用戶感知不到)

解決方案:使用allowsContentModifications屬性

-(NSMutableArray*)getLocalSchedules{

? ? NSMutableArray *allowsModifyEvents = [NSMutableArray array];

? ? NSDate *startDate = startdate;

? ? NSDate *endDate = enddate;

? ? NSPredicate *pre = [self.store predicateForEventsWithStartDate:startDate endDate:endDate calendars:nil];

? ? NSArray *events = [self.store eventsMatchingPredicate:pre];

? ? events = [eventssortedArrayUsingSelector:@selector(compareStartDateWithEvent:)];

? ? for(EKEvent*eventinevents) {

? ? ? ? if (event.calendar.allowsContentModifications == YES) {

? ? ? ? ? ? [allowsModifyEventsaddObject:event];

? ? ? ? }

? ? }

? ? returnallowsModifyEvents;

}

是的,由于我們手動(dòng)添加的數(shù)據(jù)都是可以手動(dòng)編輯的,所以event的allowsContentModifications這一只讀屬性剛好可以用到。可以減少很多系統(tǒng)日歷自帶的event對象,比如節(jié)假日、節(jié)氣等等。


問題三:日程需要分賬戶

解決方案:使用EKCalendar

//為日程添加日歷分類

EKSource*myLocalSource =nil;

EKCalendar*myLocalCalendar;

NSArray *calendars = [self.store calendarsForEntityType:EKEntityTypeEvent];

NSString*calendarTitle = [NSStringstringWithFormat:@"%@",userName];//這里使用username是因?yàn)槲覀兊腁PP可以進(jìn)行用戶切換,產(chǎn)品經(jīng)理希望不同用戶的日程保存到不同的分類下。但是由于EKCalendar的calendarIdentifier屬性也是只讀的,所以目前只能用username進(jìn)行本地和服務(wù)端返回?cái)?shù)據(jù)的匹配。

//日歷優(yōu)先取本地已有的

for(EKCalendar*calendar in calendars) {

? ? ? ? if([calendar.title isEqualToString: calendarTitle]) {

? ? ? ? ? ? myLocalCalendar = calendar;

? ? ? ? ? ? break;

? ? ? ? }

}

? ? //本地沒有則新建

? ? if(myLocalCalendar ==nil) {

? ? ? ? //先取已經(jīng)存在本地的個(gè)人source

? ? ? ? for(EKSource*calendarSource in self.store.sources) {

? ? ? ? ? ? if(calendarSource.sourceType==EKSourceTypeLocal) {

? ? ? ? ? ? ? ? myLocalSource = calendarSource;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? //EKSource的類型有很多種,Local類型在用戶打開日歷的cloud同步以后會(huì)變成CalDAV類型

? ? ? ? if(myLocalSource ==nil) {

? ? ? ? ? ? for(EKSource*calendarSource in self.store.sources) {

? ? ? ? ? ? ? ? if(calendarSource.sourceType==EKSourceTypeCalDAV&&

? ? ? ? ? ? ? ? ? ? [calendarSource.titleisEqualToString:@"iCloud"]) {//該判斷條件不知道有沒有更好的方案,也是在網(wǎng)上找到的。

? ? ? ? ? ? ? ? ? ? myLocalSource = calendarSource;

? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }


? ? ? ? myLocalCalendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:self.store];

? ? ? ? myLocalCalendar.title= calendarTitle;

? ? ? ? CGColorSpaceRef rgbSapceRef = CGColorSpaceCreateDeviceRGB();

? ? ? ? CGFloatrgbComponents[] = {1,0,0,1};// RGBA 顏色組件

? ? ? ? CGColorRefrgbColorRef =CGColorCreate(rgbSapceRef, rgbComponents);

? ? ? ? myLocalCalendar.CGColor= rgbColorRef;

? ? ? ? myLocalCalendar.source= myLocalSource;

? ? ? ? NSError*err;

? ? ? ? [self.store saveCalendar:myLocalCalendar commit:YES error:&err];

? ? ? ? if (err) {//新建日歷失敗的話則將日程的日歷分類到默認(rèn)日歷下

? ? ? ? ? ? [newEventsetCalendar:[self.store defaultCalendarForNewEvents]];

? ? ? ? }else{

? ? ? ? ? ? [newEventsetCalendar:myLocalCalendar];

? ? ? ? }

? ? }else{

? ? ? ? [newEventsetCalendar:myLocalCalendar];

? ? }


結(jié)論:EventKit框架中有太多的只讀屬性的對象,其實(shí)正確的做法是把已經(jīng)存到本地的EKEvent對象的eventIdentifier屬性返回給我們自己的服務(wù)器,讓后臺與業(yè)務(wù)日程進(jìn)行關(guān)聯(lián)。但是目前該方案由于種種原因沒有最終拍死,所以只能原生負(fù)責(zé)第一期的需求先實(shí)現(xiàn)。后面再慢慢埋坑吧~基本上一些重要的代碼也就上面一點(diǎn)點(diǎn),就不上demo了。其實(shí)我蠻喜歡做這樣的需求的,沒有UI\UE。后臺或者前端返回來數(shù)據(jù)我就處理數(shù)據(jù)就好了。不到一天時(shí)間就能搞定,不過前期一定要做好預(yù)研工作,把問題盡快的暴露給項(xiàng)目組,然后大家一起討論解決方案,后面才能水到渠成。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容