回答您的问题
让我们首先注意 createMyAction: Action<String, Void> 引用名为 Action 的类型 (struct),就好像它是一个泛型一样,但您没有声明它,因此它不起作用。
要回答嵌套struct Action 的问题,可以参考其外部class MyViewModel - 是的,您可以参考static 属性,如下所示:
struct Foo {
struct Bar {
let biz = Foo.buz
}
static let buz = "buz"
}
let foobar = Foo.Bar()
print(foobar.biz)
但是你应该避免这样的循环引用。我将省略任何可能能够在非静态属性上实现这种循环引用的丑陋技巧(可能涉及可变的可选类型)。这是一种代码味道。
对 MVVM 的建议
听起来您想将Action 声明为函数?我自己也在使用这个协议:
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
最初灵感来自SergDort's CleanArchitecture。
您可以从UIViewController 准备input 的实例(包含Observables)并调用transform 函数,然后映射变换的Output(即Observabless)以更新图形用户界面。
因此,此代码假定您具有基本的响应式知识。至于Observables,您可以在RxSwift 或ReactiveSwift 之间进行选择——是的,它们的名字相似。
如果您对 Rx 感到满意,那么它是通过简单的 GUI 异步更新实现良好 MVVM 架构的绝佳方式。在下面的示例中,您将找到类型为 Driver,它记录在 here 中,但简短的解释是您希望将其用于 input from 视图和 input to 视图,因为它会更新 GUI 线程上的视图并保证不会出错。
CleanArchitecture 包含例如PostsViewModel :
final class PostsViewModel: ViewModelType {
struct Input {
let trigger: Driver<Void>
let createPostTrigger: Driver<Void>
let selection: Driver<IndexPath>
}
struct Output {
let fetching: Driver<Bool>
let posts: Driver<[PostItemViewModel]>
let createPost: Driver<Void>
let selectedPost: Driver<Post>
let error: Driver<Error>
}
private let useCase: PostsUseCase
private let navigator: PostsNavigator
init(useCase: PostsUseCase, navigator: PostsNavigator) {
self.useCase = useCase
self.navigator = navigator
}
func transform(input: Input) -> Output {
let activityIndicator = ActivityIndicator()
let errorTracker = ErrorTracker()
let posts = input.trigger.flatMapLatest {
return self.useCase.posts()
.trackActivity(activityIndicator)
.trackError(errorTracker)
.asDriverOnErrorJustComplete()
.map { $0.map { PostItemViewModel(with: $0) } }
}
let fetching = activityIndicator.asDriver()
let errors = errorTracker.asDriver()
let selectedPost = input.selection
.withLatestFrom(posts) { (indexPath, posts) -> Post in
return posts[indexPath.row].post
}
.do(onNext: navigator.toPost)
let createPost = input.createPostTrigger
.do(onNext: navigator.toCreatePost)
return Output(fetching: fetching,
posts: posts,
createPost: createPost,
selectedPost: selectedPost,
error: errors)
}
}