本地推送通知UserNotifications在Swift中的实现方式

简介

消息推送相信在很多人的眼里都不陌生了吧?像即时聊天微信,好友发信息给你时会在顶部弹下小窗口提醒你。也像是在影院APP预订了电影票,在开场前一小时你也会收到提醒。这类推送是需要经过后端发送请求的,需要服务器发送推送请求,又或者使用如极光推送等第三方渠道。

那么如果我们的APP不需要连网呢?这是不是就不能使用消息推送了?不是的,苹果还提供给我们本地消息通知服务,即便APP不连网也能使用,功能也很强大可靠。本地时钟的应用场景很广泛,例如手机上的时钟、日历等。

那么你知道如何去实现它吗?这篇文章将告知你答案,同时以两个小案例作为例子,以便更好地去理解它。

笔者环境

Xcode - Version 11.5 (11E608c)

Swift - version 5.2.4 (swiftlang-1103.0.32.9 clang-1103.0.32.53).

权限获取

UserNotifications 是 iOS10 推出来的框架,因此你只能在 10 或以上的版本使用它。推送服务和以往一样,也是需要用户授权的,当用户同意后才能正常注册消息通知,当用户拒绝时应该引导用户去打开APP的通知权限。利用requestAuthorization方法弹出并获取通知权限,接收的参数options是具体的授权选项,一般有弹窗、未读数量图标和声音即可,并在回调闭包中可以获取授权结果和错误。

UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (status, err) in
    if !status {
    		print("用户不同意授权通知权限")
        return
    }
}

status 为布尔类型,true 表示用户同意,false 即拒绝。在此种情况下,我们可以使用弹窗去引导用户去打开通知权限,需要明确告知用户打开后有什么好处,如果关闭会造成什么影响等等。如果让用户手动打开设置,找到APP,为APP开启权限,这样未免太过复杂,所幸的是可以通过以下代码为用户直接跳转至该应用的权限设置中心。

guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
if UIApplication.shared.canOpenURL(url) {
    UIApplication.shared.open(url, completionHandler: nil)
}

应弹窗提示用户,待用户同意后才跳转至设置,不然容易引起用户的不满心理。

触发器

本地消息通知一般有以下三种类型的触发器,它们都是继承于类UNNotificationTrigger:

  • UNTimeIntervalNotificationTrigger - 在经过特定的时间后触发本地消息推送;
  • UNCalendarNotificationTrigger - 在特定的时间点触发本地消息推送;
  • UNLocationNotificationTrigger - 在进入或离开特定的地理位置时触发本地消息推送。

UNTimeIntervalNotificationTrigger

手机上的时钟用过吧,里面的计时器功能就可以用UNTimeIntervalNotificationTrigger实现,比如开始计时30分钟,那么在计时器完成的时候就是使用通知提醒。

那么设置在经过特定的时间后触发本地消息推送,一般都经由以下几个步骤:

  • 首先创建UNMutableNotificationContent类,设定标题和内容,如果你有子标题还可以设置子标题,一般很少见到会设置子标题的应用。
  • 创建触发器,这里就是UNTimeIntervalNotificationTrigger,设定执行秒数和是否循环通知。
  • 创建通知请求UNNotificationRequest,这里需要指定通知的identifier,内容和触发器,至于identifier,你可以随意定义。
  • 最后将通知请求添加到系统的通知中心UNUserNotificationCenter即可。

例子,创建一个通知,在5秒后执行消息推送。实例代码展示如下:

let content = UNMutableNotificationContent()
content.title = "添加朋友 对着月亮敲代码"
//content.subtitle = "子标题"
content.body = "公众号 gh_6a83a7c19315"
content.badge = 1

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "Notification", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { err in
    err != nil ? print("添加本地通知错误", err!.localizedDescription) : print("添加本地通知成功")
}

