Swift Callkit扩展--来电标记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lin1109221208/article/details/86157017

需求:需要实现项目中根据号码从服务器查询返回的标记写入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下载

如果上面的链接打不开-->github下载

猜你喜欢

转载自blog.csdn.net/lin1109221208/article/details/86157017
今日推荐