iOS10推送通知进阶(Notification Extension)

Notification Extension

iOS10 添加了很多的Extension,与通知相关的 extension 有两个:Service ExtensionContent Extension
我们先来了解一下Service Extension,这个东西主要是干啥的呢?
主要是,让我们在收到远程推送的时候<注意哈,必须是远程推送>,展示之前对通知进行修改,因为我们收到远程推送之前会先去执行Service Extension中的代码。这样就可以在收到远程推送展示之前为所欲为了。
用一张图来表示下这种流程:

Service Extension能干啥?
举个栗子:
1、通过远程推送,推送的内容的title="1",我可以在收到推送将要显示之前将标题修改成title="2",那么推送条展示的title就是2。
2、第一点也就是举个栗子,一般不会有这种需求,既然能修改内容,那么可以在发送推送的时候发送一段用公钥加密的内容,然后设备收到推送之后,用私钥进行解密然后再去展示。有啥好处呢,这么折腾。有很多的推送用的是三方的<极光推送,友盟推送,百度推送等等>。这样你推送的东西,就不会让三方看到了。如果是金融之类的,要更注意保密吧。
3、当远程推送需要展示多媒体的时候,也需要在这下载,下载完毕之后,获取本地下载文件路径再进行展示。对下载附件多媒体得需要Service Extension这玩意儿。

介绍完毕,开始玩一下。
1、创建一个Service Extension

创建完毕之后看一下项目的变化

大概这几个比较明显的地方
创建完毕,看一下里面的相关的方法
方法override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)

大概理解下:
收到一个远程推送的时候就会调用这个方法,你有30秒的时间来修改接收到的推送的内容<你可以去修改通知的内容,你也可以去下载推送的附件>,如果超过30秒了,你没有做任何的处理,系统就会自动的调用override func serviceExtensionTimeWillExpire(),如果你还不做出相应的操作,那么将显示最开始的通知的内容。

怎么进行测试呢?
第一步,选择要测试的target,然后 run

第二步,选择自己的app

第三步,来个小小的断点

第四步,发个远程推送,本地推送自己可以尝试,是没反应的。

2、玩一把

{
  "aps":{
    "alert":{
      "title":"iOS 10 title",
      "subtitle":"iOS 10 subtitle",
      "body":"iOS 10 body"
    },
    "category":"saySomethingCategory",
    "sound":"default",
    "badge":3
  }
}

推送收到了,但是没有走断点。
原因是推送的内容中缺少一个字段

mutable-content 表示我们会在接收到通知时对内容进行更改。

{
  "aps":{
    "alert":{
      "title":"iOS 10 title",
      "subtitle":"iOS 10 subtitle",
      "body":"iOS 10 body"
    },
     "mutable-content":1,
    "category":"saySomethingCategory",
    "sound":"default",
    "badge":3
  }
}

添加上之后再玩一把,在展示推送之前进行走断点了。
放过断点,我们发现标题已经被更改了。

3、尝试更改其他的内容

 override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {
            // Modify the notification content here...
            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
            bestAttemptContent.body = "我是新修改的body"
            bestAttemptContent.title = "我是新修改的title"
            bestAttemptContent.subtitle = "我是subTitle"

            contentHandler(bestAttemptContent)
        }
    }

更改推送内容,有没有这种需求呢?
我感觉有,比如你是一家金融公司,你给用户单独推送的东西不想让别人抓包或者其他的方式看到,那么你可以在推送的时候进行加密,然后到了客户端之后再进行解密。这样做的话就比较安全一些。

4、展示远程推送过来的图片
先说一下思路,收到推送之后,判断有相关附件的话就去下载,下载完毕存储到沙盒中,然后显示。即时是过了30秒如果图片没下载下来,也会显示最开始的推送的内容的。
主要代码:

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {
            // Modify the notification content here...
            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
            bestAttemptContent.body = "我是新修改的body"
            bestAttemptContent.title = "我是新修改的title"
            bestAttemptContent.subtitle = "我是subTitle"

            // 获取相关的附件,看是不是有相关的附件
            let userInfoDic = bestAttemptContent.userInfo
            let apsDic:[AnyHashable : Any] = userInfoDic["aps"] as! [AnyHashable : Any]
            if apsDic["my-attachment"] != nil{
                // 如果说存在附件的话
                loadAttachmentForUrlString(urlStr: apsDic["my-attachment"] as! String, completionHandler: { (attachment) in

                    if attachment != nil{
                        // 如果说 attachment 不为空的话
                        bestAttemptContent.attachments = [attachment!,attachment!]
                    }
                    contentHandler(bestAttemptContent)
                })

            }else{
                // 如果说不存在附件的话
                contentHandler(bestAttemptContent)
            }

        }
    }

下载方法,下载完毕之后通过block进行回调

// MARK:- 下载相关的附件信息
    func loadAttachmentForUrlString(urlStr:String,completionHandler:@escaping CompletionHandlerBlock) -> Void {

        let attachmentURL:URL = URL(string: urlStr)!

        // 开启线程去下载
        DispatchQueue.global().async {

            let urlSession = URLSession.shared.dataTask(with: attachmentURL, completionHandler: { (data, response, error) in

                // 下载完毕,或者报错,转到主线程中
                DispatchQueue.main.async {
                    var attachment : UNNotificationAttachment? = nil
                    if let data = data{

                        // 加工目的 url,将下载好的推送附件添加到沙盒中
                        let ext = (urlStr as NSString).pathExtension
                        let cacheURL = URL(fileURLWithPath: FileManager.default.cachesDirectory)
                        let finalUrl = cacheURL.appendingPathComponent(urlStr.md5).appendingPathExtension(ext)

                        print("finalUrl ************* \(finalUrl)")
                        // 将下载好的附件写入沙盒

                        if let _ = try? data.write(to: finalUrl) {
                            // 写入
                            attachment = try? UNNotificationAttachment(identifier: "pushAttachment", url: finalUrl, options: nil)
                            completionHandler(attachment)
                        }else{
                            completionHandler(attachment)
                        }

                    }else{
                        completionHandler(attachment)
                    }
                }
            })

            urlSession.resume()
        }
    }

这里没有去写用SDWebImageView下载,也没有用AFNetworking等三方库去下载,我也想啊,但是报错。报错信息如下

推送如下,收到推送去下载,但是说下载失败,报错原因

 MyServiceExtension[12788:2223445] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

解决方案:

终于的终于,实现效果:

Paste_Image.png\
当然了对于这种推送附件缓存,建议每次推送的时候清理一下,不然越来越大了。并且还发现在不同的target下的沙盒地址是不一样的

我感觉要是清理的话,可以在每次收到远程推送的时候清理一下。

远程推送视频,音乐的,我木有相关的url资源,木有尝试,不过应该差不多。

有一些小伙伴问,为什么配置完毕之后回报错,真机运行的时候也报错
类似这样的

myServiceExtension has conflicting provisioning settings.
myServiceExtension is automatically signed,
 but code signing identity iPhone Developer: xxxxxxx has been manually specified. Set the code signing identity value to "iPhone Developer"
in the build settings editor, or switch to manual signing in the project editor.'

解决的办法参考如下:\
1、选择 Automaticlly manage signing\
选择相关的team

2、signing的相关设置
都设置成Developer,这个真机调试,打包测试,上线这里都可以不用修改。Xcode会根据你的操作自动配置相关的证书和Profile文件,前提是你的相关环境下的证书和Profile文件都是齐全的<这个应该不是问题,不然你选择当前环境下的包是怎么打出来的>

最后,献上参考Demo地址:https://github.com/RunOfTheSnail/PushDemo001

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冯子一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值