IOS 推送通知 本地推送和远程推送

什么是推送通知?

首先明确:**此处的推送通知跟我们的”NSNotification”没有半毛钱关系
可以理解为: 向用户推送一条信息来通知用户某件事情
作用: 可以在APP退到后台,或者关闭时;继续推送一条消息告诉用户某件事情

推送通知的应用场景?

  • (1) 一些任务管理APP,会在任务时间即将到达时,通知你做该任务;
    (2) 健身App定时提醒你应该健身了;
    (3) 买过电影票后,提前半小时告诉你,电影即将开场;
    (4) 当你QQ或者微信收到消息时,即使退到后台,或者关闭APP,也可以收到信息通知告诉我们;
    (5) 电商APP,推送一条消息通知我们有新品上架等等

推送通知的分类

  • 本地推送通知

    “本地”可以理解为”不联网”;即使没有网络情况下,也可以推送通知消息
    应用场景: 确定知道未来某个时间点应该提醒用户什么
    通知发送方:开发人员负责在app内部发送

  • 远程推送通知

    与“本地”相对,表示,必须在联网情况下才会向用户推送通知消息
    远程推送服务,又称为APNs(Apple Push Notification Services)

    应用场景:

    1. 不确定未来某个时间点应该提醒用户什么,临时性的
    2. 当APP彻底退出时也想继续让用户获取一些最新消息
  • 使用原则: 谁能确定通知时间和内容, 谁就可以发送(开发人员在APP内部通过代码发送=本地通知; 服务器可以确定通知时间和内容=远程通知)*

远程推送的呈现效果

image.png

本地推送通知

在iOS8.0之后 使用本地通知需要得到用户的许可 在didFinishLaunchingWithOptions里面添加请求

if(系统版本 >= 8.0)
{
    // 注册接收通知的类型
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
    [application registerUserNotificationSettings:settings];

    // 注册允许接收远程推送通知
    [application registerForRemoteNotifications];
}
else
{
    // 如果是iOS7.0,使用以下方法注册
    [application registerForRemoteNotificationTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound];
}

//swfit:

 if #available(iOS 8.0, *)
        {
            // 注册接收通知的类型
            let type = UIUserNotificationType.alert.rawValue | UIUserNotificationType.badge.rawValue | UIUserNotificationType.sound.rawValue
            let settings = UIUserNotificationSettings(types: UIUserNotificationType(rawValue: type), categories: nil)
            
            UIApplication.shared.registerUserNotificationSettings(settings)
            // 注册允许接收远程推送通知
            UIApplication.shared.registerForRemoteNotifications()
        }
          

发送一个本地通知

@IBAction func puchNotionfication(_ sender: Any)
    {
    // 如果是ios8.0以前, 以下代码, 可以直接发送一个本地通知,
    // 但是, 如果是ios8.0以后, 你需要主动的请求授权, 才可以
    // 通知显示的条件
    // 必须不能再前台
        
        //1.创建一个t本地通知
        let localNoti = UILocalNotification()
        //2. 设置通知内容
        localNoti.alertBody = "这是一个好日字"
        //3. 设置发送通知的时间触发时间
        localNoti.fireDate = Date(timeIntervalSinceNow: 4) 
    
        //重复周期
        // localNoti.repeatInterval = .weekday
        //设置滑动文字
        localNoti.hasAction = true
        localNoti.alertAction = "回复"
        // 启动图片(当用户点击了本地通知, 启动我们APP 的时候, 带的启动图片)
               // 如果是在ios9.0以前, 当锁屏界面, 出现一个通知, 用户点击了通知, 启动APP 的时候, 会自动将我们设置的图片, 当做启动图像 来显示
               // ios9.0, 这个属性, 不太灵
               // 如果这个图片,找不到, 会使用系统默认的启动图片
        localNoti.alertLaunchImage = "2"
        
        
               // 设置通知弹框的标题
               // 标题, 这对于, 通知中心的通知有效
        if #available(iOS 8.2, *)
        {
            localNoti.alertTitle = "斗地主"
        }

        // 设置图标右上角的数字(0 代表不显示)
        localNoti.applicationIconBadgeNumber = 3
         // userInfo 额外信息
        localNoti.userInfo = ["name":"哥哥", "sex":"女"]
        

    
        //应用程序级别的操作 ,调度本地通知,完成之后,会在特定的fireDate发出通知

        UIApplication.shared.scheduleLocalNotification(localNoti)
        
    }

