Userdefaults с опубликованным перечислением

попробуйте сохранить настройки пользователя, но UserDefaults не работает, Xcode 12.3, swiftui 2.0, когда я перезагружаю свое приложение, мои настройки не обновляются для нового значения)

 class PrayerTimeViewModel: ObservableObject {
@Published var lm = LocationManager()
@Published var method: CalculationMethod = .dubai {
    didSet {
        UserDefaults.standard.set(method.params, forKey: "method")
        self.getPrayerTime()
    }
}
  
func getPrayerTime() {
    let cal = Calendar(identifier: Calendar.Identifier.gregorian)
    let date = cal.dateComponents([.year, .month, .day], from: Date())
    let coordinates = Coordinates(latitude: lm.location?.latitude ?? 0.0, longitude: lm.location?.longitude ?? 0.0)
    var par = method.params
    par.madhab = mashab
    self.times = PrayerTimes(coordinates: coordinates, date: date, calculationParameters: par)
}

и просмотрите .. обновление с помощью AppStorage

 struct MethodView: View {

@ObservedObject var model: PrayerTimeViewModel
@Environment(\.presentationMode) var presentationMode
@AppStorage("method", store: UserDefaults(suiteName: "method")) var method: CalculationMethod = .dubai
var body: some View {
    List(CalculationMethod.allCases, id: \.self) { item in
        Button(action: {
            self.model.objectWillChange.send()
            self.presentationMode.wrappedValue.dismiss()
            self.model.method = item
            method = item
        }) {
            HStack {
                Text("\(item.rawValue)")
                if model.method == item {
                    Image(systemName: "checkmark")
                        .foregroundColor(.black)
                }
            }
        }
    }
}

}


person Kamilpro    schedule 13.02.2021    source источник
comment
Куда вы загружаете значение из UserDefaults? Вы также должны показать код для CalculationMethod   -  person Andrew    schedule 13.02.2021
comment
Я обновляю свое представление с помощью AppStorage, checkOut, моего навыка Swiftui Junior), возможно, я не понимаю   -  person Kamilpro    schedule 13.02.2021
comment
Вы все еще не показали CalculationMethod, что это? что такое method.params? Также похоже, что вы сохраняете и извлекаете из двух разных UserDefaults наборов (стандартный и метод).   -  person Andrew    schedule 13.02.2021
comment
CalculationMethod - это перечисление с кейсами, поэтому params in is var внутри этого перечисления, он переключает регистр и возвращает некоторые параметры, поэтому пользовательский вариант выбора только в SettingView, но я не могу сохранить)   -  person Kamilpro    schedule 13.02.2021
comment
Кажется, вы сохраняете параметры в userdefaults, но затем используете перечисление с оболочкой свойств AppStorage. Может надо код для CalculationMethod?   -  person Andrew    schedule 13.02.2021


Ответы (2)


У вас есть две проблемы.

Во-первых, как я уже упоминал в своем комментарии выше, вы используете два разных пакета для UserDefaults. Это означает, что вы сохраняете и извлекаете из двух разных мест. Либо используйте UserDefaults.standard, либо используйте один с выбранным вами набором UserDefaults(suitName: "method") - вам не нужно использовать набор, если вы не планируете делиться своими настройками по умолчанию с другими расширениями, тогда было бы разумно сделать это.

Во-вторых, вы храните неправильный элемент в UserDefaults. Вы сохраняете вычисленное свойство params, а не фактическое значение перечисления. Когда вы пытаетесь получить значение, он терпит неудачу, поскольку он не получает то, что ожидает, и использует значение по умолчанию, которое вы установили.

Вот простой пример, показывающий, что вы могли бы сделать. Существует простое перечисление, которое имеет необработанное значение (String) и соответствует Codable, а также имеет вычисляемое свойство. Это соответствует вашему перечислению.

Я добавил инициализатор в свой ObservableObject. Это служит цели заполнения моих опубликованных Place из UserDefaults при создании объекта Race.

Затем в моем ContentView я обновляю place в зависимости от нажатия кнопки. Это обновляет пользовательский интерфейс и обновляет значение в UserDefaults.

Этого должно быть достаточно, чтобы вы поняли, как это работает.

enum Place: String, Codable {
    case first
    case second
    case third
    case notPlaced

    var someComputedProperty: String {
        "Value stored: \(self.rawValue)"
    }
}

class Race: ObservableObject {
    @Published var place: Place = .notPlaced {
        didSet {
            // Store the rawValue of the enum into UserDefaults
            // We can store the actual enum but that requires more code
            UserDefaults.standard.setValue(place.rawValue, forKey: "method")

            // Using a custom suite
            // UserDefaults(suiteName: "method").setValue(place.rawValue, forKey: "method")
        }
    }

    init() {
        // Load the value from UserDefaults if it exists
        if let rawValue = UserDefaults.standard.string(forKey: "method") {
            // We need to nil-coalesce here as this is a failable initializer
            self.place = Place(rawValue: rawValue) ?? .notPlaced
        }

        // Using a custom suite
        // if let rawValue = UserDefaults(suiteName: "method")?.string(forKey: "method") {
        //    self.place = Place(rawValue: rawValue) ?? .notPlaced
        // }
    }
}

struct ContentView: View {
    @StateObject var race: Race = Race()

    var body: some View {
        VStack(spacing: 20) {
            Text(race.place.someComputedProperty)
                .padding(.bottom, 20)
            Button("Set First") {
                race.place = .first
            }
            Button("Set Second") {
                race.place = .second
            }
            Button("Set Third") {
                race.place = .third
            }
        }
    }
}

Дополнение:

Поскольку перечисление соответствует Codable, можно было бы использовать AppStorage для чтения и записи свойства. Однако это не приведет к обновлению значения в вашем ObservableObject, поэтому они могут легко рассинхронизироваться. Лучше всего иметь одно место, где вы контролируете ценность. В этом случае ваш ObservableObject должен быть источником истины, и все обновления (чтение и запись в UserDefaults) должны происходить через него.

person Andrew    schedule 13.02.2021

Вы пишете в одном домене UserDefaults, но читаете из другого. Предполагая, что вы намерены использовать только набор UserDefaults, вы должны изменить его в модели, например

@Published var method: CalculationMethod = .dubai {
    didSet {
        UserDefaults(suiteName: "method").set(method.params, forKey: "method")
        self.getPrayerTime()
    }
}

или если вы хотите использовать стандартный, просто используйте AppStorage с конструктором по умолчанию, например

// use UserDefaults.standard by default
@AppStorage("method") var method: CalculationMethod = .dubai
person Asperi    schedule 13.02.2021
comment
все еще не работает (попробуйте посмотреть мой GitHub, ссылка в комментариях) - person Kamilpro; 13.02.2021