ミリ秒の情報をそのまま保存できない
RLMObjectのNSDate型プロパティは、そのまま保存すると、ミリ秒の情報をカットします。(執筆点のRealmの最新版は、Objective-C/Swiftいずれも0.98.0です。)マルチプラットフォームの互換性のためだそうです。
問題が起きるのは、例えば、1秒未満のうちに10連続でRLMObjectを生成したようなときです。NSDate型プロパティをキーにしてソートしても、順番が意図どおりにならない場合が生じます。10件全て同じ日時になるからです。
ミリ秒を保存するには?
ミリ秒の情報を保存するには、どうしたらよいか。
工夫としては、NSDateの値をDouble(NSTimeIntervalはDouble型の別名)やStringに変換して保存する方法 があります。(Realmの岸川さん、トルクスの山田さん、教えていただまきして、ありがとうございました。)
変換を意識しないで利用するには?
Double <-> NSDateの変換を意識せず利用するために、工夫できないか考えました。
次のような構造を作ってみました。NSDate型のコンピューテッドプロパティを用意して、それを通じて保存用のDoubleのプロパティにアクセスするというものです。
例えば、PersonオブジェクトでNSDate型のcreatedAtプロパティを使いたい場合は、以下のようになります。(Realm 0.98.0, Swift2.2)
class Person: RLMObject {
//生成日時のプロパティ
dynamic var createdAtDouble: Double = 0.0 //保存用。ミリ秒まで保存するため。
dynamic var createdAt:NSDate { //コンピューテッドプロパティ
get {
return NSDate(timeIntervalSinceReferenceDate: createdAtDouble)
}
set(date) {
createdAtDouble = date.timeIntervalSinceReferenceDate
}
}
//保存しないプロパティ
override static func ignoredProperties() -> [String] {
return ["createdAt"]
}
override init() {
super.init()
//デフォルト値をセット
createdAt = NSDate()
}
}
Personを保存すると、createdAtDoubleの値がRealm上に保存されているので、ミリ秒の情報を保持できます。一方、利用上は、Person.createdAtにアクセスするだけでNSDateとして扱えるので、扱いがラクになりました。
ソートキーはDouble型のプロパティ名
ソートキーは"createdAtDouble"とする必要があります。ignoredProperties()による除外が効いているので、"createdAt"でsortedResultsUsingProperty( )しようとすると、こうなります。
Terminating app due to uncaught exception 'Invalid sort property', reason: 'Cannot sort on property 'createdAt' on object of type 'Person': property not found.'
この点についても、なるべく意識せずに実装できるようにしたいです。
"createdAt"でソートしようとしたときに、代わりに"createdAtDouble"を使うように置き換える処理を入れました。例えば、次のような流れになります。
static func fetch(sortKey: String = "createdAtDouble") -> RLMResults {
// 1. sortKeyが"createdAt"の場合、"createdAtDouble"とみなす。
var workingKey = sortKey
if sortKey == "createdAt" { workingKey = "createdAtDouble" }
// 2. sortedResultsUsingProperty()の処理
return Person.allObjects().sortedResultsUsingProperty(workingKey, ascending: true)
}
表面上は"createdAt"というキーでソートしているかのように実装できるようになりました。
参照
Realm Swift 対応しているデータ型
https://realm.io/jp/docs/swift/latest/#section-5
追記
Realm 0.101.0の仕様変更により、ミリ秒までNSDate型で保存できるようになりました。