推送-JPush(极光推送)的使用

前言

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

为什么要使用第三发推送

自己做推送从开发成本上来说需要专人进行开发,并且需要一定数量的服务器和带宽支持,在开发完成后的使用过程中还需要有专人进行维护。

但是如果使用第三方,你只需要集成SDK就可以实现功能,不仅减小了开发成本与维护成本,甚至在推送稳定性上第三方也会比自己做的推送更好一些。第三方推送目前开始尝试精准推送,说白了就是将不同的内容推送给不同的人群,比如将北京的新闻推送给北京的用户。

现在做第三方推送的服务提供商有百度云推送、友盟推送、极光推送等,各有各的优势,但是实现底层都差不多。因此除非公司特别大,特别有实力,不然使用第三方推送服务提供商要远比自己开发好的多。

源码实现推送服务

注册推送
// 引 JPush功能所需头 件
#import "JPUSHService.h"
// iOS10注册APNs所需头 件
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#import <UserNotifications/UserNotifications.h>
#endif

<JPUSHRegisterDelegate>


- (void)appSetForLaunchOptions :(NSDictionary *)launchOptions
{
    //*******************************************************************
    
    // Required
    // notice: 3.0.0及以后版本注册可以这样写,也可以继续 旧的注册 式
    JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
    entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
        // 可以添加 定义categories
        // NSSet<UNNotificationCategory *> *categories for iOS10 or later
        // NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9
    }
    [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
    
    
    // Required
    // init Push
    // notice: 2.1.5版本的SDK新增的注册 法,改成可上报IDFA,如果没有使 IDFA直接传nil
    // 如需继续使 pushConfig.plist 件声明appKey等配置内容,请依旧使 [JPUSHService setupWithOption:launchOptions] 式初始化。
    [JPUSHService setupWithOption:launchOptions
                           appKey:@"a8731XXXXXXXXXXXXXXXX"
                          channel:@"App Store"
                 apsForProduction:0
            advertisingIdentifier:nil];
    
    
    [JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID)     {
        if(resCode == 0){
            NSLog(@"registrationID获取成功:%@",registrationID);
            [[MethodTool shareTool]setUserDefaults:registrationID :@"registrationID"];
        }
        else{
            NSLog(@"registrationID获取失败,code:%d",resCode);
        }
    }];
    
    //*******************************************************************
}
  
 // 调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [self appSetForLaunchOptions:launchOptions];
}

#pragma mark ----------------------推送注册结果返回--------------

- (void)application:(UIApplication *)application  didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    /// Required - 注册 DeviceToken
    [JPUSHService registerDeviceToken:deviceToken];
}
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    //Optional
    NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
}
iOS 10 系统之前

这个系统级别的方法,被触发的条件:

  • 程序在前台收到推送 (不会有顶部横条提示)
  • 通过点击推送启动程序
  • iOS10的静默式推送
//ios 7 前端 、点击、静默式推送有   包括iOS10的静默式推送
  - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
    NSLog(@"fetchCompletionHandler: /n %@",userInfo);
    
    [[MethodTool shareTool]setUserDefaults:@"推送后台" :@"tuisong" ];
    
    // Required, iOS 7 Support
    [JPUSHService handleRemoteNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNewData);
    
    [JPUSHService setBadge:0];//清空JPush服务器中存储的badge值。
    [self getMessageDetail:[[MethodTool shareTool]cleanData:userInfo[@"fid"]]];
}

这个方法可以不用实现

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    // Required,For systems with less than or equal to iOS6
    
    NSLog(@"didReceiveRemoteNotification: /n %@",userInfo);
    [JPUSHService handleRemoteNotification:userInfo];
}
iOS 10 系统及其之后

这个方法是推送横幅出现之前会被调用

// iOS 10 Support  
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger)
                                                                                                                                                 )completionHandler {
    // Required
    NSDictionary * userInfo = notification.request.content.userInfo;
    NSLog(@"111111  didReceiveNotificationResponse: /n %@",userInfo);
    
    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]
        ]) {
        [JPUSHService handleRemoteNotification:userInfo];
    }
    completionHandler(UNNotificationPresentationOptionAlert);
    
}

这个方法被触发的条件:

  • 程序在前台受收到推送 ,会有顶部横条提示,点击横幅时
  • 通过点击推送启动程序时
