2016-01-07 2 views
0

Я использую realm в моем проекте Swift, и у меня возникают проблемы, частично обновляющие объекты.Realm: Частичное обновление

Проблема заключается в том, что у меня есть объект, содержащий информацию с сервера плюс генерируемые пользователем информацию. В моем случае это Тема, которая по умолчанию может быть видимой или скрытой, но пользователь может изменить значение видимости.

Когда я запускаю мое приложение в первый раз, когда я называю мой сервер API для извлечения информации для создания Темы объекта: он имеет как visibility значения undefined. Затем пользователь делает выбор и устанавливает значение visibility на visible.

Во второй раз, когда я запускаю приложение, я снова получаю информацию с сервера, и я воссоздаю Тема. Затем я вызываю метод Realm add:update: для обновления объекта, но это еще раз обновляет свойство visibility до undefined.

Я знаю, что существует другой метод create:value:update:, но это означает, что мне нужно создать большой словарь со всеми значениями, которые я хочу обновить. Мои объекты модели не такие маленькие, в некоторых случаях у меня много свойств, и словарь будет огромным. Мне не нравится этот подход, его сложно поддерживать.

У вас есть какой-либо намек на то, как обращаться с таким случаем?

possibile способом было бы создать еще один объект (таблицу), которая имеет отношение к Тема и одно свойство visibility, не переопределен, когда я снова создать тему, но это звучит странно, чтобы создать таблицу только для этого вещь.

+0

Создает ли ваш объект 'Тема' свойство первичного ключа? Если нет, то каждый раз, когда вы вызываете 'add: update:', вы фактически создаете новую копию объекта в Realm. – TiM

+0

О, извините, я забыл упомянуть, что это так. – Wolf

ответ

0

Это, вероятно, было бы легче решить, если вы проходили словарь в create:value:update:, так как вы можете просто опустить visibility собственности, но, как вы сказали, для ограничения размера, вы используете новый экземпляр модели объекта, который имеет visibility уже заданное другим ответом на ответ сервера, тогда это не может помочь. Царство не может сказать, что вы хотите сохранить это особое свойство и изменить остальное.

Если вы используете первичный ключ, то самым быстрым решением для этого было бы просто предварительно загрузить исходный объект через свой первичный ключ, занести в его свойство visibility, а затем повторно установить его после того, вы выполнили обновление.

Прерывание объекта visibility в его собственном объекте будет работать, но вы, скорее всего, столкнулись бы с той же проблемой, когда новый экземпляр объекта установил это свойство в nil.

+0

Да, ты прав. Я закончил создание другого модельного объекта для хранения этой информации и предварительной загрузки. Он работает, я посмотрю, лучше ли это или лучше использовать свойство и получить его. – Wolf

1

У меня возникла аналогичная проблема с тем, как работает наша конечная точка обслуживания. Он обеспечивает полное представление сущности наряду с меньшими представлениями свойств этого объекта, которые я хотел бы обновить модель, когда они попали. Пример:

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

Одна конечная точка получает все 4 поля, другая конечная точка просто получает average и id, а третий только likes и id, однако я хотел бы создать/обновить модель из любого из них. Теперь я мог использовать create(value:update:) на необработанном JSON, который был возвращен, однако моя модель отображает различные ключевые слова и выполняет другие преобразования (с датами и другими вещами), которые я хочу сохранить, поэтому можно просто позвонить create(value:update:) с помощью JSON dict.

Мы получили вокруг этого используя 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 (отражение/не играет/играет красиво со списками: /)

+0

Разве это не так, что каждый тип может быть ImlicitlyUnwrappedOptonal? Тогда использование этого массива недостаточно. Может быть, вы можете изменить if:: String (reflecting: type (of: any)). HasPrefix ("Swift.ImplicitlyUnwrappedOptional <") –

+0

Вы также можете использовать https://github.com/evermeer/EVReflection и расширить объект с помощью EVRизбираемый протокол. Тогда у вас будет .toDictionary(), который имеет большую поддержку массивов, дженериков, вложенных объектов и наследования. –

+0

@ EdwinVermeer, да, возможно, что это возможно, обработка IUO - это хак-стоп-стоп-исправление из-за разрыва рефлекса Swift 3 на IUO Кроме того, мы решили перевернуть наш собственный, чтобы мы могли контролировать, что происходит с RealmLists, например, поскольку обработка их с помощью дженериков/протоколов не так хорошо работает. Я проверю этот протокол, чтобы убедиться, что это может быть лучше. – apocolipse

Смежные вопросы