2017.2.6 追記:Swift 3対応版の記事を書きました!
以前から気になっていた Realm
ですが、先日受講した岸川先生の授業をきっかけに、実際に触ってみたくなりました。
Realm を理解するには何か作ってみるのが一番ってことで、簡単なGPSロガーを作ってみました。
次のような機能があります。
- Startボタンを押すと位置情報を記録開始
- アプリをバックグラウンドに落としても記録し続ける
- 位置情報が取得されると地図にもピンが立つ
distanceFilter
はとりあえず 100m にセット- 1日経過したデータは自動削除
- Stopボタンを押すと位置情報の取得終了
***
以下、メモです。
2015.9.23 UPDATE: Swift 2.0 に対応したソースコードを追記しています。
RealmSwift インストール
次のような Podfile を用意して pod install
するだけでした。
use_frameworks! pod 'RealmSwift'
Swift 2.0: Realm, RealmSwift の両方を記載する必要があります
use_frameworks! pod 'Realm', :git => 'https://github.com/realm/realm-cocoa.git', :branch => 'swift-2.0' pod 'RealmSwift', :git => 'https://github.com/realm/realm-cocoa.git', :branch => 'swift-2.0'
RealmSwift インポート
import RealmSwift
モデルクラス作成
位置情報を格納するためのモデルクラスを作ります。とても簡単ですね。
class Location: Object { dynamic var latitude:Double = 0 dynamic var longitude:Double = 0 dynamic var createdAt = NSDate(timeIntervalSince1970: 1) }
Realm では次の型が使用できるようです。Bool, Int8, Int16, Int32, Int64, Double, Float, String, NSDate(少数点以下は切り捨て), NSData
位置情報を保存する
Realm への書き込みは別スレッドで行うようにしました。(Realmのサンプルコードがとても参考になりました)
private func addCurrentLocation(rowLocation: CLLocation) { let location = makeLocation(rowLocation) let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_async(queue) { // Get the default Realm let realm = Realm() realm.beginWrite() // Create a Location object realm.add(location) realm.commitWrite() } }
Swift 2.0:
private func addCurrentLocation(rowLocation: CLLocation) { let location = makeLocation(rowLocation) let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_async(queue) { // Get the default Realm let realm = try! Realm() realm.beginWrite() // Create a Location object realm.add(location) try! realm.commitWrite() } }
makeLocation
メソッドを作って、CLLocation
から Location
を作るようにしています。
保存した位置情報を取り出す
LIMIT句が使えるのかわからなかったので、とりあえず全件取り出し。
private func loadSavedLocations() -> Results<Location> { // Get the default Realm let realm = Realm() token = realm.addNotificationBlock { [weak self] notification, realm in self?.tableView.reloadData() } // Load recent location objects return realm.objects(Location).sorted("createdAt", ascending: false) }
Swift 2.0:
private func loadSavedLocations() -> Results<Location> { // Get the default Realm let realm = try! Realm() token = realm.addNotificationBlock { [weak self] notification, realm in self?.tableView.reloadData() } // Load recent location objects return realm.objects(Location).sorted("createdAt", ascending: false) }
通知(addNotificationBlock
)の仕組みを使って、変更がある度にテーブルをリロードするようにしています。使い方が間違っていたら教えて下さい!
古いデータの削除
こちらも別スレッドで。1日経過したデータを消すようにしてます。
private func deleteOldLocations() { let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_async(queue) { // Get the default Realm let realm = Realm() realm.beginWrite() // Delete old Location objects var oldLocations = realm.objects(Location).filter(NSPredicate(format:"createdAt < %@", NSDate().dateByAddingTimeInterval(-86400))) realm.delete(oldLocations) realm.commitWrite() } }
Swift 2.0:
private func deleteOldLocations() { let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_async(queue) { // Get the default Realm let realm = try! Realm() realm.beginWrite() // Delete old Location objects let oldLocations = realm.objects(Location).filter(NSPredicate(format:"createdAt < %@", NSDate().dateByAddingTimeInterval(-86400))) realm.delete(oldLocations) try! realm.commitWrite() } }
ToDo
- limit句は使用できるか調べる
- Auto Incrementのような仕組みはあるのか調べる
まとめ
CoreData を初めて使った時と比べると、かなりスムーズに導入・実装ができました。ドキュメントが充実しているのも安心ですね。 今後は Realm を積極的に使っていきたいと思います。
ソースコード
こちらに全てアップしてあります。Realm を使い慣れている方にとってはツッコミどころ満載だと思いますので、ご指摘など歓迎です:-)
2015.9.23 UPDATE: Swift 2.0 に対応しました!