技术选型 - BB9z/iOS-Project-Template GitHub Wiki
选型这东西有时效性,故在每条开头标记了时间。
@2020-09
日常开发中,我会使用以下工具辅助开发:
- InjectionIII
- Xcode Playground
- SwiftUI Preview
它们共同的特点是可以让我们更快地看到编辑后的运行结果。
Injection 这个工具有很长的历史了,大部分时间稳定好用,比经常出问题的 Playground 和 Preview 舒心太多。
新版的原理是把修改后的文件打包成动态库载入到 app 中,再替换掉方法实现,这种 patch 的方法当然不是万能的,比如增删方法、属性,对 lazy 属性进行修改都做不到,as? 转换有时也会失败。
我一般只用来修改方法的实现和刷新 Storyboard,大部分情况够用,留个心眼不行重跑一下。
Playground 可以通过 framework 的方式载入项目和第三方代码,Xcode 12 后更支持 SPM,从选中 scheme 导入代码;载入 Storyboard 中的视图也是支持的,但不推荐,因为资源文件必须是编译好的,需要手动或通过脚本拷贝,不如用 SwiftUI 预览特性。
优点:相对轻量快速,适合辅助实现,验证能独立的简短代码片段。
缺点:虽然支持导入外部代码、资源,但还是有些麻烦;另外运行偶尔会卡住,代码错误的即时提醒经常失效。
使用参考:
- Explore Packages and Projects with Xcode Playgrounds (WWDC 2020, Session 10096)
- Playground Support - Apple Developer Documentation
- Add a color, file, or image literal to a playground - Xcode Help
- Use a custom framework in a playground - Xcode Help
我已经在项目里用它对 UIKit 的内容进行预览,如果运行顺利还是挺爽的,刷新彻底,支持完整的项目交互。
缺点主要是新东西,Apple 的实现 bug 太多,有时影响心情。项目如果不是 iOS 13+ 的需要额外配置不少东西。Xcode 11 跑起来经常后续的修改提示超时,需要重跑一下;Xcode 12 目前经常遇到模拟器启动不了的问题。编译时间没有缩短到可以忽略的程度。
使用参考:
- Structure your app for SwiftUI previews (WWDC 2020, Session 10149)
- How to debug your SwiftUI previews in Xcode - Apple Developer
- Mastering Xcode Previews (WWDC 2019, Session 233)
@2020-08
目前的方案是 Objective-C 分类扩展 UserDefaults,用宏生成属性,GitHub 上 Swift 库翻了个遍,看了三十个以上,暂时不换成 Swift 实现,下面分析一下目前的问题。
字段定义有三个要素:
- key,指定存储到 UserDefaults 的哪个 key 中;
- 默认值,这个没有我也能接受;
- 指定从哪个 UserDefaults 读取。
Key 主要有三个要求:
- 定义能避免冲突,最好在编译时就能保证互斥,以避免把不同的类型、不同意义的值存在同一个 key 中;
- 值支持自动生成,无需额外的 string 定义。重复定义不仅麻烦,还意味着迟早会输错,无论手打还是复制粘贴。自定义不是必须,有更好;
- 与类型绑定,不能通过各种类型方法存取,否则和定义一堆 key 直接用 UserDefaults 区别不大。
指定从哪个 UserDefaults 读取我需要用起来方便,因为会区分公共 UserDefaults 和用户私有 UserDefaults,而且这两个存储需要 key 不一样。
继续分析前看一下,目前两种流行的实现方式(Property wrapper 和定义 key 对象)示例:
// 1
@Defaults("foo", defaultValue: "bar")
var foo: String
// 2
@Defaults(keyPath: \.foo)
var foo: String
extension Defaults.Keys {
// 3
static let foo = Key<String>("foo", defaultValue: "bar")
// 4
var foo: Key<String> { .init(defaultValue: "bar") }
}- Property wrapper,最大的问题是 key 字符串的传入无法避免,指定存储倒是可以通过另写一个 wrapper 解决;
- 在其他地方定义了 key,可以将 property wrapper 改造成这样;
- 定义 key 对象,key 字符串的痛点无法避免,指定存储可以通过其他方式解决;
- 算是 3 的改进版,技术上可以通过
#function达到自动生成 key 的目标。
考察一圈目前有三个备选:
- Nirma/Default - 比较喜欢这个库,key 默认结构名/类名,key 和存储的自定义方式是重写属性,但现有体系迁移到这种方式比较困难
- sunshinejr/SwiftyUserDefaults - 应该是目前 star 最多的库,较完善,但 key 暂没有自动生成
- Storage - 参考了 SwiftyUserDefaults。用方法名解决了 Swift 中 key 的问题,支持多种存储算是亮点,看着比较理想。新生库,关注度不足
还有些其他需求,最好有,均不强制:版本迁移,命名空间,直接在 UserDefaults 上扩展。
其他值得参考或有意思的实现:
- UserDefaultsEVO - key 的定义与使用舒服,类型支持不佳,与 key 定义分离
- KissDefault - 从堆栈信息里提取 key,生产环境不可接受
- RCUserDefaults - 虽然是 Swift 写的,但本质是 Objective-C 库,运行时动态声明属性。除了不能直接支持 Codable 等 Swift 特性,用起来舒服
- macmade/Preferences.swift - Swift 反射机制实现的,运行时属性声明,生产环境使用需要优化
- TSUD - 不符合日常的使用方式,完全不同的风格
- SVMPrefs - 定义文件生成代码
出局的实现
- omaralbeik/UserDefaultsStore - 有点吧 UserDefaults 当数据库要存很多对象的意思,还有些奇怪的限制
- PersistenceKit - 把上面的实现集成作为存储之一
- UserDefaultsGenerator - 定义文件生成代码
- Prephirences - 看着不好用
- Palau - 改用 SwiftyUserDefaults 就好了
- tasanobu/TypedDefaults - 不好用
- shimesaba9/SwiftDefaults - Swift 反射机制发现属性,注册 KVO 同步
Property wrapper 实现
- Pyroh/DefaultsWrapper
- strawb3rryx7/UserDefault
- V8tr/UserDefaultsPropertyWrapper
- mrfour0004/UserDefaultsStorable
- looseyi/SwiftUserDefaults
Key 对象实现
- sindresorhus/Defaults
- nmdias/DefaultsKit
- OneStore
- dalu93/Defaults
- divadretlaw/UserDefaults
- MSAUserDefaults
类型与 key 定义分离
- i-schuetz/UserDefaults
- andyyhope/Blog_UserDefaultsProtocol - 能实现这种命名空间也挺有意思的,但同一个空间下字段只能是同一类型不能忍
@2020-07
如果你对 SwiftUI 还不了解,强烈花几个小时体验一下官方教程,直观的分步教学,惊艳的网页效果。
SwiftUI,顾名思义这是一个 UI 框架,它把对 view 的调试时间缩短成几秒至即时,这对开发帮助很大。新的绑定、观察等数据流支持,也是很香。
但真实生产环境的落地还要再等等,初代需 iOS 13,惊艳但是个半成品,缺的东西太多,不足以支撑现实中的复杂需求;iOS 14 的 beta 版目前来看补上很多东西,但应该还不够,至少对导航的控制几乎无力,连 popToRootController 都无法直接办到,setViewControllers 又怎么办,需要它的业务需求很常见的。再等 iOS 15+ 么,这样国内的环境哪年能完全用它?
@2020-09
SwiftUI 的布局重度依赖各种 Stack view,Auto Layout 的缺失使得布局上的控制有些无力,尤其当内容和内容之间挤压需要协调同时需要特殊对齐的时候,无法做自适应布局。
SwiftUI 绘制方面很强大。