Lasso is an iOS application architecture for building discrete, composable and testable components both big and small - from single one-off screens, through complex flows, to high-level application structures.
Without a set of structural principles, it's very easy for an application's code base to become hard to both reason about, and maintain. In particular, these problems will eventually arise:
Lasso encourages a strong separation of concerns by clearly defining discrete, single-responsibility components where specific types of code should live, and a clear, flexible way for these components to communicate. Larger units of behavior are easily composed, and re-composed.
We generally think of a screen as a single page/view in an app - e.g., a login view, a contacts list view, an audio settings view, etc.
In Lasso, a Screen
is the collection of types used to implement a single view:
The View
(i.e., a UIViewController
) is responsible for:
State
(i.e., the content) of the screenStore
(i.e., the decision maker)Lasso views tend to be small, with practically zero logic in them.
A unidirectional data flow is used to ensure consistency: a View
never re-renders anything in direct response to a user action. Views
send Actions
to the Store
, which updates the State
, which come back to the View
as State
change notifications.
The Store
is where a screen's decisions are made, and is the source of truth for the screen's State
. A Store
is responsible for:
Actions
(i.e. events sent from the View
)State
A Store
can also generate an Output
when an event occurs that is more appropriately handled elsewhere. E.g., a login screen might generate a "user did login" output as a signal to a higher-level system to move to a logged-in portion of an app; or a contact list screen might generate a "did select contact" output as a signal to a higher-level flow to either present or push a contact detail screen.
A Flow
represents a feature - or area - of an app, and is commonly composed of a collection of Screens
. For example, a "new user" flow might be composed of a series of one-time informational screens followed by a single "let's get started" screen.
A Flow
is instantiated and started within an appropriate context of a view hierarchy (e.g., a "sign up" flow might be presented on a menu screen, or a "welcome" flow might be pushed onto a navigation stack). The Flow
starts by creating its initial Screen
, and listens for Output
signals. As Outputs
arrive, the Flow
decides what to do with them - it can create and place another Screen
into the view hierarchy, emit its own Output
(when an event occurs that is more appropriately handled elsewhere), or whatever is appropriate for the Flow
.
Since Screens
and Flows
are encapsulated modules with discrete entry and exit points, it's quite easy and common for a Flow
to manage both Screens
and Flows
. In this way, it becomes possible to define your application as a hierarchy of components, reducing complexity from the top level down.
To run the example project
pod install
from the Example
directoryLasso.xcworkspace
Screen
Screens
with a Flow
Lasso supports iOS 13 and up, and can be compiled with Swift 4.2 and up.
Note: Lasso v.1.3.0 has added support for SwiftUI, and has a minimum deployment target of iOS 13.0. If you need support for earlier versions of iOS, please use v1.2.1.
The core Lasso framework is added to the primary target in your Podfile:
Pod 'Lasso'
Also add LassoTestUtilities
to your test target(s):
Pod 'LassoTestUtilities'
The Lasso package URL is:
`https://github.com/ww-tech/lasso.git`
For sample usage, see: Swift Package Manager Sample.
To add Lasso
as a tuist TargetDependency
:
.project
dependencylet 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 ]
)
Also add LassoTestUtilities
to your test target(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 ]
)
We love contributions!
If you have a feature in mind, and/or have found a bug, the best thing to do is:
Steven Grosmark, Trevor Beasty, Yuichi Kuroda, and the WW iOS Team.
Lasso is licensed under the Apache-2.0 Open Source license.
You are free to do with it as you please. We do welcome attribution, and would love to hear from you if you are using it in a project!
link |
Stars: 86 |
Last commit: 5 days ago |
Add better support for AnyViewStore
s that have NoAction
- https://github.com/ww-tech/lasso/pull/67
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics