ios入门之消息推送详解手机开发

前言

在去年的苹果大会上,苹果带来的iOS 10 系统中将之前繁杂的推送通知统一成UserNotifications.framework 来集中管理和使用通知功能,还增加一些实用的功能——撤回单条通知、更新已展示通知、中途修改通知内容、在通知中显示多媒体资源、自定义UI等功能。
那么在ios10之前,ios的消息推送是怎么分类的呢?

ios 10之前

在ios之前,iOS推送分为Local Notifications(本地推送) 和 Remote Notifications(远程推送)。

本地推送

不需要服务器支持(无需联网)就能发出的推送通知,app本地创建通知,加入到系统的Schedule里,如果触发器条件达成时会推送相应的消息内容,如常见的定时任务闹钟等。
这里写图片描述

使用上也是非常简单。

/* 
 @property(nonatomic,copy) NSDate *fireDate; 
 @property(nonatomic,copy) NSTimeZone *timeZone; 时区 
 
 @property(nonatomic) NSCalendarUnit repeatInterval; 重复间隔(枚举) 
 @property(nonatomic,copy) NSCalendar *repeatCalendar; 重复日期(NSCalendar) 
 
 @property(nonatomic,copy) CLRegion *region 设置区域(设置当进入某一个区域时,发出一个通知) 
 
 @property(nonatomic,assign) BOOL regionTriggersOnce YES,只会在第一次进入某一个区域时发出通知.NO,每次进入该区域都会发通知 
 
 @property(nonatomic,copy) NSString *alertBody;       
 
 @property(nonatomic) BOOL hasAction;                是否隐藏锁屏界面设置的alertAction 
 @property(nonatomic,copy) NSString *alertAction;    设置锁屏界面一个文字 
 
 @property(nonatomic,copy) NSString *alertLaunchImage;   启动图片 
 @property(nonatomic,copy) NSString *alertTitle 
 
 @property(nonatomic,copy) NSString *soundName; 
 
 @property(nonatomic) NSInteger applicationIconBadgeNumber; 
 
 @property(nonatomic,copy) NSDictionary *userInfo; // 设置通知的额外的数据 
 */ 
 
- (IBAction)addLocalNote:(id)sender { 
    // 创建一个本地通知 
    UILocalNotification *localNote = [[UILocalNotification alloc] init]; 
 
    // 设置本地通知的一些属性(通知发出的时间/通知的内容) 
    // 设置通知发出的时间 
    localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0]; 
    //设置通知的内容 
    localNote.alertBody = @"吃饭了吗?"; 
    //设置锁屏界面的文字 
    localNote.alertAction = @"查看具体的消息"; 
    //设置锁屏界面alertAction是否有效 
    localNote.hasAction = YES; 
    //设置通过点击通知打开APP的时候的启动图片(无论字符串设置成什么内容,都是显示应用程序的启动图片) 
    localNote.alertLaunchImage = @"111"; 
    //设置通知中心通知的标题 
    localNote.alertTitle = @"222222222222"; 
    //设置音效 
    localNote.soundName = @"buyao.wav"; 
    //设置应用程序图标右上角的数字 
    localNote.applicationIconBadgeNumber = 1; 
    //设置通知之后的属性 
    localNote.userInfo = @{@"name" : @"张三", @"toName" : @"李四"}; 
 
    //调度通知 
    [[UIApplication sharedApplication] scheduleLocalNotification:localNote]; 
}

这里写图片描述

当用户点击本地推送通知的时候,会自动打开app,这里有2种情况:app在后台运行,或者被系统进程杀死,对于这两种情况,我们怎么处理呢?

app后台运行

这里写图片描述
这时候我们只需要调用下AppDelegate方法即可。代码实现

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification 
{ 
    // 跳转逻辑 
    if (application.applicationState == UIApplicationStateActive) return; 
 
    if (application.applicationState == UIApplicationStateInactive) { 
        // 当应用在后台收到本地通知时执行的跳转代码 
        [self jumpToSession]; 
    } 
 
    NSLog(@"local notifacation %@", notification); 
} 
 
