需求:需要实现项目中根据号码从服务器查询返回的标记写入calllkit放骚扰标记系统库中,来电时显示该标记
最终的效果如图所示:
一、准备工作
1、新建一个swift 项目
2、创建callkit扩展:选择file->new->target,选择Call Directory Extension,输入扩展的名称,创建好后选择Activate
3、想实现项目与扩展的数据共享,需要打开Capabilities-->App Groups的开关,并点击“+”新建一个app groups(一般命名是以group. 开头)
4、同样的,项目中也需要打开Capabilities-->App Groups的开关,选择3中创建好的app groups
5、运行程序,需要在设置-->电话-->来电阻止与身份识别-->找到项目打开对应的开关
准备工作做好了,下面代码逻辑的展示
1、在主界面创建3个按钮,分别为 检查权限、写入共享数据、读取共享数据
1)创建扩展Manager、文件Manager、存储数据的dic、以及app group的url,并初始化
var exManager : CXCallDirectoryManager!
var fileManager : FileManager!
var dic: NSMutableDictionary!
var containURL : URL!
override func viewDidLoad() {
super.viewDidLoad()
dic = NSMutableDictionary.init()
exManager = CXCallDirectoryManager.sharedInstance
fileManager = FileManager.default
dic.setValue("中介", forKey: "8613120076711")
dic.setValue("骗子", forKey: "8612345678901")
}
2)检查权限:目的是检查callkit标记功能的权限是否开启(开启方法:见上面第5条),若未开启,数据是无法写入系统标记库的,需要用户手动开启
exManager.getEnabledStatusForExtension(withIdentifier: CALLKITEX_IDENTIFIER) { (status : CXCallDirectoryManager.EnabledStatus, error) in
if error != nil{
self.promissiondDesLabel.text = "权限获取发生错误"+error.debugDescription
}else{
switch status {
case .disabled:
self.promissiondDesLabel.text = "权限未开启"
break
case .enabled:
self.promissiondDesLabel.text = "权限已开启"
break
default:
self.promissiondDesLabel.text = "权限未知"
break
}
}
}
3)写入共享数据:在app group中创建共享文件,并将数据存储到文件里,然后将数据录入系统
self.containURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: APPGROUP_IDENTIFIER)
self.containURL = containURL.appendingPathComponent(FILE_NAME)
let filepath = self.containURL.path
let jsonStr = NSMutableString.init()
jsonStr.append("[")
for (number,identifier) in dic {
let number = number as! String
let identifier = identifier as! String
let dicStr = String.init(format: "{\"%@\":\"%@\"},\n", number,identifier)
jsonStr.append(dicStr)
}
jsonStr.append("]")
print("jsonstr \(jsonStr)")
do{
try jsonStr.write(toFile: filepath, atomically: true, encoding: String.Encoding.utf8.rawValue)
}catch let error {
print("写入文件出错 \(error)")
}
//将数据录入系统
if self.promissiondDesLabel.text == "权限已开启" {
exManager.reloadExtension(withIdentifier: CALLKITEX_IDENTIFIER) { (error) in
if error != nil{
print("写入系统出错 \(error)")
}else{
print("写入系统成功")
}
}
}
注:写入报错主要有以下几个原因:
(1)Error Domain=com.apple.CallKit.error.calldirectorymanager Code=1 ;CXErrorCodeCallDirectoryManagerErrorNoExtensionFound 该错误可能出现的原因是identifier 设置的不对 注意不要使用app groups 使用的是Call Directory Extension 的identifier
(2)Error Domain=com.apple.CallKit.error.calldirectorymanager Code=2 ;CXErrorCodeCallDirectoryManagerErrorLoadingInterrupted加载时被中断有可能是因为addAllIdentificationPhoneNumbersToContext中数据处理出错,打断点调试一下
(3)Error Domain=com.apple.CallKit.error.calldirectorymanager Code=3;CXErrorCodeCallDirectoryManagerErrorEntriesOutOfOrder可能是因为加载数据格式错误比如号码中带有符号,号码没有增序排列
(4)Error Domain=com.apple.CallKit.error.calldirectorymanager Code=4;CXErrorCodeCallDirectoryManagerErrorDuplicateEntries可能是的数据有重复
(5)Error Domain=com.apple.CallKit.error.calldirectorymanagerCode=6;CXErrorCodeCallDirectoryManagerErrorExtensionDisabled 权限未打开
3)读取共性数据:目的在于查看数据是否已经写入系统,并且可读取
self.containURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: APPGROUP_IDENTIFIER)
self.containURL = containURL?.appendingPathComponent(FILE_NAME)
//打开文件,如果file为nil则说明文件不存在
let file = fopen((self.containURL?.path as NSString?)!.utf8String, "r")
if file==nil {
print("共享文件不存在_说明尚未写入数据")
}else{
print("共享文件存在")
var str:String!
var jsonData : Data!
var array : NSMutableArray = NSMutableArray.init()
var dic : NSMutableDictionary = NSMutableDictionary.init()
do {
str = try String.init(contentsOf: containURL!, encoding: String.Encoding.utf8)
jsonData = str.data(using: String.Encoding.utf8)!
let originalArray = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers)
if (originalArray as AnyObject).isKind(of: NSArray.classForCoder()){
print("解析类型是数组")
array.addObjects(from: originalArray as! [Any])
if array.count == 0 {
return
}
//2、利用数组去重
var temp = [NSDictionary]()
var idxArr = [String]()
for dic in array{
let dic = dic as! NSDictionary
let number = dic.allKeys[0] as! String
let identifier = dic.allValues[0] as! String
if !idxArr.contains(number){
idxArr.append(number)
temp.append(dic)
}
}
print("解析类型是数组 temp \(array) \(temp)")
}else{
print("解析类型是字典")
for (number, identifier) in (originalArray as! NSDictionary){
dic.setValue(identifier, forKey: number as! String)
}
print("解析类型是字典 temp \(originalArray) \(dic)")
}
}catch let error {
print("共享内存读取失败 \(error)")
}
}
2、查看创建好的扩展,里面有个CallDirectoryHandler.swift的文件,号码写入系统标记库的逻辑就是在这个类里面执行
注:ios11中callkit扩展新增了两个方法,由于需要兼容10及以上的版本,所以将多出来的以及没用到方法注释掉了
目的:将共享目录中对应文件的数据读取出来,添加到系统让骚扰标记库中
号码的格式需要注意:1)拦截号码或者号码标识的情况下,号码必须要加国标区号!,
2)数组内号码要按升序排列
里面主要用到了两个方法
1)beginRequest //开始请求的方法,在打开设置-电话-来电阻止与身份识别开关时,系统自动调用
2)addAllIdentificationPhoneNumbers //添加标记号码:根据生产的模板,只需要修改CXCallDirectoryPhoneNumber数组,数组内号码要按升序排列
具体代码如下:
//拦截号码或者号码标识的情况下,号码必须要加国标区号!!!!!!!!
import Foundation
import CallKit
class CallDirectoryHandler: CXCallDirectoryProvider {
//开始请求的方法,在打开设置-电话-来电阻止与身份识别开关时,系统自动调用
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
context.delegate = self
addAllIdentificationPhoneNumbers(to: context)
context.completeRequest()
}
//添加标记号码
private func addAllIdentificationPhoneNumbers(to context: CXCallDirectoryExtensionContext) {
var containerURL : URL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.group.com.callkit")!
containerURL = containerURL.appendingPathComponent("data")
//打开文件,如果file为nil则说明文件不存在
let file = fopen((containerURL.path as NSString?)!.utf8String, "r")
if file == nil {
return
}
// containerURL = containerURL?.appendingPathComponent("Library/Caches/callkit.json")
var str:String!
var jsonData : Data!
var array : NSArray!
do {
str = try String.init(contentsOf: containerURL, encoding: String.Encoding.utf8)
jsonData = str.data(using: String.Encoding.utf8)!
array = (try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSArray)!
if array.count == 0 {
return
}
print("string extension "+str+" \(jsonData) \(array) ")
for dic in array {
let dic = dic as! NSDictionary
let number = dic.allKeys[0]
let identifier = dic.allValues[0]
print("string extension \(number) \(identifier) ")
let phoneNum = CXCallDirectoryPhoneNumber(truncating: NSNumber.init(value: ((number as? NSString)?.integerValue)!))
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNum, label: identifier as! String)
}
}catch let error {
print("string extension error \(error)")
}
}
}
extension CallDirectoryHandler: CXCallDirectoryExtensionContextDelegate {
func requestFailed(for extensionContext: CXCallDirectoryExtensionContext, withError error: Error) {
// An error occurred while adding blocking or identification entries, check the NSError for details.
// For Call Directory error codes, see the CXErrorCodeCallDirectoryManagerError enum in <CallKit/CXError.h>.
//
// This may be used to store the error details in a location accessible by the extension's containing app, so that the
// app may be notified about errors which occured while loading data even if the request to load data was initiated by
// the user in Settings instead of via the app itself.
}
}
demo链接:demo下载
|
请发表评论