有一处小 Tips,UNTimeIntervalNotificationTrigger创建时的repeats选项,如果你设定为循环通知时,即需要每隔N秒触发一次通知,那么你必须至少设置为60秒的时间间隔,如若低于60秒,你将会得到这样一条错误。

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'time interval must be at least 60 if repeating' *** First throw call stack: ( 0 CoreFoundation 0x00007fff23c7127e __exceptionPreprocess + 350 1 libobjc.A.dylib 0x00007fff513fbb20 objc_exception_throw + 48 2 CoreFoundation 0x00007fff23c70ff8 +[NSException raise:format:arguments:] + 88 3 Foundation 0x00007fff256e9b51 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191 4 UserNotifications 0x00007fff2c7dfc7c -[UNTimeIntervalNotificationTrigger _initWithTimeInterval:repeats:] + 277

UNCalendarNotificationTrigger

手机上的日历用过吧,在新建日程的时候,你可以选择一个提醒时间,这样它就会在你设定的提醒时间提醒你,这种情况就很适合用UNCalendarNotificationTrigger去实现。

举个例子,我们要在每晚7点提醒用户看公众号。

let content = UNMutableNotificationContent()
content.title = "添加朋友 对着月亮敲代码"
//content.subtitle = "子标题"
content.body = "公众号 gh_6a83a7c19315"
content.badge = 1

let dateComponents = DateComponents(hour: 19) // 1
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true) // 2
let request = UNNotificationRequest(identifier: "Notification", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { err in
    err != nil ? print("添加本地通知错误", err!.localizedDescription) : print("添加本地通知成功")
}

1 - 创建时间元件,19点即为晚上7点

2 - 创建UNCalendarNotificationTrigger对象,并将dateComponents赋值到dateMatching,repeats为true,重复在每天19点收到通知提醒。

UNLocationNotificationTrigger

这个触发器不在此篇文章讲述,留给你们自己去实现和测试结果。

图标

还记得刚刚设置的属性badge吗,我们设置值为1,这意味着在iPhone桌面上的应用图标在收到通知时,右上角圆点内所展示的数字就是badge的值。

这个属性值是applicationIconBadgeNumber,它是UIApplication的属性,设置为0即为隐藏,默认也是0。

UIApplication.shared.applicationIconBadgeNumber = 0

消息推送回调代理

接收用户对消息推送的反馈事件,比如说应用在后台收到了通知,用户点击了这条通知进入到了APP里面,我们需要获取这个事件去做一些处理,比如跳去某个界面,这里例子不讲这么复杂,只通过简单地判断用户是通过哪个通知进来的。

接收回调代理事件前,需要遵循UNUserNotificationCenterDelegate协议,并设置delegate接收的对象。

xtension AppDelegate: UNUserNotificationCenterDelegate {}

UNUserNotificationCenter.current().delegate = self

在Swift语言中,可以通过extension扩展类遵循的协议,并在extension。

当应用在前台运行时,收到的是这个-userNotificationCenter:willPresentNotification:withCompletionHandler:代理方法。UNNotification对象存储了传递到应用的一些数据,通过此对象可以拿到此条通知关联的触发器notification.request.trigger,从而判断其类型。

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    guard let trigger = notification.request.trigger else { return; }
    if trigger.isKind(of: UNTimeIntervalNotificationTrigger.classForCoder()) {
        print("Notification did receive, Is class UNTimeIntervalNotificationTrigger")
    } else if trigger.isKind(of: UNCalendarNotificationTrigger.classForCoder()) {
        print("Notification did receive, Is class UNCalendarNotificationTrigger")
    }
}

当应用在后台,或者被杀死的状态下,收到的是这个-userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:代理方法。此方法接收UNNotificationResponse类型的参数,它里面包含notification属性,因此可以参考上面的代码进行触发器的判断。

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    guard let trigger = response.notification.request.trigger else { return; }
    if trigger.isKind(of: UNTimeIntervalNotificationTrigger.classForCoder()) {
        print("Notification did receive, Is class UNTimeIntervalNotificationTrigger")
    } else if trigger.isKind(of: UNCalendarNotificationTrigger.classForCoder()) {
        print("Notification did receive, Is class UNCalendarNotificationTrigger")
    }
}

总结

本地通知有三种类型的触发器,分别是UNTimeIntervalNotificationTrigger、UNCalendarNotificationTrigger和UNLocationNotificationTrigger。

UNTimeIntervalNotificationTrigger在设置循环通知时,所设定的时间隔不能低于60秒,否则会报运行时错误。

Demo 源码下载

我已经把 Demo 上传至 GitHub 上面,项目名字是 SwiftUI-Tutorials,https://github.com/GarveyCalvin/SwiftUI-Tutorials,目录名为GCLocalUserNotification,有需要的朋友可以去下载运行一下,当然你也可以跟着文章去做一遍,这样更有利于你掌握此方面的知识。

以上就是本地推送通知UserNotifications在Swift中的实现方式的详细内容,更多关于Swift推送的资料请关注我们其它相关文章!

(0)

