如何为 [[Int?]] 和 [[String?]] 实现核心数据可转换类型
How to implement Core Data Transformable type for [[Int?]] and [[String?]]
对于我的问题,我已经在 GitHub 准备了 a simple SwiftUI project。
后端将以下 JSON 数据发送到我的应用程序,代表一个 15 x 15 字母的游戏:
{
"gid":266,
"letters":[
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,"H", null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,"U", null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,"E", null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]
],
"values":[
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null, 4, null,null,null,null,null,null,null],
[null,null,null,null,null,null,null, 1, null,null,null,null,null,null,null],
[null,null,null,null,null,null,null, 1, null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]
],
"tiles":[
{"col": 8, "row": 7, "value": 1, "letter": "E"},
{"col": 7, "row": 7, "value": 1, "letter": "U"},
{"col": 6, "row": 7, "value": 4, "letter": "H"}
]
}
如您所见,letters
和 values
是 String?
和 Int?
的二维数组,这就是我在 GameModel.swift 用于 JSON 解析:
struct GameModel: Codable, Identifiable {
var id: Int32 { gid }
let gid: Int32
let letters: [[String?]]
let values: [[Int32?]]
let tiles: [TileModel]? // the previous move as an array
// create a new Core Data entity and copy the properties
func toEntity(viewContext: NSManagedObjectContext) -> GameEntity {
let gameEntity = GameEntity(context: viewContext)
gameEntity.gid = self.gid
gameEntity.letters = self.letters
gameEntity.values = self.values
gameEntity.tiles = self.tiles
return gameEntity
}
}
struct TileModel: Codable {
let col: Int
let row: Int
let value: Int
let letter: String
}
我正在尝试使用 Transformable
核心数据类型来解析它们,因此我将这 3 行添加到 Persistence.swift
let container: NSPersistentContainer
init(inMemory: Bool = false) {
ValueTransformer.setValueTransformer(ValuesToDataTransformer(), forName: .valuesToDataTransformer)
ValueTransformer.setValueTransformer(LettersToDataTransformer(), forName: .lettersToDataTransformer)
ValueTransformer.setValueTransformer(TilesToDataTransformer(), forName: .tilesToDataTransformer)
container = NSPersistentContainer(name: "TransApp")
我还添加了 3 个文件:
我的问题是我的自定义 ValueTransformer
sub 类 无法编译。
错误是:
Static method 'unarchivedObject(ofClass:from:)' requires that '[[Int32?]]' conform to 'NSCoding'
其他 2 人也类似。
这是一种完全不同的方法,可以避免 Transformable
。它仅将 TileModel
数组保存为 JSON 字符串,并使用计算属性创建网格并将自定义类型与 JSON.
相互转换
实际上你根本不需要解码网格。
在实体中将 tiles
声明为 String
@NSManaged public var tiles: String?
添加计算的 属性 以将 TileModel
的数组转换为 JSON
var tileArray: [TileModel]? {
get {
guard let data = tiles?.data(using: .utf8) else { return nil }
return try? JSONDecoder().decode([TileModel].self, from: data)
}
set {
guard let data = try? JSONEncoder().encode(newValue) else { tiles = nil; return }
tiles = String(data: data, encoding: .utf8)
}
}
和创建网格的计算属性,没有理由将它们保存在实体中
var values : [[Int?]] {
var grid : [[Int?]] = [
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil]]
tileArray?.forEach{ grid[[=12=].col][[=12=].row] = [=12=].value }
return grid
}
var letters : [[String?]] {
var grid : [[String?]] = [
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil],
[nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil]]
tileArray?.forEach{ grid[[=12=].col][[=12=].row] = [=12=].letter }
return grid
}
对于我的问题,我已经在 GitHub 准备了 a simple SwiftUI project。
后端将以下 JSON 数据发送到我的应用程序,代表一个 15 x 15 字母的游戏:
{
"gid":266,
"letters":[
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,"H", null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,"U", null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,"E", null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]
],
"values":[
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null, 4, null,null,null,null,null,null,null],
[null,null,null,null,null,null,null, 1, null,null,null,null,null,null,null],
[null,null,null,null,null,null,null, 1, null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]
],
"tiles":[
{"col": 8, "row": 7, "value": 1, "letter": "E"},
{"col": 7, "row": 7, "value": 1, "letter": "U"},
{"col": 6, "row": 7, "value": 4, "letter": "H"}
]
}
如您所见,letters
和 values
是 String?
和 Int?
的二维数组,这就是我在 GameModel.swift 用于 JSON 解析:
struct GameModel: Codable, Identifiable {
var id: Int32 { gid }
let gid: Int32
let letters: [[String?]]
let values: [[Int32?]]
let tiles: [TileModel]? // the previous move as an array
// create a new Core Data entity and copy the properties
func toEntity(viewContext: NSManagedObjectContext) -> GameEntity {
let gameEntity = GameEntity(context: viewContext)
gameEntity.gid = self.gid
gameEntity.letters = self.letters
gameEntity.values = self.values
gameEntity.tiles = self.tiles
return gameEntity
}
}
struct TileModel: Codable {
let col: Int
let row: Int
let value: Int
let letter: String
}
我正在尝试使用 Transformable
核心数据类型来解析它们,因此我将这 3 行添加到 Persistence.swift
let container: NSPersistentContainer
init(inMemory: Bool = false) {
ValueTransformer.setValueTransformer(ValuesToDataTransformer(), forName: .valuesToDataTransformer)
ValueTransformer.setValueTransformer(LettersToDataTransformer(), forName: .lettersToDataTransformer)
ValueTransformer.setValueTransformer(TilesToDataTransformer(), forName: .tilesToDataTransformer)
container = NSPersistentContainer(name: "TransApp")
我还添加了 3 个文件:
我的问题是我的自定义 ValueTransformer
sub 类 无法编译。
错误是:
Static method 'unarchivedObject(ofClass:from:)' requires that '[[Int32?]]' conform to 'NSCoding'
其他 2 人也类似。
这是一种完全不同的方法,可以避免 Transformable
。它仅将 TileModel
数组保存为 JSON 字符串,并使用计算属性创建网格并将自定义类型与 JSON.
实际上你根本不需要解码网格。
在实体中将
tiles
声明为String
@NSManaged public var tiles: String?
添加计算的 属性 以将
TileModel
的数组转换为 JSONvar tileArray: [TileModel]? { get { guard let data = tiles?.data(using: .utf8) else { return nil } return try? JSONDecoder().decode([TileModel].self, from: data) } set { guard let data = try? JSONEncoder().encode(newValue) else { tiles = nil; return } tiles = String(data: data, encoding: .utf8) } }
和创建网格的计算属性,没有理由将它们保存在实体中
var values : [[Int?]] { var grid : [[Int?]] = [ [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil]] tileArray?.forEach{ grid[[=12=].col][[=12=].row] = [=12=].value } return grid } var letters : [[String?]] { var grid : [[String?]] = [ [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil], [nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil]] tileArray?.forEach{ grid[[=12=].col][[=12=].row] = [=12=].letter } return grid }