MVVMCombine 1.0.0

MVVMCombine 1.0.0

Mohamed Salem 维护。



MVVMCombine Logo

MVVMCombine

制作更好的应用程序。更少的代码。使用 MVVMCombine 畅游 SwiftUI!

使用模型-视图-视图模型-协调器 (MVVM-C) 设计模式构建干净、像素完美且声明式的 UI。MVVMCombine 是一个专门为 Apple 的最新框架 Combine(与 SwiftUI 一起)开发的框架,它提供了逻辑流作为函数式响应式编程 (FRP) 的核心,具有易于阅读和自然编写的声明式 Swift 语法。

现在使用 MVVMCombine,视图模型负责以易于管理和管理的方式展示模型中的数据对象。在这方面,视图模型更多的是模型而不是视图,它处理了视图的大部分(如果不是全部)显示逻辑,并通过协调器处理导航行为。

目录

功能

  • 使用属性包装器进行依赖注入
  • 动态注册、解析或注入服务
  • 为每个视图动态注册和注入视图模型
  • 在其对应的视图模型中管理视图的生命周期
  • 通过视图模型协调器协调视图的导航
  • 根视图、链接、表单和标签项的协调器
  • 动态可调用的视图模型输出工厂
  • 动态成员查找视图模型输入
  • 将自定义单元格视图绑定到视图项
  • 完整文档

入门

需求

  • iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+
  • Xcode 11.0+
  • Swift 5+

安装

CocoaPods

CocoaPods 是 Cocoa 项目的依赖管理器。有关使用和安装说明,请访问他们的网站。要在 Xcode 项目中使用 CocoaPods 集成 MVVMCombine,请在您的 Podfile 中指定它

pod 'MVVMCombine'

工作原理

架构

以下图表说明了模型-视图-视图模型-协调器(MVVM-C)设计模式的基本架构

MVVM-C Architecture

  • 模型:指代领域模型或数据访问层,它们都表示内容。
  • 视图:用户在屏幕上看到的结构、布局和外观。它显示模型表示,接收用户与视图的交互(点击、键盘、手势等),并通过定义链接视图和视图模型的数据绑定(属性、事件回调等)将这些交互传递给视图模型。
  • 视图模型:视图的抽象,公开属性和命令。与MVC模式的控制器或MVP模式的展示者不同,MVVM有一个绑定器,它自动化视图与视图模型中绑定属性的通信。视图模型被描述为模型中数据的某种状态。
  • 协调器:负责处理导航流程,根据从相应视图启动的视图模型事件决定何时何地移动。

有关MVVM-C iOS架构的详细信息,请参阅以下教程

优点

以下是使用MVVM-C架构的一些优点:

  • 不同类型代码的清晰分离应该会使得进入到那些更细粒度、更专注的部分并做出更改变得更容易,无需担心。
  • 在MVVM中,每一段代码都更加细分,如果实现得当,您的外部和内部依赖将与核心逻辑的代码分开,在核心逻辑部分做出更改时进行测试。这使得针对核心逻辑编写单元测试变得容易很多。
  • 这些部分更有可能变得更加可复用。
  • 协调器简化了视图模型之间的导航逻辑和数据共享。

用法

依赖注入

通过子类化MwxServices并重写registerServices()来注册项目中使用的所有服务。

按如下方式注册每个服务

Mwx.register(BackendService.init).as(BackendProtocol.self).lifeCycle(.weakSingle)

其中lifeCycle可以是singleprototypeweakSingleobjectGraph

为了解决服务

let backendService = Mwx.resolve(BackendProtocol.self)

并且将其作为实例属性注入,使用@Service如下所示

@Service var backendService: BackendProtocol

MVVM 流程

让所有视图遵守 MwxView 接口,并声明 vm 属性,以注入相应的视图模型,使用 @ViewModel,如下所示

struct HomeView: MwxView
@ViewModel var vm: HomeViewModel

让视图模型遵守 MwxObservableViewModel 接口,并确定其视图为一个泛型,如下所示

class HomeViewModel: MwxObservableViewModel<HomeView>

如果使用导航视图,则让主体视图始终被 MwxNavigationBody 包装;如果使用标签视图,则使用 MwxTabBody 包装;否则只使用 MwxBody,并将其绑定到其 vm 以同步视图及其视图模型生命周期 didAppeardidDisappear,如下所示

var body: some View {
    MwxNavigationBody {
        Text("Hello World!")
    }
    .bind(to: vm)
}

一旦这样做,你现在可以覆盖视图模型生命周期,如下所示

override func didLoad() {
}

override func didAppear() {
}

override func didDisappear() {
}

列表单元格

让你的列表行遵守 MwxCell 接口,并声明 item 属性,以确定其相应的视图项,使用 @ViewItem,如下所示

struct ListCell: MwxCell
@ViewItem var item: ListViewItem

让视图项遵守 MwxViewItem 接口,如下所示

class ListViewItem: MwxViewItem

协调器

为了创建一个新协调器,确定其视图模型和展示样式。目前支持4种展示样式:roottablinksheet。声明 MwxCoordinator,如下所示

let detail = DetailViewModel.link(self)

对于标签协调器,在视图模型中覆盖 func tabs() -> [MwxTab],以确定要协调哪些标签,并生成相应的标签项,如下所示

let home = HomeViewModel.tab(self)
let contact = ContactViewModel.tab(self)

override func tabs() -> [MwxTab] {
    return [
        home,
        contact
    ]
}

对于链接和表单协调器,确定由链接或表单协调的视图,如下所示

Text("Link here!").coordinated(by: vm.detail)

为了显示链接和表单协调器,在视图模型中使用 show(),使用 pop() 来停用链接,并使用 dismiss() 来停用表单,如下所示

func showDetail() {
    detail.show()
}
func save() {
    pop()
}

为了声明一个回调,当协调器停用时使用 onDisappear: (() -> Void) 闭包,如下所示

let detail = DetailViewModel
                    .sheet(self)
                    .onDisappear {
                        print("Sheet Dismissed!")
                    }

输入

使用输出属性构建视图模型输入,MwxOutput 是一个动态可调用对象,因此您可以动态声明输入键,如下所示

let input = output(name: "Mike",
                    job: "Engineer")

在其协调器声明中传递输入到视图模型,如下所示

let detail = DetailViewModel
                    .sheet(self)
                    .with(input)
                    .onDisappear {
                        print("Sheet Dismissed!")
                    }

或者,您也可以在显示链接或表单协调器时传递它,如下所示

func showDetail() {
    detail.show(with: input)
}

最后,在视图模型内部,使用动态成员查找来解析并获取给定键的任何输入值,如下所示

override func didAppear() {
    if let name = input.name {
    }
}

对于标签页协调器,它们的输入需要提供标题键和图像键,以渲染相应标签页项的标题和图像,如下所示

var contactInput: MwxInput {
    output(title: "Contact",
           image: "contact")
}

ContactViewModel
            .tab(self)
            .with(contactInput)

演示

Unicorn 是一个示例演示,总结了 MVVMCombine 的主要功能。基本上,你可以添加、编辑和删除独角兽。在演示中使用了标签页、链接和表格协调器。

MyUnicornGiF

在运行之前

在运行此演示之前,请访问 crudcrud,并复制在您的 REST 端点显示的特定 API 密钥,然后将其粘贴到 DataUrlService 中的 secret 属性。

这非常重要,以便后端 API 正常工作!

协调器层级

以下图例说明了在示例演示中管理视图导航的协调器层级

Coordinator Hierarchy

待办事项

  • 运行时自动视图模型注册
  • 运行时自动视图到视图模型的查找
  • 支持自定义协调器
  • 支持视图模型发布者输入
  • 提供数组到列表/表单适配器
  • 提供动态样式引擎
  • 提供 iPad 分割视图的协调器

致谢

我要感谢

  • crudcrud,使用他们出色的服务,在不编写后端代码的情况下构建动态 CRUD 操作!
  • LiteCode,以及他们出色的SwiftDI,用于使用@propertyWrapper进行依赖注入!

授权

MVVMCombine采用MIT授权发布。有关详情,请参阅LICENSE

作者

Mohamed Salem