使用 struct id 将收藏夹保存到 UserDefaults

Posted

技术标签:

【中文标题】使用 struct id 将收藏夹保存到 UserDefaults【英文标题】:Saving favorites to UserDefaults using struct id 【发布时间】:2021-02-08 23:54:58 【问题描述】:

我正在尝试将用户最喜欢的城市保存在 UserDefaults 中。发现此解决方案保存了结构 ID - 构建并运行但似乎没有保存:在应用重新启动时,先前点击的按钮被重置。

我很确定我错过了什么……

这是我的数据结构和类:

struct City: Codable 
    var id = UUID().uuidString
    var name: String


class Favorites: ObservableObject 
    private var cities: Set<String>
    let defaults = UserDefaults.standard

    var items: [City] = [
        City(name: "London"),
        City(name: "Paris"),
        City(name: "Berlin")
    ]

    init() 
        let decoder = PropertyListDecoder()
        if let data = defaults.data(forKey: "Favorites") 
            let cityData = try? decoder.decode(Set<String>.self, from: data)
            self.cities = cityData ?? []
            return
         else 
            self.cities = []
        
    

    func getTaskIds() -> Set<String> 
        return self.cities
    

    func contains(_ city: City) -> Bool 
        cities.contains(city.id)
    

    func add(_ city: City) 
        objectWillChange.send()
        cities.contains(city.id)
        save()
    

    func remove(_ city: City) 
        objectWillChange.send()
        cities.remove(city.id)
        save()
    

    func save() 
        let encoder = PropertyListEncoder()
        if let encoded = try? encoder.encode(tasks) 
            defaults.setValue(encoded, forKey: "Favorites")
        
    

这里是 TestDataView

struct TestData: View 

    @StateObject var favorites = Favorites()
    
    var body: some View 
        ForEach(self.favorites.items, id: \.id)  item in
            VStack 
                Text(item.title)
          
                Button(action: 
                    if self.favorites.contains(item) 
                        self.favorites.remove(item)
                     else 
                        self.favorites.add(item)
                    
                ) 
                    HStack 
                        Image(systemName: self.favorites.contains(item) ? "heart.fill" : "heart")
                            .foregroundColor(self.favorites.contains(item) ? .red : .white)
                    
                
            
        
    

【问题讨论】:

【参考方案1】:

有几个问题,我将在下面解决。这是工作代码:

struct ContentView: View 

    @StateObject var favorites = Favorites()
    
    var body: some View 
        VStack(spacing: 10) 
            ForEach(Array(self.favorites.cities), id: \.id)  item in
                VStack 
                    Text(item.name)
              
                    Button(action: 
                        if self.favorites.contains(item) 
                            self.favorites.remove(item)
                         else 
                            self.favorites.add(item)
                        
                    ) 
                        HStack 
                            Image(systemName: self.favorites.contains(item) ? "heart.fill" : "heart")
                                .foregroundColor(self.favorites.contains(item) ? .red : .black)
                        
                    
                
            
        
        
    


struct City: Codable, Hashable 
    var id = UUID().uuidString
    var name: String


class Favorites: ObservableObject 
    @Published var cities: Set<City> = []
    @Published var favorites: Set<String> = []
    let defaults = UserDefaults.standard

    var initialItems: [City] = [
        City(name: "London"),
        City(name: "Paris"),
        City(name: "Berlin")
    ]

    init() 
        let decoder = PropertyListDecoder()
        
        if let data = defaults.data(forKey: "Cities") 
            cities = (try? decoder.decode(Set<City>.self, from: data)) ?? Set(initialItems)
         else 
            cities = Set(initialItems)
        
        self.favorites = Set(defaults.array(forKey: "Favorites") as? [String] ?? [])
    

    func getTaskIds() -> Set<String> 
        return self.favorites
    

    func contains(_ city: City) -> Bool 
        favorites.contains(city.id)
    

    func add(_ city: City) 
        favorites.insert(city.id)
        save()
    

    func remove(_ city: City) 
        favorites.remove(city.id)
        save()
    

    func save() 
        let encoder = PropertyListEncoder()
        if let encoded = try? encoder.encode(self.cities) 
            self.defaults.set(encoded, forKey: "Cities")
        
        self.defaults.set(Array(self.favorites), forKey: "Favorites")
        defaults.synchronize()
    

原版问题:

    最大的问题是items 在每次新发布时都会重新创建,City 有一个id,在创建时分配了一个UUID。这保证了每次新的发布,每批城市都会有不同的UUIDs,所以储蓄的情况永远不会奏效。 存在一些常见的拼写错误和对实际不存在的属性的引用。

我做了什么:

    citiesfavorites 都设为@Published 属性,这样您就不必手动调用objectWillChange.send 在初始化时,加载城市收藏夹。这样一来,城市在最初创建后将始终具有相同的 UUID,因为它们是从保存状态加载的 保存时,我同时保存Sets -- 收藏夹城市 在原始ForEach 中,我遍历所有cities,然后只标记属于favorites 的部分

重要提示:在测试时,我发现至少在 Xcode 12.3 / ios 14.3 上,同步到 UserDefaults,即使使用 now-不推荐的synchronize 方法。我一直想知道为什么当我杀死然后重新打开应用程序时我的更改没有反映出来。最终发现如果我给它大约 10 到 15 秒的时间来同步到 UserDefaults 然后关闭应用程序然后再次打开它,一切都会正常工作。

【讨论】:

感谢您的回复和详细的解释!奇迹般有效。我对错别字不利,因为它是一个更大项目的一部分,我将其分解并更改了名称以使其更简单。我知道问题出在哪里,现在一切都说得通了。非常感谢!

以上是关于使用 struct id 将收藏夹保存到 UserDefaults的主要内容,如果未能解决你的问题,请参考以下文章

如何将新列添加到权限表

检查行是不是存在,Laravel

struct tag

如何将对象列表中的布尔收藏夹保存到 Flutter 中的共享首选项

使用 Alamofire 在 user_id iOS 中保存数据

golang中struct成员变量的标签(Tag)说明和获取方式