@AppStorage no UserDefaults在SwiftUI init/didSet代码中均不起作用

dz6r00yl  于 2023-04-19  发布在  Swift
关注(0)|答案(2)|浏览(104)

这段代码不起作用:AppStorage实际上并不保存数据。

struct DayInfoView: View {

    @State private var selectedDate: Date
    @Environment(\.presentationMode) var presentationMode
    @AppStorage("checkmarkVisible") private var checkmarkVisibleData: Data = Data()
    @State private var checkmarkVisible: Set<Date> = [] {
        didSet {
            if let encoded = try? JSONEncoder().encode(checkmarkVisible) {
                checkmarkVisibleData = encoded
            }
            print(checkmarkVisible)
        }
    }
var body: someView {
//// some code
Button(action: {
                        checkmarkVisible.insert(selectedDate)
                    }) {
                        Text("Button 2")
                            .foregroundColor(.white)
                            .padding()
                            .background(Color.blue)
                            .cornerRadius(8)
                    }
}
}

同样的错误行为也适用于直接的userdefaults方法调用。

class UserDefaultsService {
    static let shared = UserDefaultsService()

    func save<T: Codable>(_ value: T, forKey key: String) {
        if let encoded = try? JSONEncoder().encode(value) {
            UserDefaults.standard.set(encoded, forKey: key)
            UserDefaults.standard.synchronize()
        }
    }

    func load<T: Codable>(forKey key: String, defaultValue: T) -> T {
        if let data = UserDefaults.standard.data(forKey: key),
           let value = try? JSONDecoder().decode(T.self, from: data) {
            return value
        }
        return defaultValue
    }
}

struct DayInfoView: View {
    @State private var selectedDate: Date
    @Environment(\.presentationMode) var presentationMode

    private let userDefaultsService = UserDefaultsService.shared

    @State private var checkmarkVisible: Set<Date> = [] {
        didSet {
            userDefaultsService.save(checkmarkVisible, forKey: "checkmarkVisible")
            print(checkmarkVisible)
        }
    }

    init(selectedDate: Date) {
        _selectedDate = State(initialValue: selectedDate)
        let storedCheckmarkVisible: Set<Date> = userDefaultsService.load(forKey: "checkmarkVisible", defaultValue: Set<Date>())
        _checkmarkVisible = State(initialValue: storedCheckmarkVisible)
    }

为什么?如何解决这个问题?
synchronize也不行。

备注

这种行为是不可预测的。有时它会保存或不保存。

00jrzges

00jrzges1#

我已经找到了解决方案-使用文件。但我不明白,如何使用appstorage/userdefaults

final class UserDefaultsService {
    static let shared = UserDefaultsService()

    private let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

    func save<T: Codable>(_ value: T, forKey key: String) {
        let fileURL = directory.appendingPathComponent(key)
        if let encoded = try? JSONEncoder().encode(value) {
            do {
                try encoded.write(to: fileURL, options: .atomic)
            } catch {
                print("Error saving data to disk: \(error)")
            }
        }
    }

    func load<T: Codable>(forKey key: String, defaultValue: T) -> T {
        let fileURL = directory.appendingPathComponent(key)
        if let data = try? Data(contentsOf: fileURL),
           let value = try? JSONDecoder().decode(T.self, from: data) {
            return value
        }
        return defaultValue
    }
}
bxjv4tth

bxjv4tth2#

@AppStorage@SceneStorage需要RawRepresentable<String>
只需将此代码添加到.swift文件中。

//Allows all Codable Arrays to be saved using AppStorage
extension Set: RawRepresentable where Element: Codable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
              let result = try? JSONDecoder().decode(Self.self, from: data)
        else {
            return nil
        }
        self = result
    }

    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
              let result = String(data: data, encoding: .utf8)
        else {
            return "[]"
        }
        return result
    }
}

然后你只需要一个变量,你不需要@StatedidSet

struct RawView: View {
    @AppStorage("checkmarkVisible") private var checkmarkVisible: Set<Date> = []
    @State var selectedDate: Date = Date()
    var body: some View {
        VStack{
            Text(checkmarkVisible.count.description)
            Button(action: {
                checkmarkVisible.insert(selectedDate)
                selectedDate = Date()
            }) {
                Text("Button 2")
                    .foregroundColor(.white)
                    .padding()
                    .background(Color.blue)
                    .cornerRadius(8)
            }
        }
    }
}

注意AppStorageUserDefaults只应该用于小的东西。设置像音量,音高等。

相关问题