使用 struct id 将收藏夹保存到 UserDefaults
Saving favorites to UserDefaults using struct id
我试图在 UserDefaults 中保存用户最喜欢的城市。找到保存结构 ID 的解决方案 - 构建并运行但似乎没有保存:在应用程序重新启动时,先前点击的按钮被重置。
我很确定我遗漏了什么……
这是我的数据结构和 class:
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)
}
}
}
}
}
}
有几个问题,我将在下面解决。这是工作代码:
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
。这样就保证了每次新上线,每批城市都会有不同的UUID
s,所以节省的情况是行不通的。
- 有一些一般的拼写错误和对实际不存在的属性的引用。
我做了什么:
- 制作了
cities
和 favorites
两个 @Published 属性,这样您就不必手动调用 objectWillChange.send
- 在初始化时,将和两个城市加载到收藏夹中。这样,城市一旦最初创建,将始终具有相同的 UUID,因为它们是从保存的状态中加载的
- 在保存时,我保存了两个
Set
——收藏夹 和 城市
- 在原来的
ForEach
中,我遍历了所有 cities
,然后只标记属于 favorites
的部分
重要说明: 在测试时,我发现至少在 Xcode 12.3 / iOS 14.3 上,同步到 UserDefaults
是慢,即使使用 now-unrecommended synchronize
方法。我一直想知道为什么当我杀死然后 re-opened 应用程序时我的更改没有反映出来。最终发现,如果我给它大约 10-15 秒的时间同步到 UserDefaults
,然后终止应用程序然后再次打开它,一切都会正常。
我试图在 UserDefaults 中保存用户最喜欢的城市。找到保存结构 ID 的解决方案 - 构建并运行但似乎没有保存:在应用程序重新启动时,先前点击的按钮被重置。
我很确定我遗漏了什么……
这是我的数据结构和 class:
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)
}
}
}
}
}
}
有几个问题,我将在下面解决。这是工作代码:
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
。这样就保证了每次新上线,每批城市都会有不同的UUID
s,所以节省的情况是行不通的。 - 有一些一般的拼写错误和对实际不存在的属性的引用。
我做了什么:
- 制作了
cities
和favorites
两个 @Published 属性,这样您就不必手动调用objectWillChange.send
- 在初始化时,将和两个城市加载到收藏夹中。这样,城市一旦最初创建,将始终具有相同的 UUID,因为它们是从保存的状态中加载的
- 在保存时,我保存了两个
Set
——收藏夹 和 城市 - 在原来的
ForEach
中,我遍历了所有cities
,然后只标记属于favorites
的部分
重要说明: 在测试时,我发现至少在 Xcode 12.3 / iOS 14.3 上,同步到 UserDefaults
是慢,即使使用 now-unrecommended synchronize
方法。我一直想知道为什么当我杀死然后 re-opened 应用程序时我的更改没有反映出来。最终发现,如果我给它大约 10-15 秒的时间同步到 UserDefaults
,然后终止应用程序然后再次打开它,一切都会正常。