iOS 普通推送和静默推送【公司要求用个推】

写在前面:应用场景很重要

如果你的公司有这样一个硬性的需求:用户将APP退出到后台的时候,当该用户收到推送的时候,不让用户感觉到推送过来了,不想打扰用户,但是还想拿到推送内容做一些事情。
在开发人员看来就是要实现如下功能:通知栏没有文字、没有内容、也没有声音,同时还要能实现客户端退出到后台时,还能执行xcode中的某个特定方法,那么此时就可以用到静默推送


必须知道个推中这些名词的含义,后面再出现这些名词就不做阐述了

离线:APP在后台运行/APP未启动/APP被杀死。
在线:APP在前台运行。
iOS 个推透传机制

iOS消息推送方式只有两种:

APNs的通知栏消息
个推的透传消息


以下说的是普通推送和静默推送的不同之处:

普通推送:当APP离线时,当有推送下达的时候,走的是APNs,所以手机会有铃声、手机顶部出现横幅/通知栏会收到通知。

  • APP离线时,相关方法什么时候执行,看下面1-4就知道了:
    1.只有点开通知栏中的通知进入APP/点开横幅中的通知进入APP,才会执行方法AAA;不点击不执行。
    2.点击应用图标进入APP,一定不会执行方法AAA。
    3.APP离线收到推送,点击应用图标进入APP/点开通知栏中的通知/点开横幅中的通知进入APP,会执行个推提供的 方法BBB的离线的透传消息
    4.APP在线收到推送,就不走APNs,会直接执行个推提供的 方法BBB的在线的透传消息

  • 总结:综合1-4,只要点开APNs发来的通知,那么先执行方法AAA,再执行个推的方法BBB


方法AAA
iOS 10之前,点通知,会调用如下方法。
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
     ...   
  做你想做的操作。例如更新UI,跳转界面,文字转语音并读出来,顶部弹框,操作userInfo中的内容。
     ...
}


iOS 10 及以后版本,点击通知,会调用如下方法
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler{
    
      ...
     做你想做的操作。例如更新UI,跳转界面,文字转语音并读出来,顶部弹框,操作userInfo中的内容。
      ...

    NSLog(@"didReceiveNotification:%@", response.notification.request.content.userInfo);
    // [ GTSdk ]:将收到的APNs信息传给个推统计
    [GeTuiSdk handleRemoteNotification:response.notification.request.content.userInfo];
    completionHandler();
    
}
方法BBB
  • 离线的透传消息:程序离线收到推送时候,当由离线进入在线的时候会执行(offLine为YES)。离线的时候不会执行该方法。
  • 在线的透传消息:程序在线收到推送的时候会执行(offLine为NO)。
接收个推推送的透传消息,就会执行如下代理方法
- (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId {

   拿到payloadData并转成字符串,然后做你想做的操作 :更新UI,跳转界面,文字转语音并读出来,顶部弹框

   if (!offLine) {// offLine为NO表示在线的透传消息。
       
    }else{// offLine为YES表示离线的透传消息。
    
   }
}

普通推送->服务端格式:
    $alertmsg=new DictionaryAlertMsg();//  必须有。    声明DictionaryAlertMsg的对象alertmsg
    $alertmsg->body=$msgContent;//  必须有。   为body赋值
    $alertmsg->title=SYS_ZH_NAME;//  必须有。   为title赋值

    $apn = new IGtAPNPayload();// 必须有。   声明IGtAPNPayload的对象apn
    $apn->alertMsg=$alertmsg;// 必须有alertmsg,且alertmsg中一定有title以及和body,因为这就是客户端在通知栏/横幅看到的标题和内容。
    $apn->contentAvailable=0;// 必须为0 
    $apn->sound=$client_notice;// 必须有sound
    $apn->badge=1;// 角标,可有可无
    $apn->add_customMsg("msg",$msgContent);// msg,可有可无
普通推送->客户端格式:
{
  "aps" : {
    "sound" : "notice_type1.caf",// 必须有sound
    "alert" : { // 必须有。    服务器端一定要有alertmsg
      "title" : "一秒招聘",// 必须有
      "body" : "有一条新的招工信息,点击查看详情" // 必须有 
    },
      "badge" : 1,// 角标,可有可无
  },
      "msg" : "有一条新的招工信息,点击查看详情"// msg可有可无
}

静默推送(安安静静的、用户感知不到):当APP离线时,当有推送下达的时候,通知栏/横幅中没有文字,同时也不会发出声音)

  • 1.应用被杀死或者应用未启动,不会执行如下的代码。
  • 2.应用退出到后台,当收到通知的时候(不点开通知,也不打开APP哦),就会立刻执行如下的代码CCC。 所以静默推送的定义也就出现了:应用收到通知后在后台(background)状态下可以执行下面一段代码CCC,可用于从服务器获取内容更新,做你想做的任何操作(跳转界面,文字转语音并读出声音),所以静默推送不同于其他推送,其他推送不能执行代码CCC。
  • 3.APP离线收到推送,点击应用图标进入APP/点开通知栏中的通知/点开横幅中的通知进入APP,会执行个推提供的 方法DDD的离线的透传消息
  • 4.APP在线收到推送,就不走APNs,会直接执行个推提供的 方法DDD的在线的透传消息
