iOS 项目技术选型

  • 暗黑模式方案

    • 明确需求: 不会增加除系统暗黑模式外的其他模式
    1. 系统暗黑模式方案:
      • 优点:
        • 系统Assets资源支持动态色,动态图,原生支持,代码侵入性最小
        • 默认支持系统更换模式时已经创建的页面实时刷新效果,不需要销毁根控制器重新创建
        • Assets创建动态色,动态图完美支持xib,不需要额外适配
      • 缺点:
        • 仅支持iOS 13+系统
        • 仅支持日间暗黑两种模式,无法新增其他主题
        • layer层级需要自己控制刷新粒度
    2. SwiftTheme方案:
      • 优点:
        • 可以服务器动态下发个性化主题,方便增加除暗黑模式外的其他主题
        • 不受系统版本限制
      • 缺点:
        • 系统控件的color,image等相关属性全都不能用,必须全部替换成theme_color,theme_image等扩展属性,才能实现更换主题时已经创建的页面实时刷新效果,否则必须销毁根控制器重新创建
        • xib适配方案有两种:
          1. 通过扩展给xib扩展新属性设置color,image等相关,设置扩展属性时通过选中主题设置对应颜色,不能直接设置控件的color,image等原生属性
          2. 将xib中的控件关联到代码中,通过代码设置theme_color,theme_image等扩展属性
        • 代码侵入性太强
    • 最终方案选择: 系统暗黑模式方案(需求明确不会新增除暗黑模式外的其他模式更优)
  • 多语言国际化方案

    • 明确需求: app内提供更换语言功能, 在app内更改语言后,已经创建的页面及时刷新,不能销毁根控制器重新创建。
    • 难点:
      • 跟app关联的语言国际化,只能通过代码控制,xib怎么国际化?
      • 不销毁根控制器重新创建,已经创建的页面怎么刷新?如果是每个页面加通知,那每个页面需要刷新的部分怎么抽取?对业务代码逻辑影响多大?是否有明确规范,新人接替是否好维护?
      • 跟语言关联的有静态页面控件刷新,业务逻辑刷新(如banner图上有文字,语言改变后banner图也需要刷新)
      • 怎么控制刷新粒度,如果栈里的静态控件太多,业务逻辑刷新太多,一次性全部刷新是否会影响性能?还是分批刷新?
    1. 切换根控制器方案: 切换语言直接重新创建根控制器
      • 优点:
        • 逻辑简单,代码0浸入
      • 缺点:
        • 必须退回根控制器重新加载,不能停留在当前页面实时刷新
    2. 通知方案: 每个页面监听语言改变来刷新需要刷新的控件和逻辑
      • 优点:
        • 已经创建的页面能实时刷新
      • 缺点:
        • 每个页面都需要监听通知
        • 语言改变时所有创建的页面控件和逻辑都同时刷新,会影响一定性能
        • 写业务代码需要关心哪些控件需要刷新,剥离出来刷新,不太友好
    3. i18n自定义方案: 向每个控件注入一段block,语言改变时调用,也可以在改变时做标记,在页面将要出现的时刷新
      • 优点:
        • 已经创建的页面能实时刷新
        • 写业务代码不需要关心哪些控件需要刷新
        • 静态控件统一规范,统一写法,业务逻辑刷新单独控制
      • 缺点:
        • 代码中设置text,title,attributedText等文本的地方都需要加前缀.i18n.text来设置,浸入性较大
    4. 触发生命周期函数: 语言改变时将当前栈里的控制器销毁并替换新的,让控制器重新走生命周期函数
      • 优点:
        • 已经创建的页面能实时刷新
        • 逻辑简单,代码0浸入
      • 缺点:
        • 需要明确当前栈结构,遍历移除旧的,创建新的替换,需要明确创建新控制器依赖的参数,绝对不能更改栈层级结构和控制器类型,
        • 如果每个控制器的子控制器太多,怎么处理?如果子控制器也重新创建,那么子控制器布局呢?
        • 没有好的思路明确上述依赖
    • 最终方案选择: i18n自定义方案
  • 资源管理方案(R.swift)

    • 优点:
      • 解决图片名称,国际化key,字体名称等字符串硬编码问题,转成强类型方便排错,方便写代码联想
    • 缺点:
      • 为了保持统一性,都在R结构体文件中生成对应字符串映射的值,导致R文件过大,打开反应迟钝
  • 模块解耦方案

    • 明确需求: 后续进行组件化
    1. 路由方案(URLNavigator):

      • 优点:

        • 极高的动态性,适合h5跳转
        • 方便地统一管理多平台的路由规则
        • 易于适配 URL Scheme
        • 因为路由表存在,结构清晰
      • 缺点:

        • 传参方式有限,无法利用编译器进行参数类型检查,所有的参数都只能从字符串中转换而来
        • 依赖于字符串硬编码,难以管理
        • url 的"注册"、"使用"必须用相同的字符规则
    2. Target-Action方案(CTMediator): 利用分类为路由工具添加新接口,在接口中通过字符串获取对应的类,再用runtime创建实例,动态调用实例的方法

      • 优点:
        • 利用分类可以明确声明接口,进行编译检查
        • 实现方式轻量
      • 缺点:
        • 需要在 mediator 和 target 中重新添加每一个接口,模块化时代码较为繁琐
        • 在分类中仍然引入了字符串硬编码,内部使用字典传参,一定程度上也存在和 URL 路由相同的问题
        • 无法保证所使用的模块一定存在,target 模块在修改后,使用者只有在运行时才能发现错误
        • 过于依赖 runtime 特性, 在 Swift 中扩展 mediator 时,无法使用纯 Swift 类型的参数
        • 创建过多的 target 类
    3. protocol 匹配方案(BeeHive): 将 protocol 和对应的类进行字典匹配,之后就可以用 protocol 获取 class,再动态创建实例

      • 优点:

        • 利用接口调用,实现了参数传递时的类型安全
        • 直接使用模块的 protocol 接口,无需再重复封装
      • 缺点:

        • 由框架来创建所有对象,创建方式有限,例如不支持外部传入参数,再调用自定义初始化方法
        • 用 OC runtime 创建对象,不支持 Swift
        • 只做了 protocol 和 class 的匹配,不支持更复杂的创建方式和依赖注入
        • 无法保证所使用的 protocol 一定存在对应的模块,也无法直接判断某个 protocol 是否能用于获取模块
    • 最终方案选择: 路由(URLNavigator)方案
  • 空页面占位视图方案

    • 明确需求: 空数据,页面加载失败默认展示占位视图
    1. DNZEmptyDataSet:
      • 优点:
        • tableView,collectionView自动显示隐藏
      • 缺点:
        • 不支持普通view
        • 不能覆盖父视图,如果父视图有其他控件,需要单独隐藏
        • 网络加载失败时不能显示
    2. 自定义YLEmptyDataSet:
      • 优点:
        • 支持普通view, 覆盖父视图, 不需要对父视图的其他控件进行单独隐藏
        • tableView,collectionView自动显示隐藏 - 提供网络加载失败默认视图
        • 缺点:
          - 加到普通view上必须手动隐藏
    • 最终方案选择: 自定义YLEmptyDataSet
  • 状态管理方案

    • ReactorKit
  • 网络层方案

    • Moya/RxSwift + HandJson
  • App缓存方案

    • 基于缓存库再次封装Cache
  • Loading+Toast方案

    • 自定义HUD
  • 代码格式化

SwiftLint (程序静态分析)方案
安装:可以使用homebrew进行全局安装:
需要在已经安装了homebrew 前提下:
打开终端输入: brew install SwiftLint

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容