0

我有一个项目,它使用 ObjectMapper 进行 JSON 序列化和反序列化,并使用 Realm 在本地缓存数据。我从 Rest API 获取数据并将它们正确保存到领域数据库中,我已经使用 RealmStudio 进行了检查。问题是当我从数据库中检索数据时,对象类型的属性返回 nil!这是我的课,foodFact 是 Object 类型,当从数据库中获取 FoodRecipe 数据时为 nil。有什么想法吗?

 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 为零!我在我的 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 上时,它显示为零!

4

2 回答 2

1

这是在黑暗中拍摄的。实际设置类和填充它们的代码不包含在问题中,所以我所做的是获取您的代码,将其剥离一点并运行几个测试。使用我的代码,数据可以正确写入和读取。

这是精简的课程

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 = ;
    };
}

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

于 2019-05-09T20:40:57.910 回答
0

经过搜索我发现这个问题是在我使用 Mirror 获取 Realm Object 属性时发生的。要解决这个问题,首先应该将 Object 与 Realm 分离。使用下面的代码可以帮助您分离领域对象,其次您可以使用带有分离对象的镜像来获取属性。

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())
    }
}
于 2019-07-06T07:44:55.140 回答