简体   繁体   English

如何在 Core Data 中存储具有通用属性的 Swift object?

[英]How do you store Swift object with generic properties in Core Data?

I have an app that needs to store data relating to SwiftUI control configuration for parameters (eg Slider upper and lower bounds, step, etc).我有一个应用程序需要存储与参数的 SwiftUI 控制配置相关的数据(例如 Slider 上限和下限、步骤等)。 The parameter type can be either Int or Double and so it would make sense to encapsulate this information in a struct like this:参数类型可以是 Int 或 Double ,因此将这些信息封装在这样的结构中是有意义的:

struct ParameterProfile<T: Comparable & SignedNumeric> {
var parameterName: String
var bounds: ClosedRange<T>
var units: String
var controlStep: T
}

I am wanting to make these profiles customisable and, as the user may have the app on multiple devices, I'd like to store this in Core Data in order to allow synchronisation via CloudKit.我想让这些配置文件可定制,并且由于用户可能在多个设备上拥有该应用程序,我想将其存储在 Core Data 中以便允许通过 CloudKit 进行同步。

The options at this point seem to be converting the above to a NSManagedObject or using an NSManagedObject to provide the data to instantiate the struct.此时的选项似乎是将上述转换为 NSManagedObject 或使用 NSManagedObject 提供数据以实例化结构。

In the first instance, using an NSManagedObject and serialising the entire class before storing in Core Data seems to be the most pragmatic way forward using Codable so the resulting object would look like this:首先,使用 NSManagedObject 并在存储到 Core Data 之前序列化整个 class 似乎是使用 Codable 的最实用的方法,因此生成的 object 看起来像这样:

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!
    }
}

However, generic types cannot be represented in Core Data even though I have added a type constraint to indicate that the generic type (whatever it is) must be Codable.然而,即使我添加了一个类型约束来指示泛型类型(无论它是什么)必须是可编码的,但泛型类型不能在 Core Data 中表示。

As things stand, it looks like I'll have to create an NSManagedObject that effectively wraps a struct that has optional properties to represent the type of valid range, something like:就目前情况而言,看起来我必须创建一个 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")
        }
    }
}

} }

The above approach is rough code for illustration only but it gives you an idea - basically it's similar to the way one would store an enumeration in Core Data.上面的方法只是用于说明的粗略代码,但它给了你一个想法——基本上它类似于在 Core Data 中存储枚举的方式。 It seems a lot of code to store relatively simple data and I'll have to handle the type for the SwiftUI control separately.存储相对简单的数据似乎有很多代码,我必须单独处理 SwiftUI 控件的类型。 I suspect this is necessary due to the limitations of ObjC, but is this the best way of handling the issue or are there other strategies由于 ObjC 的限制,我怀疑这是必要的,但这是处理问题的最佳方法还是有其他策略

Using generics and Core Data together is not easy so I think that in this case where you have only two parameter type, Int and Double, that the easiest solution is to create an NSManagedObject subclass for each.将 generics 和 Core Data 一起使用并不容易,所以我认为在这种情况下,您只有两个参数类型,Int 和 Double,最简单的解决方案是为每个参数创建一个 NSManagedObject 子类。 It's not the most optimal solution but it is quite straightforward and easy to work with.这不是最佳解决方案,但它非常简单且易于使用。 You will of course need to duplicate your fetch request etc but on the other hand you won't need to do any complicated casting to the correct generic type.您当然需要复制您的获取请求等,但另一方面,您不需要对正确的泛型类型进行任何复杂的转换。

Here is how the Double variant could look这是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)
    }
}

and the same for IntInt一样

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)
    }
} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM