热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《Widgets边看边写》第三部分:Timelines的进阶使用

widgets,边看,边,写,第三,部分,timelin

作者:雪人少校(William),iOS 开发者,现就职于字节跳动音乐团队。

Session: https://developer.apple.com/videos/play/wwdc2020/10036/

文中涉及的demo在这里[1]

整个《Widgets 边看边写》系列中的其他文章:《Widgets 边看边写》第一部分:冒险开始了《Widgets 边看边写》第二部分:Timelines 的基本使用

开始之前,我们先介绍下面三个对象(在本系列之前的 session 中已经提到过,但因为它们相对抽象,且能体现 Widget 的设计思想,为了方便理解,我们用简单的语言再描述一下)

  • Timeline:时间线,是一个关于更新策略的封装,里面包装了若干TimelineEntry

  • TimelineEntry:时间线策略的控制点,主要用来控制,更新的具体时间点(会结合Timeline的其他属性)

  • TimelineProvider:提供Timeline的那个对象,系统通过它拿到Widget的Timeline

TimelineEntry 是最本质的那个对象,用来控制Widget更新时机

按苹果一贯控制且克制的风格,关键事情要拿在自己手里,在整个 iOS 系统中进行平衡——对于 Widget 来说,“关键的事情”是电量 & 流量的开销

这种机制确保了一个 Widget 不能随意刷新自己的内容,而是要通过有计划,有规律的方式来更新

特别注意:

TimelineEntry里面的Date不是一个精确的时间,只是一种给系统的建议,系统根据自身情况来决定是否需要更新,极端情况下,比如低电,甚至不能保证更新

本篇讲动态配置,如果没有看之前部分,可以把demo下载下来直接从第三部分开始

本篇 Widget:Leadbard Widget —— 一个将角色按 health 排列的 Widget,同时点击每个角色头像能够唤起主app,并进入该角色的详情页面,先整体看一下:

主App Widget

URL Session

Widget可以通过网络请求来获取数据 —— TimelineProvider 是通过回调工作的,这样会让网络处理比较简单

直接看代码(入口 CharacterDetail.loadLeaderboardData)

主旨是:

  • 1 这里其实就是一个普通的 URLSession,利用 dataTask 的方式请求这个 URL,然后在回调里处理返回的数据
  • 2 这里把 fauxResponse(其实就是个字符串,没啥神秘的)写入了一个在临时目录的文件 “userData.json”

Q:但这是什么操作?!写个文件,然后请求文件 URL,然后在回调里处理文件数据?!

A:这里没问题,且说明了一件事:Widget 里可以调用 URLSession 系列的 API,间接证明了 Widget 是有网络能力的

说到这里,我们仔细想一下,为啥要在 demo 里证明 Widget 有哪些能力?

了解过 Extension 开发的同学应该明白,苹果有好多 extension 类型:

ShareExtension 点击“信息”

时间上先有虎嗅 App 进程,然后才有“信息”界面,我们合理推论一下

  • 低成本:“信息”的界面运行在“虎嗅”进程里了

  • 高成本:“信息”也是独立进程和“虎嗅”跨进程通讯

  • 从安全角度考虑,利用进程间的页表隔离,彻底隔离两个进程的数据(符合苹果一贯的谨慎)

无论哪种,都说明 extension 的进程和他的属主 app 的进程并不一定是同一个

之所以不一定,看 Extension 的类型,反例是 NotificationExtension

回到主题,Widget 也是 Extension,它有自己的运行时环境,且在 iPhone/iPad 这样的用电池的设备上,在能用的 API 集合上必然是受限的!

Demo 刚刚说明了,URLSession 的 API 是可以放心使用的

理解了这些,后面很多内容就比较 make sense 了!

Background Session

用过后台下载特性的同学都知道,background session 在完成的时候,可选后台唤起主 app,从 AppDelegate 的这两个回调

不理解 Background Session 的同学去看看这里,这里我们不展开

对于 Widget 来说,Widget(进程)同样有能力处理 Background Session 回调

在没有 AppDelegate 的情况下,Widget 要怎么做呢?

答案是:在 StaticConfiguration 中,有一个配置 “onBackgroundURLSessionEvents”,可以注册一些回调,可以用来代替 AppDelegate 的角色

比如,注册一个处理回调

Widget唤起宿主App

想要做到这样的效果,应该怎么做?

该 SwiftUI Link API 出场了,从 AllCharactersView 讲起(我们 Leaderboard Widget 的主要视图)

这里嵌入了一个 Link(角色的url)

Q:就是 Deeplink 而已,有什么特殊的?A:是 Deeplink,但不是通过 URLScheme 做的

感兴趣的读者可以去工程里求证

看这里(注意这是主 App 的视图,不是 Widget)

链接匹配则上面的 NavationLink 会通过 SwiftUI 的数据绑定,显示对应的界面

