Fellow Travellers

消息推送(iOS)

孙俊祥
字数统计: 1.7k阅读时长: 6 min
2017/08/23 Share

APNS的推送机制

首先我们看一下苹果官方给出的对IOS推送机制的解释。如下图



Provider就是我们自己程序的后台服务器,APNS是Apple Push Notification Service的缩写,也就是苹果的推送服务器。

上图可以分为三个阶段:

第一阶段:应用程序的服务器端把要发送的消息、目的iPhone的标识打包,发给APNS。

第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。

第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。

APNS推送通知的详细工作流程

下面这张图是说明APNS推送通知的详细工作流程:



根据图片我们可以概括一下:

1、应用程序注册APNS消息推送。
2、IOS从APNS Server获取devicetoken,应用程序接收device token。
3、应用程序将device token发送给程序的PUSH服务端程序。
4、服务端程序向APNS服务发送消息。
5、APNS服务将消息发送给iPhone应用程序。

客户端实现起来相对容易,但后台要支持APNS的话还是比较复杂的,因此,我使用口碑较好的第三方推送-极光推送。

准备工作

1、申请推送证书

http://www.2cto.com/kf/201604/500620.html

2、将推送证书导出为p12文件,并上传至极光推送

3、导入JPUSH框架

xcode 8以下 建议手动导入JPUSH,由于缺少系统库UserNotifications.framework会导致框架无法使用

• 在极光官网下载最新SDK

• 将SDK包解压,在Xcode中选择“Add files to ‘Your project name’…”,将解压后的lib子文件夹(包含JPUSHService.h、jpush-ios-x.x.x.a、jcore-ios-x.x.x.a)添加到你的工程目录中。

• 添加Framework

o    CFNetwork.framework
o    CoreFoundation.framework
o    CoreTelephony.framework
o    SystemConfiguration.framework
o    CoreGraphics.framework
o    Foundation.framework
o    UIKit.framework
o    Security.framework
o    libz.tbd (Xcode7以下版本是libz.dylib)
o    AdSupport.framework (获取IDFA需要;如果不使用IDFA,请不要添加)
o    UserNotifications.framework (Xcode8及以上)
o    libresolv.tbd (JPush 2.2.0及以上版本需要, Xcode7以下版本是libresolv.dylib)

• 设置 Search Paths 下的 User Header Search Paths 和 Library Search Paths,比如SDK文件夹(默认为lib)与工程文件在同一级目录下,则都设置为”$(SRCROOT)/{静态库所在文件夹名称}”即可。

允许Xcode7支持Http传输方法

如果您使用的是2.1.9及以上的版本则不需要配置此步骤

如果用的是Xcode7或更新版本,需要在App项目的plist手动配置下key和值以支持http传输:

选择1:根据域名配置

•    在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
•    然后给它添加一个NSExceptionDomains,类型为字典类型;
•    把需要的支持的域添加給NSExceptionDomains。其中jpush.cn作为Key,类型为字典类型。
•    每个域下面需要设置2个属性:NSIncludesSubdomains、NSExceptionAllowsInsecureHTTPLoads。 两个属性均为Boolean类型,值分别为YES、YES。



如图:

选择2:全局配置

<key>NSAppTransportSecurity</key> 
<dict> 
  <key>NSAllowsArbitraryLoads</key> 
  <true/> 
</dict>   

代码集成

初始化JPUSH, types属性设置收到通知时,APP的一些相应方式,包括bageValue,提示音或者震动,来提示用户接收到通知。在XCODE8以下,由于缺少UserNotifications.framework框架,所以无法使用IOS的特性,而极光的代理属性洽洽需要配合UserNotifications才能使用,因此此处传nil即可。

appKey:极光开发者后台提供,你可以通过注册账号并创建应用获得

channel:通常为Publish channel

func setPushNoti(launchOptions:[NSObject: AnyObject]?){
        //setup apns
        let entity = JPUSHRegisterEntity()
        entity.types = (1 << 0) | (1 << 1) | (1 << 2)
        JPUSHService.registerForRemoteNotificationConfig(entity, delegate: nil)

        //set jpush
        JPUSHService.setupWithOption(launchOptions, appKey: K_APP_KEY, channel: K_CHANNEL, apsForProduction: false)
        setJPUSH()
    }

依次添加以下代码

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData){
        JPUSHService.registerDeviceToken(deviceToken)
    }//将苹果通知中心的deviceToken返回给极光推送后台

func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError){
        print("注册apns失败 \n \(error.localizedDescription)")
    }//当然了,你需要关键的LOG

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
        JPUSHService.handleRemoteNotification(userInfo)
        completionHandler(UIBackgroundFetchResult.NewData)
        print("ios7, 及以上系统收到通知\(userInfo)")
        urlString = userInfo["url"] as? String
        if urlString != nil && (application.applicationState == .Active){
            NSNotificationCenter.defaultCenter().postNotification(NSNotification(name: "NewVersion", object: urlString, userInfo: nil))
        }
    }//XCODE7 的环境下,消息的解析在这里实现。

至此IOS的推送的集成基本完成了。但是要做到接收通知,打开通知栏就打开一个URL,还需要进一步的研究。(推送的消息中,content-alive字段必须为true否则将无法从服务器获取通知的内容)

1、当程序进程被杀死,通过通知栏启动程序(注意,如果收到通知,仍然通过ICON去启动程序,通知的内容是无法获取到的,必须通过通知才可以)

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
       print("launchOptions ----",launchOptions)
      if launchOptions != nil{
            let remoteNoti = launchOptions![UIApplicationLaunchOptionsRemoteNotificationKey]
           urlString = remoteNoti!["url"] as? String
        }
}//获取通知内容

2、当程序 处于后台,程序可以正常通过通知进入,并触发一个特定的操作

3、当程序处于前台

ios 10以下,是没有什么反应的,不弹出横幅,也无通知的声音。因此,可以通过添加这么一个判断来触发界面提示更新

if urlString != nil && (application.applicationState == .Active){
            NSNotificationCenter.defaultCenter().postNotification(NSNotification(name: "NewVersion", object: urlString, userInfo: nil))
        }

IOS10 及以上则既可以弹出横幅又可以有声音,相关的逻辑可以在JPUSH的代理方法里去实现

关于分组推送和精确推送

极光推送里面的精准推送是通过idfa去实现的,但前提是app里面需要有明显的广告功能,如果没有的话,app将无法上架,再者,这个东西也不是完全的可靠,如果用户在设置里面停止广告跟踪的话,精准推送还是会失效的。
思路:jpush中又一个registuserID这个id有个特点,就是程序如果被卸载,则重新生成一个新的来唯一表示设备,我们可以将用户ID作为key registuserID为value,如果重新安装,只需更新这个registuserID的值,即可唯一表示设备

分组推送

思路:先给用于一个别名alias,再给这个用户打上他所具备的标签,如
alias:liming, tag1:汽车 tag2:游戏 tag3:旅游
laias:jone tag1:美食 tag2:游戏 tag3:电影


推送的时候,可以通过对tag的交并补等集合操作,实现对不同群体用户的信息的分类推送,如游戏这条推送,liming和jone都能收到

CATALOG
  1. 1. APNS的推送机制
    1. 1.1. APNS推送通知的详细工作流程
    2. 1.2. 准备工作
    3. 1.3. 允许Xcode7支持Http传输方法
    4. 1.4. 代码集成
    5. 1.5. 关于分组推送和精确推送