SwiftUI:对要保存在 AppStorage 中的结构进行编码
SwiftUI: Encode a struct to be saved in AppStorage
目前正在尝试在 swiftUI 中构建我的第一个应用程序。我认为最简单的部分变成了一场噩梦......在 AppStorage 中保存一个结构,以便在应用程序重新启动时可用
我有两个结构要保存。第一个是播放器,我已经实现了 RawRepresentable
struct Player: Codable, Identifiable {
let id: Int
let name: String
let gamePlayed: Int
let bestScore: Int
let nbrGameWon: Int
let nbrGameLost: Int
let totalScore: Int?
}
typealias PlayerList = [Player]
extension PlayerList: RawRepresentable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(PlayerList.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
}
}
在我的视图中这样调用:
struct AddPlayerView: View {
@State var name: String = ""
@State var isDisabled: Bool = false
@State var modified: Bool = false
@AppStorage("players") var players: PlayerList = PlayerList()
...
}
上面的工作,现在我也想保存当前的游戏数据,我有以下结构:
struct Game: Codable, Identifiable {
var id: Int
var currentPlayerIndexes: Int
var currentRoundIndex: Int?
var dealerIndex: Int?
var maxRounds: Int?
var dealResults: [Int: Array<PlayerRoundSelection>]?
var currentLeaderIds: Array<Int>?
var isGameInProgress: Bool?
}
extension Game: RawRepresentable {
public init?(rawValue: String) {
if rawValue == "" {
// did to fix issue when calling AppStorage, but it is probably a bad idea
self = Game(id:1, currentPlayerIndexes:1)
}
else {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Game.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
}
}
一旦我尝试修改结构,它就会调用 rawValue
并且编码失败并显示以下内容:
error: warning: couldn't get required object pointer (substituting NULL): Couldn't load 'self' because its value couldn't be evaluated
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=2, address=0x7ffee49bbff8).
这里是访问结构的部分代码:
struct SelectPlayersView: View {
@AppStorage("currentGame") var currentGame: Game = Game(rawValue: "")!
....
NavigationLink(
destination: SelectModeTypeView(), tag: 2, selection: self.$selection) {
ActionButtonView(text:"Next", disabled: self.$isDisabled, buttonAction: {
var currentPlayers = Array<Int>()
self.players.forEach({ player in
if selectedPlayers.contains(player.id) {
currentPlayers.insert(player.id, at: currentPlayers.count)
}
})
// This used to be a list of indexes, but for testing only using a single index
self.currentGame.currentPlayerIndexes = 6
self.selection = 2
})
...
我在这里找到了编码代码:https://lostmoa.com/blog/SaveCustomCodableTypesInAppStorageOrSceneStorage/
我的理解是,在编码中使用 self
,它会生成一个无限循环,因此无法访问。
我真的不知道如何正确编码这个,任何帮助,链接将不胜感激
我遇到了同样的问题,我想在这里分享我的经验。
我最终发现,当与 RawRepresentable 结合使用时,您显然不能依赖默认的 Codable 协议实现。
所以当我用 CodingKeys 和所有的东西做我自己的 Codable 实现时,它成功了!
我认为您的 Game Codable 实现类似于:
enum CodingKeys: CodingKey {
case currentPlayerIndexes
case currentRoundIndex
// <all the other elements too>
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.currentPlayerIndexes = try container.decode(Int.self, forKey: .currentPlayerIndexes)
self.currentRoundIndex = try container.decode(Int.self, forKey: .currentRoundIndex)
// <and so on>
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(currentPlayerIndexes, forKey: .currentPlayerIndexes)
try container.encode(currentRoundIndex, forKey: .currentRoundIndex)
// <and so on>
}
然后我想知道为什么你的Player coding/decoding 没有工作,发现一个数组(即PlayerList,即[Player]
), 工作正常。
目前正在尝试在 swiftUI 中构建我的第一个应用程序。我认为最简单的部分变成了一场噩梦......在 AppStorage 中保存一个结构,以便在应用程序重新启动时可用
我有两个结构要保存。第一个是播放器,我已经实现了 RawRepresentable
struct Player: Codable, Identifiable {
let id: Int
let name: String
let gamePlayed: Int
let bestScore: Int
let nbrGameWon: Int
let nbrGameLost: Int
let totalScore: Int?
}
typealias PlayerList = [Player]
extension PlayerList: RawRepresentable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(PlayerList.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
}
}
在我的视图中这样调用:
struct AddPlayerView: View {
@State var name: String = ""
@State var isDisabled: Bool = false
@State var modified: Bool = false
@AppStorage("players") var players: PlayerList = PlayerList()
...
}
上面的工作,现在我也想保存当前的游戏数据,我有以下结构:
struct Game: Codable, Identifiable {
var id: Int
var currentPlayerIndexes: Int
var currentRoundIndex: Int?
var dealerIndex: Int?
var maxRounds: Int?
var dealResults: [Int: Array<PlayerRoundSelection>]?
var currentLeaderIds: Array<Int>?
var isGameInProgress: Bool?
}
extension Game: RawRepresentable {
public init?(rawValue: String) {
if rawValue == "" {
// did to fix issue when calling AppStorage, but it is probably a bad idea
self = Game(id:1, currentPlayerIndexes:1)
}
else {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Game.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
}
}
一旦我尝试修改结构,它就会调用 rawValue
并且编码失败并显示以下内容:
error: warning: couldn't get required object pointer (substituting NULL): Couldn't load 'self' because its value couldn't be evaluated
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=2, address=0x7ffee49bbff8).
这里是访问结构的部分代码:
struct SelectPlayersView: View {
@AppStorage("currentGame") var currentGame: Game = Game(rawValue: "")!
....
NavigationLink(
destination: SelectModeTypeView(), tag: 2, selection: self.$selection) {
ActionButtonView(text:"Next", disabled: self.$isDisabled, buttonAction: {
var currentPlayers = Array<Int>()
self.players.forEach({ player in
if selectedPlayers.contains(player.id) {
currentPlayers.insert(player.id, at: currentPlayers.count)
}
})
// This used to be a list of indexes, but for testing only using a single index
self.currentGame.currentPlayerIndexes = 6
self.selection = 2
})
...
我在这里找到了编码代码:https://lostmoa.com/blog/SaveCustomCodableTypesInAppStorageOrSceneStorage/
我的理解是,在编码中使用 self
,它会生成一个无限循环,因此无法访问。
我真的不知道如何正确编码这个,任何帮助,链接将不胜感激
我遇到了同样的问题,我想在这里分享我的经验。
我最终发现,当与 RawRepresentable 结合使用时,您显然不能依赖默认的 Codable 协议实现。 所以当我用 CodingKeys 和所有的东西做我自己的 Codable 实现时,它成功了!
我认为您的 Game Codable 实现类似于:
enum CodingKeys: CodingKey {
case currentPlayerIndexes
case currentRoundIndex
// <all the other elements too>
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.currentPlayerIndexes = try container.decode(Int.self, forKey: .currentPlayerIndexes)
self.currentRoundIndex = try container.decode(Int.self, forKey: .currentRoundIndex)
// <and so on>
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(currentPlayerIndexes, forKey: .currentPlayerIndexes)
try container.encode(currentRoundIndex, forKey: .currentRoundIndex)
// <and so on>
}
然后我想知道为什么你的Player coding/decoding 没有工作,发现一个数组(即PlayerList,即[Player]
), 工作正常。