如何从领域对象属性中修复零值?

时间:2019-05-08 20:31:05

标签: realm rx-swift xcode10 objectmapper combinelatest

我有一个项目,该项目使用ObjectMapper进行JSON序列化和反序列化以及Realm在本地缓存数据。我已经从Rest API中获取数据,并将其正确保存到RealmStudio中检查过的领域数据库中。问题是当我从数据库检索数据时,对象类型的属性返回nil!这是我的课程,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: 经过进一步的审查,我意识到我可以在FoodRecipePersistence中检索具有所有属性的FoodRecipe,但是在我的ViewModel中,FoodFact为零! 我在我的ViewModel中使用RxSwift,下面是代码片段:

   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 :(得分:0)

这是在黑暗中拍摄的照片。问题中没有包含实际设置类并填充它们的代码,因此我所做的就是拿走您的代码,将其剥离下来并进行了一些测试。使用我的代码,可以正确写入和读取数据。

这是精简的课程

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

我在UI中有两个按钮,一个按钮创建数据并将其存储在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 = ;
    };
}

因此,我们知道读取和写入数据的核心代码正在工作,这样就可以使问题完全由我剥离的代码(不是很多)或其他原因引起。希望这将为进一步的故障排除提供一些指导。

答案 1 :(得分:0)

搜索后,我发现使用Mirror获取领域对象属性时发生了此问题。要解决此问题,首先应将Object与Realm分离。使用下面的代码可以帮助您分离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())
    }
}