Parse を利用して 座標を保存するアプリサンプル

  • 7
    Like
  • 0
    Comment
More than 1 year has passed since last update.

Parse は MaaS サービスで、自前でサーバサイドを用意しなくて済む便利なものです。
Parse が提供しているAPIを利用することで、データ保管、データ取得、Push 通知などができます。
Parse の API を利用して座標を記録するアプリケーションの作り方を紹介したいと思います。

※ もっといい方法があればコメント等でお知らせいただけると嬉しいです。

motion アプリの作成

motion アプリの作成を作ります。

$ motion create ParseGeoMotion
$ cd ParseGeoMotion

UIViewController に 座標の表示および記録、停止ボタンを生成する

BubbleWrap という Ruby っぽくかけるラッパーがあるので、それを利用した方法を紹介します。
まず、 Gemfile に BubbleWrap を導入する準備をします。

$ {editor} Gemfile
gem 'bubble-wrap'

Rakefile に BubbleWrap を読み込むようにします。

$ {editor} Rakefile
require 'bubble-wrap/all'

次に、座標、CoreLocation マネージャの起動/停止するボタンを表示するビューを用意します。

$ {editor} app/app_delegate.rb
class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    true
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds).tap do |w|
      geo_view_controller = GeoViewController.new
      w.rootViewController = geo_view_controller
      w.makeKeyAndVisible
    end
  end
end

$ {editor} app/controller/geo_view_controller.rb
class GeoViewController < UIViewController
  include BW::KVO

  def viewDidLoad
    super
    self.view.backgroundColor = UIColor.whiteColor

    @lat_label = UILabel.new
    @lat_label.text = 'lat:'
    @lat_label.frame = [[10, 40], [200, 20]]

    @lng_label = UILabel.new
    @lng_label.text = 'lng:'
    @lng_label.frame = [[10, 60], [200, 20]]

    @start = UIButton.buttonWithType(UIButtonTypeRoundedRect)
    @start.frame = [[10, 300], [50, 30]]
    @start.setTitle('start', forState:UIControlStateNormal)
    @start.when UIControlEventTouchUpInside do
      addLocation
    end

    @stop = UIButton.buttonWithType(UIButtonTypeRoundedRect)
    @stop.frame = [[50, 300], [50, 30]]
    @stop.setTitle('stop', forState:UIControlStateNormal)
    @stop.when UIControlEventTouchUpInside do
      delLocation
    end

    self.view.addSubview(@lat_label)
    self.view.addSubview(@lng_label)
    self.view.addSubview(@start)
    self.view.addSubview(@stop)

    # ロケーションマネージャの初期設定
    @location_manager ||= CLLocationManager.alloc.init.tap do |lm|
      lm.desiredAccuracy = KCLLocationAccuracyBest
      lm.distanceFilter = 10
      lm.startMonitoringSignificantLocationChanges
      lm.delegate = self
    end
  end

  # 座標の記録を開始
  def addLocation
    coordinate = @location_manager.location.coordinate
    latitude = coordinate.latitude
    longitude = coordinate.longitude
    @lat_label.text = 'lat: ' + latitude.to_s
    @lng_label.text = 'lng: ' + longitude.to_s
  end

  # 座標の記録を停止
  def delLocation
    @location_manager.stopMonitoringSignificantLocationChanges
    @lat_label.text = 'lat: '
    @lng_label.text = 'lng: '
  end
end

これで、座標を表示するところまでできました。
※ ロケーションマネージャの設定は「大幅変更位置情報サービス」や「標準位置情報サービス」等によって異なります。ここでは省略します。

次に Parse を利用して座標を保管する準備に入ります。

Parseの準備

下記URLでサインアップを行います。
https://parse.com/#signup

Parseアプリの作成
Dashboardで「create app」とあるので、クリックしてアプリ名を入力して作成します。
作成したアプリ名のダッシュボードを開いて、「Settinlg > Application Keys」から「Application ID」と「Client Key」をコピーします。
rubymotion の PJフォルダ、app/app_delegate.rb に 下記のコードを入れる(githubなどに公開する場合はAPI Keyの漏洩などに注意してください)

