本文旨在对 iOS 推送(以下简称 推送)进行一个完整的剖析,如果你之前对推送一无所知,那么在你认真地阅读了全文后必将变成一个推送老手,你将会对其中的各种细节和原理有充分的理解。以下是 pikacode 使用 iOS 推送的一些经验,欢迎互相交流,指出错漏之处。

推送服务 可以说是所有 App 的标配,不论是哪种类型的 App,推送都从很大程度上决定了 App 的 打开率、使用率、存活率 。因此,熟知并掌握推送原理及方法,对每一个开发者来说都是必备技能,对每一个依赖 App 的公司来说都至关重要。

从 iOS 10 新增的 UserNotifications Framework 可以发现,Apple 整合了原有散乱的 API,并且增加了许多强大的功能。以 Apple 官方的角度来看,也必然是相当重视推送服务对 App 的影响、以及对 Apple 生态圈长远发展的影响。

准备篇


Tip 1:推送 必须 购买 Apple 开发者账号,并使用特定的 推送证书

  • 使用免费帐号不能推送

  • 那如果我们使用的是第三方推送服务(以下简称 第三方 )呢?比如「极光推送」。也必须购买开发者帐号。因为所有的第三方都会将推送请求发至 APNs(Apple Push Notification service 苹果推送通知服务),所有推送均是由 APNs 下发。

  • 如何注册及正确的配置证书,参考这里 iOS 证书设置指南

原理篇


Tip 2:推送本身是 iOS 系统的行为,所以在 App 没有运行的时候:

  • 仍然能够推送及接收(通知中心通知、顶部弹窗、刷新 App 右上角的小圆点即 badge [以下简称 角标] 等都会由系统来控制和展示)

  • 收到推送时,是无法在 App 的代码中获取到推送内容的。因为沙盒机制,此时 App 的任何代码都不可能被执行

