koogawa blog

iOS、Android、foursquareに関する話題

RealmSwiftで簡単なGPSロガー作ってみたのでメモ

2017.2.6 追記:Swift 3対応版の記事を書きました!

blog.koogawa.com


以前から気になっていた Realm ですが、先日受講した岸川先生の授業をきっかけに、実際に触ってみたくなりました。 Realm を理解するには何か作ってみるのが一番ってことで、簡単なGPSロガーを作ってみました。

f:id:koogawa:20150802150835p:plain

次のような機能があります。

  • 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 を使い慣れている方にとってはツッコミどころ満載だと思いますので、ご指摘など歓迎です:-)

github.com

2015.9.23 UPDATE: Swift 2.0 に対応しました!