Xcode8下快速集成极光推送并兼顾iOS10

北战南征 提交于 2019-12-03 16:42:06

前言

iOS10下,远程推送发生了变化,新增的UserNotifications.framework将本地推送和远程推送整合在一起。 最近刚刚更新了Xcode8,自动创建证书和配置文件确实方便许多。琰君分享下以开发环境为例(非生产环境)在Xcode8下,快速集成极光推送并兼顾iOS10。

注意:极光推送的远程推送是免费的,但相应的用户统计,终端统计等功能是不免费。如果你需要用户统计,终端统计这些功能意味着需要付费才能使用。

参考链接

开发证书配置
APNs 推送原理及问题
iOS 推送全解析,你不可不知的所有 Tips!

接下来假设你都了解证书相关的,推送的一些概念和极光推送的知识。那么相信你应该知道需要一台iOS设备和一个苹果开发者帐号才能去体验远程推送。如果你清楚远程推送的原理,那可以直接从以下第2步开始。

集成步骤

  1. 远程推送原理
  2. Xcode8创建项目,配置自动创建App ID/证书/配置文件
  3. 创建APNs 推送证书
  4. 在极光推送后台创建应用,并上传APNs 推送证书
  5. 项目中集成极光推送SDK
  6. 获取 APNs(通知) 推送内容
  7. 极光推送后台发送远程推送测试

1. 远程推送原理

当iOS设备联网的情况下,苹果服务器和iOS设备建立了一个长链接,即便应用处于挂起和后台的状态,苹果可以给iOS设备中的应用发送通知。

远程推送原理

概念

  1. iOS:iOS设备
  2. APNS Server:苹果服务器
  3. Your App: 自己的应用,例如咕咚
  4. Your Server:自己的服务器,例如咕咚服务器

推送流程

  1. iOS 应用注册推送通知,iOS设备将设备的UDID和应用的Bundle ID到苹果服务器。
  2. 苹果服务器将接收到的UDIDBundle ID加密生成一个deviceToken,并返回给对应iOS应用。
  3. iOS应用将接收到的deviceToken发送到自己的服务器,服务器并保存。
  4. 自己的服务器需配置APNs 推送证书,当需要推送时,去后台查询推送目标设备的deviceToken,将消息和deviceToken一起发送给苹果服务器。
  5. 苹果服务器通过deviceToken找到对应设备下的对应应用,推送消息。

说明

  1. 我们的应用真机测试需要创建对应App ID/证书/配置文件, 这步可以用Xcode8 自动创建,而不需要到苹果开发者平台去创建。如果不是Xcode8,则需要去开发者平台申请,并且在Xcode配置,保证真机测试即可。
  2. 我们自己的服务器需要配置 APNs 推送证书,同样的分为开发证书和生产证书。需要到苹果开发者平台去申请。
  3. 如果不借助第三方推送平台,我们需要完成推送流程中的1,3,4步。借助极光推送,那么我们只需要完成推送流程中的1,3步,但同样需要我们申请APNs 推送证书,并且上传到极光推送平台。

2. Xcode8配置自动创建App ID/证书/配置文件

  1. 在Xcode8下,确保已经登录付费苹果开发者帐号。请开启Application Target的Capabilities->Push Notifications选项。

开启远程推送

2.勾选自动管理App ID证书配置文件

勾选自动管理

3.检查Xcode生产的App ID证书配置文件这步配置好了就可以真机运行了。

检查Xcode生产的App ID/证书/配置文件

3. 创建APNs 推送证书

  1. 登录开发者网站,并点击按钮创建证书

点击按钮创建证书

2.选择开发环境APNs 推送证书点击右下角按钮创建

创建开发环境APNs 推送证书

3.选择工程的App ID

选择工程的App ID

4.上传证书请求文件,不知道的童鞋可参照开发证书配置

上传证书请求文件

5.点击下载证书,并双击安装到钥匙串.

安装APNs 推送证书

6.打开钥匙串,并参照下图指示,找到对应的APNs 推送证书,并选择右键导出.

钥匙串导出证书

7.设置证书名字,并选择证书存放位置。

设置证书名字

8.设置证书密码,可不填。极光推送支持证书设置密码,但像leanCloud推送不支持证书设置密码,各位童鞋可按照实际情况来。最后输入电脑开密码,导出证书。

设置证书密码

输入电脑开机密码导出证书

4. 在极光推送后台创建应用,并上传APNs 推送证书

1.登录极光推送平台,切换到控制台,并创建应用。接下来在应用信息中上传开发APNs 推送证书生产环境APNs 推送证书配置类似,不做赘述.

上传开发APNs 推送证书

  1. 核对应用的Bundle ID 等信息是否正确.

    核对Bundle ID 等信息

5. 项目中集成极光推送SDK

  1. 导入极光推送SDK
    jpush-ios-2.1.9.a
    JPUSHService.h
  2. 导入系统依赖库
    CFNetwork.framework
    CoreFoundation.framework
    CoreTelephony.framework
    SystemConfiguration.framework
    CoreGraphics.framework
    Foundation.framework
    UIKit.framework
    Security.framework
    libz.tbd
    Adsupport.framework (获取IDFA需要;如果不使用IDFA,请不要添加)
    UserNotifications.framework(Xcode8及以上)
  3. AppDelegate.m 配置
    导入以下头文件

    #import "JPUSHService.h"
    #import <AdSupport/AdSupport.h>
    #ifdef NSFoundationVersionNumber_iOS_9_x_Max
    #import <UserNotifications/UserNotifications.h>
    #endif

    并且遵守<JPUSHRegisterDelegate>协议

    @interface AppDelegate ()<JPUSHRegisterDelegate>
    @end

    注册远程推送

    -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    
     if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
    //iOS10以上
         JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
         entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;
         [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
     }else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
    //iOS8以上可以添加自定义categories
         [JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge |
                                                           UIUserNotificationTypeSound |
                                                           UIUserNotificationTypeAlert)
                                               categories:nil];
     }
     else {
         //iOS8以下categories 必须为nil
         [JPUSHService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
                                                           UIRemoteNotificationTypeSound |
                                                           UIRemoteNotificationTypeAlert)
                                               categories:nil];
     }
    BOOL isProduction = NO;// NO为开发环境,YES为生产环境
     //广告标识符
     NSString *advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
     //Required(2.1.5版本的SDK新增的注册方法,改成可上报IDFA,如果没有使用IDFA直接传nil
     [JPUSHService setupWithOption:launchOptions appKey:@"极光推送AppKey"
                           channel:nil
                  apsForProduction:isProduction
             advertisingIdentifier:advertisingId];
     return YES;
    }

    得到苹果服务器返回的deviceToken,上传到极光推送服务器。

    -(void)application:(UIApplication *)application
     didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    [JPUSHService registerDeviceToken:deviceToken];
    }

    注册远程通知失败,比如没有联网的状态下。

    -(void)application:(UIApplication *)application
     didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
    }

    6.获取 APNs(通知) 推送内容

    iOS 设备收到一条推送(APNs),用户点击推送通知打开应用时,应用程序根据状态不同进行处理需在 AppDelegate 中的以下方法中添加代码以获取apn内容
    1.如果 App 状态为未运行,此函数将被调用,如果launchOptions包含UIApplicationLaunchOptionsRemoteNotificationKey表示用户点击apn 通知导致app被启动运行;如果不含有对应键值则表示 App 不是因点击apn而被启动,可能为直接点击icon被启动或其他。

    -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    // apn 内容获取:
    NSDictionary *remoteNotification = [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey]
    }

    2.基于iOS 6 及以下的系统版本,如果 App状态为正在前台或者点击通知栏的通知消息,那么此函数将被调用,并且可通过AppDelegate的applicationState是否为UIApplicationStateActive判断程序是否在前台运行。

    -(void)application:(UIApplication *)application 
    didReceiveRemoteNotification:(NSDictionary *)userInfo{
    }

    3.基于iOS 7 及以上的系统版本,如果是使用 iOS 7 的 Remote Notification 特性那么此函数将被调用

    -(void)application:(UIApplication *)application
     didReceiveRemoteNotification:(NSDictionary *)userInfo
           fetchCompletionHandler:
               (void (^)(UIBackgroundFetchResult))completionHandler {
    [JPUSHService handleRemoteNotification:userInfo];
    NSLog(@"iOS7及以上系统,收到通知:%@", [self logDic:userInfo]);
    
    completionHandler(UIBackgroundFetchResultNewData);
    }
    -(NSString *)logDic:(NSDictionary *)dic {
     if (![dic count]) {
         return nil;
     }
     NSString *tempStr1 =
     [[dic description] stringByReplacingOccurrencesOfString:@"\\u"
                                                 withString:@"\\U"];
     NSString *tempStr2 =
     [tempStr1 stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
     NSString *tempStr3 =
     [[@"\"" stringByAppendingString:tempStr2] stringByAppendingString:@"\""];
     NSData *tempData = [tempStr3 dataUsingEncoding:NSUTF8StringEncoding];
     NSString *str =
     [NSPropertyListSerialization propertyListFromData:tempData
                                      mutabilityOption:NSPropertyListImmutable
                                                format:NULL
                                      errorDescription:NULL];
     return str;
    }

    4.基于iOS 10及以上的系统版本,
    [application: didReceiveRemoteNotification:]将会被系统废弃,
    由新增UserNotifications.framework中的以下两个方法替代。

    [UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:]
    [UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]

在极光推送SDK2.1.9版本以后可实现SDK封装的JPUSHRegisterDelegate协议方法,适配iOS10新增的delegate协议方法。
即以下两个方法:

#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#pragma mark- JPUSHRegisterDelegate
-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center 
willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler { 

 NSDictionary * userInfo = notification.request.content.userInfo;
  UNNotificationRequest *request = notification.request; // 收到推送的请求
  UNNotificationContent *content = request.content; // 收到推送的消息内容
  NSNumber *badge = content.badge;  // 推送消息的角标
  NSString *body = content.body;    // 推送消息体
  UNNotificationSound *sound = content.sound;  // 推送消息的声音
  NSString *subtitle = content.subtitle;  // 推送消息的副标题
  NSString *title = content.title;  // 推送消息的标题

 if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
    NSLog(@"iOS10 前台收到远程通知:%@", [self logDic:userInfo]);
  }
// 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以设置
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert); 
}
//
-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center 
didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
NSDictionary * userInfo = response.notification.request.content.userInfo;
  UNNotificationRequest *request = response.notification.request; // 收到推送的请求
  UNNotificationContent *content = request.content; // 收到推送的消息内容
  NSNumber *badge = content.badge;  // 推送消息的角标
  NSString *body = content.body;    // 推送消息体
  UNNotificationSound *sound = content.sound;  // 推送消息的声音
  NSString *subtitle = content.subtitle;  // 推送消息的副标题
  NSString *title = content.title;  // 推送消息的标题
 if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
    NSLog(@"iOS10 收到远程通知:%@", [self logDic:userInfo]);
  }
completionHandler();  // 系统要求执行这个方法
}
#endif

7. 极光推送后台发送远程推送测试

1.登录极光推送,切换到控制台,并点击对应的应用,点击推送按钮

点击推送按钮

点击发送通知

2.设置推送内容

设置推送内容


选择推送环境iOS开发环境,目标人群,发送时间。再点击可选设置设置消息的具体内容。

屏幕快照

3.设置消息具体内容。远程推送分为普通推送/后台推送/静默推送3种类型,并且类型由推送消息设置来决定。关于如何设置请参考iOS 推送全解析,你不可不知的所有 Tips!

屏幕快照 2016-09-26 上午10.54.59.png

4.iOS设备接收到远程推送

接收到远程推送

5.推送历史可以在这里看得到,但有延迟,可能远程推送已接收到,推送历史数据还没有更新到最新。

屏幕快照 2016-09-28 上午10.23.16.png

关于Xcode8下集成极光远程推送的简单介绍,到这里就结束了。


 

文/赵先生Try(简书作者)
原文链接:http://www.jianshu.com/p/53e0244e6081
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!