Event

2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信 3

2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信 3

この記事はiOSDC2022で発表したセッション、Swiftで我が家を より便利に、安全に!の発表の第2部の内容をまとめた記事の第3弾になります。
この記事は、「2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信2」 の記事の続編です。もしまだ読んでいない方は読んでいただけると!
この記事では、CoreMotionを使って、iPhoneの高度情報をラズパイに送信する方法をまとめます。

2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信 2この記事は、「Swiftで我が家をより便利に、安全に」の発表の2部「2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信 」の内容の一部をまとめた記事になります!...

iPhoneで高度情報を取得する

家主が在宅中かどうかの判定に使用する位置情報と高度情報をラズパイに送るために、まずはiPhoneでのデータを取得できるようにしましょう

今回位置情報の取得方法については、割愛します(ネットの海にいっぱい参考記事があるので!)

CoreMotionを使って、高度情報を取得する

swiftで高度情報を取得するには、CoreMotionCMAltimeterというclassを使います。
CMAltimeterから取得できる高度情報は、絶対高度相対高度の2つがあります。

絶対高度と相対高度とは?

絶対高度→海抜から何メートルか
相対高度→データ取得開始時から何メートル変化したか

今回は、実際に家主(正確にはiPhone)が標高何mにあり、その高さが事前に設定した家の標高と一致するかの判定に利用するので、絶対高度を利用します!
ちなみにSwiftHomeAppのアプリでは参考のため、相対高度の取得方法も実装してあります。

またiPhoneでデータを実際に取得するには、Info.plistに以下の追記が必要です

Privacy - Motion Usage Description

次に実際に高度情報を取得するコードを記載します。(GitHubはこちら)

CMAltimeterのインスタンスを作り、②デバイスで高度情報が取得できるかどうかを確認します。
そしてCMAltimeterインスタンスのstartAbsoluteAltitudeUpdatesメソッドを呼び出すと、そのクロージャーが定期的に呼ばれ、そこから高度情報を取得できます。
絶対高度の場合、data!.altitudeから、m単位でデータを取得できます。

import CoreMotion

private let altimeterMeter = CMAltimeter() // ① setup instance
if CMAltimeter.isAbsoluteAltitudeAvailable() { // ②Check if CMAltimeter is enable or not
    altimeterMeter.startAbsoluteAltitudeUpdates(to: OperationQueue.main) { data, error in
        // ③Called at a certain timing and can acquire data
        if error != nil { return }
        print(data!.altitude) // 25.007457757686876868(e.g, unit is m)
    }
}

試しに相対高度をprintしてみました↓
実機を持って、それを上げたり、下げたり、屈伸みたいにすれば、色々な相対高度を出力できます(笑)
(キャプチャ撮った時は、iPhoneとlightningケーブル繋いだMacの両方持ってましたが、ワイヤレスデバッグすればよかった笑)

ちなみにワイヤレスデバッグについては、以前↓の記事で紹介しています!

xcode-build-wireless
【Xcode】ケーブルで接続せずに、実機ビルドをする方法ケーブルで接続せずに、Xcodeで実機ビルドをする方法をまとめてみました!...

実際にSwiftHomeAppのデバイス情報を表示する画面↓でも、位置情報と高度情報は正しく表示できることが確認できました!(完全に個人情報なので、一部マスクしてます!)

高度情報をHeroku経由で、ラズパイに送信する

それでは最後に、第1弾、第2弾の記事で実装したAPIとHerokuのサーバーに送信してみましょう!

まずは取得したデータをSwiftHomeCoreに定義した共通モデルDeviceInfoModelに設定します。
そしてそのモデルをDictionary型に変換します。
ちなみにこのasDictionaryメソッドは、自分がアプリ開発で共通で利用できるように作ったextensionをまとめたSwiftExtensionsというPackageに定義したものです。(詳しくは以下の記事に記載しています)

xcode-publish-package
LocalPackageをXcodeからPackageとして公開するアプリで作成したLocalPackageをXcodeからPackageとしてGitHubに公開する方法をまとめました!...
// ①Use common Model defined in SwiftHomeCore
let model = DeviceInfoModel(deviceId: UIDevice.current.identifierForVendor!.uuidString,
                            deviceLatitude: userCoordinate.latitude,
                            deviceLongitude: userCoordinate.longitude,
                            absoluteAltimeter: absoluteAltimeterValue)
let parameters = try! model.asDictionary(using: encoder) // ②extension to convert to Dictionary

あとはPostするだけです。
↓のコードでは、Alamofireを使ってPostしています。
headerには、WebSocketの時と同じようにBasic認証の設定をしています。

ちなみにemptyResponseCodes: [200]の設定は、今回実装したAPIは200のレスポンスの時に、.okのレスポンスを返すだけで、jsonなどは返ってこないので、空です。
しかしAlamofireでは、空のレスポンスが返ってくると、decode失敗としてエラーになってしまったので、「200の場合は、空になる!」と明示的に書いてあげています。

AF.request(urlString,
           method: .post, parameters: parameters, // ①Set DeviceInfoModel parameters
           headers: headers) // ②Same setting as webSocket header
.responseDecodable(of: Empty.self, emptyResponseCodes: [200]) { response in
    switch response.result {
    case .success: print("Success")
    // (Omit: error handling)
    }
}

Postした後に、実際にHerokuのPostgresにデータが保存されているかを確認しましょう!

HerokuにはDataclipsというPostgresに保存されているデータを確認する機能があり、postgresqlを実行して、データを確認できます(参考)。
Herokuのダッシュボード > ユーザーアイコンの左側のメニュー > Dataclips > 作成したアプリを選択、と進むことで、クエリ入力画面↓へ進みます

今回は、以下のようなクエリを実行し、保存してあるデバイスの情報を全て表示しました
ちなみにTable名を””で囲っているのは、postgresqlではテーブル名は、自動で小文字に変換されるようで、そのまま実行すると、大文字始まりで登録したTable名を検索することができません。
そこで”DeviceInfo”とすることで、大文字始まりのTable名として、クエリを実行することができます。

SELECT * FROM "DeviceInfo";

その結果↓のようにちゃんとデータが保存されていることが確認できました!🎉

まとめ

これで2部の「Vaporを動かす- iPhoneとラズパイ間をVaporで通信」のまとめは終わりです。
かなりの長編になりました!!
Vaporを使うことで、簡単にSwiftでサーバーを構築でき、一部コードをクライアント側でも共通化できるのはかなり開発体験としてはいいなと思いました!

次は、「3. ラズパイでセンサーの値を読み取る – 重量センサーの値を取得する」のまとめ記事になります

3. ラズパイでセンサーの値を読み取る - 重量センサーの値を取得するこの記事は、「Swiftで我が家をより便利に、安全に」の発表の3部「3. ラズパイでセンサーの値を読み取る - 重量センサーの値を取得する 」の内容をまとめた記事になります!...
+1

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA