我想利用一种架构设计,使我能够在视图模型(How To Feed ViewModels)中清楚地指定输入和输出,但对如何最好地集成视图的“工作”部分感到好奇建模到这种结构中。
我倾向于使用Actions(也许不是很优雅)将UI元素绑定到他们需要执行的工作。当然,问题在于这些动作中的某些动作依赖于视图模型属性,因此由于属性尚未初始化,因此无法像输入和输出一样在init()中创建它们。可以通过将它们定义为私有惰性变量来解决此问题,然后通过本质上为Action提供公共接口的结构将其公开。但是,它似乎运行得并不好,我正在学习,如果您花费大量精力使结构能够按您的意愿行事,那可能是代码的味道。下面的代码示例-欢迎提出建议:-)
protocol PatientListViewModelType: ViewModelType { }
final class PatientListViewModel: PatientListViewModelType {
// MARK:- Protocol conformance
typealias Dependencies = HasPatientService
struct Input {
let patient: AnyObserver<Patient>
}
struct Output {
let sectionedPatients: Observable<[PatientSection]>
let patient: Observable<Patient>
}
let input: Input
let output: Output
struct Actions {
let deletePatient: Action<Patient, Void>
let togglePatient: (Patient) -> CocoaAction
let updatePatient: (Patient) -> Action<String, Void>
}
lazy var action: Actions = Actions(deletePatient: self.deletePatient,
togglePatient: self.togglePatient,
updatePatient: self.updatePatient)
// MARK: Setup
private let dependencies: Dependencies
private let patientSubject = ReplaySubject<Patient>.create(bufferSize: 1)
// MARK:- Init
init(dependencies: Dependencies) {
self.dependencies = dependencies
let sectionedPatients =
dependencies.patientService.patients()
.map { results -> [PatientSection] in
let scheduledPatients = results
.filter("checked == nil")
.sorted(byKeyPath: "created", ascending: false)
let admittedPatients = results
.filter("checked != nil")
.sorted(byKeyPath: "checked", ascending: false)
return [
PatientSection(model: "Scheduled Patients", items: scheduledPatients.toArray()),
PatientSection(model: "Admitted Patients", items: admittedPatients.toArray())
]
}
self.output = Output(sectionedPatients: sectionedPatients,
patient: patientSubject.asObservable() )
// this is immediately overriden during binding to VC - it just allows us to exit the init without errors
self.input = Input(patient: patientSubject.asObserver())
}
// MARK:- Actions
private lazy var deletePatient: Action<Patient, Void> = { (service: PatientServiceType) in
return Action { patient in
return service.delete(realmObject: patient)
}
}(self.dependencies.patientService)
lazy var togglePatient: (Patient) -> CocoaAction = { [unowned self] (patient: Patient) -> CocoaAction in
return CocoaAction {
return self.dependencies.patientService.toggle(patient: patient).map { _ in }
}
}
private lazy var updatePatient: (Patient) -> Action<String, Void> = { [unowned self] (patient: Patient) in
return Action { newName in
return self.dependencies.patientService.update(patient: patient, name: newName).map { _ in }
}
}
}
答案 0 :(得分:0)
一旦我有机会坐下来玩耍,答案实际上很简单。我已将Actions放入Output结构(似乎是最合逻辑的地方),而不是像以前那样创建专用接口。当然,下一个问题是“动作”是否最适合该问题,但我将在稍后处理...
final class PatientListViewModel: PatientListViewModelType {
// MARK:- Protocol conformance
typealias Dependencies = HasPatientService
struct Input {
let patient: AnyObserver<Patient>
}
let input: Input
struct Output {
let sectionedPatients: Observable<[PatientSection]>
let patient: Observable<Patient>
let deletePatient: Action<Patient, Void>
let togglePatient: (Patient) -> CocoaAction
let updatePatient: (Patient) -> Action<String, Void>
}
let output: Output
// MARK: Setup
private let dependencies: Dependencies
private let patientSubject = ReplaySubject<Patient>.create(bufferSize: 1)
// MARK:- Init
init(dependencies: Dependencies) {
self.dependencies = dependencies
let sectionedPatients =
dependencies.patientService.patients()
.map { results -> [PatientSection] in
let scheduledPatients = results
.filter("checked == nil")
.sorted(byKeyPath: "created", ascending: false)
let admittedPatients = results
.filter("checked != nil")
.sorted(byKeyPath: "checked", ascending: false)
return [
PatientSection(model: "Scheduled Patients", items: scheduledPatients.toArray()),
PatientSection(model: "Admitted Patients", items: admittedPatients.toArray())
]
}
let deletePatient: Action<Patient, Void> = { patientService in
return Action { patient in
return patientService.delete(realmObject: patient)
}
}(dependencies.patientService)
let togglePatient: (Patient) -> CocoaAction = { patient in
return CocoaAction {
return dependencies.patientService.toggle(patient: patient)
.map { _ in }
}
}
let updatePatient: (Patient) -> Action<String, Void> = { patient in
return Action { newName in
return dependencies.patientService.update(patient: patient, name: newName)
.map { _ in }
}
}
// this is immediately overriden during binding to VC - it just allows us to exit the init without errors
self.input = Input(patient: patientSubject.asObserver())
self.output = Output(sectionedPatients: sectionedPatients,
patient: patientSubject.asObservable(),
deletePatient: deletePatient,
togglePatient: togglePatient,
updatePatient: updatePatient)
}