其他的属性和方法

调度本地推送通知(调度完毕后,推送通知会在特地时间fireDate发出)
[[UIApplication sharedApplication] scheduleLocalNotification:ln];

获得被调度(定制)的所有本地推送通知
@property(nonatomic,copy) NSArray *scheduledLocalNotifications;
(已经发出且过期的推送通知就算调度结束,会自动从这个数组中移除)

取消调度本地推送通知
- (void)cancelLocalNotification:(UILocalNotification *)notification;
- (void)cancelAllLocalNotifications;

立即发出本地推送通知
- (void)presentLocalNotificationNow:(UILocalNotification *)notification;


每隔多久重复发一次推送通知
@property(nonatomic) NSCalendarUnit repeatInterval;

点击推送通知打开app时显示的启动图片
@property(nonatomic,copy) NSString *alertLaunchImage;

附加的额外信息
@property(nonatomic,copy) NSDictionary *userInfo;

时区
@property(nonatomic,copy) NSTimeZone *timeZone;
(一般设置为[NSTimeZone defaultTimeZone] ,跟随手机的时区)

点击本地通知

当用户点击本地推送通知,会自动打开app,这里有2种情况

  1. app并没有关闭,一直隐藏在后台
    让app进入前台,并会调用AppDelegate的下面方法(并非重新启动app)
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
  1. app已经被关闭(进程已死)
    启动app,启动完毕会调用AppDelegate的下面方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象

添加一些额外的操作

IMG_0497.PNG
func registerAuthor()
    {
        // Override point for customization after application launch.
           if #available(iOS 8.0, *)
           {
               // 注册接收通知的类型
               let type = UIUserNotificationType.alert.rawValue | UIUserNotificationType.badge.rawValue | UIUserNotificationType.sound.rawValue
            
               
            
            
            //1.创建z一组操作行为
            let category = UIMutableUserNotificationCategory()
            //1.1设置组标识
            category.identifier = "select"
            //1.2设置组里面的行为
            let action1 = UIMutableUserNotificationAction()
            action1.identifier = "queding"
            action1.title = "确定"
            // behavior todo
                       
            // 代表, 用户如果点击了这个动作, 拉到底, 是在前台运行这个动作, 还是在后台
            action1.activationMode = .foreground
             // 必须要解锁之后, 行为才会执行(如果activationMode, 是前台状态, 那这个属性, 就会被忽略)
            action1.isAuthenticationRequired = true
            // 是否是破坏性行为(会使用一个红色的标识, 来标识这个按钮)
            action1.isDestructive = false
            
            //1.1设置组标识
            category.identifier = "select"
            
            //1.2设置组里面的行为
            let action2 = UIMutableUserNotificationAction()
            action2.identifier = "huifu"
            action2.title = "回复"
            
            // behavior todo
            if #available(iOS 9.0, *)
            {
                action2.behavior = .textInput
                action2.parameters = [UIUserNotificationTextInputActionButtonTitleKey: "确定"]
                    
            }
                                      
            // 代表, 用户如果点击了这个动作, 拉到底, 是在前台运行这个动作, 还是在后台
            action2.activationMode = .background
            // 必须要解锁之后, 行为才会执行(如果activationMode, 是前台状态, 那这个属性, 就会被忽略)
            action2.isAuthenticationRequired = false
            // 是否是破坏性行为(会使用一个红色的标识, 来标识这个按钮)
            action2.isDestructive = false
            
            
            let actions = [action1, action2]
            
            // 如果针对于弹框样式的通知
            // default 代表, 最多可以显示4个按钮
            // minimal, 代表,最多可以显示2个按钮
            category.setActions(actions, for: .default)
            
            //附加操作行为组
            let categorys : Set<UIUserNotificationCategory> = [category]
            
               let settings = UIUserNotificationSettings(types: UIUserNotificationType(rawValue: type), categories: categorys)
                UIApplication.shared.registerUserNotificationSettings(settings)
            
               // 注册允许接收远程推送通知
               UIApplication.shared.registerForRemoteNotifications()
           }
               
        
    }
     // completionHandler, 系统提供的回调代码块, 执行这个代码块, 到时候, 系统会采集一些信息
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void)
    {
        print(identifier,notification)
        print("old")
        completionHandler()
    }
    // 9.0
    // 如果实现了这个方法, 那么上面一个方法, 就不再执行
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
        print("new")
        print(identifier,responseInfo)
        completionHandler()
    }

