获取UserDefaults以保留自定义数据类型

时间:2020-06-29 01:25:41

标签: swift swiftui userdefaults

我正在尝试将一个变量设置为UserDefault,以便当用户打开此应用程序(显示问题列表)时,他们会得到上一次在应用程序中最后一次选择的“打包”信息。 / p>

该包由此“ defaultPack”变量确定,该变量设置为UserDefaults,并且该包的数据显示在启动屏幕上。有一个菜单,用户可以选择另一个包,其中的链接带有@Binding变量,该变量在主屏幕上更新该包,该变量应返回到主屏幕,然后也更新UserDefaults。

它似乎正确设置了defaultPack变量,但不能设置UserDefaults,因为每当我重新打开该应用程序时,它将重置为原始默认“ Pack”,而不是用户上次选择的那个。 / strong>

是否有任何需要更改的想法才能正确更新此UserDefaults?

这是一种自定义数据类型,因此我使用的是UserDefaults扩展名,用于对该值进行编码/解码以进行存储。

这是我的带有defaultPack变量的班级:

final class UserInformation: ObservableObject {
    @Published var defaultPack: Pack {
        didSet {
            do {
                try UserDefaults.standard.setObject(defaultPack, forKey: "defaultPack")
            } catch {
                print(error.localizedDescription)
            }
        }
    }
    init() {
        self.defaultPack = UserDefaults.standard.object(forKey: "defaultPack") as? Pack ?? samplePack
    }
}

这是UserDefaults的扩展名,其中包含UserDefaults的可编码/可解码指令

//extension to UserDefaults to include the encodable/decodable instructions for UserDefaults
extension UserDefaults: ObjectSavable {
    func setObject<Object>(_ object: Object, forKey: String) throws where Object: Encodable {
        let encoder = JSONEncoder()
        do {
            let data = try encoder.encode(object)
            set(data, forKey: forKey)
        } catch {
            throw ObjectSavableError.unableToEncode
        }
    }
    
    func getObject<Object>(forKey: String, castTo type: Object.Type) throws -> Object where Object: Decodable {
        guard let data = data(forKey: forKey) else { throw ObjectSavableError.noValue }
        let decoder = JSONDecoder()
        do {
            let object = try decoder.decode(type, from: data)
            return object
        } catch {
            throw ObjectSavableError.unableToDecode
        }
    }
}

这是我的视图,其中包含默认的Pack:

struct PackTopics: View {
    @ObservedObject var userInformation: UserInformation
    @State var showPacksList: Bool = false

    var packsButton: some View {
        Button(action: { self.showPacksList.toggle()
        }) {
            Image(systemName: "tray.full")
                .imageScale(.large)
                .accessibility(label: Text("Show Packs List"))
                .padding()
        }
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(userInformation.defaultPack.sections, id: \.self) { section in
                    Section(header: SectionHeader(linkedSection: section))
                    {
                        ForEach (section.topics, id: \.self) { topic in
                            TopicLine(linkedTopic: topic)
                        }
                    }
                }
            }
            .navigationBarTitle(Text(userInformation.defaultPack.name))
            .navigationBarItems(trailing: packsButton)
            .sheet(isPresented: $showPacksList) {
                PackPage(isPresented: self.$showPacksList, newPack: self.$userInformation.defaultPack)
            }
            }
        }
    }

这是与上一个视图中的pack变量绑定的视图,应该更新defaultPack:

struct PackPage: View {
    @Binding var isPresented: Bool
    @Binding var newPack: Pack
    let userDefaults = UserDefaults.standard

    var body: some View {
        VStack {
            HStack {
                Text("Question Packs")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .padding()
                Spacer()
            }
            Text("Available Packs")
                List {
                    HStack(alignment: .top) {
                        ForEach(purchasedPacks, id: \.self) { pack in
                            UnlockedPackTile(tilePack: pack)
                                .onTapGesture {
                                    print("Originally tapped \(pack.name)")
                                    self.newPack = pack
                                    do {
                                        let userDefaultPack = try self.userDefaults.getObject(forKey: "defaultPack", castTo: Pack.self)
                                        print("Default pack is now set to \(userDefaultPack)")
                                    } catch {
                                        print(error.localizedDescription)
                                    }
                                    self.isPresented.toggle()
                            }
                        }
                    }
                }

添加输出 *

2020-06-30 07:04:06.608783-0500 Travel Conversation Starters[2524:131054] [Agent] Received remote injection
2020-06-30 07:04:06.609182-0500 Travel Conversation Starters[2524:131054] [Agent] Create remote injection Mach transport: 6000023ca0d0
2020-06-30 07:04:06.609622-0500 Travel Conversation Starters[2524:131000] [Agent] No global connection handler, using shared user agent
2020-06-30 07:04:06.609840-0500 Travel Conversation Starters[2524:131000] [Agent] Received connection, creating agent
2020-06-30 07:04:07.916795-0500 Travel Conversation Starters[2524:131000] [Agent] Received message: < DTXMessage 0x600002cc4c00 : i2.0e c0 object:(__NSDictionaryI*) {
    "products" : <NSArray 0x600000acc200 | 1 objects>
    "id" : [0]
    "scaleFactorHint" : [2]
    "providerName" : "28Travel_Conversation_Starters20ContentView_PreviewsV"
    "updates" : <NSArray 0x7fff8062d430 | 0 objects>
} > {
    "serviceCommand" : "forwardMessage"
    "type" : "display"
}
2020-06-30 07:04:16.447421-0500 Travel Conversation Starters[2524:131000] [Common] _BSMachError: port 9f2f; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND"
**Originally tapped Family Topics**
**Default pack is now set to Pack(id: 3541AF19-8CDB-4D9B-B261-7194DDD8516E, name: "Family Topics")** 

2 个答案:

答案 0 :(得分:1)

前段时间,发生了类似的错误。在序列化为Userdefault时,我没有任何错误,但是@Published和didSet属性观察器中存在错误。 您确定要调用didSet吗? 就我而言,当我更改变量时,它没有触发didSet属性观察器。我最终使用了自定义绑定,如下所示:

https://www.hackingwithswift.com/books/ios-swiftui/creating-custom-bindings-in-swiftui

您还可以尝试将对象复制到temp变量中,然后再次将其重置,以查看是否调用didSet。

我知道这听起来像是一种解决方法,而不是适当的解决方案。但这是了解这里发生的事情的开始。

答案 1 :(得分:1)

@FitzChill,我不知道如何使用此多行代码添加注释,但是检查didSet可以帮助我进行故障排除。看来我的init有问题。

我将其更改为do-catch闭包,现在看来工作正常。

    init() {
        do {
            self.defaultPack = try UserDefaults.standard.getObject(forKey: "defaultPack", castTo: Pack.self)
        } catch {
            print(error.localizedDescription)
            self.defaultPack = samplePack
        }
    }