构建易维护的 Design System: 为什么 SwiftUI 会是更好的选择

该文已同步发布至 Thoughtworks Insights – What benefits does SwiftUI offer for building a design system? 原文链接 SwiftUI 自 iOS 13 发布以来,虽然已经面向公众近 4 年,但由于在实现复杂布局时的性能不佳,以及因其内置组件的底层实现变更(iOS 16 上 List 的底层实现从 UITableView 改成了 UICollectionView ),导致开发者们原本良好运行代码随系统升级被破坏了。iOS 14 之前 SwiftUI 的开发者体验也让人一言难尽。尽管有很多的槽点,但我们还是能发现社区整体上还是比较接纳 SwiftUI。所以如果你对 SwiftUI 还有所犹豫,不清楚为何要使用它,这篇文章或许能够带来一些新的想法。 本篇文章主要是想要通过 Design System 为切入点,同大家讨论相比起 UIKit,为什么更推荐使用 SwiftUI 来实现大多数业务场景下的 UI 组件。 首先简单概括一下 Design System 是什么,Design System 是一个包含了设计原则、组件库和代码资源等系统化的指导,旨在促进团队间的协作和提高项目的一致性。它可以帮助团队更快速、高效地构建应用程序,同时确保应用程序的外观和交互保持一致。 接下来就进入正题,从以下几点来探讨一下通过 SwiftUI 构建 Design System 有哪些优势。 声明式语法会更具有可读性和易于实现 内建的一致性和统一性表达 单向数据流带来的可预测性 与 Design System 有更相似的哲学思想 声明式语法会更具有可读性和易于实现 首先从实现和维护成本上来说,SwiftUI 与 Apple 现有的 UIKit 和 AppKit 不同,SwiftUI 采用了声明式语法构建 UI。由于声明式语法更关注于描述 UI 的最终效果,而不是具体实现方式。这使得通过声明式语法编写的 UI 组件更具可读性,有助于团队更好地协作实现 Design System。 ...

June 2, 2023

参加 Apple 开发者线上活动是什么样的体验?

从朋友圈看到思琦发了一个《使用 SwiftUI 打造卓越体验》的 Apple 开发者线上活动的报名链接,刚好最近参与的项目也在大规模使用 SwiftUI 就报名了,即使时间很不凑巧是工作日(另一报名要求是必须要有中国区的开发者账号)。 活动分两天第一天主要是一些 SwiftUI 的介绍,SwiftUI 对数据的处理和布局的一些要点。第二天是社区开发者交流。 技术细节后面可以分好几篇博客来描述,但总的来说技术相关的收获其实和自己去看 WWDC 差不多。并且由于长期以 2x 速度观看这类视频,刚开始还出现了一些不适应。但想着这是来自苹果的开发者的分享,还是管住了想拿起手机的手,毕竟机会难得。 事实证明确实认真观看还是有收获,主要是以下几点: 知道了一些 SwiftUI 相关的 Xcode 快捷操作,比如 Preview 和代码其实是可以相互影响的。Library 中可以搜索 ViewModifier 等等。 来自苹果的工程师对 SwiftUI 这样响应式 UI 编写方式的数据流思考的建议。 一些编写 SwiftUI 代码时能够让渲染引擎更高效的建议。 除了上面开发者分享的内容之外,最重要的是可以提出一些自己在使用 SwiftUI 时遇到的问题,感觉只要描述得够清楚并且和本次活动主题相关,那么都能够得到苹果开发者的解答,虽然受形式或时间限制,答案并不一定完整,但当一个 SwiftUI 的疑问是由苹果的工程师解答时,即使只是关键字的指引也能起到画龙点睛的作用(比如 StateObject 与 ObservedObject 的差异)。 第二天更多的是听社区开发者的一些交流,收获到了不少之前并不知道的资料。一并列在下方。 ps:感觉参加这次活动对自己来说还有一个副作用就是,由于资料的推荐者有足够的可信度,并且提供了一个可靠的学习路线,所以会用格外的认真来对待它们。 Swift UI 的入门指引 WWDC - Introduction to SwiftUI WWDC - SwiftUI Essentials 十个很有代表性的 Playground 交互式的 SwiftUI 入门教程 使用 SwiftUI 构建完整 App 的交互式教程 SwiftUI 数据处理要素 ...

March 28, 2022

SwiftUI 状态管理 —— Composible Binding

在 SwiftUI 中,需要通过数据来驱动 UI 的变化。数据结构抽象描述的质量也影响着我们对 SwiftUI 界面的维护。 通常数据中可能存在很多状态,如果使用很多的 boolean 值来描述这些状态,那么 App 的可维护性可能会大大降低。 管理独立状态的问题 假设我们有一个 App,用户可以在登录与非登录状态下进行操作。所以我们的界面需要兼容这两种状态,其描述可能是这样的: class AppState: ObservableObject { @Published var user: User? = nil @Published var error: Error? = nil var authenticated: Bool { user != nil } var hasError: Bool { error != nil } } 基于这样的状态描述,如果我们想创建一个仅展示用户名的组件大概会是这样: var body: some View { Group { if state.hasError { Text("Oops, sth went wrong: \(state.error!.localizedDescription)") } if state.authenticated { Text("Hello \(state.user?.name ?? "Unknown")!") } else { Text("Hello, stranger") } } } 粗看没有什么问题,实际上在维护这样的数据结构时就需要格外小心了。比如第一次我们登录失败,为了展示错误信息给 error 设置了值之后。必须在登录成功之后要及时地去清空 error,否则即使 state.authenticated 等于 true,用户依然无法看到正确的信息。 这还仅仅是有两个状态的情况下,像这样独立状态属性会带来很大的维护成本,开发者需要牢记各个属性之间的依赖关系,甚至编写界面的时候,还需要注意代码执行顺序。 引入状态机 把状态抽象成带有 associated values 的 enum 是个更好的选择,比如: class AppState: ObservableObject { enum AccountState { case authenticated(User) case unauthenticated case error(Error) } @Published var accountState: AccountState = .unauthenticated } // 界面中的使用 var body: some View { VStack { switch state.accountState { case .authenticated(let user): Text("Hello \(user.name)!") case .unauthenticated: Text("unregister") case .error(let error): Text("Oops, sth went wrong: \(error.localizedDescription)") } } 这样被状态机驱动的界面看起来要直观多了。并且在每个状态中对数据的操作,也由 enum 赋予了隔离能力。 ...

January 3, 2022