Tip 3:手机向 APNs 注册推送服务

  1. 在代码中注册推送服务:

    #ifdef __IPHONE_8_0if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {     UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil];
         [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    } else {     UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
         [[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes];
    }#else
         UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
         [[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes];#endif
  2. 在第1次触发这段代码的时候,会有1个系统弹窗,询问你是否允许该 App 要给你推送信息。当你选择 允许 时,系统会打包 App+手机唯一标识+证书 信息发送至 APNs 服务器注册推送服务,APNs 系统会对该手机安装的该 App 是否有推送权限进行验证,所以必须要加入了 Apple Deveice 的手机,使用对应 App 的 推送证书 才能够成功的注册。

  3. 如果注册成功,则可以在 AppDelegate.m 的如下方法中获取到 deviceToken,它是对 该手机+该App 组合的一个唯一标识,当使用远程推送时,只需将推送消息发给指定的 deviceToken 即可使推送信息传达给指定手机的指定 App 上。因此如果你使用第三方,就需要在这个方法里将 deviceToken 传给第三方。(在 iOS 9 为了更好的保护用户隐私,会出现多次重复删除/安装 App 导致 deviceToken 不断变化的情况)

    -(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {  
       [JPUSHService registerDeviceToken:deviceToken];//将 deviceToken 传给极光推送}
  4. 综上,注册及接收推送 必须 使用真机,必须 连网

Tip 4:推送从 服务端 --> App 代码 的过程

  1. 使用你们公司或第三方的服务端向 APNs 发送推送请求(包含 推送内容+App描述+手机描述 )

  2. APNs 接收并验证推送请求

  3. APNs 利用网络搜索并定位指定设备,下发推送

  4. 手机收到推送,系统根据 App 状态进行处理

    • 前台收到:

      • 系统会将推送内容传到 didReceiveRemoteNotification

    • 后台收到:

      • 如果开启了 Remote Notification ,系统将推送传到 didReceiveRemoteNotification:fetchCompletionHandler:(见 Tip 5 - 后台推送)

      • 展示弹窗、推送中心、声音、角标

    • 退出收到:

      • 如果点击推送弹窗/通知中心而启动 App,系统将推送传到 didFinishLaunchingWithOptions

      • 展示弹窗、推送中心、声音、角标

推送内容篇


Tip 5:推送分为 本地/远程 2 种类型:

  • 本地推送,可指定推送时间,在该时间准时弹出推送通知

  • 远程推送,分为 普通推送/后台推送/静默推送 3 种类型。存在延迟问题(由于 Tip 1 第 2 点,APNs 的不稳定及高峰时段的巨量请求所致)

    • 普通推送

      • 就是我们在手机上平时见到的推送

      • 包含声音、弹窗、角标、自定义字段

      • App

        • 处于前台,不会弹窗,可通过 didReceiveRemoteNotification 获取推送内容(前台弹窗的方法看这里

        • 处于后台,会弹窗,无法获取推送内容

        • 处于退出,会弹窗,无法获取推送内容

        • 点击图标启动,无法获取推送内容

        • 点击推送弹窗启动,在 didFinishLaunchingWithOptions 获取推送内容

      • 推送内容类似如下:

        {
          "_j_msgid" = 200806057;//第三方附带的 id,用于在后台查询送达情况
          aps =     {
            alert = "显示内容";
            badge = 1;//App 角标,可推送 n、+n、-n 来实现角标的固定、增加、减少
            sound = default;//推送声音,默认系统三全音,如需使用自己的声音,需要将声音文件拖拽&拷贝至 Xcode 工程目录任意位置,并在推送时指定其文件名
          };
          key1 = value1;//自定义字段,可设置多组,用于处理内部逻辑
          key2 = value2;
        }
    • 后台推送

      • 各种显示效果跟普通推送完全一样

      • 必须携带 "content-available" = 1;

      • 必须携带 alertbadgesound 中 至少 1 个字段

      • 仅 iOS 7 以后支持

      • 必须在 Xcode 工程中 TARGETS - Capabilities - Background Modes - Remote notifications 开启该功能,具体可参照 iOS 7 Background Remote Notification

      • App

        • 处于前台,可通过 didReceiveRemoteNotification 获取推送内容

        • 处于后台,可通过didReceiveRemoteNotification:fetchCompletionHandler: 获取推送内容//获取情况中与 普通推送 的唯一不同点,此时 iOS 系统允许开发者在 App 处于后台的情况下,执行一些代码,大概提供几分钟的时间,可以用来偷偷的刷新 UI、切换页面、下载更新包等等操作

        • 处于退出,无法获取推送内容

        • 点击图标启动,无法获取推送内容

        • 点击推送弹窗启动,在 didFinishLaunchingWithOptions 获取推送内容

    • 推送内容类似如下:

      {
          "_j_msgid" = 2090737306;
            aps =     {
            alert = "显示内容";
            badge = 1;
            "content-available" = 1;//必带字段
            sound = default;
          };
          key1 = value1;
      }
    • 静默推送

      • 必须携带 "content-available" = 1;,因此静默必然是后台的

      • 必须不携带 alertbadgesound

      • 可携带自定义字段

      • 在用户完全不知情的情况下被 App didReceiveRemoteNotification 接收并处理(仅限 App 处于前台时,其他状态因没有任何提示,故无法被用户触发并被代码截获。)

      • 推送内容类似如下:

        {
          "_j_msgid" = 3938587719;
          aps =     {
            alert = "";
            "content-available" = 1;//必带字段
          };
          key1 = value1;
        }

别名/标签篇


别名、标签、Registration ID 均是第三方提供的用于分类推送的功能。

Tip 6:推送根据对象的不同可分为:

  • 广播

    • 无差别发送给所有用户

  • 别名 alias 推送

    • 1 个手机的 1 款 App 只能设置 1 个 alias(可修改)

    • 用于指定一些基本属性,如男/女性用户

    • 推送时可指定多个 alias 来下发同一内容

    • 仅指定 alias 的用户能够收到推送

  • 标签 tag 推送

    • 可设置多个、可增加、清空

    • 用于指定多样的属性,如 1000+daily+discount 可用于表示 月消费超过 1k喜欢购买日用品偏好折扣商品 的用户

    • 如果要 删除,需要在上次设置时,将设置的 tags 保存至 NSUserDefaults,本次剔除不需要的 tag 后,再重新设置

    • 推送时可指定多个 tag 来下发同一内容

    • 手机如果设置了推送指定的多个 tag 中 任一个 tag,都能够收到推送消息。如指定 1000+globe+original (千元级消费者、全球购、原价),那么设置了 100+globe+discount(百元级消费者、全球购、折扣价)的用户可以收到该推送消息。

  • Registration ID 推送

    • 在 Tip 3 的第 3 步时将 deviceToken 提供给第三方之后,其服务器会自动生成的指向该手机的唯一 id

    • 可在推送时指定多个 id 来下发消息

    • 可用于对 核心用户旗舰用户 的 精准推送

自定义消息篇


Tip 7:自定义消息(以下简称 消息 )和推送的区别,消息:

  • 不需要 Apple 推送证书

  • 由第三方的服务器下发,而不是 APNs

  • 相比推送,更快速,几乎没有延迟,可用于 IM 消息的即时送达

  • 通过长连接技术下发消息,因此

    • 手机必须启动并与第三方服务器建立连接

    • 如果手机启动立刻切至后台,很可能连接没有建立

    • 手机必须处于前台才能收到消息

    • 手机从后台切回前台,会自动重新建立连接,并收到离线消息

  • 没有任何展示(弹窗、通知中心、角标、声音),因此可以:

    • 自定义字段实现 UI 效果

    • 完全在静默情况下处理 App 内部逻辑

    • 使用一些 App Store 审核不会通过的功能,在审核时关闭功能,上架后通过接收消息,开启相关功能

组合大招篇


Tip 8:tags 的组合技巧

  • 见 Tip 5 - 标签 tag 推送

  • 可以通过 App 自己的服务端来统计分析用户行为,然后将指定的 tags 发送至手机,手机接收后再为用户打上对应的 tags

Tip 9:推送+消息的组合技巧

  • 首先来看推送和消息各自的特性

    • 推送

      • 展示性:提醒作用

      • 延迟性:不稳定

      • 全局性:不论 App 处于哪种状态均能接收

      • 丢失性:因为各种网络原因,可能丢失。在客户端不能获取历史纪录。

    • 消息

      • 静默性:处理逻辑

      • 即时性:稳定

      • 前台性:只有处于前台才能收到

      • 存留性:必然送达。在客户端可以获取历史纪录。

  • 由于各自的特性都完全相反,因此 2 者结合使用是使得 App 性能最大化的必然选择:

    • 情景一:
      QQ/微信 聊天。会同时下发一组 推送+消息 ,如果用户没有启动 QQ,虽有延迟但必然能够先收到 推送,在受到推送的提醒之后,用户打开 App,此时收到了离线 消息,即时更新 UI,与好友即时的发送/接收消息。(在收到推送后,断网,然后启动 App,你会发现此时手机里并不会显示刚刚推送的内容,因为它是依靠拉取消息来刷新页面的,而不是不够稳定的推送)

    • 情景二:(期待您的补充...)

文章转自 Pikacode的简书
 

iOS 推送全解析的更多相关文章

  1. iOS 推送全解析,你不可不知的所有 Tips!

    本文旨在对 iOS 推送进行一个完整的剖析,如果你之前对推送一无所知,那么在你认真地阅读了全文后必将变成一个推送老手,你将会对其中的各种细节和原理有充分的理解.以下是 pikacode 使用 iOS ...

  2. iOS 10 推送全解析,注意事项

    本文旨在对 iOS 推送进行一个完整的剖析,如果你之前对推送一无所知,那么在你认真地阅读了全文后必将变成一个推送老手,你将会对其中的各种细节和原理有充分的理解.以下是 pikacode 使用 iOS ...

  3. iOS 推送问题全解答《十万个为啥吖?》

    Q 1:为啥收不到推送(1)? 如果收到推送时,App 在前台运行,那么: iOS 10 before 顶部横幅不会弹出.没有任何展示,你以为「没有收到推送」. iOS 10 after 如果没有实现 ...

  4. “iOS 推送通知”详解:从创建到设置到运行

    这是一篇编译的文章,内容均出自Parse.com的iOS开发教程,同时作者还提供了视频讲解.本文将带领开发者一步一步向着iOS推送通知的深处探寻,掌握如何配置iOS推送通知的奥义. 介绍一点点背景资料 ...

  5. iOS推送 再备

    这是一篇编译的文章,内容均出自Parse.com的iOS开发教程,同时作者还提供了视频讲解.本文将带领开发者一步一步向着iOS推送通知的深处探寻,掌握如何配置iOS推送通知的奥义. 介绍一点点背景资料 ...

  6. 手把手教你搞定个推iOS推送SDK集成

    以下是一位开发者在集成个推iOS推送SDK过程中的真实经历. 作者:Ezreallp 一次偶然的机会,公司的项目要用到推送,我自己本来就很懒,不愿意去弄整套APNS的流程,刚好之前跟朋友聊起过他们的产 ...

  7. iOS推送证书转pem文件

    iOS推送证书转 .pem文件. 推送证书转pem文件openssl x509 -in apns_miaobozhibo.cer -inform der -out apns_miaobozhibo.p ...

  8. IOS 推送-客户端处理推送消息

    IOS 推送-客户端处理推送消息 1.推送调用顺序 APN push的消息到达后,UIApplicationDelegate有两个方法和处理消息有关: 1)application:didReceive ...

  9. IOS 推送-配置与代码编写

    IOS 推送配置与代码编写 这里介绍IOS的推送,本文章已经在IOS6/7/8上都能运行OK,按照道理IOS9应该没问题. 大纲: 1.文章前提 2.推送介绍 3.推送文件账号设置 4.推送证书介绍 ...

随机推荐

  1. Django基础-过滤器

    1.可以通过过滤器来修改变量的显示,过滤器的形式是:{{ variable | filter }},管道符号'|'代表使用过滤器 2.过滤器能够采用链式的方式使用,例如:{{ text | escap ...

  2. C#:常规属性和自动实现的属性

    根据属性的实现方式,属性可分为自动实现的属性和常规属性. 常规属性需要具体的人为的实现get访问器或者set访问器,而且一般需要有一个字段与之相对应:而自动实现的属性的get和set访问器的实现部分被 ...

  3. fzu 1015 土地划分(判断线段相交+求出交点+找规律)

    链接:http://acm.fzu.edu.cn/problem.php?pid=1015  Problem 1015 土地划分 Accept: 714    Submit: 1675Time Lim ...

  4. iOS案例:读取指定目录下的文件列表

    // // main.m // 读取指定目录下的文件列表 // // Created by Apple on 15/11/24. // Copyright © 2015年 Apple. All rig ...

  5. iOS - UITouch

    前言 NS_CLASS_AVAILABLE_IOS(2_0) @interface UITouch : NSObject @available(iOS 2.0, *) public class UIT ...

  6. tomcat 启用Gzip 压缩进行优化

    打开conf/server.xml文件可以看到: <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->     <Con ...

  7. linux学习笔记2-命令总结2

    权限管理命令  chmod 其他权限管理名  chgrp  chown  umask ========================================================= ...

  8. POCO C++库学习和分析——任务

    1.任务的定义 任务虽然在Poco::Foundation库的目录中被单独划出来,其实可以被看成线程的应用,放在线程章节.首先来看一下Poco中对任务的描述: *task主要应用在GUI和Sever程 ...

  9. struct和class

    先概述一下: 1.C# 是纯面向对象语言,struct 与 class 都是继承Object,都是对象.struct 是值类型.class 是引用类型. 2.struct是值类型,在Stack上分配地 ...

  10. 转:为什么C++中空类和空结构体大小为1?

    参考:http://www.spongeliu.com/260.html 为什么C++中空类和空结构体大小为1? On November 17, 2010, in C语言, 语言学习, by spon ...