iOS 不用VOIP也可以实现微信来电邀请推送持续响铃震动及及时来电取消

iOS 不用VOIP也可以实现微信来电邀请推送持续响铃震动及及时来电取消

  • 前言
    • 一 、关于NotificationServiceExtension
      • 1. 创建
      • 2. 推送机制
      • 3. API 实例方法
  • 二、 APNS Push Payload
    • 2. 来电邀请
    • 2. 来电取消
  • 三、数据共享
    • 3. 配置共享域
  • 四 知识点就到这里,下面是主要代码
    • 4. 1 NotificationService.m 文件
    • 4.2 前后台问题
  • 五、参考文章
  • 六、VOIP

前言

写这个功能花费我了很长一段时间,简直头秃。不知道有没有相同感受的。
一开始原以为使用NotificationServiceExtension推送服务扩展轻轻松松就可以实现,然后一顿操作,发现了很多问题。
重新查看了官方文档,发现以目前的机制根本无法实现这个功能。
后来又一顿资料搜索发现了VOIP这个东西,刚刚好正是我所需要的,但是问题来了,中国地区在iOS13后,已经禁用了该功能,一旦使用这个功能就面临着,上架被拒的风险,当然如果你是在国外上架的,当我没说。

当撸掉头上一戳毛之后,终于看到了希望的曙光.
请继续往下翻。

一 、关于NotificationServiceExtension

1. 创建

使用Xcode打开项目,选中File -> New -> Target...,在出现的弹窗中选择Notification Service Extension模板。如下图所示:


点击Next后,你需要填写特定于应用程序的相关信息。添加完毕,点击Finish可以在项目的TARGETS里看到多了Service Extension一项。如图所示:

而项目中则会生成NotificationService文件夹,以及相应的类文件和plist文件,如图所示:

2. 推送机制

当你配置了通知服务扩展程序后,每个通知都会执行以下过程:

  • App收到通知。
  • 系统创建扩展类的实例对象并在后台启动它。
  • 你的扩展程序会执行内容编辑和/或下载某些内容操作。
  • 如果你的扩展程序执行太长时间(最多30s)而不能完成它的工作,将会收到通知并被立即终止。
  • 通知显示给用户。

3. API 实例方法

3.1 在该方法内可以对通知进行任何必要的更改,并在完成后通知系统。最多处理时长为30s, 超过30s系统会自动退出通知横幅。

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler;

官方解释
Override this method and use it to modify the UNNotificationContent object that the system delivers with the notification. At some point during your implementation, execute the contentHandler block and pass it your modified content. If you decide not to modify the content, call the contentHandler block with the original content from the request parameter.
You can modify any of the content from the original request. You might customize the content for the current user or replace it altogether. You can use this method to download images or movies and add them as attachments to the content. You may also modify the alert text as long as you don’t remove it. If the content object doesn’t contain any alert text, the system ignores your modifications and delivers the original notification content.
Your extension has a limited amount of time (no more than 30 seconds) to modify the content and execute the contentHandler block. If you don’t execute that block in a timely manner, the system calls your extension’s serviceExtensionTimeWillExpire method to give you one last chance to execute the block. If you don’t, the system presents the notification’s original content to the user.

3.2serviceExtensionTimeWillExpire,在这里给你提供最后一次执行contentHandler代码块的机会。如果你什么都没做,系统将向用户显示通知的原始内容,你做的所有修改都不会生效

// 扩展程序被系统终止之前会被调用
- (void)serviceExtensionTimeWillExpire;

官方解释
If your didReceiveNotificationRequest:withContentHandler: method takes too long to execute its completion block, the system calls this method on a separate thread to give you one last chance to execute the block. Use this method to execute the block as quickly as possible. Doing so might mean providing some fallback content. For example, if your extension is still downloading an image file with the intent of attaching it to the notification’s content, update the notification’s alert text to indicate that an image download is in progress. If you fail to execute the completion block from the didReceiveNotificationRequest:withContentHandler: method in time, the system displays the notification’s original content.

二、 APNS Push Payload

推送的内容需要服务器配合下,需要知道推送的类型,来电邀请、取消做处理,普通推送就不用管正常走就行了。

2. 来电邀请

{  "_j_business" = 1;
    "_j_data_" = "{\"data_msgtype\":1,\"push_type\":4,\"is_vip\":1}";
    "_j_msgid" = 18101194842707757;
    "_j_uid" = 75039914131;
    aps =     {  alert =         {  title = "来电邀请";
        };
        badge = 0;
        "mutable-content" = 1;
        sound = "laidian.caf";
    };
    "extra_key" = "extra_value";
    pushType = 1;
}

mutable-content必须设置为1;
附加字段与服务器约定好,例如:
pushType: 推送类型 来电邀请为1,来电取消为2.

2. 来电取消

{  "_j_business" = 1;
    "_j_data_" = "{\"data_msgtype\":1,\"push_type\":4,\"is_vip\":1}";
    "_j_msgid" = 18101194848382700;
    "_j_uid" = 75039914131;
    aps =     {  alert =         {  title = "\U8bed\U97f3\U901a\U8bdd\U53d6\U6d88"