前言
最近兩天公司有需求研究CallKit來電識別和號碼拉黑,在網上找了一些相關博文。但是發現對于沒接觸過這方面的人來說,還是會遇到一些問題,所以打算自己再寫一篇詳盡的文章。
懶得看的請直接下載最下面demo。
細節
在沒有了解CallKit這兩個功能之前,因為下面的內容,導致產生一些失(cao)敗(dan)的嘗試。包括但不限于以下:
- 如果該號碼已存在手機里,標記識別將會無效
- 如果屏蔽某號碼,看不到該號碼來電
- 打開主App和擴展App的數據共享,以實現兩個應用共享數據
- 添加到call表單中的電話號碼必須帶上國家區號
- 添加號碼到表單之前,需要對數據進行去重和升序排列
- 最大數量在100萬到200萬之間,原因可能是超時無效
- 目前好像只能全量更新數據,不能增量更新,效率比較低
過程
1. 首先,建立一個工程或者在原有工程上,加入擴展應用 Call Directory Extension:
- 選擇new->target->Call Directory Extension
選擇new->target->Call Directory Extension
-
在這個文件中,蘋果默認已經寫好了注入事件的代碼,并默認添加了兩個手機號供參考格式。
CallDirectoryHandle.m 正是因為這倆默認號碼的格式,容易產生誤導,如果按照這個格式來寫,不帶國家區號的話,號碼是無法添加到 CXCallDirectoryExtensionContext 里面的,也就是會添加失敗,導致產生了一種“這不可能”的心態。
1B5104F2-0BEB-40B9-BB09-30E3CB814253.png
- 在做這里的時候,我想當然的認為,它只是一個本地沙盒或者數據庫類型的介質,也就是任何形式的字符數據都可以存儲進去(CXCallDirectoryPhoneNumber是int64_t),實際上,它在存儲前進行了數據格式判斷,不帶 #國家區號# 的數據是無法存儲成功的,直接失敗并導致權限丟失。
權限打開方式:設置->電話->來電阻止與身份識別
- 我嘗試了一下是否可以用openUrl方式,直接讓應用跳轉到來電身份權限頁面。遺憾的是失敗了,可能是沒有開放這個接口,或者我沒找到。第一行跳轉到電話設置頁面有效。
Phone — prefs:root=Phone
Phone — prefs:root=Phone&CALL_BLOCKING&IDENTIFICATION
2. 接著,進行Main App和Extension App之間數據通訊。
- 測試功能時,直接在擴展應用 Call Directory Extension 中寫入了自己的號碼嘗試成功。不過,擴展APP和主APP雖然在同一個工程里,但是是兩個應用,傳遞數據的話需要用到數據共享:App Group.
共享數據.png
- App共享數據的方法有兩種,至于寫入數據的方式,可以采用數組、字典或者數據庫。
- 使用共享url地址,寫入文件
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.RexLocalGroup"];
- 使用本地沙盒,寫入沙盒中
NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.RexLocalGroup"];
- 簡書上有人采用 fgets(char *buf, int bufsize, FILE *stream) 函數讀行的形式來解析數據,我試了一下,可以實現存入100萬條數據,但200萬的時候會異常,應該是蘋果做了一些限制。不過這種方式,只適合從服務器拉取數據文件寫入,如果想在移動端去添加數據到文件里,就會非邏輯化。所以如果想實現移動端主動寫入的話,用數據庫會容易操作一些。
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.RexLocalGroup"];
containerURL = [containerURL URLByAppendingPathComponent:@"Library/"];
NSString * dbPath = [NSString stringWithFormat:@"%@%@",containerURL.absoluteString, @"LocalNumberDB.sqlite"];
_dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
- 主應用中把數據庫建立到這個數據共享URL位置,然后,在擴展應用中提取數據并填入CXCallDirectoryExtensionContext表單中。這樣就實現了數據傳遞。比較遺憾的是目前只能是全量更新號碼庫,無法實現增量更新,所以更新一條數據也需要全部重新存一遍,速度比較慢,可能這也是種隱私保護。
NSArray * contacts = [[FMDataBaseManager shareInstance] getAllContacts:kNumberTable];
for (int i= 0; i < contacts.count; i ++) {
@autoreleasepool {
ContactModel * contact = contacts[i];
if (contact.phoneNumber && contact.identification) {
CXCallDirectoryPhoneNumber phoneNumber = [contact.phoneNumber longLongValue];
NSString * label = contact.identification;
[context addIdentificationEntryWithNextSequentialPhoneNumber:phoneNumber label:label];
}
contact = nil;
}
}
- 簡單幾步,即可感覺到這個強(mei)大(luan)的(yong)功能的作用。
有疑問可以留言
參考項目下載
做了一個完整的demo放在了GitHub上,有興趣可以下載。