iOS-APNS(Apple Push Notification service)
推送介绍: 在我们手机联网的时候,会自动与苹果服务器进行一个链接,这个链接叫长链接,所以我们才可以接受到推送通知,断网的情况下是不可能接受到推送通知的。
(这里补充一个小知识:就是我们平时用的HTTP就是属于短链接,你请求一次,就给你反馈一次相应的数据或者状态信息)
当我们的手机软件,已经退出了或者程序被杀死了,但是我们还是会受到推送消息。因为苹果为我们提供了一个服务器,这个服务器叫“APNS服务器”。这个服务器就是专门用来推送通知的。只要你的手机联网,你的手机就会与这个服务器进行一个长链接。
举个例子:
比如一个xx服务器想要单独给使用他们软件的其中一个用户推送一个消息,这个消息想想告诉用户一些信息如:版本更新消息或者购买某一个商品的状态信息等等。这个xx服务器就会把这条消息,和deviceToken信息发给苹果的APNS服务器,苹果APNS服务器就会根据这个deviceToken,找到对应的设备,然后推送给他。
deviceToken:手机的UDID + APP的BundleID
(每个手机的UDID都不一样,可以说是手机的生份证)
deviceToken的来源:(上图)
deviceToken并不是在手机上生成的
是当初手机安装这个软件的时候,它把手机的UDID和安装这个软件的BundleID发送给苹果的APNS服务器
苹果的APNS服务器就会把接受的UDID和BundleID进行加密生成我们的deviceToken
然后就会把这个deviceToken在返回给我们
我们就可以拿到这个deviceToken传给xxx软件的服务器
这个服务器就会把deviceToken存到数据库
发送流程:
服务器要发生一条通知消息,首先会去数据库找到,要推送用户的deviceToken
找到了之后就会把推送的消息和deviceToken一起发给苹果的PANS服务器
然后苹果的就会根据这个deviceToken,进行解析找到相应的设备,进行推送
简单来说就是根据UDID找到你手机 在根据BundleID,找到手机上对应的BundleID
一.获取deviceToken代码:
iOS8以前
/**
*枚举类型
UIRemoteNotificationTypeNone = 0, //不接收推送消息
UIRemoteNotificationTypeBadge = 1 << 0, 接收图标数字
UIRemoteNotificationTypeSound = 1 << 1, 接收时的音效
UIRemoteNotificationTypeAlert = 1 << 2, 接收消息文字弹窗
UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3, 接收订阅消息
*/
UIRemoteNotificationType type = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert;
/**
当用户第一次启动程序的时候就获取deviceToken
该方法在iOS8就过期le
*/
//调用该方法,系统就会自动发送UDID和当前程序的BundleID到苹果的APNS服务器
[application registerForRemoteNotificationTypes:type];
iOS8~iOS10
//ios8以后的
/**
UIUserNotificationTypeNone = 0,
UIUserNotificationTypeBadge = 1 << 0,
UIUserNotificationTypeSound = 1 << 1,
UIUserNotificationTypeAlert = 1 << 2
*/
UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:type categories:nil];
//注册通知类型
[application registerUserNotificationSettings:setting];
//申请使用通知
[application registerForRemoteNotifications];
iOS10.0后新的推送方式
首先要添加头文件
import
设置代理
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error) {
NSLog(@"succeeded!");
}
}];
二.获取到deviceToken后会调
/**
获取到用户当前程序的deviceToken后会会调这个方法
*/
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
NSLog(@"%@",deviceToken);
//在这里我们就可以把deviceToken上传给我我们软件的服务器
//deviceToken:就是我们需要的deviceToken;
// NSString *token=[NSString stringWithFormat:@"%@",deviceToken];
// token=[token stringByReplacingOccurrencesOfString:@"<" withString:@""];
// token=[token stringByReplacingOccurrencesOfString:@">" withString:@""];
// token=[token stringByReplacingOccurrencesOfString:@" " withString:@""];
}
三.获取推送的信息
iOS7以前
/*
ios7以前苹果支持多任务, iOS7以前的多任务是假的多任务
而iOS7开始苹果才真正的推出了多任务
接收到远程服务器推送过来的内容就会调用
注意: 只有应用程序是打开状态(前台/后台(程序没有杀死)), 才会调用该方法
如果应用程序是关闭状态会调用didFinishLaunchingWithOptions
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
/*
如果应用程序在后台 , 只有用户点击了通知之后才会调用
如果应用程序在前台, 会直接调用该方法
即便应用程序关闭也可以接收到远程通知,但是改方法不会调用
*/
NSLog(@"%@", userInfo);
}
如果应用程序是关闭状态会调用didFinishLaunchingWithOptions
我们的推送消息就会放在launchOptions这里面
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.取出数据
NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo) {
NSLog(@"%@", userInfo);
}
return YES;
}
iOS7~iOS10.0
//接收到远程服务器推送过来的内容就会调用
// ios7以后用这个处理后台任务接收到得远程通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(@"%@", userInfo);
NSNumber *contentid = userInfo[@"content-id"];
if (contentid) {
//注意: 在此方法中一定要调用这个调用block, 告诉系统是否处理成功.
// 以便于系统在后台更新UI等操作
/*
UIBackgroundFetchResultNewData, 成功接收到数据
UIBackgroundFetchResultNoData, 没有;接收到数据
UIBackgroundFetchResultFailed 接收失败
*/completionHandler(UIBackgroundFetchResultNewData);
}else
{
completionHandler(UIBackgroundFetchResultFailed);
}
}
iOS10.0以后有2个方法
/**
*iOS10.0以后的方法
*/
//App在后台运行及程序退出杀死 会调用的方法
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response withCompletionHandler:(nonnull void (^)(void))completionHandler{
NSDictionary *userInfo = response.notification.request.content.userInfo;
NSLog(@"App在后台时候-%@", userInfo);
completionHandler();
}
// App在前台时候回调:用户正在使用状态
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
NSDictionary *userInfo = notification.request.content.userInfo;
NSLog(@"App在前台时候回调-%@", userInfo);
//可以设置当收到通知后, 有哪些效果呈现(声音/提醒/数字角标)
completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}
在app进入后台的时候,app会进入休眠的状态,不会执行任何操作。如果我们想让app进入后台后,接受到了通知后,依然进行一些操作(更新界面)就要配置我的的工程
1.开启多任务(后台运行模式)
在iOS7以前虽然有了多任务,但是这个功能并不完善,到了iOS7以后,这个多任务才可以正常使用
2.通知的格式也要改一下
后台推送
支持系统:iOS7及以上
推送格式:
{"aps":{"content-available":1"},"content":"hhhh"}
最后附上AppDelegate.m参考
#import "AppDelegate.h"
#import
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error) {
NSLog(@"succeeded!");
}
}];
} else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0){
//ios8以后的
/**
UIUserNotificationTypeNone = 0,
UIUserNotificationTypeBadge = 1 << 0,
UIUserNotificationTypeSound = 1 << 1,
UIUserNotificationTypeAlert = 1 << 2
*/
UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:type categories:nil];
//注册通知类型
[application registerUserNotificationSettings:setting];
//申请使用通知
[application registerForRemoteNotifications];
}else{
/**
*枚举类型
UIRemoteNotificationTypeNone = 0, //不接收推送消息
UIRemoteNotificationTypeBadge = 1 << 0, 接收图标数字
UIRemoteNotificationTypeSound = 1 << 1, 接收时的音效
UIRemoteNotificationTypeAlert = 1 << 2, 接收消息文字弹窗
UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3, 接收订阅消息
*/
UIRemoteNotificationType type = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert;
/**
当用户第一次启动程序的时候就获取deviceToken
该方法在iOS8就过期le
*/
//调用该方法,系统就会自动发送UDID和当前程序的BundleID到苹果的APNS服务器
[application registerForRemoteNotificationTypes:type];
}
//取出推送通知:
//当程序被杀死或者退出了,接受到的推送信息会保留在launchOptions中,当我们重新打开软件的时候再取出来
//取出在程序退出的时候接受到的推送消息
NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo) {
NSLog(@"%@", userInfo);
}
return YES;
}
/**
获取到用户当前程序的deviceToken后会会调这个方法
*/
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
NSLog(@"%@",deviceToken);
//deviceToken:就是我们需要上传服务器的deviceToken;
}
/*
*iOS7以前
*ios7以前苹果支持多任务, iOS7以前的多任务是假的多任务
*而iOS7开始苹果才真正的推出了多任务
*接收到远程服务器推送过来的内容就会调用
*注意: 只有应用程序是打开状态(前台/后台(程序没有杀死)), 才会调用该方法
*如果应用程序是关闭状态会调用didFinishLaunchingWithOptions
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
/*
如果应用程序在后台 , 只有用户点击了通知之后才会调用
如果应用程序在前台, 会直接调用该方法
即便应用程序关闭也可以接收到远程通知,但是改方法不会调用
*/
NSLog(@"%@", userInfo);
}
/**
*ios7以后
*ios7以后用这个处理后台任务接收到得远程通知
*接收到远程服务器推送过来的内容就会调用
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(@"%@", userInfo);
NSNumber *contentid = userInfo[@"content-id"];
if (contentid) {
//注意: 在此方法中一定要调用这个调用block, 告诉系统是否处理成功.
// 以便于系统在后台更新UI等操作
/*
UIBackgroundFetchResultNewData, 成功接收到数据
UIBackgroundFetchResultNoData, 没有;接收到数据
UIBackgroundFetchResultFailed 接收失败
*/
completionHandler(UIBackgroundFetchResultNewData);
}else
{
completionHandler(UIBackgroundFetchResultFailed);
}
}
/**
*iOS10.0以后的方法
*/
//App在后台运行及程序退出杀死 会调用的方法
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response withCompletionHandler:(nonnull void (^)(void))completionHandler{
NSDictionary *userInfo = response.notification.request.content.userInfo;
NSLog(@"App在后台时候-%@", userInfo);
completionHandler();
}
// App在前台时候回调:用户正在使用状态
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
NSDictionary *userInfo = notification.request.content.userInfo;
NSLog(@"App在前台时候回调-%@", userInfo);
//可以设置当收到通知后, 有哪些效果呈现(声音/提醒/数字角标)
completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}
@end