相关推荐

  • swift5.3 UIColor使用十六进制颜色的方法实例

    本文环境 Xcode 12 Swift 5.3 iOS 13 UI 给出的颜色往往都是十六进制的,如 #1a1a1a 等,但是我们在 iOS中是不能直接使用的,查询了一些代码,发现比较老旧,这里给出一个改进版本 使用 Extension 扩展 新建一个 swift 文件 比如我的 string.swift ,复制以下代码 // // String.swift // bestWhiteNoise // // Created by 袁超 on 2020/10/10. // import Founda

  • Swift无限循环控件开发

    无限循环控件是一个常常用到的一个控件,尤其是一些广告或者应用内容公告通知,或者新闻滚动的设计,都是必备的.这种控件网上也有很多,也有很多可以自定义的版本,功能非常强大. 但对于我们开发者来说,在具体的应用上风格和样式都是比较统一的,一般只需要自己特定的一种风格或样式即可,引入第三方显然有点大材小用.那么我们怎么能简单而且又快速的造一个无限循环的控件呢,只要我们知道无限循环的原理,那么我们就很自由的按照需求快速的完成.今天我们就讲讲这个'造轮'过程. 首先我们简单分析一下无限循环的原理.一个控件的

  • 深入探究Swift枚举关联值的内存

    enum Season { case Spring, Summer, Autumn, Winter } let s = Season.Spring 这是枚举最基础的用法,但是在swift中,对枚举的功能进行了加强,也就是关联值. 关联值可以将额外信息附加到 enum case中,像下面这样子. enum Test { case test1(v1: Int, v2: Int, v3: Int) case test2(v1: Int, v2: Int) case test3(v1: Int) cas

  • swift4.2实现新闻首页导航

    对于仿照新闻首页的页面,已经有比较好用的OC版本,现在我们来写一个swift版本的. 设备:xcode 10.2     语言:swift 4.2 效果图: 我们先创建一个多控制器的导航栏,直接上代码: // // JHSBarItemView.swift // ScrollBarController // // Created by yaojinhai on 2019/4/15. // Copyright © 2019年 yaojinhai. All rights reserved. // i

  • 在Swift中使用KVO的细节以及内部实现解析(推荐)

    KVO是什么? KVO 是 Objective-C 对观察者设计模式的一种实现.[另外一种是:通知机制(notification),详情参考:iOS 趣谈设计模式--通知]: KVO提供一种机制,指定一个被观察对象(例如A类),当对象某个属性(例如A中的字符串name)发生更改时,对象会获得通知,并作出相应处理:[且不需要给被观察的对象添加任何额外代码,就能使用KVO机制] 在MVC设计架构下的项目,KVO机制很适合实现mode模型和view视图之间的通讯. 例如:代码中,在模型类A创建属性数据

  • Swift 中如何使用 Option Pattern 改善可选项的 API 设计

    SwiftUI 中提供了很多"新颖"的 API 设计思路和 Swift 的使用方式,我们可以进行借鉴,并反过来使用到普通的 Swift 代码中.PreferenceKey 的处理方式就是其中之一:它通过 protocol 的方式,为子 view 们提供了一套模式,让它们能将自定义值以类型安全的方式,向上传到父 view 去.如果有机会,我会再专门介绍 PreferenceKey,但这种设计的模式其实和 UI 无关,在一般的 Swift 里,我们也能使用这种方法来改善 API 设计. 在

  • Swift 进阶 - map 和 flatMap的使用

    map 和 flatMap 主要分在集合上的使用和在可选类型上的使用,下面分别来看下. 集合上使用 map 和 flatMap 先看如下的代码: func getInfos(by name: String) -> [String] { if name == "Jack" { return ["Male", "25", "New York"] } else if name == "Lucy" { ret

  • 分析Swift性能高效的原因

    自从2014年Apple发布Swift语言以来,历时六年多,Swift已经发布到5.3版本,在5.0版本已经ABI stability,5.2版本也已经module stability,不管是语言还是基础库都日趋稳定,目前国内外大厂也都积极拥抱Swift阵营. 绝大多数公司选择Swift语言开发iOS应用,主要原因是因为Swift相比Objc有更快的运行效率,更加安全的类型检测,更多现代语言的特性提升开发效率:这一系列的优点使Swift语言的热度越来越高. 大多数人知道Swift语言相比于Obj

  • Swift UIButton使用教程

    一.UIButton基本操作 1.创建按钮 let btn: UIButton = UIButton()//没有样式 let btns:UIButton =UIButton(type: UIButtonType)//有样式 let button = UIButton(frame:CGRect(x:10, y:150, width:100, height:30))//简化创建方式 UIButtonType有以下类型 public enum UIButtonType : Int { case cus

  • 详解Swift 结构体

    Swift 结构体是构建代码所用的一种通用且灵活的构造体. 我们可以为结构体定义属性(常量.变量)和添加方法,从而扩展结构体的功能. 与 C 和 Objective C 不同的是: 结构体不需要包含实现文件和接口. 结构体允许我们创建一个单一文件,且系统会自动生成面向其它代码的外部接口. 结构体总是通过被复制的方式在代码中传递,因此它的值是不可修改的. 语法 我们通过关键字 struct 来定义结构体: struct nameStruct { Definition 1 Definition 2

随机推荐