现代应用程序通常包含复杂的异步流程和生命周期。 Flow 是一个 Swift 库,旨在通过解决以下三个主要问题来简化这些流程的构建:
Flow 的设计非常周到,力求做到:
在 Flow 中,Disposable
协议用于生命周期管理
extension UIView {
func showSpinnerOverlay() -> Disposable {
let spinner = ...
addSubview(spinner)
return Disposer {
spinner.removeFromSuperview()
}
}
}
let disposable = view.showSpinnerOverlay()
disposable.dispose() // Remove spinner
Disposable
资源可以收集在一个公共的 DisposeBag
中
let bag = DisposeBag() // Collects resources to be disposed together
bag += showSpinnerOverlay()
bag += showLoadingText()
bag.dispose() // Will dispose all held resources
Signal<T>
类型用于事件处理。信号由标准 UI 组件提供
let bag = DisposeBag()
// UIButton provides a Signal<()>
let loginButton = UIButton(...)
bag += loginButton.onValue {
// Log in user when tapped
}
// UITextField provides a ReadSignal<String>
let emailField = UITextField(...)
let passwordField = UITextField(...)
// Combine and transform signals
let enableLogin: ReadSignal<Bool> = combineLatest(emailField, passwordField)
.map { email, password in
email.isValidEmail && password.isValidPassword
}
// Use bindings and key-paths to update your UI on changes
bag += enableLogin.bindTo(loginButton, \.isEnabled)
最后,Future<T>
类型处理异步操作
func login(email: String, password: String) -> Future<User> {
let request = URLRequest(...)
return URLSession.shared.data(for: request).map { data in
User(data: data)
}
}
login(...).onValue { user in
// Handle successful login
}.onError { error in
// Handle failed login
}
这三种类型带有许多扩展,允许我们组合复杂的 UI 流程
class LoginController: UIViewController {
let emailField: UITextField
let passwordField: UITextField
let loginButton: UIButton
let cancelButton: UIBarButtonItem
var enableLogin: ReadSignal<Bool> { /* Introduced above */ }
func login(email: String, password: String) -> Future<User> { /* Introduced above */ }
func showSpinnerOverlay() -> Disposable { /* Introduced above */ }
// Returns future that completes with true if user chose to retry
func showRetryAlert(for error: Error) -> Future<Bool> { ... }
// Will setup UI observers and return a future completing after a successful login
func runLogin() -> Future<User> {
return Future { completion in // Complete the future by calling this with your value
let bag = DisposeBag() // Collect resources to keep alive while executing
// Make sure to signal at once to set up initial enabled state
bag += enableLogin.atOnce().bindTo(loginButton, \.isEnabled)
// If button is tapped, initiate potentially long running login request using input
bag += combineLatest(emailField, passwordField)
.drivenBy(loginButton)
.onValue { email, password in
login(email: email, password: password)
.performWhile {
// Show spinner during login request
showSpinnerOverlay()
}.onErrorRepeat { error in
// If login fails with an error show an alert...
// ...and retry the login request if the user chooses to
showRetryAlert(for: error)
}.onValue { user in
// If login is successful, complete runLogin() with the user
completion(.success(user))
}
}
// If cancel is tapped, complete runLogin() with an error
bag += cancelButton.onValue {
completion(.failure(LoginError.dismissed))
}
return bag // Return a disposable to dispose once the future completes
}
}
}
9.3+
9.0+
10.11+
9.0+
2.0+
github "iZettle/Flow" >= 1.0
platform :ios, '9.0'
use_frameworks!
target 'Your App Target' do
pod 'FlowFramework', '~> 1.0'
end
import PackageDescription
let package = Package(
name: "Your Package Name",
dependencies: [
.Package(url: "https://github.com/iZettle/Flow.git",
majorVersion: 1)
]
)
可以在以下位置找到 Flow 主要领域的介绍:
为了了解更多关于可用功能的信息,我们鼓励您浏览经过广泛文档记录的源文件。代码完成也应该帮助您发现信号和 future 上可用的许多转换。
要了解更多关于 Flow API 背后的设计,我们建议阅读以下文章。 它们更深入地探讨了 Flow 的类型和 API 的外观和行为方式,并让您深入了解它们的实现方式
要了解如何使用 Flow 构建其他框架
如果您的目标是 iOS,我们强烈建议您还查看这些基于 Flow 构建的框架
Flow 经过数年的开发、演变和实地测试,并在 iZettle 广受好评的销售点应用程序中得到广泛应用。
您可以在我们的 Slack 工作区与我们协作。 提出问题,分享想法,或者只是参与正在进行的讨论。 要获取邀请,请写信至 iz-apps-platform-ios@paypal.com