代码CCC
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
  ...   
做你想做的操作:这里我用苹果自带的文字转语音播放userInfo里面的指定内容
  ...
}
方法DDD
  • 离线的透传消息:程序离线收到推送时候,当由离线进入在线的时候会执行(offLine为YES)。离线的时候不会执行该方法。
  • 在线的透传消息:程序在线收到推送的时候会执行(offLine为NO)。
接收个推推送的透传消息,就会执行如下代理方法
- (void)GeTuiSdkDidReceivePayloadData:(NSData *)payloadData andTaskId:(NSString *)taskId andMsgId:(NSString *)msgId andOffLine:(BOOL)offLine fromGtAppId:(NSString *)appId {

   拿到payloadData并转成字符串,然后做你想做的操作 :更新UI,跳转界面,文字转语音并读出来,顶部弹框

   if (!offLine) {// offLine为NO表示在线的透传消息。
       
    }else{// offLine为YES表示离线的透传消息。
    
   }
}
静默推送->服务端格式:
    $apn = new IGtAPNPayload();
    $apn->alertMsg="";
    $apn->sound="com.gexin.ios.silence";
    $apn->contentAvailable=1;
    $apn->badge=1;
    $apn->add_customMsg("msg",$msgContent);
静默推送->客户端格式:
{
  "aps" : {
    "content-available" : 1,// 必须为1
    "badge" : 1 // 角标,可有可无
     // 一定不能有alert,因为alert如果有内容,在客户端的通知栏/横幅上会有通知。
  },
  "msg" : "有一条新的招工信息,点击查看详情" // 可有可无
}

服务端(我们公司是PHP)配置静默推送的格式如下(非常严格,非常严格,非常严格,一项不满足,就不是静默推送,那就变成了普通的有文字有声音的推送):

1.传入的alertMsg对应的值一定为空或者压根就不传alertMsg字段。

$apn->alertMsg="";
  1. contentAvailable的值一定为1
$apn->contentAvailable=1;

3.sound对应的值一定为com.gexin.ios.silence.改成其他的字符串的话,应用在后台收到推送时,会听到"铛"的一声。 或者sound对应的值为任意常量也可以实现静音(真机测试过一次,发现确实没有声音。测试次数过少,不是太敢断定,如果有想测试的,可以将sound的值设置成常量试一下)。


$apn->sound="com.gexin.ios.silence";

4.其他的倒无关紧要了,不影响静默推送的格式。


综合1.2.3.4,静默推送,php服务端要设置的核心代码必定是下面的这种格式:
    $apn->alertMsg="";// alertMsg一定不要有值
    $apn->sound="com.gexin.ios.silence";
    $apn->contentAvailable=1;// 一定为1

以我们项目中静默推送的实战演练

