iOS10 推送extension之 Service Exten

作者: 踩坑小分队 | 来源:发表于2016-12-06 18:02 被阅读3718次

    看之前如果你对iOS10 的推送还处于一片空白,建议先看
    iOS 10 推送你玩过了吗?


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

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

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

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

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

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

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

    Paste_Image.png

    第二步,选择自己的app


    Paste_Image.png

    第三步,来个小小的断点


    Paste_Image.png

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

    2、玩一把

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

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

    Paste_Image.png
    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
      }
    }
    

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


    Paste_Image.png
    Paste_Image.png

    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)
            }
        }
        
    
    Paste_Image.png

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

    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等三方库去下载,我也想啊,但是报错。报错信息如下


    Paste_Image.png

    有知道怎么解决的,跪求。
    推送如下,收到推送去下载,但是说下载失败,报错原因

     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

    终于的终于,实现效果:

    Paste_Image.png
    当然了对于这种推送附件缓存,建议每次推送的时候清理一下,不然越来越大了。并且还发现在不同的target下的沙盒地址是不一样的
    Paste_Image.png
    我感觉要是清理的话,可以在每次收到远程推送的时候清理一下。

    远程推送视频,音乐的,我木有相关的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


    Paste_Image.png

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

    image.png

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

    参考资料:
    http://www.cocoachina.com/ios/20160628/16833.html
    https://onevcat.com/2016/08/notification/
    http://www.cnblogs.com/lidongq/p/5968923.html
    https://developer.apple.com/reference/usernotifications/unnotificationattachment
    图画的不错
    http://www.jianshu.com/p/2f3202b5e758
    http://www.cocoachina.com/ios/20161021/17820.html

    相关文章

      网友评论

      • ios开发者:我做的,在MyServiceExtension中挂断点,推送不走这里,是什么原因?
        我选择的推送证书,是我app的推送证书,不是MyServiceExtension中BundleId对应的证书,这个有影响吗?
        踩坑小分队:@ios开发者
        参考下另一篇文章
        https://www.jianshu.com/p/5fc3b4155257
        ios开发者:@踩坑小分队 我找到原因了,是ios的版本设置错了
        但是我又遇到一个问题,我语音播放的内容比较长,播放不完,但是我看你的demo是可以完全播放完的,我想问一下,你怎么处理的?
        我发现的现象是,我这边,推送成功后,推送的消息框就出现了,但是我看你demo是在播放后之后才出现推送的提示框的?
        踩坑小分队:@ios开发者 这别挂断点,挂断点,有时候,extension中的代码反而不走了,可以通过修改推送条的内容来看是不是走了extension中的代码,我之前我记得中没有配置相关的证书,你看下我的demo
      • PGOne爱吃饺子:大佬你好,请问你的这个收到推送的时候展示图片为啥还要存到沙河里面啊,下载出来了直接展示不就可以了么,干什么还要缓存。还问一下那个图片是怎么显示的,我怎么没有看到展示的代码,谢谢
      • 190CM:你好 如何在扩展中设置角标?
        踩坑小分队:@190CM 角标显示多少的逻辑,不应该是后台那边的吗,后台给你推多少就显示多少啊
        190CM:@踩坑小分队 比如收到远程推送 连续收到 角标应该设置为2 但是不启动APP主程序代码不走 如何在扩展中设置角标呢?
        踩坑小分队:@190CM 啥需求?
      • FredYJH:你好,我想直接推送图片或语音(10s),不通过下载可以吗??
        踩坑小分队:@FredYJH可以啊,提前吧图片或者语音放到本地,或者推送文字,转语音
      • 张小达:太赞了!救命了都!!!!:+1:
      • goyohol:厉害!回头试试~
      • whereandhere:你好,我想问下带这个扩展的怎么打包啊,打包选App Store的时候,UNNotificationContentExtension 选择automatically 只能出来developer证书,打包失败
        踩坑小分队:不好意思,前一段时间没关注简书,有点忙,你是不是配置的不对啊,正常打appStore的包就可以啊,搞定了吗?
      • 风吹柳絮如花落:集成了NotificationServiceExtension,发布到APP store,iOS 10以下的手机能安装么
        风吹柳絮如花落:@踩坑小分队 亲 我这里用NotificationServiceExtension这个扩展,只要已收到携带有mutable-content": "1"这个字段的,都会打印Program ended with exit code: 0,并且不会走NotificationService里任何代码,直接弹出了原始的push
        风吹柳絮如花落:@踩坑小分队 https://zhidao.baidu.com/question/1801940902903964387.html
        踩坑小分队:应该能安装,不然其他有这个功能的app咋安装的
      • 这个姑凉儿:我也使用的notifaction service extension 也加了mutable-content这个字段推送可以正常收到但是不走notifaction service extension 文件里断点,想知道为什么
        踩坑小分队:@HHLM 这个具体看你的业务了,可以自己后台写,可以用三方的。测试的话可以用三方的推送小工具
        HHLM:楼主你推送用的什么
        踩坑小分队:https://www.jianshu.com/p/5fc3b4155257

        这里下面我写了相关的调试方法,你看下对你有帮助吗
      • zl_xust:关于那个使用afnet 和sdweb 下载图片报错,我猜测是不是cocopods 导入的时候target没有包括NotificationServiceExtension的target,猜测,没有试验过
        宁静1致远:你猜测对了,但是如何解决?
      • 83cc6d590693:请问楼主大神iOS10远程推送过来的音频和视频 播放的时候声音特别小 但是插上耳机是正常的 这种问题哪位大神遇到过 求解决办法 求求
        踩坑小分队:@淡笑红尘
        我这边重新试了一下,都能听到声音,但是我记得刚开始做的时候,视频播放没有声音,也可能记错了,或者Demo演示的视频的声音比较小。

        现在实验的结果就是,声音的确不大,但是能够听清楚。视频和音频的声音都不大。我想是苹果那边就这么处理的,因为你的推送的目的说白了,就是概览一下相关的内容而已。想要看相关的东西的话还是引导用户进入app中去查看吧,个人理解。

        现在我还没有解决办法,如果你有的话,欢迎交流。
        淡笑红尘:@踩坑小分队 没有特殊处理啊,就是一步步来的,就是声音特别小,本地和远程的都小,你的声音大不大?怎么处理的
        踩坑小分队:我有一个问题,我这边实验的时候,本地推送的视频是没有声音的,你那边怎么做到播放视频是有声音的?
      • 0e66ef859546:不太明白,如果推送不加密,第三方如何通过抓包获得内容?不都是tls的协议吗?
      • 路有点颠簸:你好,我这个创建出Target在证书那就要报错,‘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.’
        然后用真机运行又报错
        myServiceExtension has conflicting provisioning settings. myServiceExtension is automatically signed, but code signing identity iPhone Developer: xxxxxx 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.
        Code signing is required for product type 'App Extension' in SDK 'iOS 10.3'
        请问这个需要对之前的证书做什么处理吗
        踩坑小分队:@罗罗罗罗亚索 断点调试,我文章中有写操作过程
        路有点颠簸:@踩坑小分队 恩恩,我解决了,我也是这样处理的,只是断点没执行,我再看看
        踩坑小分队:我更新了下博客,你看文章的最后,能解决你的问题吗
      • 李大戮:请问下Extension需要开启AppGroups吗?
        踩坑小分队:iOS10 通知extension之 Content Extension你玩过了吗?
        http://www.jianshu.com/p/00f671d204d4
        这里面提到了跨不同的extension访问沙盒资源了,不知道你说的是不是这种情况?
        李大戮:@小岩同学 不用了,聊天IM中extension和containing app想共享通知附件,所以可能需要用到App Groups
        踩坑小分队:@李大戮 我这边在实现上面的功能的时候没有用到,你那边什么场景下用到了?
      • 千虑必有一得:怎么自己发远程推送呢?设备和Xcode 换连着吗?不应该应用不在线才算远程推送吗?这几个不太清楚,有点模糊。。。
        踩坑小分队:@honeycao 建议你先看一下这个:http://www.jianshu.com/p/5d83250fb5a1,iOS10 远程推送你玩过了吗?
        这里面有详细的介绍。

      本文标题:iOS10 推送extension之 Service Exten

      本文链接:https://www.haomeiwen.com/subject/zuelmttx.html