$ {editor} app/app_delegate.rb
Parse.setApplicationId("abcdef", clientkey:"012345")

cocoapods を利用して Parse-iOS-SDK をインストールします。

$ {editor} Gemfile
gem 'motion-cocoapods'
$ bundle install

Rakefile に Parse-iOS-SDK をインストールする設定をします。

$ {editor} Rakefile
# ...
require 'motion-cocoapods'
# ...
Motion::Project::App.setup do |app|
  app.vendor_project('vendor/Pods/Parse-iOS-SDK/Parse.framework',
                      :static,
                      :products => ['Parse'],
                      :headers_dir => 'Headers')
  # ...
  app.frameworks += %w(AudioToolbox CFNetwork MobileCoreServices QuartzCore Security StoreKit SystemConfiguration)
  app.libs += %W(/usr/lib/libz.dylib /usr/lib/libsqlite3.dylib)
  app.pods do
    pod 'Parse-iOS-SDK'
end

[参考]
RubyMotion で Parse を使ってみました - Watson's Blog
http://watson1978.github.io/blog/2012/09/19/using-parse/

設定終了したら Parse-iOS-SDK をインストールします。

$ pod setup
$ rake pod:install

Parse へデータを保存

Parse でデータを保存する時は PFObject クラスを利用します。

$ {editor} app/controller/geo_view_controller.rb
# …
  # 座標の記録を開始
  def addLocation
    coordinate = @location_manager.location.coordinate
    latitude = coordinate.latitude
    longitude = coordinate.longitude
    @lat_label.text = 'lat: ' + latitude.to_s
    @lng_label.text = 'lng: ' + longitude.to_s
    geoSave(latitude.to_s, longitude.to_s)
  end
# …
  def geoSave(lat, lng)
    geoData = PFObject.objectWithClassName("GeoData")
    point = PFGeoPoint.geoPointWithLatitude(lat, longitude:lng)
    geoData["addData"] = NSDate.date.to_s
    geoData["point"] = point
    geoData.save
  end

Parse では座標を取り扱いやすくするために PFGeoPoint というオブジェクトがあります。

保存手段は同期、非同期があり、全て4種類あるので紹介します。

  // 同期保存を行う
  geoData.save

  // 非同期保存を行う
  geoData.saveInBackground

  // 非同期に保存を行い、保存後の処理の指定にCallbackを指定する
  geoData.saveInBackgroundWithTarget(self, selector:foo)

  // 非同期に保存を行い、保存後の処理の指定にBlockを指定する
  geoData.saveInBackgroundWithBlock(
    lambda do |succeeded, error|
      if succeeded
        puts "オブジェクトの非同期保存に成功しました"
      else
          puts error.description
      end
    end
  )

  // アプリケーションの状態に応じて保存を行い、保存後の処理の指定にBlockを指定する
  geoData.saveEventually(
    lambda do |data, error|
      if data
        puts "オブジェクトの保存に成功しました"
      else
        puts error.description
      end
    end
  )

[参考]
iOS/OS X Developer Guide | Parse
https://parse.com/docs/ios_guide#objects-saving/iOS

Parse に保存したデータを取得

ここでは単に Parse に保存したデータを取得して標準出力する方法を紹介します。

  query = PFQuery.queryWithClassName("GeoData")
  query.findObjectsInBackgroundWithBlock(
    lambda do |objects, error|
    if not error
      objects.each do |geoData|
        puts geoData["point"]
      end
    else
      puts error.description
    end
  )

Parse を利用したアプリケーションの例としては完全ではないですが、どのようにして利用するか事前準備などを含めて紹介しました。
Parse を利用したアプリケーション作成の参考になれば幸いです。
本リポジトリはGithub にも上げています。
https://github.com/makotoworld/ParseGeoMotion

RubyMotion に触れてみて

RubyMotionのよさは、極端にいうと、Objective-Cをかかなくていい部分だと思います。
ただ、たまにrubymotion の gem があまりにも Ruby よりの書き方、Objective-C として 公式な記述とかけはなれていて混乱してしまうケースも遭遇します。(単にうちの読解力の問題ですが)