关于数据绑定的细节,可以参考这篇[2]

Widget Bundle

目前,我们在Widget Gallery里还只能看到一个Widget

就是被 @main 标识的那个Widget

但一个 Widget Bundle 可以容纳多个 Widget,@main 可以移动到 bundle 这里

⚠️:从这里开始,我们看的是 Demo 里 Game Status Final 目录下的工程

现在能看到多个 Widget 了

动态配置 Widget

此 Target 主要是给 Widget 提供配置项目,下面详细说

Widget 可以有一些静态配置,在 part2 里介绍过

视频有点抽象,补一张图,“编辑小组件”就是这里所讨论的配置

一个配置可以通过 XCode 增加一个 IntentDefinition 来定义

不要被 SiriKit 带偏了,我们后面讨论的东西和 Siri 没有直接关系

这里之所以会出现 SiriKit 的身影,是因为现代版本的 iOS 里,Springboard 和 SiriKit 是分不开的,所以 Springboard 上的配置很多都和 SiriKit 相关

那这个配置到底干嘛的?

定义点了编辑之后,用户能看的项目

讲动态之前,先看静态配置:

注意几个细节

  • 左边栏里上面那个才是动态配置,这个是静态配置,

  • 配置项目是hero

  • 静态配置一般是通过Enum来表达,那故名思议,这当然是静态配置

  • 动态配置当然是程序控制有哪些项目的

再看Enum

一系列固定值,符合预期

再看看动态配置

这和刚刚的静态配置看起来一样的?

静态配置 动态配置

发现了吗?配置项的类型不同

这里在视频中没有详细说,但有必要了解清楚,那按苹果工具的思路,我们尝试探索一下:点这里

然后对新加的参数调整一下类型

>

答案了出现了!

为了逻辑上的连贯,在这里稍微展开介绍一下视频里没提到的知识点:

XCode 对于 intentdefinition 文件是有自动代码生成的,我们点定义看一下(注意上方文件导航)

没有工程路径!说明这是一个生成的类

Show in Finder 看实际路径

一路追上去,是 DerivedData

对于动态配置,这里是关键

然后我们看一下 Hero 属性,看起来是一个形式化定义,是不是又生成代码了?

是的,读者有兴趣可以点Hero自行查看

让我们回到主线,打开 IntentHandler.swift 文件看一眼:

把两个集合合并好后,给系统回调

但系统怎么知道是 IntentHandler.swift 文件?

iOS 开发里代码逻辑的上找不出关联的东西,找 Info.plist

看到了吧?

长按编辑 Hero 配置项出现 配置内容

remoteCharacters 的内容在最后

到这里,视频就完了,但为了理解上的完整,我们再说一下如果用户真的选择了某个角色会发生什么:

我们开篇就说了这个类——提供 Timeline 的那个对象

Widget 配置中有它

Provider 有个回调

public func timeline(for configuration: DynamicCharacterSelectionIntent, with context: Context, completion: @escaping (Timeline) -> Void) {
        let selectedCharacter = character(for: configuration)
        let endDate = selectedCharacter.fullHealthDate
        let oneMinute: TimeInterval = 60
        var currentDate = Date()
        var entries: [SimpleEntry] = []
        
        while currentDate < endDate {
            let relevance = TimelineEntryRelevance(score: Float(selectedCharacter.healthLevel))
            let entry = SimpleEntry(date: currentDate, relevance: relevance, character: selectedCharacter)
            
            currentDate += oneMinute
            entries.append(entry)
        }
        
        let timeline = Timeline(entries: entries, policy: .atEnd)
        
        completion(timeline)
    }

注意第 2 行

func character(for configuration: DynamicCharacterSelectionIntent) -> CharacterDetail {
        let name = configuration.hero?.identifier
        
        return CharacterDetail.characterFromName(name: name)
    }

会去拿 hero.identifier,然后和自己定义的角色匹配,匹配到了就会更新界面!

所以,当用户选择了 Widget 的具体配置项目之后,会触发这里的刷新

func timeline(for configuration: DynamicCharacterSelectionIntent, with context: Context, completion: @escaping (Timeline) -> Void)

总结

终于讲完了,为了理解上的完整性,本文做了很多扩展性的说明,感谢各位读者的耐心阅读;这是 Widget 系列的最后一篇,WidgetKit 的概念很棒,也非常好用!希望大家能真正读懂原理,创造更多优秀的 Widget!

推荐阅读

✨ Apple Widget:下一个顶级流量入口?

为 Widgets 构建 SwiftUI 视图

《Widgets 边看边写》第一部分:冒险开始了

《Widgets 边看边写》第二部分:Timelines 的基本使用

关注我们

我们是「老司机技术周报」,每周会发布一份关于 iOS 的周报,也会定期分享一些和 iOS 相关的技术。欢迎关注。

支持作者

