Swift 领域:写入数据库的对象不正确

Swift realm : incorrect object written to db

这是来自 Realm 的 class 到 add/get 数据:

import Foundation
import RealmSwift

class WeatherDataOneDay {
    var temperature: Int = 0
    var temperatureDescription: String = ""
    var condition: Int = 0
    var city: String = ""
    var weatherIconName: String = ""
    var sunset: Double = 0
    var sunrise: Double = 0
    var humidity: Int = 0
    var windSpeed: Float = 0.0
    var date: Double = 0
    var feelsLike: Int = 0
    
    var clouds: Int = 0
    var lon: Float = 0.0
    var lat: Float = 0.0
    
    func printSelf()
    {
        print("temperature : \(temperature)")
        print("temperatureDescription : \(temperatureDescription)")
        print("condition : \(condition)")
        print("city : \(city)")
        print("weatherIconName : \(weatherIconName)")
        print("sunset : \(sunset)")
        print("sunrise : \(sunrise)")
        print("humidity : \(humidity)")
        print("windSpeed : \(windSpeed)")
        print("date : \(date)")
        print("feelsLike : \(feelsLike)")
        print("clouds : \(clouds)")
        print("lon : \(lon)")
        print("lat : \(lat)")
    }
}

@objcMembers class WeatherDataOneDayCached: Object {
    dynamic var poi: String?
    
    dynamic var temperature: Int?
    dynamic var temperatureDescription: String?
    dynamic var condition: Int?
    dynamic var city: String?
    dynamic var weatherIconName: String?
    dynamic var sunset: Double?
    dynamic var sunrise: Double?
    dynamic var humidity: Int?
    dynamic var windSpeed: Float?
    dynamic var date: Double?
    dynamic var feelsLike: Int?
    dynamic var clouds: Int?
    dynamic var lon: Float?
    dynamic var lat: Float?

    override static func primaryKey() -> String? {
        return "poi"
    }
    
    func printSelf()
    {
        print("temperature : \(temperature)")
        print("temperatureDescription : \(temperatureDescription)")
        print("condition : \(condition)")
        print("city : \(city)")
        print("weatherIconName : \(weatherIconName)")
        print("sunset : \(sunset)")
        print("sunrise : \(sunrise)")
        print("humidity : \(humidity)")
        print("windSpeed : \(windSpeed)")
        print("date : \(date)")
        print("feelsLike : \(feelsLike)")
        print("clouds : \(clouds)")
        print("lon : \(lon)")
        print("lat : \(lat)")
    }

}

class WeatherDataOneDayDbProvider {
    private var realm: Realm?
    
    init() {
        var config = Realm.Configuration()
        config.fileURL = config.fileURL!.deletingLastPathComponent().appendingPathComponent("weather_data_one_day.realm")
        realm = try? Realm(configuration: config)        
    }
            
    func getOneDayWeatherDataForPoi(poi : String) -> WeatherDataOneDay? {
        let weatherData = realm?.object(ofType: WeatherDataOneDayCached.self, forPrimaryKey: poi)
        if let weatherData = weatherData {
            let result = WeatherDataOneDay()
            result.temperature = weatherData.temperature ?? 0
            result.temperatureDescription = weatherData.temperatureDescription ?? ""
            result.condition = weatherData.condition ?? 0
            result.city = weatherData.city ?? ""
            result.weatherIconName = weatherData.weatherIconName ?? ""
            result.sunset = weatherData.sunset ?? 0.0
            result.sunrise = weatherData.sunrise ?? 0.0
            result.humidity = weatherData.humidity ?? 0
            result.windSpeed = weatherData.windSpeed ?? 0.0
            result.date = weatherData.date ?? 0.0
            result.feelsLike = weatherData.feelsLike ?? 0
            result.clouds = weatherData.clouds ?? 0
            result.lon = weatherData.lon ?? 0.0
            result.lat = weatherData.lat ?? 0.0
            
            return result
        } else {
            return nil
        }
    }
    
    func addOneDayWeatherDataForPoi(poi : String, weatherData: WeatherDataOneDay) {
        let cachedWeatherData = WeatherDataOneDayCached()
        cachedWeatherData.poi = poi
        
        cachedWeatherData.temperature = weatherData.temperature
        cachedWeatherData.temperatureDescription = weatherData.temperatureDescription
        cachedWeatherData.condition = weatherData.condition
        cachedWeatherData.city = weatherData.city
        cachedWeatherData.weatherIconName = weatherData.weatherIconName
        cachedWeatherData.sunset = weatherData.sunset
        cachedWeatherData.sunrise = weatherData.sunrise
        cachedWeatherData.humidity = weatherData.humidity
        cachedWeatherData.windSpeed = weatherData.windSpeed
        cachedWeatherData.date = weatherData.date
        cachedWeatherData.feelsLike = weatherData.feelsLike
        cachedWeatherData.clouds = weatherData.clouds
        cachedWeatherData.lon = weatherData.lon
        cachedWeatherData.lat = weatherData.lat
                
        if isWeatherDataExist(poi: poi) {
            try? realm?.write {
                realm?.add(cachedWeatherData, update: .all)
            }
        } else {
            try? realm?.write {
                realm?.add(cachedWeatherData)
            }
        }
    }
        
