带有已发布枚举的用户默认值

时间:2021-02-13 08:14:27

标签: swift swiftui 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)
                }
            }
        }
    }
}

}

2 个答案:

答案 0 :(得分:1)

你有两个问题。

首先,正如我在上面的评论中提到的,您正在为 UserDefaults 使用两个不同的套件。这意味着您正在从两个不同的位置进行存储和检索。使用 UserDefaults.standard 或与您选择的套件 UserDefaults(suitName: "method") 一起使用 - 您不必使用套件,除非您计划与其他扩展共享您的默认设置,那么这样做是明智的。< /p>

其次,您在 UserDefaults 中存储了错误的项目。您正在存储计算属性 params 而不是实际的枚举值。当您尝试检索该值时,它会失败,因为它没有获得预期的值并使用您设置的默认值。

这是一个简单的例子,展示了你可以做什么。有一个简单的枚举,它具有原始值 (String) 并符合 Codable,它还具有计算属性。这与您的枚举匹配。

我已将初始化程序添加到我的 ObservableObject。这用于在构造 Place 对象时从 UserDefaults 填充我发布的 Race

然后在我的 ContentView 中,我根据按下的按钮更新 place。这会更新 UI 并更新 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 的读取和写入)都应该通过那里进行。

答案 1 :(得分:0)

您在一个 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