关键代码如下:
function IGtTransmissionTemplateDemo($appid,$appkey,$msgContent,$keyType,$keyId,$temp_ietm="",$client_notice="default"){
    $msg = array(
        'keyType' => $keyType,
        'keyId' => $keyId,
        'msg' => $msgContent,
        'nickname' => $temp_ietm
    );
    $msg = json_encode($msg);
    $template =  new IGtTransmissionTemplate();
    $template->set_appId($appid);//应用appid
    $template->set_appkey($appkey);//应用appkey
    $template->set_transmissionType(2);//透传消息类型
    $template->set_transmissionContent($msg);//透传内容
  
    $apn = new IGtAPNPayload();
    $alertmsg=new DictionaryAlertMsg();
    $alertmsg->body=$msgContent;
    $alertmsg->actionLocKey="ActionLockey";
    $alertmsg->locKey=$msgContent;
    $alertmsg->locArgs=array("locargs");
    $alertmsg->launchImage="launchimage";
    //        IOS8.2 支持
    $alertmsg->title=SYS_ZH_NAME;
    $alertmsg->titleLocKey=SYS_ZH_NAME;
    $alertmsg->titleLocArgs=array("TitleLocArg");
    // $apn->alertMsg=$alertmsg;
    $apn->alertMsg="";
    $apn->sound="com.gexin.ios.silence";
    $apn->contentAvailable=1;
    $apn->badge=1;
    $apn->add_customMsg("payload","payload");
    $apn->add_customMsg("keyType",$keyType);
    $apn->add_customMsg("keyId",$keyId);
    $apn->add_customMsg("nickname",$nickname);
    $apn->add_customMsg("msg",$msgContent);
 
    $apn->category="ACTIONABLE";
    $template->set_apnInfo($apn);

    return $template;
}

来来来,一起截图圈重点。
image.png
以我们公司做的产品为例:雇主在使用iOS客户端发单的时候,后台监听到雇主成功发单后,会访问个推提供的某个SDK,经过一系列的操作。最终iOS客户端在后台收到推送通知的时候,一定会执行xcode中的如下代码
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{  
    NSLog(@"%@",userInfo);
}

经过真机调试,符合静默推送(因为我的iOS真机设备在后台收到推送时,没有声音,没有文字,并且还执行了一段xcode中的方法,方法就在下面),

以下截图是采用静默推送的方式,iOS客户端在后台收到通知时,执行xcode中的下面的方法,打印的userInfo中的内容。

  • (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
// userInfo中的内容
{
  "_gurl_" : "sdk.open.extension.getui.com:8123",
  "_gmid_" : "OSL-0613_EK270rsK7ZA3frD1xut5B5:6b5a07fe4ef9477caa22c01a93aed697:4f561d975e202fcdb57a47068c5b956b",
  "keyId" : "1134",
  "aps" : {
    "content-available" : 1,
    "mutable-content" : 1,
    "badge" : 1,
    "category" : "ACTIONABLE"
  },
  "keyType" : "7",
  "payload" : "payload",
  "msg" : "有一条新的招工信息,点击查看详情",
  "_ge_" : "1"
}

iOS 普通推送和静默推送【公司要求用个推】_第1张图片
image.png

PS:既然已经走到了这里,那么我们就可以实现这种恶搞的效果了:

用户已经将App退出到后台,此刻来了一个推送,通知栏没有标题和内容,但是用户能听到一段语音。用户很懵逼,不知道是哪个APP发出的声音。
实现方法:按照上面的一模一样的步骤配置成静默推送。然后配置如下方法,因为下面的这个方法是静默推送必定走的方法(我们在里面偷偷地写上了文字转语音的代码,哈哈哈~~~)

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
   // 拿到userInfo中的关键内容,然后用苹果自带的功能进行文字转语音
}
iOS 普通推送和静默推送【公司要求用个推】_第2张图片
image.png

静默推送遇到的坑(静默推送硬生生的做成了普通推送)。

下面就来看看由于我们公司的后台,设置静默推送不规范导致的iOS客户端在后台收到推送时,有声音有文字的情况。本质上变成了普通推送(有声音有文字)。

我们公司的后台说是 设置的是静默推送,但实际上从他给我PHP文件来看,没按照静默推送的格式来。导致客户端在后台时,通知栏会有通知的内容展示,同时也会有声音。
这与静默推送(收到推送,没有声音没有文字)的标准相悖了,导致一直在这个问题上卡了很久。
在此记录下来,以备不时之需,希望有遇到和我一样问题的朋友,少走一些坑。


