2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RealmでNSDateのミリ秒を含む情報を保存する

Last updated at Posted at 2016-04-10

ミリ秒の情報をそのまま保存できない

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)

Person.swift
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"を使うように置き換える処理を入れました。例えば、次のような流れになります。

Person.swift
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型で保存できるようになりました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?