Swift-本地通知和远程通知

指推送通知跟NSNotification有区别:

1、NSNotification是系统内部发出通知,一般用于内部事件的监听,或者状态的改变等等,是不可见的
2、本地通知与远程通知是可见的,主要用于告知用户或者发送一些App的内容更新,推送一些相关的消息,让用户知道App内部发生了什么事情。

通知的注意点

1、App在前台运行的时候,通知不会展示出来
2、点击通知,默认会自动打开推送通知的App
3、不管App是否打开,通知都可以如期发出

1. 什么是本地推送通知

不需要联网就可以发出的通知

使用场景:

提醒用户完成一些任务,比如:定时提醒,生活备注,看电影等等

推送通知属性:
// 枚举值-发出通知的时间(有局限性)
@property(nonatomic) NSCalendarUnit repeatInterval;
// 自定义-发出通知的时间(可以自由设定时间)
@property(nonatomic,copy) NSCalendar *repeatCalendar;
// 区域-创建只需要创建一个中心点与半径就可以了
@property(nonatomic,copy) CLRegion *region
// 进入区域发出一个通知,设置yes,只会发出一个通知,设置NO就会每次进入这个区域都发送
@property(nonatomic,assign) BOOL regionTriggersOnce NO
// 设置通知的内容
@property(nonatomic,copy) NSString *alertBody;      
 // 决定alertAction是否生效
@property(nonatomic) BOOL hasAction;
// 设置滑块的文字
@property(nonatomic,copy) NSString *alertAction;    

// 设置点击通知的启动图片(一般设置App启动图片后,这里可以随便写)
@property(nonatomic,copy) NSString *alertLaunchImage;
// 设置alertTitle,就是通知内容上面的文字
@property(nonatomic,copy) NSString *alertTitle
 // 设置弹出的声音
@property(nonatomic,copy) NSString *soundName;
 // 设置App的消息条数
@property(nonatomic) NSInteger applicationIconBadgeNumber;
 // 设置通知一些额外数据
 @property(nonatomic,copy) NSDictionary *userInfo;
如何发出本地通知:
//发出一个电池电量为100的本地通知
let alertBody = "电池电量通知"
let localNotify = UILocalNotification()
localNotify.alertBody = alertBody
localNotify.soundName = UILocalNotificationDefaultSoundName
localNotify.userInfo = ["battery": 100]
UIApplication.shared.presentLocalNotificationNow(localNotify)

使用注意:
iOS7,不需要用户授权就可发出通知,而iOS8以后,必须用户授权才可以发出通知

//申请授权
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        guard #available(iOS 10, *) else {
            // 请求授权
            let typeValue = UIUserNotificationType.alert.rawValue |
                UIUserNotificationType.badge.rawValue |
                UIUserNotificationType.sound.rawValue
            let type = UIUserNotificationType(rawValue: typeValue)
            let setting = UIUserNotificationSettings(types: type, categories: nil)
            UIApplication.shared.registerUserNotificationSettings(setting)
            
            // 发送请求
            UIApplication.shared.registerForRemoteNotifications()
            return
        }
        let typeValue = UNAuthorizationOptions.alert.rawValue |
            UNAuthorizationOptions.badge.rawValue |
            UNAuthorizationOptions.sound.rawValue
        UNUserNotificationCenter.current().requestAuthorization(options: UNAuthorizationOptions(rawValue: typeValue)) { (granted, error) in
            if granted { // 同意
                UIApplication.shared.registerForRemoteNotifications()
            } else { // 拒绝
                /// 弹出提示框
            }
        }
}

监听通知,如果用户打开通知,可以让用户进入一些特定的界面

func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
    NSLog(notification)
    guard let userInfo = notification.userInfo else {
        return
    }
    //处理通知
}
2. 什么是远程推送通知

<1>从远程服务器推送给客户端的通知,需要连接网络
<2>远程推送服务,又称为APNs 苹果推送通知服务(Apple Push Notification services)
<3>模拟器无法调试远程推送

传统推送通知

传统推送通知是相对于APNs而言的,传统推送通知的原理是:当APP打开时和App的服务器建立一个长连接(需要网络),当需要通知的时候,App服务器通过长连接向对应的客户端发送数据,当客户端接收到数据时使用UILocalNotfication本地通知的方式来展示,这样就实现了传统推送通知。

传统推送通知必须要联网,如果关闭了App或者打开了App但是无法连接服务器了,这些情况都收不到通知了。

APNs

所有苹果设备在联网状态下都会与苹果服务器建立长连接,连接是双向的,苹果设备可以向苹果服务器发送请求,苹果服务器也可以向苹果设备发送请求。

