SwiftUI 是一种非常简单的创新方法,可以利用 Swift 的强大能力在所有苹果设备平台上构建用户界面。因对 Swift 水平有限,这里不会深入的讨论 Swift 语法特性和 Combine 的使用及实现原理,而主要介绍 SwiftUI 一些情况。
一、SwiftUI 诞生
2019 年 WWDC 现场,苹果宣布推出 SwiftUI,SwiftUI 是基于开发语言 Swift 建立的框架,可以用于 watchOS、tvOS、macOS 等苹果旗下系统。开发者可以通过命令或拖拽等方式,轻松编辑语言。目前,SwiftUI 只支持 Xcode 11、iOS 13 版本及以上,SwiftUI 将于秋季正式推出,因此采用 SwiftUI 开发的 APP 可能还需要一些时间。
因为声明式 UI已经成为主流,声明式编程是未来
- 代码是描述程序在做什么,可阅读性会大大提高;
- 组件状态更容易维护在内部,减少副作用;
- 组件之间的边界更明显,从而让组件复用更灵活。
二、SwiftUI 设计目标
SwiftUI 的愿景是降低开发 iOS 门槛,吸引更多开发者、丰富 iOS 的业态。为了达到这个目的,苹果一直在尝试 所见即所得 的理念,从早期 xib 到 stroyboard。这么多年了,这几项技术发展不尽如人意,并没有在生产环境中流行起来,但苹果一直在动这方面的心思。
随着整个软件开发领域的演进,近年来涌现的 React,Vue,Flutter 技术,逐渐证明了一个事实;在 UI 开发领域,描述性语言是最佳方式
这催生了 SwiftUI 全新的 UI framework 的诞生,在开发 SwiftUI 伊始,SwiftUI 团队就确认了基本的原则:
- 用原生语言的编写描述性界面
- 数据驱动,A Single Source Of Truth
为什么 SwiftUI 对于开发者那么重要呢?原因其实很简单,因为新的 SwiftUI 框架利用了苹果自定义创建的 Swift 语言的特性,使开发人员更容易、更快地构建更好的用户界面代码,同时错误会更少,也能创造更多的功能。SwiftUI 让苹果开发者专注于打造自己的原创内容,同时可开发出更统一、更强大、外观更现代的软件,影响将是巨大的。
三、界面描述语言(DSL)
先抛开 xml 语言,再看看下面的描述语言:
- HTML,界面描述能力最好,和实际节点真·一一对应(::before 除外),但是缺乏编程能力
- Flutter,具有很强的编程能力,但使用代码函数调用、入参、形参、代码缩进来模拟界面的层级结构的方式,不够优雅,层次不精确,而且函数调用里的 ,、{}的符号的存在,导致布局代码观感很差,神似型不似,这点很致命。加上不区分 UI 结构和 UI 属性,以及一套代码到处跑的理念和我们苹果承认设备差异性是相左的
- JSP、jQote、php,这些模板技术既有编程语言,又可以输出 UI 模板,但是太灵活,不可控,原始,不利于做封装和优化
- JSX、Pug、Vue,Pug 脱胎自 html,拥有极其简洁的界面描述语法;JSX、Vue 的编程能力和界面描述能力平衡的很好,但是他们需要对模板编译然后再用 JS 渲染,Pug 式简洁的语法 + 受限的编程能力。最重要,不能引入编译过程
也就是说他必须是 100 % 的 Swift 源码—— 不需要引入新的布局语言,而是用 Swift 语言自身来实现界面描述语法,这才是真正的挑战。
四、SwiftUI 的组件
有了布局的 DSL 语法,我们还需要内置的 SwiftUI 组件,支撑快速开发能力。有两个原则:
- 提供最朴素的组件集
- 提供扩展能力
4.1 组件定制原则
SwiftUI 自带 Design System。Design System 遵循的是基本的Human Interface Guidelines - Design的条款。在一定程度上,你必须遵守这些设计原则,这样产出来的 App UI,自动符合 Human Interface Guidelines - Design 的要求,比如:
- List 必须有 seperator
- 元素之间默认拥有符合苹果美感的间距
- 容器元素布局符合 Guidelines
- 无处不在的留白等
有些属性你甚至没有开放接口可以定制,如 Scrollview 你甚至不能设置 is PageEnabled。定制参数以初始化参数的形式存在,额外行为通过 view modifier 来实现。我们认为在 apple-style 里,你只需要关心这些参数就够了,如果有已有的样式行为不能满足,你需要通过适配自定义 UIView 到 SwiftUI 的方式实现。
4.2 更简化的布局系统
在 React-Native 身上,可以看到了 css 的一个子集完成可以满足日常的布局需要,复杂如 float, 在 Web 领域甚至都没有在用 float 的本意使用, native 里更不需要。而 iOS 上的 Autolayout 用来解决适配问题的方式,多少有些繁琐,以进一步简化:
- flex
- position 、offset
- 盒子模型,既不是 content-box 也不是 border-box,和设置 padding 顺序有关、只有 padding 没有 margin
上面的设计要素是完备的,布局系统简化带来运行效率和开发效率的双提升,对 SwiftUI 的运行时实现也是个很大的简化。
4.3 SwiftUI 的扩展能力
在 SwiftUI 的封装里不容许暴露潜在的危险。考虑:
自定义组件的标签也不容许包含 SwiftUI 组件作为子元素
考虑到还要支持组件的 preview,要求我们实现一切 View 都可以被单独展示,在 SwiftUI 里所有的 View 都是相同的,都可以被 present 和 push。只是在考虑到和 UIKit 协同时,我们单独在 UIViewRepresentable 的基础上封装了 UIViewControllerRepresentable,记住 万物皆为 View(对标 Flutter:一切都是 widget)
除了 UI 组件的扩展性,当一个 View 内部因为异步网络数据或者复杂的事件交互变多时,我们参考了 iOS 里比较流行的 MVVM、VIPER 架构,解决传统的 MVC 框架下 ViewController 过于膨胀的问题 —— 引入Coordinater 机制。
UIViewRepresentable 负责单纯的 UI 数据刷新逻辑, Coordinater 加工数据和内部事件更新数据;并且严格限制在 View 实例里,被 @State 标记的变量只在 var body: some View {} 只读属性里被改变,强制 View 和 BindableObject 实例的职责分离,首先是加工数据然后是使用数据渲染,不许混杂。
至此,涉及对 UI 静态部分的 SwiftUI 设计基本成型。
五、SwiftUI 的开发范式
日常开发工作,项目业务类型和项目体量形态差异会导致用相同的 API,产出迥异的项目代码结构和分层,有必要给出我们推荐的开发规范。让普通的开发者能够快速上手 SwiftUI,使用 SwiftUI 的设计思路来开发 APP。
SwiftUI 现在还在 beta,这后面也依赖苹果对 Swift 的控制,所以在 Swift 语言层面对 SwiftUI 这个 UI 库,进行了极大的支持,寄希望于 SwiftUI 的品质和 Apple 产品给用户的体验一样 -- 简洁、优雅,背后又蕴藏玄机。