Я чувствую, что могу понять, почему то, что я делаю, не работает, но я все еще пытаюсь обернуть голову вокруг Combine и SwiftUI, поэтому любая помощь здесь будет приветствоваться.
Рассмотрим Пример:
Приложение с одним представлением, которое хранит некоторые строки в UserDefaults и использует эти строки для отображения некоторых текстовых меток. Есть три кнопки, одна для обновления заголовка и одна для обновления двух строк, сохраненных в UserDefaults, до случайной строки.
Представление является представлением тупого средства визуализации, а строка заголовка сохраняется непосредственно в ObservableObject. посмотреть модель. Модель представления имеет опубликованное свойство, которое содержит ссылку на класс UserSettings, который реализует оболочки свойств для хранения пользовательских строк в UserDefaults.
Наблюдения:
• Нажатие «Установить новый заголовок» корректно обновляет представление, чтобы отобразить новое значение
• При нажатии любой из кнопок «Задать значение пользователя» внутренне изменяется значение, однако представление не обновляется sh. Если после одной из этих кнопок нажать «Установить новый заголовок», новые значения отображаются, когда тело представления перестраивается для изменения заголовка.
Представление:
import SwiftUI
struct ContentView: View {
@ObservedObject var model = ViewModel()
var body: some View {
VStack {
Text(model.title).font(.largeTitle)
Form {
Section {
Text(model.settings.UserValue1)
Text(model.settings.UserValue2)
}
Section {
Button(action: {
self.model.title = "Updated Title"
}) { Text("Set A New Title") }
Button(action: {
self.model.settings.UserValue1 = "\(Int.random(in: 1...100))"
}) { Text("Set User Value 1 to Random Integer") }
Button(action: {
self.model.settings.UserValue2 = "\(Int.random(in: 1...100))"
}) { Text("Set User Value 2 to Random Integer") }
}
Section {
Button(action: {
self.model.settings.UserValue1 = "Initial Value One"
self.model.settings.UserValue2 = "Initial Value Two"
self.model.title = "Initial Title"
}) { Text("Reset All") }
}
}
}
}
}
ViewModel:
import Combine
class ViewModel: ObservableObject {
@Published var title = "Initial Title"
@Published var settings = UserSettings()
}
Модель UserSettings:
import Foundation
import Combine
@propertyWrapper struct DefaultsWritable<T> {
let key: String
let value: T
init(key: String, initialValue: T) {
self.key = key
self.value = initialValue
}
var wrappedValue: T {
get { return UserDefaults.standard.object(forKey: key) as? T ?? value }
set { return UserDefaults.standard.set(newValue, forKey: key) }
}
}
final class UserSettings: NSObject, ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
@DefaultsWritable(key: "UserValue", initialValue: "Initial Value One") var UserValue1: String {
willSet {
objectWillChange.send()
}
}
@DefaultsWritable(key: "UserBeacon2", initialValue: "Initial Value Two") var UserValue2: String {
willSet {
objectWillChange.send()
}
}
}
Когда я ставлю точку останова на willSet { objectWillChange.send() }
в UserSettings, я вижу, что сообщение objectWillChange отправляется издателю, когда я ожидаю, что так говорит мне, что проблема, вероятно, в том, что представление или модель представления неправильно подписываются на него. Я знаю, что если бы у меня были UserSettings в качестве @ObservedObject
в представлении, это работало бы, но я чувствую, что это должно быть сделано в модели представления с Combine.
Чего мне здесь не хватает? Я уверен, что это действительно очевидно ...