Lasso是一个iOS应用程序架构,用于构建离散、可组合、可测试的组件,从单个屏幕到复杂的流程,再到高级应用结构。
为什么选择Lasso?
如果没有一套结构化的原则,应用程序的代码库很容易变得难以推理和维护。特别是,这些问题最终会出现
- 组件之间的紧密耦合使得更改/测试变得困难
- 业务逻辑分散在奇怪的地方,使得修改/重用/调试/测试现有代码变得困难
- 在不当的地方做出的视图表示选择,使得重构/重新组织/测试流程变得困难
- 团队间组织不一致,使得交叉协作困难
Lasso方法
Lasso鼓励强大的关注点分离,通过清楚地定义拥有单独责任的单个组件,指出特定类型的代码应该存放的位置,并提供清晰的、灵活的组件间通信方式。较大的行为单元可以轻松地组合和重新组合。
屏幕
我们通常将屏幕视为应用程序中单个页面/视图,例如登录视图、联系人列表视图、音频设置视图等。
在Lasso中,Screen
是用来实现单个视图的类型集合
View
(即UIViewController
)负责:
- 精确渲染屏幕当前的
State
(即内容) - 将用户交互传递给
Store
(即决策者) - 响应用户状态的变化以保持界面是最新的
Lasso视图通常很小,其中基本没有逻辑。
使用单向数据流来确保一致性:视图绝不会直接响应用户操作而重新渲染任何内容。视图将Actions
发送到Store
,更新State
,然后将状态变更通知作为State
发送回视图。
Store
是屏幕决策的地方,是屏幕State
的真相来源。一个Store
负责:
- 屏幕的所有业务逻辑
- 响应
Actions
(即从视图发送的事件) - 更新屏幕的
State
当发生更合适在其他地方处理的事件时,Store
也可以生成一个Output
。例如,登录屏幕可能会生成一个“用户已登录”的输出,作为信号给高级系统转移到应用程序的登录部分;或者,联系列表屏幕可能会生成一个“选中联系人”的输出,作为信号给高级流程以显示或推送一个联系人详情界面。
流程
Flow
代表应用程序的功能或区域,通常由一系列Screen
组成。例如,“新用户”流程可能由一系列一次性信息屏幕和一个单独的“让我们开始”屏幕组成。
Flow
在一个视图层的合适上下文中实例化和开始(例如,“注册”流程可能在一个菜单屏幕上展示,或者“欢迎”流程可能被推送到导航堆栈)。Flow
首先创建其初始Screen
,并监听Output
信号。随着Output
的到来,《code>Flow决定如何处理它们——它可以创建另一个Screen
并将其放入视图层级,发出自己的Output
(当发生更合适在其他地方处理的操作时),或者对Flow
适当的任何事物。
由于Screen
和Flow
是封装的模块,具有离散的输入和输出点,因此在Flow
中管理和处理Screen
以及Flow
相当常见且简单。这样,可以将应用程序定义为组件的层次结构,从顶层开始减少复杂性。
示例应用
运行示例项目
- 克隆 Lasso 仓库
- 从
Example
目录运行pod install
- 打开
Lasso.xcworkspace
了解更多
- Lasso:介绍为 iOS 优化的新架构框架 - 文章介绍了 Lasso,并提供了创建
屏幕
的具体示例 - Lasso:Flows 简介 - 文章展示了如何使用
Flow
排练多个屏幕
- Lasso 编程风格指南 - 编写 Swifty Lasso 的技巧
- 关于类型声明的注释 - 关于声明 Lasso 类型更详细的信息和最佳实践
- 内存管理 - 关于 Lasso 中的内存管理信息以及避免强引用循环的技巧
需求
Lasso 支持 iOS 13 及以上版本,可使用 Swift 4.2 及以上版本编译。
注意:Lasso v.1.3.0 已添加对 SwiftUI 的支持,最低部署目标为 iOS 13.0。如果您需要支持更早的 iOS 版本,请使用 v1.2.1。
安装
Cocoapods
Lasso核心框架添加到您的Podfile的主要目标中
Pod 'Lasso'
还要将LassoTestUtilities
添加到您的测试目标(s)
Pod 'LassoTestUtilities'
Swift包管理器
Lasso包的网址是
`https://github.com/ww-tech/lasso.git`
有关示例用法,请参阅:Swift包管理器示例。
Tuist
要将Lasso
添加为tuist的TargetDependency
- 将此仓库克隆到喜欢的位置
- 创建一个
.project
依赖项 - 将依赖项添加到您的目标依赖项中
let lasso: TargetDependency = .project(target: "Lasso", path: "../../path-to-lasso-repo-clone/")
let demo = Target(
name: "Demo",
platform: .iOS,
product: .framework,
bundleId: "com.example.Demo",
dependencies: [ lasso ]
)
还要将LassoTestUtilities
添加到您的测试目标(s)
let lassoTestUtilities: TargetDependency = .project(target: "LassoTestUtilities", path: "../../path-to-lasso-repo-clone/")
let demoTests = Target(
name: "DemoTests",
platform: .iOS,
product: .unitTests,
bundleId: "com.example.DemoTests",
dependencies: [ LassoTestUtilities ]
)
贡献
我们热衷于贡献!
如果您有一个想法的功能,或者发现了一个错误,最好的做法是
- 搜索问题,看看是否有人已经提出过!
- 创建一个新的问题,详细说明您想看到哪些改进。
- 如果您有代码更改的想法,那真是太好了!
- 将Lasso仓库Fork。
- 为您的功能更改创建一个分支。
- 打开一个PR!
作者
Steven Grosmark、Trevor Beasty、Yuichi Kuroda和WW iOS团队。
许可
Lasso是以Apache-2.0开源许可进行许可的。
您可以按照自己的意愿使用它。我们确实欢迎致谢,如果您在使用它的话,我们也很愿意听到您的意见!