Pushkit Callkit Voip通话 系统通话记录回拨调起App 微信语音效果实现

> 日了狗,本篇文章仅供参考吧,大天朝已经不让用callkit了了了了~

> 前提:

iOS版本的微信已经支持了系统层级的打电话体验(6.6版本以上),并且可以直接写入系统的通话记录,点击对应的通话记录又可以直接弹到微信里面直接拨打对应人的语音或者是视频通话。这,其实在体验上已经和系统层级的电话体验差不多了。捣鼓了好几天,算是调通了,总结了一波,同样的,如果你是来寻找代码,那么直接拉到最后!

> 原理:

Callkit:是iOS 10以后才推出的框架,个人理解就是苹果把原本系统层面的电话UI以Api的形式开放出来,让开发者可以集成到自己的项目中,着重强调一下这里的Callkit是系统层级的UI框架,其本身并没有通话功能!我们需要告知这个框架我们的操作打电话,然后通过UI交互,Callkit给我们回调的是用户的操作,比方说End,Start,Answer等,我们需要根据对应的操作在App里面完成通话的逻辑(联想类比一下iPhone和电信提供商的关系,其实真正的通讯是电信提供商去完成的)。

Pushkit:是iOS 8以后推出的一个推送方式,如果你用过静默推送,那么和这个Pushkit非常相似,简单的说,区别于传统的推送方式,Pushkit不会弹出顶部的那个提示条,而是直接进入App里面的回调,再配合上Callkit,那么就能实现类似于系统层级的打电话体验!

通话记录回拨:这个应该算是一个坑吧,后面代码中补充相关逻辑。

> 准备:

公司是用的leanCloud作为推送的三方,如果大家在集成上述两个框架并且实现Voip 类型的通话的时候,若是用的三方推送,请首先用工单的方式提问是否支持Voip类型的推送,我们这边原来使用的是传统的方式导出dev.p12 和 dis.P12(参考下面的文档)。上传之后正常的推送是没有问题的,但是Voip类型的就是发不过来,后来更换了文档Token Authentication的方式进行验证,这种方式确实比原来的方式更方便了一点,友情提示,这里面的key文件貌似只能下载一次,请谨慎保存!🤣

> 集成:

PushKit: 满足协议PKPushRegistryDelegate ,然后didFinishLaunchingWithOptions里面去添加如下代码:

  let pushRegistry = PKPushRegistry.init(queue: nil) // 这里实测 iOS 10 会比较卡,可以尝试一下在子线程里面去实现初始化,传nil是主线程。
  pushRegistry.delegate = self
  pushRegistry.desiredPushTypes = [PKPushType.voIP]

按照逻辑来说注册成功后会回调到如下方法中,获取token后,并且传给第三方服务商:

    
    func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, forType type: PKPushType) {
        var token = ""
        for i in 0..<credentials.token.count {
            token = token + String(format: "%02.2hhx", arguments: [credentials.token[i]])
        }
        print("pushRegistry token \(token)")

         let temp = AVInstallation.current()
        //保存对应的installtion
        let insTempString = deleteVoipString(str: temp.objectId ?? "")
        let ins = AVInstallation.init(objectId: insTempString + ConstStrting)
        ins.apnsTopic = "com.xxxx.xxxx.voip"
        ins.setDeviceTokenFrom(credentials.token)
        ins.saveInBackground()
        print("objectID")
        print(ins.objectId ?? "")

    }

到此为止,貌似没有任何难度~ 是的那么坑来了! 注意其中ins.apnsTopic = "com.xxxx.xxxx.voip"这一句,如果后台服务商这边是支持voip类型的推送,那么他那边的证书会解析出来正常的bundle id 和 bundle id + .voip的形式!例如 com.baofeng.helpxx那么服务商的证书那边应该解析出 至少包含 com.baofeng.helpxx 和 com.baofeng.helpxx.voip 两种格式。 leanCloud的形式如下:

Token Authentication

只有这样才能注册的时候apnsTopic写成 com.baofeng.helper.voip , 那么发送推送的时候才能回调到 pushkit的回调里面! 切记。如果这个有不明白的,欢迎留言!

另外就是我们注意集成的时候需要调节权限如下图:


权限图片1

权限图片2

Callkit:

