大多数浏览器和
Developer App 均支持流媒体播放。
-
推送通知入门
通过重要事件和更新的推送通知帮助用户最大程度地使用你的 app,并在后台提供最新数据,以便在打开你的 app 时就可得知最新数据。了解如何使用通知并提醒用户实时的相关信息。了解警报和背景通知之间的区别,了解如何在 app 中采用它们,并通过使用正确的 API 来避免错误。
资源
相关视频
WWDC23
-
下载
(你好 WWDC 2020) 你好 欢迎来到 WWDC (推送通知入门) 嗨 我是 Elliot Garner 在本次视频中 我将要讨论推送通知 我们先对推送通知做一个概述 它们是什么 以及为什么要使用它们 然后我会讨论如何把它们添加到 app 中 推送通知允许 app 参与和互动 它们使你能够为 app 提供实时更新 这在更新你的 app 时 可以带来更动态的体验 那么为 app 添加推送通知 到底有什么优点? 首先 它们不要求 app 在前台活动 无论 app 处于何种状态 推送通知都将传递到 app 如果有必要 你的 app 会被启动 推送通知能效很高 是一种与客户互动的好方式 我们有两种类型的推送通知 提醒通知和后台通知 提醒通知允许你传递可见提醒 你的 app 可以用自定义的方式与之交互 后台通知允许 app 在不在前台时 接收运行 以便使内容保持最新 现在我们先来谈谈提醒推送 提醒推送允许你提供可见提醒 来提供有关 app 的更新情况 此提醒应用于通知 可与之交互的新信息 此提醒不需要 app 处于运行状态 无论 app 状态如何 都将显示该推送 最重要的是 它是完全可定制的 提醒的外观和它的互动能力 由你决定 我们看看如何开始设置 app 的提醒通知 你需要做的第一件事是注册远程通知 这将把设备注册为 Apple 推送通知系统 即 APNs 并将设备令牌返回给 app 此令牌用于标识设备 而且你可以将设备作为通知的对象 然后 你要声明你的 AppDelegate 为 UNUserNotificationCenterDelegate 这一步完成后 你要把你的 AppDelegate 指定为通知中心的代表 这样提醒打开时 你的 app 会得到通知 一旦你调用了 registerForRemoteNotifications 你将收到这两种方法中的一个回调 如果你未能为该设备获得令牌 didFailToRegisterFor RemoteNotificationsWithError 将被调用并显示一个错误 上面写着为何注册失败
如果你成功获得令牌 你需要将该令牌发送到后端推送服务器 这样你就可以发送通知到此设备上 我们来看一个例子 设备令牌作为数据对象传递到你的 app 所以 为了将其发送到你的服务器 需要把它转换为字符串 为了实现这一点 可以将数据分离到其组件中 再将这些组件转换为十六进制字符串 然后将它们重新连接到一个字符串中 然后将该字符串追加到 URLQuery 以制作出完成合格的端点
然后 执行 URLSession 将该令牌发送到服务器 再注册到数据库中 最后 在设备可以接收任何通知之前 你需要征求权限 以便为你的 app 显示提醒 调用 requestAuthorization 会产生一个提示 询问你的 app 是否可以显示提醒 “提醒通知示例” 想发送你的通知 这个决定的结果将传递给 completionHandler 并在设备设置中进行设置 对该方法的任何后续调用 都将返回设备设置中的状态 并且不会出现提示 如你所见 此功能就是请求显示提醒权限 播放声音 并在 app 图标上显示徽章 仅在特定情景才请求授权以响应操作 在这一情景中 你更有可能获得授权 在讨论如何处理通知有效负载之前 我们看一个示例有效负载 假设你在为一家餐厅构建 app 希望该 app 能够在有新优惠时提醒用户
如你所见 提示在告诉我们 鳄梨培根汉堡现在有优惠 我们逐步了解一下这个有效负载 以确保我们理解到位 首先是 aps 代码字典 aps 代码字典告诉设备如何呈现通知 aps 代码字典里面是提醒字典 这告诉系统要用于通知的文本是什么 通知里面是标题和正文字段 标题字段 是描述通知目的的简短字符串 标题需要简短且易于理解 正文字段是提醒消息的完整描述性文本 接下来是声场 该字段是可选的 如果你希望设备在收到提醒时播放声音 那就应包含此字段 如果你想使用默认声音 像这样使用默认声音即可 但是 你也可以为 app 提供自定义声音 接下来是徽章字段 这是一个可选字段 用于修改 app 图标的徽章 这是一个绝对值将显示在 app 图标上 我们可以通过编程的方式修改此值 这样在处理通知时 一旦打开提醒 那就将此值设置为零 在 aps 字段之外 你拥有可能想提供在通知旁边的 任何自定义数据 在这里 告诉 app 今天有什么优惠 以及具体的价格是多少 现在我们知道了有效载荷的样子 我们研究一下如何处理接收到的通知 此方法是 UNUser NotificationCenterDelegate 只要打开通知 就会调用该方法 该方法有一个 completionHandler 必须从函数返回之前进行调用 传递到 app 的有效负载 可以从通知内容的用户信息属性中提取 获得有效负载后 从 JSON 代码字典的 表示形式解析数据 如果数据不符合预期 在返回前 请调用 completionHandler 这会让系统知道 你已经完成了通知打开的处理 有了数据后 就可以执行 app 所需的任何更新 你应该打开与提醒有关的 app 部分 在餐厅的示例中 提醒出现时 商品会添加到购物车 而且购物车会显示出来
完成后 调用 completionHandler 以告诉系统你完成了 这样就可以了 这就是设置 app 以处理和接收 提醒通知所需要做的一切内容 既然我们已经研究了执行提醒推送的方法 我们来讨论一下后台推送 以及如何在 app 中使用它们 后台推送和提醒推送很相似 但是有些重要的差异需要记住 后台通知允许 app 在收到推送通知后 从后台获取数据 这些应该用于 即使在 app 未运行的情况下 也可以让它保持及时更新 系统将启动 app 并提供必要的运行时间 以执行后台更新 但是也有一些局限性 系统每天将 app 限制到了 太多后台操作上 如果设备处于特定限制下 例如 设备处于低电量状态 它将不会执行后台更新 所以为了设置 app 进行注册 并处理接收后台推送 你需要做什么呢? 和提醒推送一样 你的 app 需要注册 远程通知 以便为 app 获取设备令牌
但是 和提醒推送不同的是 你无需将 app 设为 UNUserNotificationCenterDelegate 也不用将其指定到 UNUserNotificationCenter 这是因为仅在处理提醒通知时 使用 UNUserNotificationCenterDelegate 而且由于所有后台通知已在此处执行 它就没有必要了 因为调用了 registerForRemoteNotifications 你仍然需要处理接收设备令牌的事情 而且你仍然需要 将该设备发送到推送服务器 来进行自己的注册 这与提醒通知所需执行的操作完全相同 现在我们来看后台通知的有效负载 如你看到的 后台通知有效负载 比提醒通知简单得多 后台通知所需的唯一字段 是 aps 代码字典内的 content-available 字段
该字段告诉系统这是一个后台通知 应启动 app 来执行更新 就像提醒通知一样 aps 代码字典的外部可用于自定义数据 现在我们看一下如何处理后台推送 设备收到任何远程通知后 都会调用 didReceiveRemoteNotification 使用此方法来处理后台通知
该函数还有一个 completionHandler 但是 在处理提醒通知时 与 completionHandler 不同 completionHandler 有一个参数
该参数是一个枚举 会告诉系统后台更新是否失败了… 是否没有收到数据… 还是收到了新数据 这样系统就可以明智地决定 在将来何时启动 app 所以 在餐厅 app 的例子中 后台通知每天都会被用来获取菜单信息 以确保其内容保持最新状态 如果菜单当前版本的 URL 无法创建 请调用 completionHandler 并告知它后台更新失败了
URL 创建后 需要创建一个 URLSession 为今天的菜单获取数据 如果未接收到数据 请调用 completionHandler 并告知它后台没有数据 但更新成功完成
否则 获取到更新的菜单后 app 可以使用那个数据来更新其内容 一旦内容更新完成 就会调用 completionHandler 告诉系统 后台已成功更新 而且检索到了新数据
以上就是让 app 处理后台通知 所需要做的全部内容 既然你知道了什么是推送通知 如何使用以及如何执行它们 那么将它们添加到 app 中应该没问题 你需要做的就是在开发者门户中 启用推送通知 并按照这些步骤将推送通知 添加到 app 中
请下载示例 app 它有此视频中显示的所有代码 请仔细查看它 并将它作为起点 为你的 app 添加通知 一旦完成这些操作 你就能成功为你的用户创建丰富的体验 谢谢
-
-
2:02 - Registering for notifications
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UIApplication.shared.registerForRemoteNotifications() UNUserNotificationCenter.current().delegate = self return true }
-
2:36 - UIApplicationDelegate callbacks
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { // The token is not currently available. print("Remote notification is unavailable: \(error.localizedDescription)") } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Forward the token to your provider, using a custom method. self.forwardTokenToServer(token: deviceToken) }
-
3:05 - Forward token to server
func forwardTokenToServer(token: Data) { let tokenComponents = token.map { data in String(format: "%02.2hhx", data) } let deviceTokenString = tokenComponents.joined() let queryItems = [URLQueryItem(name: "deviceToken", value: deviceTokenString)] var urlComps = URLComponents(string: "www.example.com/register")! urlComps.queryItems = queryItems guard let url = urlComps.url else { return } let task = URLSession.shared.dataTask(with: url) { data, response, error in // Handle data } task.resume() }
-
3:47 - Request authorization
@IBAction func subscribeToNotifications(_ sender: Any) { let userNotificationCenter = UNUserNotificationCenter.current() userNotificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in print("Permission granted: \(granted)") } }
-
4:43 - Payload JSON
{ "aps" : { "alert" : { "title" : "Check out our new special!", "body" : "Avocado Bacon Burger on sale" }, "sound" : "default", "badge" : 1, }, "special" : "avocado_bacon_burger", "price" : "9.99" }
-
6:11 - didReceive response
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo guard let specialName = userInfo["special"] as? String, let specialPriceString = userInfo["price"] as? String, let specialPrice = Float(specialPriceString) else { // Always call the completion handler when done. completionHandler() return } let item = Item(name: specialName, price: specialPrice) addItemToCart(item) showCartViewController() completionHandler() }
-
8:16 - Register for remote notifications (Background)
class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UIApplication.shared.registerForRemoteNotifications() return true }
-
9:05 - Background Notification Payload
{ "aps" : { "content-available" : 1 }, "myCustomKey" : "myCustomData" }
-
9:33 - didReceiveRemoteNotification
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { guard let url = URL(string: "www.example.com/todays-menu") else { completionHandler(.failed) return } let task = URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data else { completionHandler(.noData) return } updateMenu(withData: data) completionHandler(.newData) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。