iOS10增加了识别骚扰电话的API,有三款主流的软件已经实现。也有一些报道分析 前几天查了查资料,动手写了一个Demo.
原理
电话号码是用Call Directory extension事先存在系统里的,当来电时,系统会查询存储的数据, 根据号码绑定的label,决定直接屏蔽还是显示来电。
When using CallKit's Call Blocking & Identification feature (new in iOS
10), phone numbers to be blocked or identified are loaded by your app's
Call Directory extension prior to an incoming call and the phone numbers
are stored by the system. Then, when an incoming call arrives, this stored
data is consulted by the system and an incoming call may either be
blocked or identified in the incoming call UI with the label provided.
For privacy and performance reasons, Call Directory app extensions are
not launched when incoming calls arrive and an app extension cannot
retrieve the phone number for an incoming call
http://*.com/questions/38098036/how-to-get-the-incoming-call-number-by-using-callkit
https://www.zhihu.com/question/47542405
步骤
1 . 新建一个工程,New 一个 Target,选Call Directory Extension
,会自动生成模版代码。
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
context.delegate = self
do {
try addBlockingPhoneNumbers(to: context)
} catch {
NSLog("Unable to add blocking phone numbers")
let error = NSError(domain: "CallDirectoryHandler", code: 1, userInfo: nil)
context.cancelRequest(withError: error)
return
}
do {
try addIdentificationPhoneNumbers(to: context)
} catch {
NSLog("Unable to add identification phone numbers")
let error = NSError(domain: "CallDirectoryHandler", code: 2, userInfo: nil)
context.cancelRequest(withError: error)
return
}
NSLog("beginRequest context ", context);
context.completeRequest()
}
private func addBlockingPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws {
// Retrieve phone numbers to block from data store. For optimal performance and memory usage when there are many phone numbers,
// consider only loading a subset of numbers at a given time and using autorelease pool(s) to release objects allocated during each batch of numbers which are loaded.
//
let phoneNumbers: [CXCallDirectoryPhoneNumber] = [ +8618005555555, +8618005555555 ]
for phoneNumber in phoneNumbers {
context.addBlockingEntry(withNextSequentialPhoneNumber: phoneNumber)
}
}
private func addIdentificationPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws {
// Retrieve phone numbers to identify and their identification labels from data store. For optimal performance and memory usage when there are many phone numbers,
// consider only loading a subset of numbers at a given time and using autorelease pool(s) to release objects allocated during each batch of numbers which are loaded.
//
// Numbers must be provided in numerically ascending order.
let phoneNumbers: [CXCallDirectoryPhoneNumber] = [ +8618005555555, +8618005555555 ]
let labels = [ "骚扰电话", "Local business" ]
for (phoneNumber, label) in zip(phoneNumbers, labels) {
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
}
}
2 . 手工给Extension 生成 一个 CallDirectoryExtension.entitlements 文件。
3 . 运行。在设备的设置 —> 电话 —> Call Blocking & Identification,开启我们的 App。
4 . 打电话
效果
API
beginRequest
该方法在 Containing App 调用 reload 或者在 设置 —> 电话 —> Call Blocking & Identification里开启权限的时候,会自动被调用。但是打断点的时候,无法进来。
open class CXCallDirectoryProvider : NSObject, NSExtensionRequestHandling {
open func beginRequest(with context: CXCallDirectoryExtensionContext)
}
给系统数据库增加骚扰电话号码
open func addBlockingEntry(withNextSequentialPhoneNumber
phoneNumber: CXCallDirectoryPhoneNumber)
- 数据量大时,需要分批处理,注意内存问题。consider only loading a subset of numbers at a given time and using autorelease pool(s) to release objects allocated during each batch of numbers which are loaded.
- 号码升序排列。 Numbers must be provided in numerically ascending order
- 号码需要加区号。 Edit retrievePhoneNumbersToIdentifyAndLabels() to include the number and label you want. Add +CountryCode at the start (not confirmed yet if this is essential)
给系统数据库增加骚扰电话号码以及提示的文本
open func addIdentificationEntry(withNextSequentialPhoneNumber phoneNumber:
CXCallDirectoryPhoneNumber, label: String)
提交
open func completeRequest(completionHandler completion:
(@escaping (Bool) -> Swift.Void)? = nil)
苹果开发交流网站上的总结
https://forums.developer.apple.com/thread/48837
I've managed to get this working, here's what I did and
some observations:
1) Create a new target of Type Call Directory Extension.
2) Edit retrievePhoneNumbersToIdentifyAndLabels() to include the number
and label you want. Add +CountryCode at the start (not confirmed yet
if this is essential)
3) Run the containing app target.
4) In setting go to Phone/Call Blocking & IdentificationAnd turn on you
extension (or it will automatically be turned on if you run the
entension target rather than the containing app target)
5) Make a call
Its not possible to hit any breakpoints in the extension if running in
XCode regardless of which target you execute. NSLog statements can be
viewed via XCode's devices, it indicates that the extension is called in
step 4) if the above steps are called. I don't know if/when its called
subsequently after that, it has to be otherwise how can an updated list
be applied?
If you want to make a change to the label, the OS does caching, so turn
off the extension, delete the containing app app, clear the call history
from the phone app, install and run again.