台風が多かったのでSwiftUIで気圧計を作る

この記事は、自分の過去記事の[「台風が来るのでiPhone6で気圧計を作る」](https://qiita.com/bellx2/items/fc1de7197f583001ca59)のアップデート版です。元記事を書いてから５年、今年の台風シーズンも終わりましたが今でも時々ブックマークやいいねがつくので、iOS13のSwiftUIで書き直してみました。今回は台風は特に意味はありませんが、続編という事で...。

SwiftUIの動作サンプルとして、前のコードの基本部分はそのままにしました（汗、

![IMG_0045.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14768/b95863f4-4e3b-e5c2-7e60-07a27d36b830.png)

SwiftUIで描いたら何もしなくてもダークモード対応になりました♪

# 環境

Xcode 11.2.1
iOS13.1.3 (iPhoneX)

# コード

とりあえず使ってみたい人は以下に公開しています。実機への転送が必要です。

https://github.com/bellx2/barometer_swiftui

# 実装説明

ContentView.swiftだけで完結です。前半部分はCMAltimeterをハンドリングするAltimatorManagerを実装します。

- 気圧計の利用にはCoreMotionのCMAltimeterクラスを使います。
- startRelativeAltitudeUpdatesで連続したデータ取得をおこないます
- doResetは相対高度をリセットして再度取得を開始します
- クラスをObservableObjectとして参照していると、WillChange.send()でPublishedにしてある値が更新されます。

```ContentView.swift

import Combine
import SwiftUI
import CoreMotion

class AltimatorManager: NSObject, ObservableObject {
    let willChange = PassthroughSubject<Void, Never>()
    
    var altimeter:CMAltimeter?

    @Published var pressureString:String = ""
    @Published var altitudeString:String = ""
    
    override init() {
        super.init()
        altimeter = CMAltimeter()
        startUpdate()
    }
    
    func doReset(){
        altimeter?.stopRelativeAltitudeUpdates()
        startUpdate()
    }
    
    func startUpdate() {
        if(CMAltimeter.isRelativeAltitudeAvailable()) {
            altimeter!.startRelativeAltitudeUpdates(to: OperationQueue.main, withHandler:
                {data, error in
                    if error == nil {
                        let pressure:Double = data!.pressure.doubleValue
                        let altitude:Double = data!.relativeAltitude.doubleValue
                        self.pressureString = String(format: "気圧:%.1f hPa", pressure * 10)
                        self.altitudeString = String(format: "高さ:%.2f m",altitude)
                        self.willChange.send()
                    }
            })
        }
    }

}
```

CMAltitudeData形式で来る値はpressureがkPaなので10倍します。altitudeはmです。

print(data!)とdata!.pressureの値が違います。どうしてだろう、謎です。←前にもひっかかったような気がする。

```swift
print(data!.pressure.doubleValue) // -> 102.06747436523438
print(data!.pressure.stringValue) // -> 102.0674743652344
print(data!) // -> Altitude 0.052845 Pressure 102066.859375 @ 91405.207467
```

[CMAltitudeData Class Reference](https://developer.apple.com/documentation/coremotion/cmaltitudedata)

## SwiftUI

後半部分はSwiftUIのコードになります。

- UI部分は並べるだけなのでシンプルです
- ObservedObjectにしてあるので値が更新されるとUIは自動的に変化します
- 何か値がないとPreviewが動かなくて寂しいのでavailabeでnilの場合の表示を追加しています。
- そのままだと文字間が狭いので一応spcacing30を追加しています。

```ContentView.swift
struct ContentView: View {
    @ObservedObject var manager = AltimatorManager()
    
    let availabe = CMAltimeter.isRelativeAltitudeAvailable()

    var body: some View {
        VStack(spacing: 30) {
            VStack(spacing: 30) {
                Text(availabe ? manager.pressureString : "----")
                Text(availabe ? manager.altitudeString : "----")
            }
            Button(action: {
                self.manager.doReset()
            }) {
                Text("リセット")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

# ごにょごにょ

betaの時からSwiftUIを試していたのですが、かなり変わってサンプルが動いたり変わったりしたのでいったん挫折していました。久しぶりに更新ついでにSwiftUIの簡単なサンプルとして作ってみました。

SwiftUIだとこの程度ならContentViewだけで完結してしまうので便利ですね。

# 参考

台風が来るのでiPhone6で気圧計を作る
https://qiita.com/bellx2/items/fc1de7197f583001ca59