    func isWeatherDataExist(poi: String) -> Bool {
        return realm?.object(ofType: WeatherDataOneDayCached.self, forPrimaryKey: poi) != nil
    }
}

class WeatherDataOneDayDB {
    private let dbDataProvider : WeatherDataOneDayDbProvider = WeatherDataOneDayDbProvider()
    
    static var shared: WeatherDataOneDayDB = {
        let instance = WeatherDataOneDayDB()
        return instance
    }()

    private init() {}
    
    func getOneDayWeatherDataForPoi(poi : String) -> WeatherDataOneDay? {
        return dbDataProvider.getOneDayWeatherDataForPoi(poi: poi)
    }
    
    func addOneDayWeatherDataForPoi(poi : String, weatherData: WeatherDataOneDay) {
        return dbDataProvider.addOneDayWeatherDataForPoi(poi: poi, weatherData: weatherData)
    }
}

这是一个测试片段:

let test = WeatherDataOneDay()
test.temperature = 26
test.temperatureDescription = "clear sky"
test.condition = 800
test.city = "Globe"
test.weatherIconName = ""
test.sunset = 1644517064.0
test.sunrise = 1644473452.0
test.humidity = 80
test.windSpeed = 2.4
test.date = 1644521917.0
test.feelsLike = 29
test.clouds = 0
test.lon = 0.0
test.lat = 0.0
print("!!!!!!!DEBUUUUUUUUG!!!!!!!!!!!")
let db = WeatherDataOneDayDB.shared
db.addOneDayWeatherDataForPoi(poi: "Москва",
                              weatherData: test)

if let obj = db.getOneDayWeatherDataForPoi(poi: "Москва") {
    obj.printSelf()
}

我看到对象没有完全写入数据库 - 只有少数成员没有问题,其余的都是零。这是我收到的:

temperature : 0
temperatureDescription : clear sky
condition : 0
city : Globe
weatherIconName : 
sunset : 0.0
sunrise : 0.0 ....

我只从主线程访问数据库。我错过了什么?

这是我的案例的解决方案(似乎是可选用法的问题):

class WeatherDataOneDayCached: Object {
    @objc dynamic var poi: String = ""
    
    @objc dynamic var temperature: Int = 0
    @objc dynamic var temperatureDescription: String = ""
    @objc dynamic var condition: Int = 0
    @objc dynamic var city: String = ""
    @objc dynamic var weatherIconName: String = ""
    @objc dynamic var sunset: Double = 0
    @objc dynamic var sunrise: Double = 0
    @objc dynamic var humidity: Int = 0
    @objc dynamic var windSpeed: Float = 0
    @objc dynamic var date: Double = 0
    @objc dynamic var feelsLike: Int = 0
    @objc dynamic var clouds: Int = 0
    @objc dynamic var lon: Float = 0
    @objc dynamic var lat: Float = 0

    override static func primaryKey() -> String? {
        return "poi"
    }
    
    func printSelf()
    {
        print("temperature : \(temperature)")
        print("temperatureDescription : \(temperatureDescription)")
        print("condition : \(condition)")
        print("city : \(city)")
        print("weatherIconName : \(weatherIconName)")
        print("sunset : \(sunset)")
        print("sunrise : \(sunrise)")
        print("humidity : \(humidity)")
        print("windSpeed : \(windSpeed)")
        print("date : \(date)")
        print("feelsLike : \(feelsLike)")
        print("clouds : \(clouds)")
        print("lon : \(lon)")
        print("lat : \(lat)")
    }

}

首先,避免可选的主键,这是没有意义的。我建议使用现代语法来定义对象:

class WeatherDataOneDayCached: Object {
    @Persisted(primaryKey: true) var poi: String
    @Persisted var temperature: Int?
    ...
}

仅此一项就可以解决问题。 您还可以为 db Provider 提供更多 Swifty 代码:

class WeatherDataOneDayDbProvider {
private var realm: Realm // No need to have it optional if you can't continue without one

init() {
    var config = Realm.Configuration()
    ...
    realm = try! Realm(configuration: config) // Just add exceptions handling or ! for prototyping
}
        
func getOneDayWeatherDataForPoi(poi : String) -> WeatherDataOneDay? {
    // Save some lines here too
    guard let weatherData = realm.object(ofType: WeatherDataOneDayCached.self, forPrimaryKey: poi) else {
        return nil
    }
    let result = WeatherDataOneDay()
    result.temperature = weatherData.temperature ?? 0
    ...
    return result
}

func addOneDayWeatherDataForPoi(poi : String, weatherData: WeatherDataOneDay) {
    try? realm.write {
        // get available object or create new in a single line
        let cachedWeatherData = realm.object(ofType: WeatherDataOneDayCached.self, forPrimaryKey: poi) ??
        realm.create(WeatherDataOneDayCached.self, value: ["poi": poi])

        cachedWeatherData.temperature = weatherData.temperature
        ...
    }
}

}

您可以直接在代码中编写 dump(object) 而不是 printSelf() 的长定义