-
暗黑模式方案
- 明确需求: 不会增加除系统暗黑模式外的其他模式
- 系统暗黑模式方案:
- 优点:
- 系统Assets资源支持动态色,动态图,原生支持,代码侵入性最小
- 默认支持系统更换模式时已经创建的页面实时刷新效果,不需要销毁根控制器重新创建
- Assets创建动态色,动态图完美支持xib,不需要额外适配
- 缺点:
- 仅支持iOS 13+系统
- 仅支持日间暗黑两种模式,无法新增其他主题
- layer层级需要自己控制刷新粒度
- 优点:
- SwiftTheme方案:
- 优点:
- 可以服务器动态下发个性化主题,方便增加除暗黑模式外的其他主题
- 不受系统版本限制
- 缺点:
- 系统控件的color,image等相关属性全都不能用,必须全部替换成theme_color,theme_image等扩展属性,才能实现更换主题时已经创建的页面实时刷新效果,否则必须销毁根控制器重新创建
- xib适配方案有两种:
- 通过扩展给xib扩展新属性设置color,image等相关,设置扩展属性时通过选中主题设置对应颜色,不能直接设置控件的color,image等原生属性
- 将xib中的控件关联到代码中,通过代码设置theme_color,theme_image等扩展属性
- 代码侵入性太强
- 优点:
- 最终方案选择: 系统暗黑模式方案(需求明确不会新增除暗黑模式外的其他模式更优)
-
多语言国际化方案
- 明确需求: app内提供更换语言功能, 在app内更改语言后,已经创建的页面及时刷新,不能销毁根控制器重新创建。
- 难点:
- 跟app关联的语言国际化,只能通过代码控制,xib怎么国际化?
- 不销毁根控制器重新创建,已经创建的页面怎么刷新?如果是每个页面加通知,那每个页面需要刷新的部分怎么抽取?对业务代码逻辑影响多大?是否有明确规范,新人接替是否好维护?
- 跟语言关联的有静态页面控件刷新,业务逻辑刷新(如banner图上有文字,语言改变后banner图也需要刷新)
- 怎么控制刷新粒度,如果栈里的静态控件太多,业务逻辑刷新太多,一次性全部刷新是否会影响性能?还是分批刷新?
- 切换根控制器方案: 切换语言直接重新创建根控制器
- 优点:
- 逻辑简单,代码0浸入
- 缺点:
- 必须退回根控制器重新加载,不能停留在当前页面实时刷新
- 优点:
- 通知方案: 每个页面监听语言改变来刷新需要刷新的控件和逻辑
- 优点:
- 已经创建的页面能实时刷新
- 缺点:
- 每个页面都需要监听通知
- 语言改变时所有创建的页面控件和逻辑都同时刷新,会影响一定性能
- 写业务代码需要关心哪些控件需要刷新,剥离出来刷新,不太友好
- 优点:
- i18n自定义方案: 向每个控件注入一段block,语言改变时调用,也可以在改变时做标记,在页面将要出现的时刷新
- 优点:
- 已经创建的页面能实时刷新
- 写业务代码不需要关心哪些控件需要刷新
- 静态控件统一规范,统一写法,业务逻辑刷新单独控制
- 缺点:
- 代码中设置text,title,attributedText等文本的地方都需要加前缀.i18n.text来设置,浸入性较大
- 优点:
- 触发生命周期函数: 语言改变时将当前栈里的控制器销毁并替换新的,让控制器重新走生命周期函数
- 优点:
- 已经创建的页面能实时刷新
- 逻辑简单,代码0浸入
- 缺点:
- 需要明确当前栈结构,遍历移除旧的,创建新的替换,需要明确创建新控制器依赖的参数,绝对不能更改栈层级结构和控制器类型,
- 如果每个控制器的子控制器太多,怎么处理?如果子控制器也重新创建,那么子控制器布局呢?
- 没有好的思路明确上述依赖
- 优点:
- 最终方案选择: i18n自定义方案
-
资源管理方案(R.swift)
- 优点:
- 解决图片名称,国际化key,字体名称等字符串硬编码问题,转成强类型方便排错,方便写代码联想
- 缺点:
- 为了保持统一性,都在R结构体文件中生成对应字符串映射的值,导致R文件过大,打开反应迟钝
- 优点:
-
模块解耦方案
- 明确需求: 后续进行组件化
-
路由方案(URLNavigator):
-
优点:
- 极高的动态性,适合h5跳转
- 方便地统一管理多平台的路由规则
- 易于适配 URL Scheme
- 因为路由表存在,结构清晰
-
缺点:
- 传参方式有限,无法利用编译器进行参数类型检查,所有的参数都只能从字符串中转换而来
- 依赖于字符串硬编码,难以管理
- url 的"注册"、"使用"必须用相同的字符规则
-
-
Target-Action方案(CTMediator): 利用分类为路由工具添加新接口,在接口中通过字符串获取对应的类,再用runtime创建实例,动态调用实例的方法
- 优点:
- 利用分类可以明确声明接口,进行编译检查
- 实现方式轻量
- 缺点:
- 需要在 mediator 和 target 中重新添加每一个接口,模块化时代码较为繁琐
- 在分类中仍然引入了字符串硬编码,内部使用字典传参,一定程度上也存在和 URL 路由相同的问题
- 无法保证所使用的模块一定存在,target 模块在修改后,使用者只有在运行时才能发现错误
- 过于依赖 runtime 特性, 在 Swift 中扩展 mediator 时,无法使用纯 Swift 类型的参数
- 创建过多的 target 类
- 优点:
-
protocol 匹配方案(BeeHive): 将 protocol 和对应的类进行字典匹配,之后就可以用 protocol 获取 class,再动态创建实例
-
优点:
- 利用接口调用,实现了参数传递时的类型安全
- 直接使用模块的 protocol 接口,无需再重复封装
-
缺点:
- 由框架来创建所有对象,创建方式有限,例如不支持外部传入参数,再调用自定义初始化方法
- 用 OC runtime 创建对象,不支持 Swift
- 只做了 protocol 和 class 的匹配,不支持更复杂的创建方式和依赖注入
- 无法保证所使用的 protocol 一定存在对应的模块,也无法直接判断某个 protocol 是否能用于获取模块
-
- 最终方案选择: 路由(URLNavigator)方案
-
空页面占位视图方案
- 明确需求: 空数据,页面加载失败默认展示占位视图
- DNZEmptyDataSet:
- 优点:
- tableView,collectionView自动显示隐藏
- 缺点:
- 不支持普通view
- 不能覆盖父视图,如果父视图有其他控件,需要单独隐藏
- 网络加载失败时不能显示
- 优点:
- 自定义YLEmptyDataSet:
- 优点:
- 支持普通view, 覆盖父视图, 不需要对父视图的其他控件进行单独隐藏
- tableView,collectionView自动显示隐藏 - 提供网络加载失败默认视图
- 缺点:
- 加到普通view上必须手动隐藏
- 优点:
- 最终方案选择: 自定义YLEmptyDataSet
-
状态管理方案
- ReactorKit
-
网络层方案
- Moya/RxSwift + HandJson
-
App缓存方案
- 基于缓存库再次封装Cache
-
Loading+Toast方案
- 自定义HUD
-
代码格式化
SwiftLint (程序静态分析)方案
安装:可以使用homebrew进行全局安装:
需要在已经安装了homebrew 前提下:
打开终端输入: brew install SwiftLint