我有一个项目,该项目使用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 !!
答案 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())
}
}