Как связать опубликованные свойства модели и viewmodel в Swift? - PullRequest
0 голосов
/ 09 марта 2020

Давайте предположим модель, которая реализует протокол ObservableObject и имеет свойство @Published name.

// MARK: Model
class ContentSinglePropertyModel: ObservableObject {
    @Published public var name: String
}

Теперь я хотел бы отобразить это имя в виде и обновлять вид при каждом изменении name в модели. Кроме того, я хотел бы использовать шаблон Model-View-ViewModel (MVVM) для достижения этой цели.

// MARK: ViewModel
final class ContentSinglePropertyViewModel: ObservableObject {
    private let model: ContentSinglePropertyModel

    @Published var name: String = ""

    init() {
        self.model = ContentSinglePropertyModel()
    }
}

// MARK: View
struct ContentSinglePropertyView: View {
    @ObservedObject var viewModel: ContentSinglePropertyViewModel

    var body: some View {
        Text(self.viewModel.name)
    }
}

Поскольку мне не нравится идея сделать модель или ее свойства опубликованными c в пределах модели представления, один из вариантов - заключить свойство модели name в модель представления. У меня вопрос : Как связать name модели и модели представления наиболее идиоматическим способом c?

Я нашел решение обновить модель представления свойство с помощью метода присвоения в Combine:

self.model.$name.assign(to: \.name, on: self).store(in: &self.cancellables)

Есть ли лучшее решение?

Мой рабочий пример:

import SwiftUI
import Combine

// MARK: Model
class ContentSinglePropertyModel: ObservableObject {
    @Published public var name: String

    init() {
        self.name = "Initial value"
    }

    func doSomething() {
        self.name = "Changed value"
    }
}

// MARK: ViewModel
final class ContentSinglePropertyViewModel: ObservableObject {
    private let model: ContentSinglePropertyModel
    private var cancellables: Set<AnyCancellable> = []

    @Published var name: String = ""

    init() {
        self.model = ContentSinglePropertyModel()

        // glue Model and ViewModel
        self.model.$name.assign(to: \.name, on: self).store(in: &self.cancellables)
    }

    func doSomething() {
        self.model.doSomething()
    }
}

// MARK: View
struct ContentSinglePropertyView: View {
    @ObservedObject var viewModel: ContentSinglePropertyViewModel

    var body: some View {
        VStack {
            Text(self.viewModel.name)

            Button("Do something!", action: {
                self.viewModel.doSomething()
            })
        }
    }
}

struct ContentSinglePropertyView_Previews: PreviewProvider {
    static var previews: some View {
        ContentSinglePropertyView(viewModel: .init())
    }
}

1 Ответ

0 голосов
/ 10 марта 2020

У вас есть дублирование, потому что предоставленная модель на самом деле не является моделью, потому что она активна (модель, по концепции, является пассивными данными, или только для хранения).

Это будет модель, так как возможный подход,

struct ContentSinglePropertyModel {
    var name: String
}

и это модель вида

// MARK: ViewModel
final class ContentSinglePropertyViewModel: ObservableObject {
    private var model: ContentSinglePropertyModel

    @Published var name: String = "" {
        didSet {
            model.name = self.name
        }
    }

    init(model: ContentSinglePropertyModel = ContentSinglePropertyModel(name: "initial value")) {
        self.model = model
        self.name = model.name
    }

    func doSomething() {
        self.name = "Changed value"
    }
}
...