SwiftCoordinator 0.2.0

SwiftCoordinator 0.2.0

Dave Neff维护。



  • Dave Neff

Coordinator

Swift 5编写的iOS导航协调器。

关于

在iOS社区中,有很多使用协调器来从UIViewController中减轻导航负担的实现。然而,由于协调器模式非常广泛,因此对于如何实现它的诠释有很多不同。

这是我对于协调器模式的个人看法。

目的

在我看来,协调器主要起到三个作用

  1. 处理至少一个,但通常是多个视图控制器之间的准备、导航和展示。
  2. 协调不同服务,如网络服务,以将业务逻辑从我们的视图控制器中提取出来。
  3. 可选地管理子协调器,以分配复杂导航路径的责任。

如何使用

Coordinator

协调者位于您的视图控制器之上,并通过代理来指导交通流量。

例如,假设您有一个用于浏览欲购买商品的视图控制器,想要查找商品详情,通常会像这样

final class BrowseProductsViewController: UIViewController {
    
    func onProductTapped(product: Product) {
        let productDetailViewController = ProductDetailViewController(product: product)          
        navigationController?.pushViewController(productDetailViewController, animated: true)    
    }
    
}

使用一个 Coordinator,它会这样拆分

// The delegate we'll use to talk to the Coordinator

protocol BrowseProductsViewControllerDelegate: class {
    func browseProductsViewController(_ controller: BrowseProductsViewController, didTapProduct product: Product)
}

// Our revised view controller, now using the delegate

final class BrowseProductsViewController: UIViewController {
    
    weak var delegate: BrowseProductsViewControllerDelegate?
    
    func onProductTapped(product: Product) {
        delegate?.browseProductsViewController(_ controller: self, didTapProduct product: product)
    }
    
}

// The view controller's owning coordinator

final class BrowseProductsCoordinator: NavigationCoordinator { 

    // Any child coordinators this Coordinator is holding on to
    var childCoordinators: [Coordinator] = []
    
    // Our custom UINavigationController wrapper
    var navigator: NavigatorType
    
    // The view controller this Coordinator is managing
    var rootViewController: BrowseProductsViewController
    
    init(navigator: NavigatorType) {
        self.navigator = navigator
        self.rootViewController = BrowseProductsViewController()
    }
    
    func start() {
        rootViewController.delegate = self 
    }

}

// With delegate conformance 

extension BrowseProductsCoordinator: BrowseProductsViewControllerDelegate {

    func browseProductsViewController(_ controller: BrowseProductsViewController, didTapProduct product: Product) {
        let productDetailViewController = ProductDetailViewController(product: product)  
        
        // In a more complex situation, here's where the Coordinator could reference services,
        // like fetching information from the network or the local data store,
        // to prepare the view controller for presentation.
        
        navigator.push(productDetailViewController, animated: true)
    }

}

在用 Coordinator 重构的代码中,我们移除了视图控制器关于其位置和目的地的知识。这使得我们的视图控制器仅仅是一个视图,使其极其可重复使用,并从路由和业务逻辑中拉出由其他东西处理。

导航器

Navigator 是一个 UINavigationController 的包装器/代理,为协调器提供在导航堆栈上推送和弹出视图控制器的方法。

使用 Navigator 而不是普通的 UINavigationController 的原因是它符合 UINavigationControllerDelegate,当控制器从堆栈中弹出时(例如,当用户在屏幕上滑动返回时)会提供一个回调。这些回调允许协调器在需要时删除任何子协调器,并从内存中释放它们。

协调器的使用方式

单个协调器可以用于将流量路由到扁平化层次结构中的多个视图控制器: 具有扁平化层次结构的协调器示例

但它也可以有一个或多个子协调器,它们负责特定的导航部分: 具有子协调器的协调器示例

这是一个非常灵活的模式,所以请按您认为最好的方式使用它!

示例项目

在这个仓库中,您将看到一些如何在实际应用程序中使用协调器模式的示例项目。

在网上找到的许多示例中,该模式通常与 viewModel 和/或使用反应式绑定("MVVM-C")结合使用。虽然这些方法可以帮助解决某些问题,但本身就可以使用而且在某些情况下可能更容易理解协调器模式而不需要这些额外的抽象。

因此,我的示例项目专注于协调器(Coordinators)和视图控制器(ViewControllers)之间的关系,有意识地避免将多个模式混合在一起,以避免混淆。

要查看示例应用,只需在Xcode中打开Example/Coordinator-Example.xcodeproj项目并运行。

致谢

感谢您阅读我对协调器的看法——如果您正在探索要在项目中使用该模式,希望这能帮到您。如果您有任何建议或改进,请随时提交PR!

我从社区中的许多示例中汲取了灵感,并想感谢以下人士提供的灵感和想法

Soroush Khanlou: 展示协调器
Ian MacCallum: 协调器、路由器和后退按钮
Paul Hudson: 如何在iOS应用中使用协调器模式