В SwiftUI у меня есть класс для пользовательских данных, и я конвертирую в него данные JSON:
class UserData: Codable {
var displayName: String
var employeeId: String
var firstName: String
var lastName: String
// more ...
}
от JSONDecoder().decode(UserData.self, from: jsonData)
И у меня есть класс UserProfile, содержащий пользовательские данные:
class UserProfile : ObservableObject {
@Published var userData: UserData?
}
Теперь проблема в том, что всякий раз, когда я обновлял userData, например
userProfile.userData?.displayName = "New John"
изменение данных не публикуется в представлении. Я знаю, что причина в том, что userData на самом деле является классом, поэтому, если я это сделаю
userProfile.userData = userProfile.userData после нового присвоения значения представление обновляется.
Мой вопрос в том, как это сделать правильно? Я не хочу вызывать userData = userData каждый раз, когда значение обновляется, это звучит неправильно.
@EmilioPelaez решит проблему?
Структура - самый простой ответ здесь
@BenzyNeez Вы не можете иметь Codable и ObservableObject одновременно для класса.
Поскольку структура работает, почему бы не сделать ответ с пояснениями.
@JoeHuang Я удалил свой предыдущий комментарий, чтобы избежать путаницы. Мой полный ответ можно найти ниже, он будет работать и для структуры (за исключением того, что вам нужно будет использовать var вместо let).
@JoeHuang опубликовал мой комментарий в качестве ответа





Обновлено: обновлено для использования objectWillChange.send(), как было предложено наблюдателями...
Таким образом, UserProfile является оболочкой для UserData, позволяющей наблюдать за изменениями базовых данных. Что вы можете сделать, так это предоставить геттеры/сеттеры в классе-оболочке для базовых свойств. Всякий раз, когда свойство обновляется с помощью установщика, класс-оболочка уведомляет своих наблюдателей, вызывая objectWillSend.send().
Если вы можете избежать опционального, это будет выглядеть примерно так:
class UserProfile : ObservableObject {
private let userData: UserData
init(userData: UserData) {
self.userData = userData
}
var displayName: String {
get { userData.displayName }
set {
// Notify observers before applying the change
objectWillChange.send()
userData.displayName = newValue
}
}
// etc.
}
Если вы не можете избежать необязательного, то, конечно, геттеры/сеттеры должны будут обрабатывать его соответствующим образом, например:
var displayName: String {
get { userData?.displayName ?? "" }
set {
if let userData {
objectWillChange.send()
userData.displayName = newValue
}
}
}
Этот подход дает вам полный контроль над тем, какие свойства базового экземпляра могут быть изменены. Например, вы можете запретить мутацию employeeId. Вы также можете сделать так, чтобы свойства, которые принадлежат друг другу, могли обновляться только вместе, например firstName и lastName.
Если вы хотите, вы все равно можете сохранить userData как опубликованное свойство и заменить его новым экземпляром.
Вызов objectWillChange.send() — гораздо более простая версия этого
Если вы сделаете UserDatastruct, любые изменения в его свойствах будут опубликованы так, как вы хотите.
По какой причине вы не можете сделать UserData структурой?