简介

消息推送相信在很多人的眼里都不陌生了吧?像即时聊天微信,好友发信息给你时会在顶部弹下小窗口提醒你。也像是在影院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

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

UNTimeIntervalNotificationTrigger

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

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

  1. 首先创建UNMutableNotificationContent类,设定标题和内容,如果你有子标题还可以设置子标题,一般很少见到会设置子标题的应用。
  2. 创建触发器,这里就是UNTimeIntervalNotificationTrigger,设定执行秒数和是否循环通知。
  3. 创建通知请求UNNotificationRequest,这里需要指定通知的identifier,内容和触发器,至于identifier,你可以随意定义。
  4. 最后将通知请求添加到系统的通知中心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接收的对象。

extension 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")
}
}

总结

  1. 本地通知有三种类型的触发器,分别是UNTimeIntervalNotificationTrigger、UNCalendarNotificationTrigger和UNLocationNotificationTrigger。
  2. UNTimeIntervalNotificationTrigger在设置循环通知时,的定时间隔不能低于60秒,否则会报运行时错误。

往期回顾

  1. SwiftUI - 一起来仿写微信APP之一首页列表视图
  2. SwiftUI - 一步一步教你使用UIViewRepresentable封装网络加载视图(UIActivityIndicatorView)

Demo 源码下载

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

如果本文章对你有帮助,请关注我,你的关注就是我后续写文章的动力,下期会更精彩噢!

关于作者

博文作者:GarveyCalvin

微博:https://weibo.com/feiyueharia

博客园:https://www.cnblogs.com/GarveyCalvin

本文版权归作者,欢迎转载,但必须保留此段声明,并给出原文链接,谢谢合作!

公众号

作者第一次运营公众号,请你们一定要关注我的公众号,给我点动力,后期主要运营公众号为主。这是第三篇发布的文章,需要你们的支持,谢谢你们!

微信群

佛系等待你们的到来,若二维码过期,请加我QQ:1147626297,记得写备注,我重新发链接给你。快来加入我的“亿”个人群吧!

SwiftUI - iOS10本地推送通知教程UserNotifications在Swift中的实现方式的更多相关文章

  1. iOS 本地推送通知

    1.什么是本地推送通知 不需要联网的情况下,应用程序经由系统发出的通知 2.本地推送的使用场景 定时提醒,如玩游戏.记账.闹钟.备忘录等 3.实现本地推送通知的步骤 创建本地推送通知的对象UILoca ...

  2. Swift 本地推送通知UILocalNotification

    Notification是智能手机应用开发中常用的信息传递机制,它不用消耗更多资源去不停的检查信息状态,可以非常好的节省资源. 在iOS中分为两种通知:本地.远程.本地的UILocalNotifica ...

  3. (七十三)iOS本地推送通知的实现

    iOS的推送通知分为本地推送和网络推送两种,如果App处于挂起状态,是可以发送本地通知的,如果已经被杀掉,则只有定时通知可以被执行,而类似于QQ的那种网络消息推送就无法实现了,因为App的网络模块在被 ...

  4. 采用Service实现本地推送通知

    在android的应用层中,涉及到很多应用框架,例如:Service框架,Activity管理机制,Broadcast机制,对话框框架,标题栏框架,状态栏框架,通知机制,ActionBar框架等等. ...

  5. iOS10 推送通知 UserNotifications

    简介 新框架 获取权限 获取用户设置 注册APNS,获取deviceToken 本地推送流程 远程推送流程 通知策略(Category+Action) 附件通知 代理回调 简介 iOS10新增了Use ...

  6. 推送通知/传感器/UIDynamic仿真(推送通知已适配iOS10)

    推送通知/传感器/UIDynamic 一.推送通知 1.推送通知简介 什么是推送通知 此处的推送通知与NSNotification没有任何关系 可以理解为,向用户推送一条信息来通知用户某件事情 作用: ...

  7. iOS开发本地推送(iOS10)UNUserNotificationCenter

    1.简介 iOS10之后苹果对推送进行了封装,UNUserNotificationCenter就这样产生了.简单介绍本地推送的使用UserNotifications官方文档说明! 2.简单使用UNUs ...

  8. IOS之推送通知(本地推送和远程推送)

    推送通知和NSNotification是有区别的: NSNotification:是看不到的 推送通知:是可以看到的 IOS中提供了两种推送通知 本地推送通知:(Local Notification) ...

  9. iOS 进阶---推送通知之本地通知

    1.推送通知的2种方式 1)本地推送通知(Local Notification) 2)远程推送通知(Remote Notification) 2.通知的作用 可以让不在前台运行的app,告知用户app ...

随机推荐

  1. SpringMvc上传图片及表单提交(单文件+实体类参数提交)

    前两天做项目用到了Springmvc的文件上传来上传图片,由于和这个普通的Java文件上传处理流程不太一样,所以做的时候碰了壁,一顿百度,博客,要不就是一部分代码,要不就是看不懂,用不会的代码,下面来 ...

  2. Django分页之应用案例

    项目文件: models.py(建表) from django.db import models # Create your models here. class Book(models.Model) ...

  3. redis学习——day02_redis数据类型

    一.简介 Redis不仅仅是简单的key-value 存储器,同时也是一种data structures server.传统的key-value是指支持使用一个key字符串来索引value字符串的存储 ...

  4. poj3694 连通无向图图加边后有多少桥

    Network Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10261   Accepted: 3807 Descript ...

  5. CF948B Primal Sport

    题目链接:http://codeforces.com/contest/948/problem/B 知识点: 素数 解题思路: \(f(x)\) 表示 \(x\) 的最大素因子.不难想到:\(X_1 \ ...

  6. Verilog代码和FPGA硬件的映射关系(三)

    组合逻辑和FPGA之间的映射关系我们知道了,那时序逻辑和FPGA之间又是一种怎样的映射关系呢?我们就以前面寄存器章节的例子来向大家说明,也一同把当时为什么用异步复位更节约资源的原因告诉大家.我们先来看 ...

  7. web自动化之文件上传操作

    #!/usr/bin/python3 # -*- coding: utf-8 -*- #Author: xiaojian #Time: 2018/11/16 20:49 import win32gui ...

  8. 跟着拉大锯大神学Android——网络编程中运行后台服务器端口占用问题

    拉大锯网页地址:https://www.sunofbeach.net/u/1153952789488054272 跟着拉大锯大神学Android,在学到网络编程时,使用了大神搭建的用于学习的后台服务器 ...

  9. CF1353D Constructing the Array(优先队列)

    Question 给你一个长度为n的全为0的序列,让你从1-n填数,填的位置为找出最长的0序列,如序列长度为奇数,则为(l+r)/2,为偶数,则为(l+r-1)/2 Solution 运用优先队列,将 ...

  10. 极客手中的利器Electron

    作为一个前端开发人员,你可能已经听说过Electron了,你知道VS Code是基于这个技术开发的.不但VS Code, 目前一些大热的软件:飞书.Slack.WhatsApp都是基于这个技术开发的. ...