以前書いた下記記事のソースコードが古くなっていたので、Swift 3対応版として書き直しました。
RealmSwiftで簡単なGPSロガー作ってみたのでメモ - koogawa blog
以前から気になっていた Realm
ですが、先日受講した id:KishikawaKatsumi さんの授業をきっかけに、実際に触ってみたくなりました。
Realm を理解するには何か作ってみるのが一番ってことで、簡単なGPSロガーを作ってみました。
次のような機能があります。
- Startボタンを押すと位置情報を記録開始
- アプリをバックグラウンドに落としても記録し続ける
- 位置情報が取得されると地図にもピンが立つ
- 1日経過したデータは起動時に自動削除
- Stopボタンを押すと位置情報の取得終了
***
以下、実装メモです。
動作環境
- Xcode 8.3
- Swift 3.0
- RealmSwift 2.4.2
- CocoaPods 1.2.0
RealmSwift インストール
次のような Podfile を用意して pod install
します。
use_frameworks! target 'GPSLogger' do pod 'RealmSwift' end post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '3.0' end end end
RealmSwift インポート
import RealmSwift
モデルクラス作成
位置情報を格納するためのモデルクラスを作ります。緯度、経度、作成日時のみを保持するようにしました。
class Location: Object { @objc dynamic var latitude: Double = 0.0 @objc dynamic var longitude: Double = 0.0 @objc dynamic var createdAt = Date(timeIntervalSince1970: 1) }
Realm では次の型が使用できます:Bool, Int8, Int16, Int32, Int64, Double, Float, String, NSDate, NSData
https://realm.io/docs/swift/latest/#supported-types
Startボタンを押したときの処理
位置情報の取得を開始します。
self.locationManager.startUpdatingLocation()
位置情報を永続化
取得できた位置情報(CLLocation
)から Location
モデルを生成し、Realmに永続化できるようにします。
let location = makeLocation(rawLocation: rowLocation) // Get the default Realm let realm = try! Realm() // Add to the Realm inside a transaction try! realm.write { realm.add(location) }
テーブルビューを更新
addNotificationBlock
メソッドでブロックを登録すると、Realmオブジェクトが更新されたときに通知を受けることができます。
self.token = realm.observe { [weak self] notification, realm in self?.tableView.reloadData() }
ここでは通知を受け取るたび(つまり位置情報が追加されるたび)にテーブルビューを更新しています。
Stopボタンを押したときの処理
位置情報の取得を停止します。
self.locationManager.stopUpdatingLocation()
通知の停止
先ほど addNotificationBlock
メソッドでブロックを登録した際に保持した NotificationToken
オブジェクトの stop
メソッドを呼ぶことで、通知の受け取りを停止します。
if let token = self.token { token.invalidate() }
アプリを起動したときの処理
すでに永続化されている位置情報をロードします。
// Get the default Realm let realm = try! Realm() // Load recent location objects return realm.objects(Location.self).sorted(byKeyPath: "createdAt", ascending: false)
createdAt
でソートをかけています。
古いデータの削除
古いデータがいつまでも残ってしまうのを防ぐため、1日経過した位置情報ログを削除します。
DispatchQueue.global().async { // Get the default Realm let realm = try! Realm() // Old Locations stored in Realm let oldLocations = realm.objects(Location.self).filter(NSPredicate(format:"createdAt < %@", NSDate().addingTimeInterval(-86400))) // Delete an object with a transaction try! realm.write { realm.delete(oldLocations) } }
データ量によっては時間がかかるので、バックグラウンドで実行するようにしています。
所感
CoreData を初めて使った時と比べると、かなりスムーズに導入・実装ができました。ドキュメントが充実しているのも安心ですね。
ソースコード
こちらに全てアップしてあります。Realm を使い慣れている方にとってはツッコミどころ満載だと思いますので、ご指摘など歓迎です:-)