后台设置的有问题的代码
function IGtTransmissionTemplateDemo($appid,$appkey,$msgContent,$keyType,$keyId,$temp_ietm="",$client_notice="default"){
    $msg = array(
        'keyType' => $keyType,
        'keyId' => $keyId,
        'msg' => $msgContent,
        'nickname' => $temp_ietm
    );
    $msg = json_encode($msg);
    $template =  new IGtTransmissionTemplate();
    $template->set_appId($appid);//应用appid
    $template->set_appkey($appkey);//应用appkey
    $template->set_transmissionType(2);//透传消息类型
    $template->set_transmissionContent($msg);//透传内容
   

    $apn = new IGtAPNPayload();
    $alertmsg=new DictionaryAlertMsg();
    $alertmsg->body=$msgContent;
    $alertmsg->actionLocKey="ActionLockey";
    $alertmsg->locKey=$msgContent;
    $alertmsg->locArgs=array("locargs");
    $alertmsg->launchImage="launchimage";
    //        IOS8.2 支持
    $alertmsg->title=SYS_ZH_NAME;
    $alertmsg->titleLocKey=SYS_ZH_NAME;
    $alertmsg->titleLocArgs=array("TitleLocArg");

    $apn->alertMsg=$alertmsg;
    $apn->contentAvailable=1;
    $apn->sound=$client_notice;
    $apn->badge=1;
    $apn->add_customMsg("payload","payload");
    $apn->add_customMsg("keyType",$keyType);
    $apn->add_customMsg("keyId",$keyId);
    $apn->add_customMsg("nickname",$nickname);
    $apn->add_customMsg("msg",$msgContent);
    $apn->category="ACTIONABLE";
    $template->set_apnInfo($apn);

    return $template;
}

截图展示后台设置的有问题的关键代码
iOS 普通推送和静默推送【公司要求用个推】_第3张图片
image.png

分析静默推送格式不规范会出现哪些问题

以下情况满足的前提条件:②的值为1,即静默推送

  • ①是变量,当①有值时,iOS客户端在后台收到推送时,通知栏会有推送标题和内容的展示。

  • ①是变量,当①没有值时,iOS客户端在后台收到推送时,通知栏没有推送标题和内容的展示。

  • ③是变量,当③有值时:

    • 如果xcode中放置的音频文件和sound对应的值一样,那么iOS客户端在后台收到推送时,就会自动
      读出音频文件的声音。
      - 的是的撒的
    • 如果xcode中放置的音频文件和sound对应的值不一样,或者xcode中根本没有音频文件,那么iOS客户端在后台收到推送时,就会听见铛的一声。
  • ③是变量,当③没有值时,那么iOS客户端在后台收到推送时,就会听见铛的一声。

PS:以下①和③不是变量的情况。当然仍满足②是静默推送
PHP后台代码中,将①设置成空字符串,iOS客户端在后台收到推送时,通知栏没有推送标题和内容的展示
PHP后台代码中,将③设置成非com.gexin.ios.silence的任意字符串或者不和xcode中的音频文件重名的,这时iOS客户端在后台收到推送的时候,就会听见铛的一声。


打印的内容如下: 经过测试iOS客户端在后台收到推送时,会读取xcode中存放的音频文件notice_type1.caf,同时通知栏会展示标题(一秒招聘) 和 内容(有一条新的招工信息,点击查看详情)
{
  "_gurl_" : "sdk.open.extension.getui.com:8123",
  "_gmid_" : "OSL-0613_WzPFJBFf1BAXhORnt7bRn2:e34f8f8517034efa9773415a8d33190f:ada94b80070b9be823f25f8ea4577b92",
  "keyId" : "1131",
  "aps" : {
    "sound" : "notice_type1.caf",
    "content-available" : 1,
    "alert" : {
      "loc-args" : [
        "locargs"
      ],
      "title" : "一秒招聘",
      "title-loc-args" : [
        "TitleLocArg"
      ],
      "title-loc-key" : "一秒招聘",
      "action-loc-key" : "ActionLockey",
      "body" : "有一条新的招工信息,点击查看详情",
      "loc-key" : "有一条新的招工信息,点击查看详情",
      "launch-image" : "launchimage"
    },
    "mutable-content" : 1,
    "category" : "ACTIONABLE",
    "badge" : 1
  },
  "keyType" : "7",
  "payload" : "payload",
  "msg" : "有一条新的招工信息,点击查看详情",
  "_ge_" : "1"
}
以上xcode控制台输出的json数据若要符合静默推送,那么必须做如下修改:对应的让服务端改掉对应的内容即可
iOS 普通推送和静默推送【公司要求用个推】_第4张图片
image.png

注意:本地存放的音频文件一定要放在该工作目录下
iOS 普通推送和静默推送【公司要求用个推】_第5张图片
image.png

PS:当然,如果返回的json数据里面sound的值为"1"或者任意字符串就会出现铛的一声,如果sound的值能和xcode的该路径下的音频文件匹配上,那么iOS客户端在后台收到推送时,会自动读出匹配到的音频。