苹果服务器常用的通知功能:

<1>时间校准
<2>系统升级
<3>查找我的iPhone
长连接的好处:更加及时

为什么需要远程推送通知:

解决获取传统数据的局限性,让数据实时更新

使用场景:

聊天功能(一般非即时聊天)、推送一下App的内部新功能、版本下载等

注:所有的苹果设备,在联网状态下,都会与苹果的服务器建立-长连接

远程通知的过程:

例如微信App:首先每个联网并打开微信的App都与微信服务器有一个长连接,当微信A用户向微信B用户发送一个消息时,微信A用户将消息发送到微信服务器,然后微信服务器判断微信B用户是否和微信服务器建立了长连接,如果有直接通过微信B用户和微信服务器建立的连接管道直接发送即可,这样微信B用户就能收到消息;如果微信B用户此时没有打开微信App,那么微信服务器就将消息发送给苹果服务器,苹果服务器再讲消息发送到某台苹果设备上。苹果是怎么知道该发送给那台设备呢?用户A发送消息时需要将用户B的UDID和微信App的Bundle ID 附带在消息上一块发送给B用户,这些消息微信服务器又发送给苹果服务器,苹果服务器通过UDID就知道发送给那台设备了,然后通过Bundle ID就知道是哪个App发送的了。苹果根据UDID + Bundle ID 生成一个deviceToken, 这样每条微信消息中都加上deviceToken苹果服务器就能识别设备和App了。

图形说明

push1.png

push2.png

push3.png

实现步骤:
1. 创建真机调试证书并配置推送证书文件:apns_development.cer和描述文件

首先创建真机证书、AppIDs(要选择Push Notifications), AppIDs创建完后可以看到状态是Configurable,是黄色的圆点,此时还不能使用推送通知,还要继续配置一下,选择Edit–>Push Notifications—> Create Certificate(创建推送通知证书),当证书创建完成后,可以看到AppID中的状态就变成了绿色的圆点(可用状态)

2. 请求苹果服务器获取deviceToken
import UserNotifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
       guard #available(iOS 10, *) else {
            // 请求授权
            let typeValue = UIUserNotificationType.alert.rawValue |
                UIUserNotificationType.badge.rawValue |
                UIUserNotificationType.sound.rawValue
            let type = UIUserNotificationType(rawValue: typeValue)
            let setting = UIUserNotificationSettings(types: type, categories: nil)
            UIApplication.shared.registerUserNotificationSettings(setting)
            
            // 发送请求
            UIApplication.shared.registerForRemoteNotifications()
            return
        }
        let typeValue = UNAuthorizationOptions.alert.rawValue |
            UNAuthorizationOptions.badge.rawValue |
            UNAuthorizationOptions.sound.rawValue
        UNUserNotificationCenter.current().requestAuthorization(options: UNAuthorizationOptions(rawValue: typeValue)) { (granted, error) in
            if granted { // 同意
                UIApplication.shared.registerForRemoteNotifications()
            } else { // 拒绝
                /// 弹出提示框
            }
        }
}
/// 当请求到DeviceToken时调用
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    NSLog(deviceToken)
}
3. 发送deviceToken给App的服务器
//这里使用第三方阿里云推送SDK,所以在didRegisterForRemoteNotificationsWithDeviceToken中将deviceToken发送出去
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    NSLog(deviceToken)
    CloudPushSDK.registerDevice(deviceToken) { (result) in
       if result!.success {
          NSLog("Register deviceToken success.")
       } else {
          NSLog("Register deviceToken failed, error:\(result!.error!)")
       }
    }
}
4.监听用户点击远程推送通知的行为
//锁屏和从后台进入前台会调用该方法
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
    NSLog(notification)
}
//在前台调用下面该方法,该方法需要设置Background Modes --> Remote Notifications
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        let status = application.applicationState
        switch status {
        case .active:
            NSLog("在前台收到推送")
            break
        case .inactive:
            NSLog("后台->前台")
            break
        case .background:
            NSLog("后台")
            break
        }
        completionHandler(.newData)
}

上面那个代理方法都是用户点击通知以后才会调用,如果想一收到消息(用户还没点击通知)就会调用该方法,需要有3个条件

  1. 设置Background Modes –> Remote Notifications
  2. 在代理方法中调用代码块completionHandler(UIBackgroundFetchResultNewData);
  3. App服务器发送数据时要增加一个”content-available”字段,值随意写
    满足以上三个条件,当接收到通知时立即会调用代理方法

Leave a Comment