Как исправить значение nil из свойства объекта realm? - PullRequest
0 голосов
/ 08 мая 2019

У меня есть проект, который использует ObjectMapper для сериализации и десериализации JSON и Realm для локального кэширования данных.Я получаю данные из Rest API и правильно сохраняю их в базе данных областей, которые я проверил с помощью RealmStudio.Проблема заключается в том, что когда я получаю данные из базы данных, свойство типа объекта возвращает ноль!Это мой класс, и foodFact - это тип объекта, который равен нулю при получении данных FoodRecipe из базы данных. Есть идеи?

 import RealmSwift
 import ObjectMapper


 typealias Syncable = Object & Mappable


class FoodFact: Syncable {
  @objc dynamic var objectId :String = ""
  @objc dynamic var calcium: String = ""
  @objc dynamic var iron: String = ""
  @objc dynamic var fiber: String = ""
  @objc dynamic var fattyTransAcide: String = ""


 override static func primaryKey() -> String? {
    return "objectId"
 }

 required convenience init?(map: Map) { self.init() }


 func mapping(map: Map) {
    self.objectId <- map["objectId"]
    self.calcium <- map["calcium"]
    self.iron <- map["iron"]
    self.fiber <- map["fiber"]
    self.fattyTransAcide <- map["fattyTransAcide"]
  }
}


class FoodRecipe: Syncable {
  @objc dynamic var objectId :String = ""
  @objc dynamic var title :String = ""
  var ingredient = List<FoodRecipeIngredient>()
  @objc dynamic var foodFact :FoodFact? //returns nil when retrieving    data


override static func primaryKey() -> String? {
    return "objectId"
}

required convenience init?(map: Map) { self.init() }

func mapping(map: Map) {
    objectId <- map["objectId"]
    title <- map["title"]
    ingredient <- (map["ingredients"],ArrayTransform<FoodRecipeIngredient>())
    foodFact <- map["foodFact"]
   }
 }

Это код, который я использую для сохранения и извлечения данных:

import Foundation
import RealmSwift

protocol BaseDao {
  associatedtype T: Object

  func save(list:[T])
  func save(object:T)
  func get() -> [T]?
  func delete(list:[T])
  func deleteAll()
}

extension BaseDao {
  func deleteAll(){
    PersistenceManager().deleteDatabase()
  }
}


class PersistenceManager {

  let realm = try! Realm()
  var notificationToken = NotificationToken()


  func deleteObjects(objs:[Object]) {
    do {
        try realm.write({
            realm.delete(objs)
        })
    } catch let error {
        assertionFailure(error.localizedDescription)
    }
}

func saveObjects(objs: [Object]) {
    print("path for realm \(String(describing: realm.configuration.fileURL))")
    do {
        try realm.write({
            realm.add(objs, update: true)
        })
    } catch let error {
        assertionFailure(error.localizedDescription)
    }

}

func saveObject(obj: Object) {
    print("path for realm \(String(describing: realm.configuration.fileURL))")
    do {
        try realm.write({
            realm.add(obj, update: true)
        })
    } catch let error {
        assertionFailure(error.localizedDescription)
    }

}

func getObjects(type: Object.Type) -> Results<Object>? {
    return realm.objects(type)
}

func deleteDatabase() {
    do {
        try realm.write({
        realm.deleteAll()
        })
    } catch let error {
        assertionFailure(error.localizedDescription)
    }
  }
 }


class FoodRecipeLocalPersistence :BaseDao {
typealias T = FoodRecipe

let persistenceManager :PersistenceManager

init(persistenceManager :PersistenceManager = PersistenceManager()) {
    self.persistenceManager = persistenceManager
}

func save(list: [T]) {
    self.persistenceManager.saveObjects(objs: list)
}

func save(object: T) {
    self.persistenceManager.saveObject(obj: object)
}

func get() -> [T]? {
    guard let data = persistenceManager.getObjects(type: T.self) else {
        return nil
    }
    return Array(data) as? [T]
}

  func delete(list: [T]) {
    self.persistenceManager.deleteObjects(objs: list)
  }
}

Обновление 1: После дальнейшего просмотра я понял, что могу получить FoodRecipe со всеми свойствами в FoodRecipePersistence, но в моемViewModel FoodFact - это ноль!Я использую RxSwift в моей ViewModel ниже фрагмент кода:

   Observable.combineLatest(selectedCategory, currentCategory, resultSelector: { (selectedCategory, currentCategory) in
        switch currentCategory {
        case .Food:
            let category = selectedCategory as? FoodRecipeCategory

            return foodRecipeLocalPersistence.get()?.filter{ $0.categoryId == category?.objectId }.map{$0} ?? []
        case .Sport:
            let category = selectedCategory as? ExerciseInstructionCategory
            return exerciseInstructionLocalPersistence.get()?.filter{ $0.categoryId == category?.objectId }.map{$0} ?? []
        }
    }).asObservable().bind(to: self.shownList).disposed(by: disposeBag)

