如何在 Core Data 中存储具有通用属性的 Swift 对象?
How do you store Swift object with generic properties in Core Data?
我有一个应用程序需要存储与参数的 SwiftUI 控件配置相关的数据(例如滑块上下限、步长等)。参数类型可以是 Int 或 Double,因此将此信息封装在这样的结构中是有意义的:
struct ParameterProfile<T: Comparable & SignedNumeric> {
var parameterName: String
var bounds: ClosedRange<T>
var units: String
var controlStep: T
}
我想让这些配置文件可自定义,并且由于用户可能在多个设备上都有该应用程序,所以我想将其存储在核心数据中,以便通过 CloudKit 进行同步。
此时的选项似乎是将上面的转换为 NSManagedObject 或使用 NSManagedObject 提供数据以实例化结构。
首先,使用 NSManagedObject 并在存储到 Core Data 之前序列化整个 class 似乎是使用 Codable 最实用的方法,因此生成的对象将如下所示:
Class ParameterProfile<T: Comparable & SignedNumeric & Codable>: Codable, NSManagedObject {
var name: String
var bounds: ClosedRange<T>
var units: String
var controlStep: T
enum CodingKeys: String, CodingKey {
// haven’t figured out this bit yet!
}
}
然而,即使我添加了一个类型约束来指示泛型类型(无论它是什么)必须是 Codable,泛型类型也不能在 Core Data 中表示。
就目前情况而言,看起来我必须创建一个 NSManagedObject 来有效地包装一个结构,该结构具有可选属性来表示有效范围的类型,例如:
class ProfileWrapper: NSManagedObject {
@NSManaged var name: String
// ...
@NSManaged var type: String
@NSManaged var lowerBound: Double
@NSManaged var upperBound: Double
var materialiseProfile: ParameterProfile {
ParameterProfile(name: name, type: type, lowerBound: lowerBound, upperBound: upperBound)
}
struct ParameterProfile {
var name: String
var intRange: ClosedRange<Int>?
var doubleRange: ClosedRange<Double>?
init(name: String, type: String, lowerBound: Double, upperBound: Double) {
self.name = name
switch type {
case "int":
intRange = Int(lowerBound)...Int(upperBound)
case "double":
doubleRange = lowerBound...upperBound
default:
fatalError("Undefined type")
}
}
}
}
上面的方法只是用于说明的粗略代码,但它给了你一个想法——基本上它类似于在 Core Data 中存储枚举的方式。存储相对简单的数据似乎有很多代码,我将不得不单独处理 SwiftUI 控件的类型。由于 ObjC 的限制,我怀疑这是必要的,但这是处理问题的最佳方式还是有其他策略
一起使用泛型和 Core Data 并不容易,所以我认为在这种情况下,您只有两个参数类型,Int 和 Double,最简单的解决方案是为每个创建一个 NSManagedObject 子类。它不是最佳解决方案,但它非常简单易用。您当然需要复制您的获取请求等,但另一方面,您不需要对正确的通用类型进行任何复杂的转换。
下面是 Double
变体的样子
class CDParameterProfileDouble: NSManagedObject {
@NSManaged var parameterName: String
@NSManaged var lowerBound: Double
@NSManaged var upperBound: Double
@NSManaged var units: String
@NSManaged var controlStep: Double
func toDomain() -> ParameterProfile<Double> {
ParameterProfile(parameterName: self.parameterName,
bounds: self.lowerBound...self.upperBound,
units: self.units,
controlStep: self.controlStep)
}
}
Int
也一样
class CDParameterProfileInt: NSManagedObject {
@NSManaged var parameterName: String
@NSManaged var lowerBound: Int
@NSManaged var upperBound: Int
@NSManaged var units: String
@NSManaged var controlStep: Int
func toDomain() -> ParameterProfile<Int> {
ParameterProfile(parameterName: self.parameterName,
bounds: self.lowerBound...self.upperBound,
units: self.units,
controlStep: self.controlStep)
}
}
我有一个应用程序需要存储与参数的 SwiftUI 控件配置相关的数据(例如滑块上下限、步长等)。参数类型可以是 Int 或 Double,因此将此信息封装在这样的结构中是有意义的:
struct ParameterProfile<T: Comparable & SignedNumeric> {
var parameterName: String
var bounds: ClosedRange<T>
var units: String
var controlStep: T
}
我想让这些配置文件可自定义,并且由于用户可能在多个设备上都有该应用程序,所以我想将其存储在核心数据中,以便通过 CloudKit 进行同步。
此时的选项似乎是将上面的转换为 NSManagedObject 或使用 NSManagedObject 提供数据以实例化结构。
首先,使用 NSManagedObject 并在存储到 Core Data 之前序列化整个 class 似乎是使用 Codable 最实用的方法,因此生成的对象将如下所示:
Class ParameterProfile<T: Comparable & SignedNumeric & Codable>: Codable, NSManagedObject {
var name: String
var bounds: ClosedRange<T>
var units: String
var controlStep: T
enum CodingKeys: String, CodingKey {
// haven’t figured out this bit yet!
}
}
然而,即使我添加了一个类型约束来指示泛型类型(无论它是什么)必须是 Codable,泛型类型也不能在 Core Data 中表示。
就目前情况而言,看起来我必须创建一个 NSManagedObject 来有效地包装一个结构,该结构具有可选属性来表示有效范围的类型,例如:
class ProfileWrapper: NSManagedObject {
@NSManaged var name: String
// ...
@NSManaged var type: String
@NSManaged var lowerBound: Double
@NSManaged var upperBound: Double
var materialiseProfile: ParameterProfile {
ParameterProfile(name: name, type: type, lowerBound: lowerBound, upperBound: upperBound)
}
struct ParameterProfile {
var name: String
var intRange: ClosedRange<Int>?
var doubleRange: ClosedRange<Double>?
init(name: String, type: String, lowerBound: Double, upperBound: Double) {
self.name = name
switch type {
case "int":
intRange = Int(lowerBound)...Int(upperBound)
case "double":
doubleRange = lowerBound...upperBound
default:
fatalError("Undefined type")
}
}
}
}
上面的方法只是用于说明的粗略代码,但它给了你一个想法——基本上它类似于在 Core Data 中存储枚举的方式。存储相对简单的数据似乎有很多代码,我将不得不单独处理 SwiftUI 控件的类型。由于 ObjC 的限制,我怀疑这是必要的,但这是处理问题的最佳方式还是有其他策略
一起使用泛型和 Core Data 并不容易,所以我认为在这种情况下,您只有两个参数类型,Int 和 Double,最简单的解决方案是为每个创建一个 NSManagedObject 子类。它不是最佳解决方案,但它非常简单易用。您当然需要复制您的获取请求等,但另一方面,您不需要对正确的通用类型进行任何复杂的转换。
下面是 Double
变体的样子
class CDParameterProfileDouble: NSManagedObject {
@NSManaged var parameterName: String
@NSManaged var lowerBound: Double
@NSManaged var upperBound: Double
@NSManaged var units: String
@NSManaged var controlStep: Double
func toDomain() -> ParameterProfile<Double> {
ParameterProfile(parameterName: self.parameterName,
bounds: self.lowerBound...self.upperBound,
units: self.units,
controlStep: self.controlStep)
}
}
Int
class CDParameterProfileInt: NSManagedObject {
@NSManaged var parameterName: String
@NSManaged var lowerBound: Int
@NSManaged var upperBound: Int
@NSManaged var units: String
@NSManaged var controlStep: Int
func toDomain() -> ParameterProfile<Int> {
ParameterProfile(parameterName: self.parameterName,
bounds: self.lowerBound...self.upperBound,
units: self.units,
controlStep: self.controlStep)
}
}