iOS推送实验室-做之前看看我少走弯路
写作原因:网上看了很多推送文章都没有完美的解答我的疑惑;主要有以下两点,1:推送来了我点击应用图标进入应用怎么取到推送消息?2:怎么保证一定能够获取到远程推送的消息,因为有的远程推送消息很重要;这里主要验证会在哪几个回调收到内容,行为(手机顶部有消息弹出我叫做行为)是怎样的
其实收到推送到进入应用有这么5种场景,1:程序运行在前台来推送了,2:程序运行在后台来推送了,我点击推送消息进入程序,3:程序运行在后台来推送了,我点击应用图标进入程序,4:程序没运行来推送了,我点击推送消息进入程序,5:程序没运行来推送了,我点击应用图标进入程序
讲推送的文章实在是太多了,我就不啰嗦原理(http://www.jianshu.com/p/ace1b422bad4)和配置(http://www.jianshu.com/p/db9c95c9e29f)了;我直接拿我的项目(项目最低适配iOS8)来从以下三个方面回答上面5种情况下会在哪些回调收到内容,行为是怎样的
一:本地推送
我们从UIApplicationDelegate中找一找哪些是和本地推送相关的回调,可以发现有下面这三个;假设现在你不知道B和C什么意思,我也是后面才想起来的(有点痴呆了),不过都写了这么多了就不想删除了:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {}//A回调
(void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandler {}//B回调
- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler {}//C回调
细心的小伙伴可能说了,A回调后面不是说让你用XXX回调代替吗?哥们,这个函数有效期是iOSx-iOS10,你慌啥啊,等iOS11出来了再换呗;然后B和C描述一样说明功能一样,我们这里使用B,C是iOS9-iOS10,B是iOS8-iOS10我们要适配版本;这里我们还不知道B和C是什么时候调用,没关系我们慢慢来做实验,我们把A和B写到AppDelegate.m中;然后我们分别来进行试验
1:A和B回调都写上
1)程序运行在前台来本地推送了
我在我项目中创建一个按钮,给按钮响应添加一个只触发一次的本地推送(代码就不写了,都会写);在这两个回调中打上断点,现在我们运行程序点击按钮然后让程序运行到前台,慢慢等待本地推送触发(看点网站)~~~
断点如约执行,先是执行了A回调,我们XCode下方打印一下notification看看是什么鬼:
{fire date = 2016年10月24日 星期一 中国标准时间 下午4:03:08, time zone = Asia/Shanghai (GMT+8) offset 28800, repeat interval = 0, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = (null), user info = {
action = GENERAL;
"company_no" = 10295;
content = "\U4e8b\U52a1\U63d0\U9192: boot1";
"from_user_no" = 21175;
"target_id" = 15812;
time = 1477296248000;
"to_user_no" = 21175;
type = CALENDARTIP;
unread = 1;
}}
内容大概是触发这次推送的时间(fire date)+时区(time zone)+间隔周期(repeat interval)+重复次数(repeat count)+下次触发时间(next fire date)+添加到本地推送的内容(user info),好了没有什么问题,然后我们继续执行程序,B怎么不执行了?我擦,我们回过头看看B回调上面的注释:
// Called when your app has been activated by the user selecting an action from a local notification.
// A nil action identifier indicates the default action.
// You should call the completion handler as soon as you've finished handling the action.
依我考过英语四级(只是考过,没有考过)的水平翻译一下,这个回调被触发当用户从本地通知点击一个action,一个不为nil的action指示默认的action,你应该写一下完成回调当你握住了这个action(我已经被我的英语水平屈服了);不过意思大概懂了是从通知栏点击某一项激活项目,那么啥算激活项目?后台进入前台还是被杀死的项目启动了?不管了,结论很重要:
会触发哪些回调:应用在前台,只会触发A回调
什么行为:没行为
2)程序运行在后台来本地推送了,我点击推送消息进入程序
依然和上面一样,我们点击按钮然后把程序运行到后台;在这两个回调中打上断点,慢慢等待本地推送触发(看点网站)~~~
好了,这时候手机顶部弹出消息了,应用图标右上角也有数字了,注意这时候两个回调一个都没有触发,就像这样:
通知栏有通知 应用图标有数字好了,我们兴奋的点一下通知栏,好的,A回调先被触发,我们打印一下notification:
{fire date = 2016年10月24日 星期一 中国标准时间 下午4:25:21, time zone = Asia/Shanghai (GMT+8) offset 28800, repeat interval = 0, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = (null), user info = {
action = GENERAL;
"company_no" = 10295;
content = "\U4e8b\U52a1\U63d0\U9192: boot3";
"from_user_no" = 21175;
"target_id" = 15815;
time = 1477297581000;
"to_user_no" = 21175;
type = CALENDARTIP;
unread = 1;
}}
这个和上面的一样,不解释了,我们继续运行,好的B没有被触发,结论:
会触发哪些回调:点击通知栏从后台进入前台,只会触发A回调
什么行为:图标有数字,通知栏有通知
3)程序运行在后台来本地推送了,我点击应用图标进入程序
依然重复上面的步骤,点击按钮,把应用运行到后台,在这两个回调中打上断点,慢慢等待本地推送触发(看点网站)~~~
好了,这时候手机顶部弹出消息了,应用图标右上角也有数字了;注意这时候两个回调一个都没有触发,和上面的一样,然后我们点击应用图标进入应用:
点应用图标进入前台好的,你没有看错,A和B一个都没有触发,那我怎么获取到本地推送消息呢,你在逗我吗?结论:
会触发哪些回调:点击应用图标从后台进入前台,不会触发A回调,不会触发B回调
什么行为:图标有数字,通知栏有通知
4)程序没运行来本地推送了,我点击推送消息进入程序
我还在想这个要怎么打断点呢,因为程序没有运行。。。那我们就用一个UIAlertView显示notification吧(后面都用显示代替断点,因为XCode8进入断点实在是太慢);这里我们在启动程序时:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {}
这里面也显示一下launchOptions,因为程序启动万一附带有数据呢;同样的,点击按钮然添加本地通知后把程序杀死,慢慢等待本地推送触发(看点网站)~~~
好了,手机顶部推送如约而至,这时候回调肯定也不会触发,我们点击通知栏启动程序:
启动参数果然,启动参数里面带上了我们的推送内容;我们点击取消,然后A和B的提示框没有弹出,结论:
会触发哪些回调:程序为运行,点击通知栏启动程序,不会触发A、B回调,启动参数有推送内容
什么行为:图标有数字,通知栏有通知
5)程序没运行来本地推送了,我点击应用图标进入程序
依然如此,我们进应用点击按钮添加推送,然后杀死程序,等待推送;推送如约而至,这时候两个回调也不会触发,我们点击应用图标进入程序,会看到下面的效果:
启动参数点击取消,继续运行,然后A和B的提示框没有弹出,结论:
会触发哪些回调:程序未运行,点击应用图标启动程序,不会触发A、B回调,启动参数也没有内容
什么行为:图标有数字,通知栏有通知
好了,是不是很蛋疼,A和B加一起B永远都不会触发;接下来我们来验证只写其中一个函数是怎样的(这时候我还没有想起来B是干啥的,还在试错呢)
2:只写A回调
验证方法和上面的一样,我就不截图了(但是我真的重复了一遍哦),直接写结论
1)程序运行在前台来本地推送了
会触发哪些回调:应用在前台,会触发A回调
什么行为:没有行为
2)程序运行在后台来本地推送了,我点击推送消息进入程序
会触发哪些回调:应用在后台,点击通知栏进入前台,会触发A回调
什么行为:图标有数字,通知栏有通知
3)程序运行在后台来本地推送了,我点击应用图标进入程序
会触发哪些回调:应用在后台,点击应用图标进入前台,不会触发A回调
什么行为:图标有数字,通知栏有通知
4)程序没运行来本地推送了,我点击推送消息进入程序
会触发哪些回调:程序未运行,点击通知栏启动程序,不会触发A回调,启动参数有内容
什么行为:图标有数字,通知栏有通知
5)程序没运行来本地推送了,我点击应用图标进入程序
会触发哪些回调:程序未运行,点击应用图标启动程序,不会触发A回调,启动参数也没有内容
什么行为:图标有数字,通知栏有通知
好的,和上面一样,所以我们现在只写B回调验证一下
3:只写B回调
马上就可以做完本地推送了,有点激动;同理,直接来结论(我还是没有想起来B是干嘛的)
1)程序运行在前台来本地推送了
会触发哪些回调:应用在前台,不会触发B回调
什么行为:没有行为
居然没有触发,我都不相信自己的眼睛,于是我又重新测了一次,还是没有触发
2)程序运行在后台来本地推送了,我点击推送消息进入程序
会触发哪些回调:应用在后台,点击通知栏进入前台,不会触发B回调
什么行为:图标有数字,通知栏有通知
3)程序运行在后台来本地推送了,我点击应用图标进入程序
会触发哪些回调:应用在后台,点击应用图标进入前台,不会触发B回调
什么行为:图标有数字,通知栏有通知
4)程序没运行来本地推送了,我点击推送消息进入程序
会触发哪些回调:程序未运行,点击通知栏启动程序,不会触发B回调,启动参数有内容
什么行为:图标有数字,通知栏有通知
5)程序没运行来本地推送了,我点击应用图标进入程序
会触发哪些回调:程序未运行,点击应用图标启动程序,不会触发B回调,启动参数也没有内容
什么行为:图标有数字,通知栏有通知
好了,现在是时候谷歌一下B函数到底是干嘛的了,为啥都没有触发;我在这里找到了答案http://www.bozhiyue.com/ios/2016/0811/358999.html,好吧我终于知道什么意思了;B函数是点击这里会触发:
这才是action啊,我的天所以我们把结论重新整理一下(好吧,这时候终于步入正轨了),
1)程序运行在前台来本地推送了
会触发哪些回调:应用在前台,不会触发B回调(因为没有点击action)
什么行为:没有行为
2)程序运行在后台来本地推送了,我点击推送消息的action
会触发哪些回调:程序运行在后台,推送action不会进入前台,不会触发B回调
什么行为:图标有数字,通知栏有通知
还是没有触发,我去;这又是闹哪样?能不能少点套路,我们继续谷歌;http://www.jianshu.com/p/803bfaae989e这里说到远程推送可以触发,那是不是本地推送不会触发呢?而且我也是加了自定义action的:
自定义action那到底是什么意思呢?我们继续往下面做实验,看看到后面的远程推送会不会触发
3)程序运行在后台来本地推送了,我点击应用图标进入程序
会触发哪些回调:应用在后台,点击应用图标进入前台,不会触发B回调(因为没有点击action)
什么行为:图标有数字,通知栏有通知
4)程序没运行来本地推送了,我点击推送消息的action
会触发哪些回调:应用未运行,点击推送消息action不会启动程序,不会触发B回调
什么行为:图标有数字,通知栏有通知
5)程序没运行来本地推送了,我点击应用图标进入程序
会触发哪些回调:应用在后台,点击应用图标进入前台,不会触发B回调(因为没有点击action)
什么行为:图标有数字,通知栏有通知
好吧,我得再写一篇文章专门写怎么自定义推送action还有上面5种情况对应的行为了,可这里不触发可能是我没有加正确;
那我们现在来看看远程推送,这次终于弄明白A和B是干嘛的了;所以我们做本地推送两个回调都要写上,因为作用不一样。
二:远程推送,不使用第三方
远程推送必须真机,提醒一下!我们先在UIApplicationDelegate中找找远程推送相关的回调(和action有关的我们就不管了,另起一篇文章写action):
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {}//B
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {}//C
然后B回调https://docs.kii.com/cn/guides/cloudsdk/ios/managing-push-notification/push-to-user/receiving-messages/有解释说的是静默推送使用B回调,那么啥是静默推送呢?http://www.devlizy.com/ios-ding-shi-huo-qu-he-jing-mo-tui-song/这里给出了解释,也就是说你的程序在后台收到远程推送将直接触发这个回调而不用等应用进入前台,这么好玩,但是我们不验证这个;C回调就是收到远程推送了,C回调注释说明了用B接收静默推送,用XXX接收远程推送,我们不管;所以我们在AppDelegate.m中加上C一个回调就行了,内容就像这样:
远程推送回调由于我们项目已经集成了个推三方推送,要实现不使用第三方推送来测试;我们就要用到下面的工具来测试环境模拟推送了(你们不用下载,看我表演就行了)。
测试环境模拟远程推送这个工具我们下载安装后,启动就看到这个界面:
模拟推送工具怎么用就不说了吧,这么简单;我们先获取设备token,然后我们在获取时得到下面的错误:
获取token失败赶紧谷歌,找到了http://blog.csdn.net/u013263917/article/details/24712797和http://blog.csdn.net/soindy/article/details/46537095,于是我重新在官网生成一次开发者证书、推送证书和描述文件等;还是一样的问题;我在想是不是因为我项目集成了个推,然后我把个推关闭了;还是一样的问题(此刻我心里有点慌);然后http://www.jianshu.com/p/c2bb07786fd1说打开这里:
开启推送这个红色的错误不用解决,然后可以看到这里:
测试和正式不一样这时候就能得到设备token了:
能获取token了用上模拟推送的工具开始验证!
一:测试C回调
1)程序运行在前台来远程推送了
启动真机,然后我们点击这里模拟推送(以下用"push"按钮代替说明):
推送等待;然后我们程序弹窗了,内容就是工具模拟的内容,结论:
会触发哪些回调:应用在前台,会触发C回调
什么行为:没有行为
2)程序运行在后台来远程推送了,我点击推送消息进入程序
把应用切换到后台,点击工具的"push"按钮,等待推送;点击推送进入前台弹出提示框,结论:
会触发哪些回调:程序运行在后台,点击推送会进入前台,触发C回调
什么行为:图标有数字,通知栏有通知
3)程序运行在后台来远程推送了,我点击应用图标进入程序
依然,切换到后台,点击工具"push"按钮,等待;没有弹窗,结论:
会触发哪些回调:程序运行在后台,点击应用图标会进入前台,不会触发C回调
什么行为:图标有数字,通知栏有通知
4)程序没运行来远程推送了,我点击推送消息进入程序
我们把程序杀死,点击工具"push"按钮,等待;点击推送栏进入程序,启动参数显示了,没有触发A回调,结论:
会触发哪些回调:程序未运行,点击推送进入前台,不会触发C回调,启动参数有内容
什么行为:图标有数字,通知栏有通知
5)程序没运行来远程推送了,我点击应用图标进入程序
杀死程序,点击"push"按钮,等待;点击图标启动程序,启动参数没有,没有触发C回调,结论:
会触发哪些回调:程序未运行,点击应用图标进入前台,不会触发C回调,启动参数无内容
什么行为:图标有数字,通知栏有通知
这个和本地推送是吻合的,但是发现了没,原生远程推送没办法保证用户一定能得到数据,因为从应用图标进入程序是得不到的,所以只有服务器存客户端进应用就拉取是不?那么第三方推送做了吗?我们满怀期待的来测试一下
三:远程推送,使用个推等第三方
同样的,我们不讲怎么配置个推http://docs.getui.com/mobile/ios/overview/;集成之后个推接收推送的回调长得像这样(你们不用集成,看我就行):
- (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId {}//A
在回调中加上显示内容代码:
个推回调同样的我们要加上原生的远程推送来看看会不会也显示出来:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {}//B
同样的我们启动程序,然后让后台给我们发一个个推推送
1)程序运行在前台来个推推送了
等待推送;这时候个推A回调弹窗了,原生B没有执行,结论:
会触发哪些回调:应用在前台,会触发个推A回调,不会触发原生B回调
什么行为:没有行为
这里我们可以认为是个推推过来的,不管原生的事情,先这么理解吧
2)程序运行在后台来个推推送了,我点击推送消息进入程序
然后,我们把程序切换到后台,等待推送;这时候我们点击推送进入前台,个推A显示了,然后原生B也弹出来了,我们来看看原生的是什么内容:
原生的被触发了这个内容不是我让后台发送的内容,应该是个推那边附加的,那么结论:
会触发哪些回调:程序运行在后台,点击推送会进入前台,触发个推A回调,然后触发原生B回调
什么行为:图标有数字,通知栏有通知
3)程序运行在后台来个推推送了,我点击应用图标进入程序
依然如此,切换到后台,等待推送;点击应用图标进入程序,个推A触发,原生B没有触发,结论:
会触发哪些回调:程序运行在后台,点击应用图标会进入前台,触发个推A回调,不触发原生B回调
什么行为:图标有数字,通知栏有通知
这就比较好了嘛,应用图标进入都会有推送内容,这就是我想要的功能
4)程序没运行来个推推送了,我点击推送消息进入程序
把程序杀死,等待推送;点击推送进入程序,启动参数有内容但不是我让后台发送的内容(也是附加内容),然后会触发个推A回调,不会触发原生B回调,结论:
会触发哪些回调:程序未运行,点击推送进入程序,启动参数有内容,触发个推A回调,不触发原生B回调
什么行为:图标有数字,通知栏有通知
附加内容还是很好理解的,就是提醒你一下有消息,然后等你正在进入的时候再给你发真正的内容
5)程序没运行来个推推送了,我点击应用图标进入程序
依然杀死程序,等待推送;点击应用图标进入程序,启动参数没有内容,个推A触发,原生B没有触发,结论:
会触发哪些回调:程序未运行,点击图标进入程序,启动无内容,触发个推A回调,不触发原生B回调
什么行为:图标有数字,通知栏有通知
好了,第三方推送能保证消息一定能发送到用户,这点比较好的,你可能会说个推怎么做到的?给你一个网址去看看
四:结论
英语真的是一个好东西,做东西之前要多看文章,看不懂就要多验证
一:使用本地推送
1:推送内容会在启动参数或本地推送回调中获取,2:应用图标进入程序不会获取到任何内容
二:使用原生远程推送
1:推送内容会在启动参数或远程推送回调中获取,2:应用图标进入程序不会获取到任何内容,3:服务器要保存推送内容,以免重要数据丢失
三:使用个推三方推送
1:用户在程序就一定能从个推回调中得到推送内容,2:原生远程推送回调和启动参数是附加内容不用处理
五:花絮
首先,自己懵逼了居然忘记下面这个回调是干嘛的,于是花了一上午来才坑:
-(void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandle{}//自定义推送消息的action被点击
然后,我前面的个推推送测试都是在测试环境进行的没有任何问题,然后第二天我又去了正式环境发现表现不一样了,我有点慌了马上加了个推服务方QQ,然后终于明白了,是自己证书的问题http://www.qingpingshan.com/rjbc/ios/139338.html;还终于弄明白了公司在个推首页为啥注册了两个同样的应用:
两个一样的应用这就是一个拿来测试环境,一个正式环境啊!
然后我已经写好了自定义action,你们可以点看看下面的拓展阅读!