远程推送

什么是远程推送通知

  • 顾名思义,就是从远程服务器推送给客户端的通知(需要联网)
  • 远程推送服务,又称为APNs(Apple Push Notification Services

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

传统获取数据的局限性
只要用户关闭了app,就无法跟app的服务器沟通,无法从服务器上获得最新的数据内容
不管用户打开还是关闭app,只要联网了,都能接收到服务器推送的远程通知

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

  • 什么是长连接
    只要联网了,就一直建立连接

  • 长连接的作用
    时间校准
    系统升级
    查找我的iPhone
    .. ...

  • 长连接的好处
    数据传输速度快
    数据保持最新状态

获取deviceToken

苹果需要用户的UDID和bundleID


获取过程

qq案例分析

  1. app 发送设备的UDID和
    应用的Bundle Identifier
    给APNs服务器

  2. 经苹果加密生成一个
    deviceToken 返还给当前app

  3. app发送当前用户的deviceToken
    和用户的标志(比如id或者qq) 给qq服务器

  4. qq服务器将用户的deviceToken存进数据库

  5. 当有人发送消息 李四的手机(昵称:李四 QQ:56789)
    发给张三(QQ12345):吃饭没?

  6. 消息先到qq服务器,然后去数据库查询张三的deviceToken

  7. qq服务器通知苹果服务器
    deviceTokoen:888
    body:李四:吃饭没?

  8. 苹果服务器通过deviceToken找到张三现在的设备

一.开发iOS程序的推送功能, iOS端需要做的事

1.请求苹果获得deviceToken
2.得到苹果返回的deviceToken,发送deviceToken给公司的服务器

  1. 监听用户对通知的点击

二.调试iOS的远程推送功能, 必备条件:

1.真机

2.调试推送需要的证书文件
1> aps_development.cer : 某台电脑就能调试某个app的推送服务
2> iphone5_qq.mobileprovision : 某台电脑就能利用某台设备调试某个程序

三.发布具有推送服务的app

1> aps_production.cer : 如果发布的程序中包含了推送服务,就必须安装这个证书
2> qq.mobileprovision : 某台电脑就能发布某个程序

制作证书

截屏2020-05-27下午4.42.59.png
截屏2020-05-27下午4.43.18.png
截屏2020-05-27下午4.43.09.png
截屏2020-05-27下午4.43.25.png

截屏2020-05-27下午4.43.35.png
截屏2020-05-27下午4.43.44.png

注册远程推送通知

客户端如果想接收APNs的远程推送通知,必须先注册(得到用户的授权)
一般在App启动完毕后就马上注册

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // 注册远程通知
       UIRemoteNotificationType type = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound;
    [application registerForRemoteNotificationTypes:type];
    return YES;
}
  • 注册成功后会调用AppDelegate的下面方法,得到设备的deviceToken
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSLog(@"%@", deviceToken);
}

点击远程推送通知

当用户点击远程推送通知,会自动打开app,这里有2种情况
app并没有关闭,一直隐藏在后台
让app进入前台,并会调用AppDelegate的下面方法(并非重新启动app)

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;

app已经被关闭(进程已死)
启动app,启动完毕会调用AppDelegate的下面方法

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