iOS-APNS(推送)

iOS-APNS(Apple Push Notification service)

推送介绍: 在我们手机联网的时候,会自动与苹果服务器进行一个链接,这个链接叫长链接,所以我们才可以接受到推送通知,断网的情况下是不可能接受到推送通知的。

(这里补充一个小知识:就是我们平时用的HTTP就是属于短链接,你请求一次,就给你反馈一次相应的数据或者状态信息)


当我们的手机软件,已经退出了或者程序被杀死了,但是我们还是会受到推送消息。因为苹果为我们提供了一个服务器,这个服务器叫“APNS服务器”。这个服务器就是专门用来推送通知的。只要你的手机联网,你的手机就会与这个服务器进行一个长链接。

举个例子:

比如一个xx服务器想要单独给使用他们软件的其中一个用户推送一个消息,这个消息想想告诉用户一些信息如:版本更新消息或者购买某一个商品的状态信息等等。这个xx服务器就会把这条消息,和deviceToken信息发给苹果的APNS服务器,苹果APNS服务器就会根据这个deviceToken,找到对应的设备,然后推送给他。

deviceToken:手机的UDID + APP的BundleID
(每个手机的UDID都不一样,可以说是手机的生份证)
屏幕快照 2018-04-23 下午7.40.45.png

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 <UserNotifications/UserNotifications.h>
设置代理<UNUserNotificationCenterDelegate>

 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以后,这个多任务才可以正常使用


29DF90FF-3C22-45AB-8FD4-DB34815D6CEE.png
2.通知的格式也要改一下

后台推送
支持系统:iOS7及以上
推送格式:
{"aps":{"content-available":1"},"content":"hhhh"}

最后附上AppDelegate.m参考

#import "AppDelegate.h"
#import <UserNotifications/UserNotifications.h>

@interface AppDelegate ()<UNUserNotificationCenterDelegate>

@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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容