Swift MVVM+Coordinator 实现

Swift MVVM+Coordinator 实现

准备

  • 环境:macOS Big Sur, Xcode 13.0, Swift 5.0, iOS 13+, bundle, cocoapods
  • 技术点: Snapkit, UITableViewDiffableDataSource, Combine

关于Coordinator

Coordinator(协调器)是路由的一种实现方式,类似于树状结构,能够清晰的表明页面的层级结构,同时解除页面间的耦合关系。核心机制就像失物招领,子节点发出需求,对应节点响应需求。

架构

结构图

MVVMC.png

职责说明

  • App Coordinator
    所有Coordinator的根节点,控制一级页面的展示逻辑,同时监听跨组件跳转需求并分发给对应的Coordinator。
  • Tab Coordinator
    一级页面根节点,控制本分支下的二级页面展示逻辑。
  • Child Coordinator
    子节点,控制本节点的页面展示逻辑,可继续延伸。
  • ViewModel
    业务逻辑层,负责接收View的请求,调用Service或coordinator实现,并将结果通过KVO或其他方式传递给Views
  • Views
    页面渲染层,负责展示UI,实现交互,提供操作输入渠道。
  • Services
    服务层,负责调用后台API或本地数据库

实现

Demo 地址

创建基础Coordinator Protocol

protocol Coordinator: AnyObject {
    var childCoordinators: [Coordinator] { set get }
    var notificationAction: RouterAction? { set get }
    var navigationController: UINavigationController { set get }
    
    @discardableResult func startChildCoordinator(_ context: Router) -> Bool
    func start(_ context: Router) -> Bool
}

  • childCoordinators: 子节点列表
  • notificationAction: 跨组件跳转、通用跳转方法
  • navigationController: 导航根controller
  • startChildCoordinator: 根据Router进行子节点跳转并返回是否消费此Router
  • start: 展示自身,并返回是否消费此Router

添加默认实现方法


extension Coordinator {
    func addChildCoordinator(_ childCoordinator: Coordinator) {
        guard !childCoordinators.contains(where: { $0 === childCoordinator }) else { return }
        childCoordinator.navigationController = self.navigationController
        childCoordinator.notificationAction = {[weak self] context in
            guard let self = self else { return }
            if !self.start(context) {
                self.notificationAction?(context)
            }
        }
        childCoordinators.append(childCoordinator)
    }
    
    func removeChildCoordinator(_ childCoordinator: Coordinator) {
        guard let index = childCoordinators.firstIndex(where: { $0 === childCoordinator }) else { return }
        childCoordinators.remove(at: index)
    }
    
    @discardableResult func startChildCoordinator(_ context: Router) -> Bool {
        for childCoordinator in childCoordinators {
            if childCoordinator.start(context) {
                return true
            }
        }
        return false
    }
}
  • addChildCoordinator: 默认设置子节点的navigationController为自身的,设置回调当子节点有无法处理的需求需要上抛时接收,如果自身无法消费则继续往上抛

创建TabCoordinator

protocol TabCoordinator: Coordinator {
    var tabBarTitle: String { get }
    
    var tabBarImage: UIImage? { get }
    
    var tabBarSelectedImage: UIImage? { get }
    
    func start()
}

使用


class HomeCoordinator: TabCoordinator {
    var notificationAction: RouterAction?
    
    var tabBarTitle: String { "首页" }
    
    var tabBarImage: UIImage? { nil }
    
    var tabBarSelectedImage: UIImage? { nil }
    
    var navigationController: UINavigationController = .init()
    
    var childCoordinators: [Coordinator] = []
        
    func start() {
        navigationController.setViewControllers([HomeViewController(coordinator: self)], animated: false)
        initChildCoordinators()
    }
    
    func initChildCoordinators() {
        addChildCoordinator(ListCoordinator())
    }
    
    func start(_ context: Router) -> Bool {
        guard let router = context as? HomeRouter else { return false }
        switch router {
        case .home:
            start()
            return true
        default:
            return startChildCoordinator(router)
        }
    }
}

关于Demo中的ListViewController

Demo中实现了Combine与UITableViewDiffableDataSource的基本用法,感兴趣的小伙伴可以看看。

Demo 地址

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容