我在我的Swift项目中使用了realm,并且我遇到了部分更新对象的问题。
问题是我有一个对象,其中包含来自服务器加上用户生成的信息的信息。在我的情况下,它是主题,默认情况下可以是可见的或隐藏的,但用户可以更改可见性值。
当我第一次启动我的应用时,我调用我的服务器API来获取信息以创建主题对象:它的visibility
值为undefined
。然后,用户做出选择并将visibility
值设置为visible
。
我第二次启动应用程序时,我再次从服务器获取信息,然后重新创建主题。
然后我调用Realm方法add:update:
来更新对象,但这会再次将visibility
属性更新为undefined
。
我知道还有另一种方法create:value:update:
,但这意味着我必须创建一个包含我想要更新的所有值的大字典。我的模型对象不是那么小,在某些情况下我有很多属性,字典会很大。
我不喜欢这种方法,维护起来很复杂。
你对如何处理这样的案件有任何暗示吗?
一种可能的方法是创建另一个与主题有关系的对象(表)和一个在我再次创建主题时未被覆盖的属性visibility
,但它听起来奇怪的是为这件事创造一张桌子。
答案 0 :(得分:1)
我的服务端点的运行方式也遇到了类似的问题。它提供了一个完整的实体视图,以及该实体上属性的较小视图,我希望在它们被命中时更新模型。 例如:
class User: Object {
var id: Int
var name: String
var age: Int
var average: Double
likes: List<User>
}
一个端点获取所有4个字段,另一个端点获取average
和id
,仅获得第三个likes
和id
,但是我想要从任何一个创建/更新模型。
现在,我可以在返回的原始JSON上使用create(value:update:)
,但是我的模型会映射不同的键名并执行其他转换的事情(还有Dates和其他事情),我想保留它们,因此out只能调用使用JSON词典create(value:update:)
。
我们通过利用Reflection和一个名为Serializable
的协议解决了这个问题,看起来像这样:
protocol Serializable {
func toDictionary() -> [String: Any]
}
extension Serializable {
func toDictionary() -> [String: Any] {
var propertiesDictionary: [String: Any] = [:]
let mirror = Mirror(reflecting: self)
for (propName, propValue) in mirror.children {
guard let propName = propName else { continue }
// Attempt to unwrap the value as AnyObject
if let propValue: AnyObject = self.unwrap(propValue) as AnyObject? {
switch propValue {
case let serializablePropValue as Serializable:
propertiesDictionary[propName] = serializablePropValue.toDictionary()
case let arrayPropValue as [Serializable]:
propertiesDictionary[propName] = Array(arrayPropValue.flatMap { $0.toDictionary() })
case let data as Data:
propertiesDictionary[propName] = data.base64EncodedString(options: .lineLength64Characters)
case _ as Bool: fallthrough
case _ as Int: fallthrough
case _ as Double: fallthrough
case _ as Float: fallthrough
default:
propertiesDictionary[propName] = propValue
}
} else {
// Couldn't treat as AnyObject, treat as Any
switch propValue {
case let arrayPropValue as [Serializable]:
propertiesDictionary[propName] = arrayPropValue.flatMap { $0.toDictionary() }
case let primative as Int8: propertiesDictionary[propName] = primative
case let primative as Int16: propertiesDictionary[propName] = primative
case let primative as Int32: propertiesDictionary[propName] = primative
case let primative as Int64: propertiesDictionary[propName] = primative
case let primative as UInt8: propertiesDictionary[propName] = primative
case let primative as UInt16: propertiesDictionary[propName] = primative
case let primative as UInt32: propertiesDictionary[propName] = primative
case let primative as UInt64: propertiesDictionary[propName] = primative
case let primative as Float: propertiesDictionary[propName] = primative
case let primative as Double: propertiesDictionary[propName] = primative
case let primative as Bool: propertiesDictionary[propName] = primative
case let primative as String: propertiesDictionary[propName] = primative
case let primative as Date: propertiesDictionary[propName] = primative
case let primative as Data: propertiesDictionary[propName] = primative
default: break
}
}
}
return propertiesDictionary
}
/// Unwraps 'any' object.
/// See http://stackoverflow.com/questions/27989094/how-to-unwrap-an-optional-value-from-any-type
/// - parameter any: Any, Pretty clear what this is....
/// - returns: The unwrapped object.
private func unwrap(_ any: Any) -> Any? {
let mi = Mirror(reflecting: any)
guard let displayStyle = mi.displayStyle else { return any }
switch displayStyle {
case .optional:
if mi.children.count == 0 {
return nil
}
if let (_, some) = mi.children.first {
return some
} else {
return nil
}
case .enum:
let implicitTypes: [Any.Type] = [ImplicitlyUnwrappedOptional<Int>.self,
ImplicitlyUnwrappedOptional<String>.self,
ImplicitlyUnwrappedOptional<Double>.self,
ImplicitlyUnwrappedOptional<Bool>.self,
ImplicitlyUnwrappedOptional<Float>.self,
ImplicitlyUnwrappedOptional<Date>.self]
if implicitTypes.contains(where: { $0 == mi.subjectType }) {
if mi.children.count == 0 { return nil }
if let (_, some) = mi.children.first {
return some
} else {
return nil
}
}
return any
default: return any
}
}
使用此功能,您可以序列化部分填充的对象,然后使用create(value: obj.toDictionary(), update: true)
值得注意的是:此处未处理RealmOptional<T>
,List<T>
和LinkingObjects<T>
,您可以为RealmOptional<T>
的显式基元类型添加案例, LinkingObjectsBase
,以及ListBase
自定义处理的跳过案例(反映/不与列表很好地匹配:/)
答案 1 :(得分:0)
如果您将字典传递给create:value:update:
,这可能会更容易解决,因为您可以简单地省略visibility
属性,但正如您所说的大小限制,您正在使用新的实例由visibility
已经通过服务器响应设置为不同值的模型对象,然后这实际上没有帮助。 Realm无法告诉您希望保留此特定属性并更改其余属性。
如果您使用的是主键,那么最快的解决方法就是事先通过主键获取原始对象,记下其visibility
属性,然后重新设置它在您执行更新后。
将visibility
属性分解为自己的对象会起作用,但是您很可能遇到与将该属性设置为nil
的新对象实例相同的问题。