iOS反骚扰电话-CallKit

iOS10增加了识别骚扰电话的API,有三款主流的软件已经实现。也有一些报道分析 前几天查了查资料,动手写了一个Demo.

原理

电话号码是用Call Directory extension事先存在系统里的,当来电时,系统会查询存储的数据, 根据号码绑定的label,决定直接屏蔽还是显示来电。

iOS反骚扰电话-CallKit
iOS-CallKit.png
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 . 打电话

效果

iOS反骚扰电话-CallKit
IMG_4610.png

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.


上一篇:OSX 安装磁盘分析工具ncdu


下一篇:.NET(C#):使用XPath查询带有命名空间(有xmlns)的XML