领域:部分更新

时间:2016-01-07 17:11:04

标签: swift realm

我在我的Swift项目中使用了realm,并且我遇到了部分更新对象的问题。

问题是我有一个对象,其中包含来自服务器加上用户生成的信息的信息。在我的情况下,它是主题,默认情况下可以是可见的或隐藏的,但用户可以更改可见性值。

当我第一次启动我的应用时,我调用我的服务器API来获取信息以创建主题对象:它的visibility值为undefined。然后,用户做出选择并将visibility值设置为visible

我第二次启动应用程序时,我再次从服务器获取信息,然后重新创建主题。 然后我调用Realm方法add:update:来更新对象,但这会再次将visibility属性更新为undefined

我知道还有另一种方法create:value:update:,但这意味着我必须创建一个包含我想要更新的所有值的大字典。我的模型对象不是那么小,在某些情况下我有很多属性,字典会很大。 我不喜欢这种方法,维护起来很复杂。

你对如何处理这样的案件有任何暗示吗?

一种可能的方法是创建另一个与主题有关系的对象(表)和一个在我再次创建主题时未被覆盖的属性visibility,但它听起来奇怪的是为这件事创造一张桌子。

2 个答案:

答案 0 :(得分:1)

我的服务端点的运行方式也遇到了类似的问题。它提供了一个完整的实体视图,以及该实体上属性的较小视图,我希望在它们被命中时更新模型。 例如:

class User: Object {
  var id: Int
  var name: String
  var age: Int
  var average: Double
  likes: List<User>
}

一个端点获取所有4个字段,另一个端点获取averageid,仅获得第三个likesid,但是我想要从任何一个创建/更新模型。 现在,我可以在返回的原始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的新对象实例相同的问题。