FlowFramework 1.10.0

FlowFramework 1.10.0

KJMåns BernhardtMåns BernhardtNataliya PatsovskaiZettle iOS OSS维护。



  • PayPal Inc.

Build Status Platforms Carthage Compatible Swift Package Manager Compatible

现代应用程序通常包含复杂的数据流和生命周期。Flow是一个Swift库,旨在通过解决三个主要问题来简化这些数据流的构建

Flow被精心设计以

  • 易于使用:API经过精心设计,易于阅读和易于使用。
  • 实用主义:演化和设计用于解决实际问题。
  • 可组合:类型以良好的方式组合,使得构建复杂的流程变得容易。
  • 高性能:Flow已被高度优化以获得高性能。
  • 并发:Flow是线程安全的,并使用一个易于理解的计划器模型。
  • 可扩展:Flow被设计为可扩展。
  • 强类型:Flow利用Swift强类型以更好地表达意图。
  • 正确性:由数百个单元测试支持,并在现场测试了多年。

示例用法

在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
    }
  }
}

要求

  • Xcode 9.3+
  • Swift 4.1
  • 平台
    • iOS 9.0+
    • macOS 10.11+
    • tvOS 9.0+
    • watchOS 2.0+
    • Linux

安装

Carthage

github "iZettle/Flow" >= 1.0

Cocoa Pods

platform :ios, '9.0'
use_frameworks!

target 'Your App Target' do
  pod 'FlowFramework', '~> 1.0'
end

Swift Package Manager

import PackageDescription

let package = Package(
  name: "Your Package Name",
  dependencies: [
      .Package(url: "https://github.com/iZettle/Flow.git",
               majorVersion: 1)
  ]
)

简介

Flow的各个主要区域的简介可以在以下位置找到:

要想了解更多的可用功能,建议您探索那些有详细文档的源文件。代码补全也应该能帮助您发现信号和未来上许多可用的转换。

了解更多

要了解Flow的API设计背后的信息,我们建议阅读以下文章。它们更深入地探讨了为什么Flow的类型和API看起来和表现是这样的,以及它们是如何实现的,并提供了一些见解。

以及学习如何使用Flow构建其他框架

基于Flow的框架

如果你的目标是iOS,我们强烈推荐你检查这些基于Flow构建的框架

  • 展示 - 从模型到结果的正式化展示
  • 表单 - 布局、样式和事件处理

实地测试

Flow在过去的几年中得到了开发、演变和实地测试,并广泛应用于iZettle备受赞誉的点销售应用程序中。

合作

你可以在我们的Slack工作空间与我们合作。提问、分享想法或也许只是参与正在进行中的讨论。要获取邀请,请给我们写信 [email protected]