обновление 2: Когда я использую

po foodRecipe.foodFact

foodFactвыводится на консоль со всеми данными и свойствами, но когда я помещаю мышь в foodRecipe.foodFact внутри редактора, он показывает nil !!!

Ответы [ 2 ]

0 голосов
/ 06 июля 2019

После поиска я обнаружил, что эта проблема возникла, когда я использовал Mirror, чтобы получить свойства Realm Object. Для решения проблемы прежде всего Объект должен быть отделен от Царства. Использование приведенного ниже кода поможет вам отделить объекты Realm, а во-вторых, вы можете использовать Mirror с отделенным объектом для получения свойств.

protocol DetachableObject: AnyObject {
    func detached() -> Self
}

extension Object: DetachableObject {

    func detached() -> Self {
        let detached = type(of: self).init()
        for property in objectSchema.properties {
            guard let value = value(forKey: property.name) else { continue }

            if property.isArray == true {
                //Realm List property support
                let detachable = value as? DetachableObject
                detached.setValue(detachable?.detached(), forKey: property.name)
            } else if property.type == .object {
                //Realm Object property support
                let detachable = value as? DetachableObject
                detached.setValue(detachable?.detached(), forKey: property.name)
            } else {
                detached.setValue(value, forKey: property.name)
            }
        }
        return detached
    }
}

extension List: DetachableObject {
    func detached() -> List<Element> {
        let result = List<Element>()

        forEach {
            if let detachable = $0 as? DetachableObject {
                let detached = detachable.detached() as! Element
                result.append(detached)
            } else {
                result.append($0) //Primtives are pass by value; don't need to recreate
            }
        }

        return result
    }

    func toArray() -> [Element] {
        return Array(self.detached())
    }
}

extension Results {
    func toArray() -> [Element] {
        let result = List<Element>()

        forEach {
            result.append($0)
        }

        return Array(result.detached())
    }
}
0 голосов
/ 09 мая 2019

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

Вот урезанные классы

class FoodFact: Object {
    @objc dynamic var objectId :String = ""
    @objc dynamic var calcium: String = ""
    @objc dynamic var iron: String = ""
    @objc dynamic var fiber: String = ""
    @objc dynamic var fattyTransAcide: String = ""

    override static func primaryKey() -> String? {
        return "objectId"
    }
}

class FoodRecipe: Object {
    @objc dynamic var objectId :String = ""
    @objc dynamic var title :String = ""
    @objc dynamic var foodFact :FoodFact? //returns nil when retrieving    data

    override static func primaryKey() -> String? {
        return "objectId"
    }
}

class PersistenceManager {
    let realm = try! Realm()
    var notificationToken = NotificationToken()

    func deleteObjects(objs:[Object]) {
        do {
            try realm.write({
                realm.delete(objs)
            })
        } catch let error {
            assertionFailure(error.localizedDescription)
        }
    }

    func saveObject(obj: Object) {
        print("path for realm \(String(describing: realm.configuration.fileURL))")
        do {
            try realm.write({
                realm.add(obj, update: true)
            })
        } catch let error {
            assertionFailure(error.localizedDescription)
        }
    }

    func getObjects(type: Object.Type) -> Results<Object>? {
        return realm.objects(type)
    }
}

У меня есть две кнопки в пользовательском интерфейсе, одна из которых создает данные и сохраняет ихв Realm и другом, который читает его обратно.

Создание данных

func doButtonAction1() {
    let ff0 = FoodFact()
    ff0.calcium = "lots of calcium"

    let fr0 = FoodRecipe()
    fr0.title = "Milk"
    fr0.foodFact = ff0

    let pm = PersistenceManager()
    pm.saveObject(obj: fr0)
}

и последующее чтение обратно

func doButtonAction2() {
    let pm = PersistenceManager()
    let recipeResults = pm.getObjects(type: FoodRecipe.self)

    if let allRecipies = recipeResults {
        for recipe in allRecipies {
            print(recipe)
        }
    }
}

при нажатии кнопки 1 путь печатается вконсоль и затем нажимая кнопку 2. Я вижу это, которое показывает, что данные были сохранены и правильно читают.

FoodRecipe {
    objectId = ;
    title = Milk;
    foodFact = FoodFact {
        objectId = ;
        calcium = lots of calcium;
        iron = ;
        fiber = ;
        fattyTransAcide = ;
    };
}

Итак, мы знаем, что основной код для чтения и записи данных работает так, что бы оставитьпроблема вызвана либо кодом, который я вычеркнул (которого было немного), либо чем-то другим полностью.Надеемся, что это даст направление для дальнейшего устранения неполадок.

...