- (void)jumpToSession 
{ 
    UILabel *redView = [[UILabel alloc] init]; 
    redView.backgroundColor = [UIColor redColor]; 
    redView.frame = CGRectMake(0, 100, 300, 400); 
    redView.numberOfLines = 0; 
    // redView.text = [NSString stringWithFormat:@"%@", launchOptions]; 
    [self.window.rootViewController.view addSubview:redView]; 
} 

app被杀死

这里写图片描述
对于app被杀死的情况,要先启动app,启动完毕会调用AppDelegate方法。
需要特别注意的是:在iOS8.0以后本地通知有了一些变化,如果要使用本地通知,需要得到用户的许可。
部分代码实现:

#define IS_iOS8 ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) 
 
@interface AppDelegate () 
 
@end 
 
@implementation AppDelegate 
 
 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
 
    /* 
     UIUserNotificationTypeNone    = 0,      不发出通知 
     UIUserNotificationTypeBadge   = 1 << 0, 改变应用程序图标右上角的数字 
     UIUserNotificationTypeSound   = 1 << 1, 播放音效 
     UIUserNotificationTypeAlert   = 1 << 2, 是否运行显示横幅 
     */ 
 
    [application setApplicationIconBadgeNumber:0]; 
 
    if (IS_iOS8) { 
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil]; 
        [application registerUserNotificationSettings:settings]; 
    } 
 
    // 如果是正常启动应用程序,那么launchOptions参数是null,其他方式需要对launchOptions设置 
    if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) { 
        // 当被杀死状态收到本地通知时执行的跳转代码 
        // [self jumpToSession]; 
        UILabel *redView = [[UILabel alloc] init]; 
        redView.backgroundColor = [UIColor redColor]; 
        redView.frame = CGRectMake(0, 100, 300, 400); 
        redView.numberOfLines = 0; 
        redView.text = [NSString stringWithFormat:@"%@", launchOptions]; 
        [self.window.rootViewController.view addSubview:redView]; 
    } 
 
    return YES; 
}

远程推送

远程推送指从远程服务器推送给客户端的通知(需要联网),远程推送服务一般采用苹果的APNS (Apple Push Notification Service)。
这里写图片描述

要实现远程推送,一般会涉及到三个阶段:

  1. APNS Pusher应用程序把要发送的消息、目的iPhone的标识打包,发给APNS。
  2. APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone。
  3. iPhone把发来的消息传递给相应的应用程序, 并且按照设定弹出Push通知。

基本配置

条件:新建一个对应你bundle的push 证书,打开Push Notifications 开关(XCode7不打开也可以正常使用,XCode8以后必须打开)。
这里写图片描述

这里写图片描述

代码实现:
注册接受APNs通知。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
 
    if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { 
        // 1.注册UserNotification,以获取推送通知的权限 
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil]; 
        [application registerUserNotificationSettings:settings]; 
 
        // 2.注册远程推送 
        [application registerForRemoteNotifications]; 
    } else { 
        [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeNewsstandContentAvailability | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound]; 
    } 
    return YES; 
}

调用AppDelegate方法,获取到用户的deviceToken。

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 
{ 
    // <32e7cf5f 8af9a8d4 2a3aaa76 7f3e9f8e 1f7ea8ff 39f50a2a e383528d 7ee9a4ea> 
    // <32e7cf5f 8af9a8d4 2a3aaa76 7f3e9f8e 1f7ea8ff 39f50a2a e383528d 7ee9a4ea> 
    NSLog(@"%@", deviceToken.description); 
}

推送通知,和本地通知一样有两种状况。

// 当接受到远程退职时会执行该方法(当进入前台或者应用程序在前台) 
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
{ 
    NSLog(@"%@", userInfo); 
 
    UIView *redView = [[UIView alloc] init]; 
    redView.backgroundColor = [UIColor redColor]; 
    redView.frame = CGRectMake(100, 100, 100, 100); 
    [self.window.rootViewController.view addSubview:redView]; 
}

苹果建议使用方法

