Weavy 1.1.0

Weavy 1.1.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2017年12月
SwiftSwift 版本4.0.2
SPM支持 SPM

Thibault Wittemberg 维护。



 
依赖项
RxSwift>= 4.0.0
RxCocoa>= 4.0.0
 


Weavy 1.1.0

Weavy Logo
Travis CI Build Status
框架 Carthage Compatible CocoaPods Compatible
平台 Platform
许可证 License

关于

Weavy 是一个基于交织模式的 iOS 应用导航框架

这份 README 是我通往这个框架整个构思过程的一个简要概述。

查看这个维基页面了解 Weavy 的更多信息:[Weavy 详细介绍](https://github.com/twittemb/Weavy/wiki/Weavy-in-details)

导航问题

关于 iOS 应用内的导航,有两个选择可行

  • 使用 Apple 和 Xcode 提供的内置机制:故事板和转换
  • 直接在代码中实现自定义机制

这两种解决方案的缺点

  • 内置机制:导航相对静态,故事板庞大。导航代码污染了 UIViewControllers
  • 自定义机制:代码设置可能困难,并且可能根据所选的设计模式(路由器、协调者)而变得复杂

Weavy 努力实现

  • 提倡将故事板切割成原子单位以促进协作和 UIViewControllers 的可重用性
  • 根据导航上下文以不同方式呈现 UIViewController
  • 简化依赖注入的实现
  • 从 UIViewControllers 中移除任何导航机制
  • 提倡响应式编程
  • 以声明性方式表达导航,同时解决大多数导航情况
  • 促进将应用程序切割成导航的逻辑块

安装

Carthage

在您的 Cartfile 中

github "twittemb/Weavy"

CocoaPods

在您的 Podfile 中

pod 'Weavy'

核心原则

在现实世界中:交织涉及使用一个 织机 来交错两组以相互垂直的线:纵向的 经线 和与之相交的 纬线

这就是我设想简单应用中的交织模式的方式

如何理解这个?

  • 这里有三个经线:应用入门设置,它们描述了应用的主要导航部分。
  • 我们也有一组纬线:仪表板(在应用启动时触发的默认纬线)、设置服务器登录

这些纬线中的每一个都将因用户操作或后端状态变化而触发。
代表一个经线和一条纬线交叉的彩色芯片将是一个特定的导航操作(例如 UIViewController 出现)。

如我们所见,某些纬线被用于多个经线,它们的触发将导致显示相同的屏幕,但具有不同的展示选项。有时这些屏幕会弹出,有时它们将推入导航堆栈。
这个草图说明了我们可以如何分解UIViewControllers和Storyboards。

经线、纬线和针脚

经线和纬线的组合描述了应用程序内部的所有可能的导航模式。
每个经线定义了一个清晰的导航区域(这使得您的应用程序分为界定良好的部分),其中每个纬线代表一个特定的导航动作(在堆栈上推入VC、弹出VC等)。

最后,针织功能必须返回一个针脚数组。针脚有助于织布机了解在下一导航步骤中将要处理什么。

为什么是针脚数组?例如,UITabbarController是一种模式,其中同时进行多个导航,我们需要告诉织布机那是在发生的。

针脚告诉织布机:您接下来要处理的是这个特定的可展示和这个特定的可织。在某些情况下,针织功能可以返回空数组,因为我们知道在当前导航之后不会有任何进一步的导航。

演示应用程序展示了几乎所有可能的情况。

可织

导航的基本原理非常简单:它由对应用状态变化的连续视图转换组成。这些变化通常是由于用户交互引起的,但也可以来自您应用程序的低级层。我们可以想象,网络会话丢失可能导致登录屏幕出现。

我们需要一种方式来表达这些变化。正如我们所见,一个经线纬线的组合代表一个导航动作。正如经线可以定义应用程序区域一样,纬线可以定义这些区域内的导航状态变化。

考虑到这一点,一个可织基本上是应用程序中能够表达一个新的纬线的东西,从而实现一个导航状态变化,导致导航动作。

纬线甚至可以嵌入内部值(如Ids、URLs等),这些值将在编织过程中传递到屏幕上。

织布机

织布机只是开发者的一种工具。一旦他已经定义了代表导航可能性的合适的经线和纬线的组合,织布机的工作就是要根据由纬线触发的导航状态变化编织这些组合。

由开发人员负责

  • 定义代表其应用程序部分(如仪表板、引导、设置等)的经线,在这些部分中需要进行显著的导航操作
  • 提供将触发织布机编织过程的可织

如何使用 Weavy

代码示例

如何声明一个经线

下面的经线用作导航堆栈。

class WatchedWarp: Warp {

    var head: UIViewController {
        return self.rootViewController
    }

    let rootViewController = UINavigationController()

    func knit(withWeft weft: Weft) -> [Stitch] {

        guard let weft = weft as? AppWeft else { return Stitch.emptyStitches }

        switch weft {

        case .movieList:
            return navigateToMovieListScreen()
        case .moviePicked(let movieId):
            return navigateToMovieDetailScreen(with: movieId)
        case .castPicked(let castId):
            return navigateToCastDetailScreen(with: castId)
        default:
            return Stitch.emptyStitches
        }

    }

    private func navigateToMovieListScreen () -> [Stitch] {
        let viewController = WatchedViewController.instantiate()
        viewController.title = "Watched"
        self.rootViewController.pushViewController(viewController, animated: true)
        return [Stitch(nextPresentable: viewController, nextWeftable: viewController)]
    }

    private func navigateToMovieDetailScreen (with movieId: Int) -> [Stitch] {
        let viewController = MovieDetailViewController.instantiate()
        self.rootViewController.pushViewController(viewController, animated: true)
        return [Stitch(nextPresentable: viewController, nextWeftable: viewController)]
    }

    private func navigateToCastDetailScreen (with castId: Int) -> [Stitch] {
        let viewController = CastDetailViewController.instantiate()
        self.rootViewController.pushViewController(viewController, animated: true)
        return [Stitch(nextPresentable: viewController, nextWeftable: viewController)]
    }

}

如何声明纬线

由于纬线被视为横跨整个应用程序的某些状态,因此使用枚举声明它们似乎很合理。

enum AppWeft: Weft {
    case apiKey
    case movieList
    case moviePicked (withId: Int)
    case castPicked (withId: Int)
    case settings
}

如何声明一个可织

从理论上讲,可织作为一个协议,可以是任何东西(一个UIViewController实例),但建议将这种行为孤立在ViewModel等中。
对于简单情况(例如,我们只需要使用第一个纬线引导经线,而不想为它编写基本可织代码的情况),Weavy提供了一个SingleWeftable类。

class WishlistViewModel: Weftable {

    init() {
        self.weftSubject.onNext(AppWeft.movieList)
    }

    @objc func settings () {
        self.weftSubject.onNext(AppWeft.settings)
    }
}

如何启动编织过程

编织过程将在AppDelegate中启动。

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var loom = Loom()
    let mainWarp = MainWarp()

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        guard let window = self.window else { return false }

        Warps.whenReady(warp: mainWarp, block: { [unowned window] (head) in
            window.rootViewController = head
        })

        loom.weave(fromWarp: mainWarp, andWeftable: SingleWeftable(withInitialWeft: DemoWeft.apiKey))

        return true
    }
}

作为奖励,织布机提供了一个Rx扩展,允许您追踪编织步骤(Loom.rx.willKnit和Loom.rx.didKnit)。

演示应用程序

提供一个演示应用程序来展示核心机制。几乎涵盖了所有类型的导航。应用程序包括

  • 一个MainWarp,表示主导航流程(一个设置屏幕和一个由标签栏控制器组成的由两个屏幕组成的仪表板)
  • 一个 WishlistWarp,表示你想观看的电影导航堆栈
  • 一个 WatchedWarp,表示你已经看过的电影导航堆栈
  • 一个 SettingsWarp,表示用户的偏好设置,采用主/详细展示方式

Demo Application

工具和依赖项

Weavy依赖于

  • SwiftLint进行静态代码分析(《Github SwiftLint》)
  • RxSwift将Wefts公开为Loom可以响应的可观察项(《Github RxSwift》)
  • 在演示应用程序中使用 Github Reusable 提高 UIStoryboardSegue切割成原子ViewController的效率