// iOS 10 Support
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)
                                                                                                                                                            ())completionHandler {
    // Required
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        [JPUSHService handleRemoteNotification:userInfo];
    }
    completionHandler(); // 系统要求执 这个 法
    
    [JPUSHService setBadge:0];//清空JPush服务器中存储的badge值。
    //用户点击横幅进来时
    [self getMessageDetail:[[MethodTool shareTool]cleanData:userInfo[@"fid"]]];
    或者
    //跳转到学校预警页面
    ManageSchoolListViewController *shcool = [[ManageSchoolListViewController alloc]init];
    shcool.notificationForPush = YES;
    UIViewController *controller = [UIApplication sharedApplication].keyWindow.rootViewController;
    if ([controller isKindOfClass:[UINavigationController class]]) {
        [(UINavigationController *)controller pushViewController:shcool animated:YES];
    }else if ([controller isKindOfClass:[UITabBarController class]]){
        UINavigationController  *VC = (UINavigationController *)[(UITabBarController *)controller selectedViewController];
        [VC pushViewController:shcool animated:YES];
    }
}
注销角标
 - (void)applicationDidEnterBackground:(UIApplication *)application {
      [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}

极光推送注意事项

  • 集成的时候具体参考iOS SDK集成指南

  • registrationID 只需要在登录时跟账号绑定,在退出的时候跟账号解绑(账号绑定的registrationID为空即可)。如果不在退出的时候清空registrationID,会出现账号退出仍然给该账号发送指定推送的Bug。
    registrationID 不会随便改变,在每次登陆后绑定一个就够了。在APP卸载后就会发生改变。

  • 使用极光推送平台时,添加附加字段

    APP收到的推送信息

     {
      "_j_business" = 1;
    "_j_msgid" = 36028797200845676;
    "_j_uid" = 6126767282;
     aps =     {
          alert = "\U6d4b\U8bd5";
          badge = 1;
          sound = default;
       };
        fid = 1;
     }  
    
  • 使用极光推送平台实现静默式推送

    静默式推送

  • 后台式推送需要开启后台模式

Paste_Image.png
  • 下面的代码一直执行不了,并且在控制台打印,JIGUANG服务器链接失败,我还以为是我的证书失效了,APPKEY变化了,纠结了一会,过一会居然自动好了,推测是极光服务器异常造成的注册registrationID 没有响应。

    [JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
      if(resCode == 0){
          NSLog(@"registrationID获取成功:%@",registrationID);
          [[MethodTool shareTool]setUserDefaults:registrationID :@"registrationID"];
      }
      else{
          NSLog(@"registrationID获取失败,code:%d",resCode);
      }
      }];
    
  • 系统推送授权弹框是否允许,如果你选允许,就会走下面第一条代理,上传完 deviceToken 极光会进行下一步 registrationID 的注册返回。

    - (void)application:(UIApplication *)application  didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
            /// Required - 注册 DeviceToken
            [JPUSHService registerDeviceToken:deviceToken];
      }
    
    -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
          //Optional
          NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
      }
    
  • 在使用极光推送管理平台的时候,一定要看清楚推送的iOS设备是开发环境还是生产环境。

    image.png

  • 获取App 运行状态:

     UIApplicationState state = [UIApplication sharedApplication].applicationState;
    
     说明:
     UIApplication.h
     typedef NS_ENUM(NSInteger, UIApplicationState) {
       UIApplicationStateActive,
       UIApplicationStateInactive,
       UIApplicationStateBackground
     } NS_ENUM_AVAILABLE_IOS(4_0);
    
     1、判断如何启动App
     启动App时会自动调用didFinishLaunchingWithOptions方法:如果launchOptions包含UIApplicationLaunchOptionsRemoteNotificationKey ,则表示用户是通过点击APNs 启动App;
     如果不含有对应键值,则表示用户可能是直接点击icon启动App。
    
       - (BOOL)application:(UIApplication *)application  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
       // apn 内容获取:NSDictionary *remoteNotification = [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey]
         ...
       }
    
     3、示例代码:
    
      // iOS 6 Remote Notificatio
      - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
     // 取得 APNs 标准信息内容
     NSDictionary *aps = [userInfo valueForKey:@"aps"];
     NSString *content = [aps valueForKey:@"alert"]; //推送显示的内容
     NSInteger badge = [[aps valueForKey:@"badge"] integerValue]; //badge数量
     NSString *sound = [aps valueForKey:@"sound"]; //播放的声音
    
     // 取得Extras字段内容
     NSString *customizeField1 = [userInfo valueForKey:@"customizeExtras"]; //服务端中Extras字段,key是自己定义的
     NSLog(@"content =[%@], badge=[%d], sound=[%@], customize field  =[%@]",content,badge,sound,customizeField1);
    
     }
    
  • 更新了程序,再次安装后还是没有推送能收到?

    • 【1】关机重启手机
    • 【2】卸载这个程序重新安装(很关键,大部分因为这)
    • 【3】极光平台的证书配置正确,本地的APPKEY也是正确的,registrationID和iAlias 也都获取到了,但是就是死活收不到推送,请检查一下工程的 Bundle identifier 和极光配置的证书的Bundle identifier 是否一致,我就遇到过这样的低级错误,浪费了好多时间!最好把 info.plist里面的Bundle identifier 手动复制后粘贴上!确保工程和平台的Bundle identifier一致!
    • 发布到蒲公英中的内测版本的APP,极光推送时,也需要推送生产环境,开发环境下的推送收不到,我记得以前是可以的,不知道什么时候变了。