写到这里我在想,如果公司没有强制说要用户在后台的时候,不许在通知栏中展示内容和标题以及发出声音,那么你可以设置成在展示内容和标题以及会发出声音。此时content-available设置为0和1就没有区别了,因为你只要保证如下条件满足即可:


   // alert里面有内容 ; 必须保证有值
   $apn->alertMsg=$alertmsg;
    // 静默推送 1
    $apn->contentAvailable=1;
    // sound对应的值是字符串com.gexin.ios.silence或者sound对应的值是常量会静音。 
    // sound对应的值是常量,比如$apn->sound=1;会听见铛的一声。
    // sound对应的值是变量,如果xcode中的音频文件名能和变量对应起来,推送来的时候,会自动检索xcode中的音频文件,检索到就会读出来
    $apn->sound=$client_notice;

我们项目中有这么个需求,APP中所有的音频要么采用文字转语音,要么用录制好的音频文件,但是因为推送的内容有变量,比如xxx签到了您的家政服务工作,所以不能用录制好的音频文件。所以需求就这么出现了:
当推送过来的时候(在线、离线),有通知、系统推送来的铛的一声没做要求、 文字转语音。明确规定,要对推送过来的通知内容进行文字转语音,不能用录制好的音频文件

分析(在线走个推,所以肯定能文字转语音,这里就不说了,以下分析的是离线(APP在后台运行/APP未启动/APP被杀死)的情况),不考虑点击通知进入APP的情况:
  • 如果单纯采用标准的静默推送,肯定达不到效果:因为只要符合静默推送的格式,来推送的时候,没有通知也没有声音(铛的一声)。

    • 当程序在后台的时候,推送来的时候,还能执行一段方法(可以文字转语音读出来)。效果:通知栏没有通知、铛的一声有无(看后台配置的sound对应的值)、文字转语音。所以不符合
    • APP被杀死或者APP未启动的时候,推送来的时候,不会执行这段方法(代码都不走了,肯定没法进行文字转语音了)。效果:通知栏没有通知、铛的一声有无(看后台配置的sound对应的值)、没有文字转语音。所以不符合
  • 如果单纯采用标准的普通推送,肯定达不到效果

    • 当程序离线(APP在后台运行/APP未启动/APP被杀死)的时候,推送来的时候,有通知,有声音(铛的一声,不是文字转语音的声音),由于不能用录制好的音频文件,所以我将xcode里面的音频文件删除了,因此不会读音频文件。普通推送不点击通知,离线不会走xcode项目里面的任何代码,所以没法进行文字转语音。效果:通知栏有通知、铛的一声有无(看后台配置的sound对应的值)、没有文字转语音。所以不符合
  • 采用普通推送+静默推送混合的方式,APP在后台运行能符合要求,APP未启动/APP被杀死不符合要求:
    先说一下普通推送+静默推送混合的后台大致格式,具体参考上面的代码:

    // alert里面有内容 。普通推送alert中有内容
    $apn->alertMsg=$alertmsg;
    // 静默推送 1  之所以设置为1,是为了走xcode里面的某个方法
    $apn->contentAvailable=1;
    // sound对应的值是字符串com.gexin.ios.silence或者sound对应的值是常量会静音。 
    // sound对应的值是常量,比如$apn->sound=1;会听见铛的一声。
    // sound对应的值是变量,如果xcode中的音频文件名能和变量对应起来,推送来的时候,会自动检索xcode中的音频文件,检索到就会读出来
    $apn->sound=1;// 有铛的声音
  • 当程序在后台的时候,推送来的时候,有通知,铛的一声有无(看后台配置的sound对应的值),同时还能执行xcode里面的一段方法(该段方法里面写了文字转语音的代码)。效果:通知栏有通知、铛的一声有无(看后台配置的sound对应的值)、文字转语音。所以符合
  • APP被杀死或者APP未启动的时候,推送来的时候,通知栏有通知、铛的一声有无(看后台配置的sound对应的值)、没有文字转语音。所以不符合
所以,针对我们公司的要求,只能采用普通推送+静默推送混合的方式,这种方式APP在后台的时候满足公司的需求,APP被杀死或者APP未启动的时候,不满足需求,这是苹果的机制问题,这是最能契合公司需求的做法了,目前只能这么做,如果有其他方法,朋友们麻烦@一下我哦。

................................

你可能感兴趣的:(iOS 普通推送和静默推送【公司要求用个推】)