/* 
 1.开启后台模式 
 2.调用completionHandler,告诉系统你现在是否有新的数据更新 
 3.userInfo添加一个字段:"content-available" : "1" : 只要添加了该字段,接受到通知都会在后台运行 
 */ 
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 
{ 
    NSLog(@"%@", userInfo); 
    UIView *redView = [[UIView alloc] init]; 
    redView.backgroundColor = [UIColor redColor]; 
    redView.frame = CGRectMake(100, 100, 100, 100); 
    [self.window.rootViewController.view addSubview:redView]; 
 
    completionHandler(UIBackgroundFetchResultNewData); 
}

UserNotitfication

iOS10 中统一了本地推送和远程推送的 API,在 UserNotifications.framework 来统一处理与推送相关任务,并增加了图片、音频、视频,自定义通知 UI 等新特性。

通知界面

这里写图片描述

多媒体

在此次版本中,iOS10 不仅新增消息的3dtouch等,还对图片、音频、视频等多媒体做了改进和优化。

类型 限制大小
图片 10M
音频 5M
视频 50M

多媒体推送代码:

if #available(iOS 10.0, *) { 
      let content = UNMutableNotificationContent() 
      content.title = "iOS10 推送测试" 
      content.body = "附件" 
      content.userInfo = ["icon":"1","mutable-content":1] 
      content.categoryIdentifier = "InputSomething" 
 
      let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false) 
 
      let requestIdentifier = "imageLocal" 
      if let imageURL = Bundle.main.url(forResource: "[email protected]", withExtension: "png"), let attachment = try? UNNotificationAttachment(identifier: "imageAttachment", url: imageURL, options: nil) { 
 
        content.attachments = [attachment] 
      } 
 
      let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger) 
 
      UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in 
        if (error != nil) { 
          print("error: /(error.debugDescription)") 
        } 
      }) 
    }

通常在做多媒体自定义推送的时候,一般会用到UNNotificationServiceExtension应用扩展,通过在 payload 中增加 mutable-content 字段来触发扩展。

{ 
    "aps":{ 
        "alert":"IOS10 推送测试", 
        "sound":"default", 
        "badge":1, 
        "mutable-content":1, 
        "category":"InputSomething" 
    }, 
    "image":"https://ws1.sinaimg.cn/mw690/934b5ef8gw1fapg2ssteej20oz0oz420.jpg" 
} 

当推送达到 app 时,会启动扩展并回调 didReceive 方法。在该方法里面可以对推送的 UNMutableNotificationContent 做出相应的修改。在 didReceive 回调方法中的 request 包含了推送的具体信息,可以通过其 userInfo 属性来解析出多媒体的 url。

let imageURL = Bundle.main.url(forResource: "lufei", withExtension: "jpg")

值得注意的是这里 Bundle 指的是扩展的沙盒,不是 app 的沙盒,所以资源的路径要正确。

而读取远程资源比读取本地资源一般要多一步保存操作。

private func downloadAndSave(url: URL, handler: @escaping (_ localURL: URL?) -> Void) { 
    let task = URLSession.shared.dataTask(with: url, completionHandler: { 
      data, res, error in 
 
      var localURL: URL? = 下载完之后保存到本地并返回本地的 url 
 
      handler(localURL) 
    }) 
 
    task.resume() 
  }

得到本地的 url 之后操作就一样了,都是通过 url 来生成一个 UNNotificationAttachment 对象。一切都操作完之后将这个 UNMutableNotificationContent 对象返还 contentHandler(bestAttemptContent)。

自定义界面

除了上述功能外,苹果还新增了自定义界面,这个绝对是大招。
这里写图片描述

其中上面的黄色区域可以理解成一个 ViewController 操作,下面绿色部分就是 Title 之类的显示内容。这部分是可以隐藏的。在扩展的目录下的 info.plist 编辑一些界面相关的东西。
这里写图片描述

说明:

  • UNNotificationExtensionCategory 触发 Extension 的 category
    这里需要在注册才能有效的触发 字符串类型
  • UNNotificationExtensionInitialContentSizeRatio 上图黄色区域的长宽比,float 类型
  • UNNotificationExtensionDefaultContentHidden 默认内容是否隐藏,Bool 类型

参考:ios 10.0之UserNotitfication

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/app/5950.html

(0)
上一篇 2021年7月17日 00:28
下一篇 2021年7月17日 00:28

相关推荐

发表回复

登录后才能评论