本文旨在对 iOS 推送进行一个完整的剖析,如果你之前对推送一无所知,那么在你认真地阅读了全文后必将变成一个推送老手,你将会对其中的各种细节和原理有充分的理解。以下是 pikacode 使用 iOS 推送的一些经验,欢迎互相交流,指出错漏之处。
推送服务可以说是所有 App 的标配,不论是哪种类型的 App,推送都从很大程度上决定了 App 的 打开率、使用率、存活率 。因此,熟知并掌握推送原理及方法,对每一个开发者来说都是必备技能,对每一个依赖 App 的公司来说都至关重要。
从 iOS 10 新增的 UserNotifications Framework
可以发现,Apple 整合了原有散乱的 API,并且增加了许多强大的功能。以 Apple 官方的角度来看,也必然是相当重视推送服务对 App 的影响、以及对 Apple iOS 生态圈长远发展的影响。
badge
[以下简称角标] 等都会由系统来控制和展示)。收到推送时,是无法在 App 的代码中获取到通知内容的。因为沙盒机制,此时 App 的任何代码都不可能被执行。在代码中注册推送服务:
#ifdef __IPHONE_8_0 if ([[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在第一次触发这段代码的时候,会有一个系统弹窗,询问你是否允许该 App 要给你推送信息。当你选择允许时,系统会打包 App+手机唯一标识+证书 信息发送至 APNs 服务器注册推送服务,APNs 系统会对该手机安装的该 App 是否有推送权限进行验证,所以必须要加入了 Apple Deveice 的手机,使用对应 App 的推送证书才能够成功的注册。如果注册成功,则可以在 AppDelegate.m
的如下方法中获取到 deviceToken
,它是对 该手机+该App 组合的一个唯一标识,当使用远程推送时,只需将推送消息发给指定的 deviceToken
即可使推送信息传达给指定手机的指定 App 上。因此如果你使用第三方,就需要在这个方法里将 deviceToken
传给第三方。(在 iOS 9 为了更好的保护用户隐私,会出现多次重复删除/安装 App 导致 deviceToken
不断变化的情况。有时会出现一条推送手机会收到 2 次的问题,属于 iOS 9 系统问题)。
如果以上步骤均成功,此时你能够取到第三方提供的设备注册 id。能否取到该 id 值,可以作为判断设备是否能够成功推送的标准(见 Tip 6 - Registration ID)。因为当你取到该值时必然:
推送证书配置正确(你拥有了推送权限)。设备成功在 APNs 注册并返回了deviceToken
(APNs 能识别你的设备了)。返回 的 deviceToken
传给第三方,成功在第三方生成了唯一标识注册 id(第三方能将你的设备信息传给 APNs 了)。综上,注册及接收推送必须使用真机,必须连网。didReceiveRemoteNotification
后台收到:如果开启了 Remote Notification
,系统将推送传到 didReceiveRemoteNotification:fetchCompletionHandler:
(见 Tip 5 - 后台推送),否则此时代码中收不到推送。展示横幅、通知中心、声音、角标。退出收到:如果点击推送横幅/通知中心而启动 App,系统将通知传到 didFinishLaunchingWithOptions
。展示横幅、通知中心、声音、角标。远程推送通知,分为 普通推送/后台推送/静默推送 3 种类型。存在延迟问题(由于 Tip 1 第 2 点,APNs 的不稳定及高峰时段的巨量请求所致)。
普通推送
就是我们在手机上平时见到的推送通知。包含声音、横幅、角标、自定义字段。App :处于前台,不会展示横幅,可通过didReceiveRemoteNotification
(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:
(iOS 7 after)获取通知内容(前台展示横幅的方法看这里)。处于后台,会展示横幅,无法获取通知内容。处于退出,会展示横幅,无法获取通知内容。点击图标启动,无法获取通知内容。点击通知横幅启动,在 didFinishLaunchingWithOptions
获取通知内容。通知内容类似如下:
{ "_j_msgid" = 200806057; // 第三方附带的 id,用于统计点击 aps = { alert = "显示内容"; badge = 1; // App 角标,可推送 n、+n、-n 来实现角标的固定、增加、减少 sound = default; // 推送声音,默认系统三全音,如需使用自己的声音,需要将声音文件拖拽&拷贝至 Xcode 工程目录任意位置,并在推送时指定其文件名 }; key1 = value1; // 自定义字段,可设置多组,用于处理内部逻辑 key2 = value2;}后台推送
各种显示效果跟普通推送完全一样。必须携带"content-available" = 1;
必须携带 alert
、badge
、sound
中 至少 1 个字段
。仅 iOS 7 以后支持。必须在 Xcode 工程中 TARGETS - Capabilities - Background Modes - Remote notifications 开启该功能,具体可参照 iOS 7 Background Remote Notification。App:处于前台,可通过didReceiveRemoteNotification
(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:
(iOS 7 after) 获取通知内容。处于后台,可通过 didReceiveRemoteNotification:fetchCompletionHandler:
获取通知内容 // 获取情况中与普通推送的唯一不同点,此时 iOS 系统允许开发者在 App 处于后台的情况下,执行一些代码,大概提供几分钟的时间,可以用来偷偷的刷新 UI、切换页面、下载更新包等等操作。处于退出,无法获取通知内容。点击图标启动,无法获取通知内容。点击推送横幅启动,在 didFinishLaunchingWithOptions
获取通知内容。通知内容类似如下:
{ "_j_msgid" = 2090737306; aps = { alert = "显示内容"; badge = 1; "content-available" = 1; // 必带字段 sound = default; }; key1 = value1;}
静默推送
没有任何展示效果。必须携带"content-available" = 1;
,因此静默必然是后台的。必须不携带 alert
、badge
、sound
。可携带自定义字段。App :处于前台,可通过didReceiveRemoteNotification
(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:
(iOS 7 after) 获取通知内容。处于后台,可通过 didReceiveRemoteNotification:fetchCompletionHandler:
获取通知内容 //获取情况中与普通推送的唯一不同点,此时 iOS 系统允许开发者在 App 处于后台的情况下,执行一些代码,大概提供几分钟的时间,可以用来偷偷的刷新 UI、切换页面、下载更新包等等操作。处于退出,无法获取通知内容。通知内容类似如下:{ "_j_msgid" = 3938587719; aps = { alert = ""; "content-available" = 1; // 必带字段 }; key1 = value1;}别名、标签、Registration ID 均是第三方提供的用于更方便地指定推送目标的功能。
NSUserDefaults
,本次剔除不需要的 tag 后,再重新设置。推送时可指定多个 tag 来下发同一内容。手机如果设置了推送指定的多个 tag 中任一个tag,都能够收到推送消息。如指定 『1000』+『globe』+『original』 (千元级消费者、全球购、原价),那么设置了 『100』+『globe』+『discount』(百元级消费者、全球购、折扣价)的用户可以收到该推送消息。Registration ID 推送第三方提供的功能。在 Tip 3 的第 3 步时将 deviceToken
提供给第三方之后,其服务器会自动生成的指向该手机的唯一 id。可在推送时指定多个 id 来下发消息。可用于对核心用户、旗舰用户的精准推送。通知 | 消息 | |
---|---|---|
送达时间 | 可能存在几秒延迟 | 几乎无延迟 |
获取时机 | 处于前台或后台能获取内容 | 仅处于前台能获取内容 |
离线内容 | 保留『一段时间』,过期会抛弃,无法查询历史内容 | 始终保留,可查询全部历史内容 |
系统展示 | 会展示(静默推送或App处于前台不展示) | 不展示 |
由于各自的特性都存在差异,因此二者结合使用是使得 App 推送性能最大化的必然选择:
情景一:QQ/微信 聊天。会同时下发一组通知+消息 ,如果用户没有启动 QQ,虽有延迟但必然能够先收到通知,在收到通知的提醒之后,用户打开 App,此时收到了离线消息,即时更新 UI,与好友即时地发送/接收消息。(在收到通知后,断网,然后启动 App,你会发现此时手机里并不会显示刚刚通知的内容,因为它是依靠拉取消息来刷新页面的,而不是不够稳定的通知)。情景二:(期待您的补充...)作者:pikacode- 极光
原文:iOS 推送全解析,你不可不知的所有 Tips!
知乎专栏:极光日报
新闻热点
疑难解答