这篇文章的内容来自于 《WWDC20 内参》。在这里给大家推荐一下这个专栏,专栏目前已经创作了 99 篇文章,只需要 29.9 元。点击【阅读原文】,就可以购买继续阅读 ~

WWDC 内参 系列是由老司机周报、知识小集合以及 SwiftGG 几个技术组织发起的。已经做了几年了,口碑一直不错。 主要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经验、苹果文档和视频内容做二次创作。

参考资料

[1]

演示 Demo: https://developer.apple.com/documentation/widgetkit/building_widgets_using_widgetkit_and_swiftui

[2]

数据绑定: https://xiaozhuanlan.com/topic/0528764139


本文分享自微信公众号 - 老司机技术周报(LSJCoding)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。


推荐阅读
  • Python 3 Scrapy 框架执行流程详解
    本文详细介绍了如何在 Python 3 环境下安装和使用 Scrapy 框架,包括常用命令和执行流程。Scrapy 是一个强大的 Web 抓取框架,适用于数据挖掘、监控和自动化测试等多种场景。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 在ElasticStack日志监控系统中,Logstash编码插件自5.0版本起进行了重大改进。插件被独立拆分为gem包,每个插件可以单独进行更新和维护,无需依赖Logstash的整体升级。这不仅提高了系统的灵活性和可维护性,还简化了插件的管理和部署过程。本文将详细介绍这些编码插件的功能、配置方法,并通过实际生产环境中的应用案例,展示其在日志处理和监控中的高效性和可靠性。 ... [详细]
  • 卓盟科技:动态资源加载技术的兼容性优化与升级 | Android 开发者案例分享
    随着游戏内容日益复杂,资源加载过程已不仅仅是简单的进度显示,而是连接玩家与开发者的桥梁。玩家对快速加载的需求越来越高,这意味着开发者需要不断优化和提升动态资源加载技术的兼容性和性能。卓盟科技通过一系列的技术创新,不仅提高了加载速度,还确保了不同设备和系统的兼容性,为用户提供更加流畅的游戏体验。 ... [详细]
  • 第六章:枚举类型与switch结构的应用分析
    第六章深入探讨了枚举类型与 `switch` 结构在编程中的应用。枚举类型(`enum`)是一种将一组相关常量组织在一起的数据类型,广泛存在于多种编程语言中。例如,在 Cocoa 框架中,处理文本对齐时常用 `NSTextAlignment` 枚举来表示不同的对齐方式。通过结合 `switch` 结构,可以更清晰、高效地实现基于枚举值的逻辑分支,提高代码的可读性和维护性。 ... [详细]
  • 在Kohana 3框架中,实现最优的即时消息显示方法是许多开发者关注的问题。本文将探讨如何高效、优雅地展示flash消息,包括最佳实践和技术细节,以提升用户体验和代码可维护性。 ... [详细]
  • iOS 设备唯一标识获取的高效解决方案与实践
    在iOS 7中,苹果公司再次禁止了对MAC地址的访问,使得开发者无法直接获取设备的物理地址。为了在开发过程中实现设备的唯一标识,苹果推荐使用Keychain服务来存储和管理唯一的标识符。此外,还可以结合其他技术手段,如UUID和广告标识符(IDFA),以确保设备的唯一性和安全性。这些方法不仅能够满足应用的需求,还能保护用户的隐私。 ... [详细]
  • Node.js 配置文件管理方法详解与最佳实践
    本文详细介绍了 Node.js 中配置文件管理的方法与最佳实践,涵盖常见的配置文件格式及其优缺点,并提供了多种实用技巧和示例代码,帮助开发者高效地管理和维护项目配置,具有较高的参考价值。 ... [详细]
  • 开发笔记:深入解析Android自定义控件——Button的72种变形技巧
    开发笔记:深入解析Android自定义控件——Button的72种变形技巧 ... [详细]
  • 解决Only fullscreen opaque activities can request orientation错误的方法
    本文介绍了在使用PictureSelectorLight第三方框架时遇到的Only fullscreen opaque activities can request orientation错误,并提供了一种有效的解决方案。 ... [详细]
  • MySQL 5.7 学习指南:SQLyog 中的主键、列属性和数据类型
    本文介绍了 MySQL 5.7 中主键(Primary Key)和自增(Auto-Increment)的概念,以及如何在 SQLyog 中设置这些属性。同时,还探讨了数据类型的分类和选择,以及列属性的设置方法。 ... [详细]
  • 在 Axublog 1.1.0 版本的 `c_login.php` 文件中发现了一个严重的 SQL 注入漏洞。该漏洞允许攻击者通过操纵登录请求中的参数,注入恶意 SQL 代码,从而可能获取敏感信息或对数据库进行未授权操作。建议用户尽快更新到最新版本并采取相应的安全措施以防止潜在的风险。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
author-avatar
mobiledu2502935431
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有