具体的集成步骤网上是很全面的我也是参考的CallKit iOS 教程 这里面写的非常全面也有对应的原理讲解!英文原文,感谢作者 和 翻译君 ~ 里面的demo可以下载跑起来尝试一下,简单易懂,具体里面的各种代码的逻辑这里就不展开说了。

提示:注意其中的各种回调方法,方法里面的处理逻辑需要各自的业务安排,另外Audio这个类里面的设置按照demo就行,要不可能没有声音。

说一下我们这边的集成原理,我们这边只需要一个唤醒的过程,Callkit就是充当了这个角色,如果App是在前台工作那么我们这边的IM通讯是不需要经过Callkit的。直接拨通对面就好了所以也就是不需要 outCalling的功能。

所以主要的方法就是向Callkit报告电话打入的如下:

  func reportIncomingCall(uuid: UUID, handle: String, hasVideo: Bool = false, completion: ((NSError?) -> Void)?) {
    
    let update = CXCallUpdate()
    update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
    update.hasVideo = hasVideo
    update.localizedCallerName = handle
    update.supportsDTMF = false
    
    provider.reportNewIncomingCall(with: uuid, update: update) { error in
      if error == nil {
        let call = Call(uuid: uuid, handle: handle)
        self.callManager.add(call: call)
      }
      
      completion?(error as? NSError)
    }
  }

tip:callkit接收到reportIncomingCall这个方法传入后选择接听,如果按了HOME键回到主页面,那么出现和系统电话一样的绿色顶部的条文,点击条文会返回App,因为Callkit并不知道什么时候结束,所以需要自己调用结束的方法哦~


主页面

注意一下这个代码:

    let update = CXCallUpdate()
    update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
    update.hasVideo = hasVideo
    update.localizedCallerName = handle
    update.supportsDTMF = false

这段代码是在 reportIncomingCall 的时候传入给provider 的update,其中的type是一个枚举包含有3个值 generic、phoneNumber、emailAddress。如果选择了 generic emailAddress 后面是不能在通话记录里面知己弹到App的! 当时我因为通讯录里面保存的名字不是中文,所以选择了generic,导致后面不能从通话记录跳转回App,所以大部分情况这里直接选中phoneNumber,那么如何让通讯录里面的名字正确的显示呢?** update.localizedCallerName** 这里面才是显示的名字,如果这个为空,那么才会出现按照value的值进行显示的情况。(ps:localizedCallerName为空,选中phoneNumber的时候value传入中文iOS 11 会把中文变成拼音🤣,日~)
所以记住,通讯录里面传入的名字是通过.localizedCallerName 来命名的~ 这里也感谢foolishBoyiOS10适配之 CallKit

系统通话记录回拨调起App

经过以上的步骤,如果没有特殊情况已经能够实现对应的功能了,现在实现从通话记录点击如下:


通话记录

点击对应的记录,应该是直接跳转到App里面,那么怎么获取对应的信息呢?
前提是你已经写入了信息,上文的CXCallUpdate里面的信息,现在就通过这种方式回传到了App里面。具体的方法是在 Appdelegate里面,如下:

  func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
        guard let handle = userActivity.startCallHandle else {
            print("Could not determine start call handle from user activity: \(userActivity)")
            return false
        }
        guard let video = userActivity.video else {
            print("Could not determine video from user activity: \(userActivity)")
            return false
        }
        print("handle = \(handle)")
        callManager.startCall(handle: handle, videoEnabled: video)
        return true
    }

这里 startCallHandle、video 是通过一个类扩展来实现的。我直接偷了一个懒,拿demo的StartCallConvertible.swift 和 NSUserActivity+StartCallConvertible.swift 直接用了。

> 总结:

  1. 经过以上的流程就可以完整的实现从通话开始-通话结束-保存记录-记录回拨的整套流程了。
  2. Viop类型的推送一定要确定好证书,Apple的证书里面有一个专门的Voip类型的,不过我没有用,这个需要根据具体的需求和三方提供商的需求。
  3. 注册的Voip类型的推送和原有的推送是两个token,如果需要必须和后台的商量好两种推送的逻辑处理。
  4. Callkit 本身并不包含即时通讯功能。

>参考:

iOS10适配之 CallKit
官方视频
CallKit iOS 教程
本文代码(这里面使用了leancloud作为推送的三方)

如果有问题或者是疑问,建议等,欢迎留言一起讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342