彻底杀掉程序,点击通知进入应用会加载 application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
然后才会加载通知的点击事件。

{
    _j_msgid = 54043216545735443;
    from = "Jpush";
    _j_business = 1;
    _j_uid = 27562363129;
    aps = {
        alert = {
            title = "notice";
            body = "hello world,welcome"
        };
        badge = 7;
        sound = "default"
    };
    param = "{"link":"www.dsfk","id":"9"}";
    sound = "default";
    type = "32"
}

给用户设置标签

消息推送,有时候只想推送给指定的人或者指定的版本,那么这时候我们就需要对设备设置标签或者别名了,这样推送的时候可以根据标签或者别名推送给指定的用户

极光设置标签官方文档

 //设置极光标签
 [JPUSHService setTags:[NSSet setWithObject:@"yk"]callbackSelector:@selector(tagsAliasCallback:tags:alias:) object:self];

  //设置标签的回调
- (void)tagsAliasCallback:(int)iResCode
                 tags:(NSSet *)tags
                alias:(NSString *)alias {
NSLog(@"TagsAlias回调:%d", iResCode);

       // 0 的时候是设置成功了。
}

+++++++++++++++++++++++++++++++++ 更新 ++++++++++++++++++++++++++++++++

设置别名

别名是为了关联账号的,registrationID 是对应一台手机的,需要在获得 registrationID 后绑定账号,生产对应的别名。后台就可以根据别名对单个用户进行推送了。

[JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
        if(resCode == 0){
            NSLog(@"registrationID获取成功:%@",registrationID);
            [[MethodTool shareTool]setUserDefaults:registrationID :@"registrationID"];
            [JPUSHService setAlias:[NSString stringWithFormat:@"%@",@"广东100008"] completion:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
                NSLog(@"rescode: %ld, \ntags: %@, \iAlias: %@\n", (long)iResCode, @"tag" , iAlias);
            } seq:0];
        }
        else{
            NSLog(@"registrationID获取失败,code:%d",resCode);
        }
    }];

如何在APP未启动的时候,获取到推送的内容

首先要开启APP的后台模式,然后需要设置推送为静默式的推送,才能在APP未启动的时候触发代理方法,在代理方法中获取到推送的内容

开启后台模式,勾选静默式推送即可

//ios 7 前端 、点击、静默式推送有   包括iOS10的静默式推送
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
   
   NSLog(@"fetchCompletionHandler: /n %@",userInfo);
   
   [[MethodTool shareTool]setUserDefaults:@"推送后台" :@"tuisong" ];
   
   // Required, iOS 7 Support
   [JPUSHService handleRemoteNotification:userInfo];
   completionHandler(UIBackgroundFetchResultNewData);
   
}

更多极光推送问题可参考:iOS 不点击通知栏,怎么获取消息内容?

如何根据推送内容动态展示角标

首先,普通的推送是无法在APP未启动的时候获取到推送内容的,除非是静默式推送。但是极光的 API中有直接设置这个推送要显示的角标 setBadge,直接设置即可。

角标始终为1?

不管我们发送几次通知,这个APP角标永远为1
去极光官网,在推送消息的时候,设置badge属性,改为+1,这样每次收到通知,角标会加1了。

小结

在实际的产品和运营中经常是使用组合技巧: Push,短信,微信推送组合协同,提高效率。可以在服务端来统计分析用户行为,然后将指定的 tags 发送至手机,手机接收后再为用户打上对应的 tags。这样就可以做到定向产品推广了。

推荐阅读更多精彩内容