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()
的长定义
这是来自 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()
的长定义