SwiftUI×HealthKitでiOSデータ入門(中編)

# -HealthKitアクセス許可編-
[SwiftUI×HealthKitでiOSデータ入門(前編)](https://qiita.com/_kenken82/private/22df88d1b148739837ea) に引き続き，Swift, HealthKit のキャッチアップをがんばっていきます．前回はほぼ環境構築だったので今回からアプリっぽくしていきたいと思います．

## アプリの画面遷移
データを取ってくるだけなら View は一つで ContentView.swift に書いていけばいいと思うのですが，SwiftUI のキャッチアップも兼ねているので，HealthKit にうつる前にアプリの起動画面から，二つ目の画面に遷移するようにしておきたいと思います．画面遷移の実装は[この記事](https://q3task.com/swiftui_navigation/)を参考にしました．前回書いたContentViewを書き換えていきます．↓ は前回までのコード．

```ContentView.swift
//
//  ContentView.swift
//  Project Name
//
//  Created by My Name on 2019/12/06.
//  Copyright © 2019 Team Name. All rights reserved.
//

import SwiftUI

// Main Process
struct ContentView: View {
    
    // 垂直方向のプレビュー設定
    var body: some View {
        VStack (alignment: .leading) {
            Text("Hello, World!")  // 1つめのテキスト (VStack内のテキストは縦に並ぶ)
                .font(.largeTitle)  // フォントを変更
                .foregroundColor(.green)  //カラーを変更
            
            // 水平方向のプレビュー設定
            HStack {
                Text("First App")  // 2つめのテキスト
                    .font(.subheadline)
                Text("-First Project-")  // Hstack内のテキストは横に並ぶ
                .font(.subheadline)
            }
        }
    .padding()  // 余白を取る
    }
}

// Mainのstructのインスタンスを生成し，画面に表示
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

まずは，二つ目の画面 (View) を作成します．プロジェクトフォルダに新しいファイルを作ります．プロジェクトのフォルダを選択し

```
File → New → File
```
で新しいファイルを作成します．ここでは  **DataCollection.swift** という名前で保存．すると新たにファイルが作成され中身は ↓ のようになっていると思います．

```DataCollection.swift
//
//  DataCollection.swift
//  Project Name
//
//  Created by My Name on 2019/12/09.
//  Copyright © 2019 Team Name. All rights reserved.
//

import SwiftUI

struct DataCollection: View {
    var body: some View {
        Text("Hello, World")
    }
}

struct DataCollection_Previews: PreviewProvider {
    static var previews: some View {
        DataCollection()
    }
}

```

ここが遷移先の View です．Hello, World! の部分などは各自書き換えてください．次に ContentView.swift を以下のように書き換えます．

```ContentView.swift
//
//  ContentView.swift
//  Project Name
//
//  Created by My Name on 2019/12/06.
//  Copyright © 2019 Team Name. All rights reserved.
//

import SwiftUI
import HealthKit
import RealmSwift
// Main Process / First View
struct ContentView: View {
    var body: some View {
        
        // 画面遷移をおこなうためのNavigationLinkが含まれるVstackはNavigationViewで囲む
        NavigationView{
        // 垂直方向のView設定
        VStack (alignment: .center, spacing: 20) {
            Text("Hello, World!")  // 1つめのテキスト (VStack内のテキストは縦に並ぶ)
                .font(.largeTitle)  // フォントを変更

            // 水平方向のプレビュー設定
            HStack {
                Text("First APp")  // 2つめのテキスト
                    .font(.subheadline)
                Text("-First Project-")  // Hstack内のテキストは横に並ぶ
                .font(.subheadline)
            }
            .padding(.bottom,100)  // 間に余白を入れる
            
            // これが画面遷移の追加部分
            // 画面遷移先のリンク(DataCollection.swift)とそのトリガーとなるテキストを設定
            VStack (alignment: .center) {
                NavigationLink(destination: DataCollection()){
                    Text("Start")  // 画面遷移を行うトリガーとなるテキスト
                    .font(.largeTitle)
                }
            }
            
        }
        }
        
    }
}

// Mainのstructのインスタンスを生成し，画面に表示
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```
これで Start ボタンをタップすると二つ目の View に画面遷移することができるはずです．コードでは微妙にレイアウトをいじっているのですが，レイアウトに関しては，「[Building Custom Views with SwiftUI まとめ](https://blog.personal-factory.com/2019/08/20/building-custom-views-with-swiftui/)」と「[Swiftで行こう！--SwiftUIもっと！3](https://note.com/dngri/n/n2879763e3d84)」の二つの記事が大変参考になりました．

次に，二つ目の View にボタンを追加してそのボタンが押されたらデータが取得されるようにしたいと思います．ボタンの設置は[こちら](https://i-app-tec.com/swiftui/swiftui-button.html)の記事を参考に行います．二つ目の View である，DataCollection.swift を ↓ のように書き換えます．

```DataCollection.swift
//
//  DataCollection.swift
//  HackdayApp
//
//  Created by Ken Honda on 2019/12/09.
//  Copyright © 2019 Ken Honda. All rights reserved.
//

import SwiftUI
import HealthKit
import RealmSwift

struct DataCollection: View {
    // @Stateを使ってUIの状態と同期をとる
    @State var labelText = "Tap Here"
    @State var flag = false
    var body: some View {
        
        // 縦にViewを並べる
        VStack() {
            Text(labelText)
                .font(.largeTitle)
                .padding(.bottom)
            // ボタンの作成(フラグを使って表示されるテキストを変える)
            Button(action: {
                //
                if(self.flag){
                    self.labelText = "Get Data"
                    self.flag = false
                }else{
                    // ボタンがタップされた時
                    self.labelText = "Succeeded!"
                    self.flag = true
                    
                }}){
                    // ボタンのテキスト
                    Text("Button")
                        .font(.largeTitle)
                        .foregroundColor(Color.white)
            }
            .padding(.all)
            .background(Color.blue)
        }
    }
}
// インスタンスの生成
struct DataCollection_Previews: PreviewProvider {
    static var previews: some View {
        DataCollection()
    }
}
```
ContentView.swift と DataCollection.swift それぞれのファイルにコードを書き足すことで画面遷移の実装を行いました．ContentView には "Start" が追加されており，遷移先の DataCollection は ↓ のようになっているはずです．
![Screen Shot 2019-12-09 at 23.48.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76122/0254a8a2-5cb2-da47-fa45-39cf45e0ca92.png)

"Button" をタップすると．
![Screen Shot 2019-12-09 at 23.56.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76122/dae72289-d8f2-bcad-047a-e0e9509bf765.png)

という感じでボタンも実装しました．

## HealthKit
いよいよここからが本当にやりたかったことになります．HelthKit のデータを取得します．使えるデータの一覧は[こちら](https://qiita.com/sato-shin/items/d7a0099750a0b9eddfd1)にまとめてありました．今回は画面遷移も行えるようにしているので，データの処理や格納などは全て二つ目の VIew である **DataCollection.swift** に書いていくことになります．先ほど作った Button が押されたらデータが取得されるというコードを書いていきたいと思います．

### HealthKit 設定

HealthKit についての最新の情報やインストラクションは，開発者向けの [HealthKit 公式ページ](https://developer.apple.com/healthkit/)にある

1. [Setting Up HealthKit](https://developer.apple.com/documentation/healthkit/setting_up_healthkit) ( HealthKitの設定とインストラクション )
2. [Design Guidance](https://developer.apple.com/design/human-interface-guidelines/healthkit/overview/) ( HealthKit利用にあたってのガイドライン )
3. [Development Guidance](https://developer.apple.com/documentation/healthkit) ( プライバシーポリシー )
この3つのページから確認できそうです．2, 3はHealthKitを利用してアプリを作る前に目を通しておいた方が良さそうです．1で実際に開発の準備を行います．

と，いうことでまずは 1  の Setting Up HealthKit の手順通りにセットアップしていきます．

#### Step1. アプリで HealthKit が有効かどうか確認する
これは[公式ページの手順](https://developer.apple.com/documentation/healthkit/setting_up_healthkit)が親切なのでその手順通りに設定していきます．

#### Step2. HealthKit が自分の現在のデバイスで利用可能かを確認する
↓ のコードを

```Swift
if HKHealthStore.isHealthDataAvailable() {
    // Add code to use HealthKit here.
}
```
以下のDataCollection.swift の "// ボタンがタップされたとき" の処理の部分に入れます．

```DataCollection.swift
else{
    // ボタンがタップされた時
    if HKHealthStore.isHealthDataAvailable() {
        // Add code to use HealthKit here.
    }
    self.labelText = "Succeeded!"
    self.flag = true
```
と書き加えてエラーが出なければOKです．

#### Step3. アプリで HealthKit インスタンスを生成
チュートリアルにある

```swift
let healthStore = HKHealthStore()
```

この1文と

```swift
let allTypes = Set([HKObjectType.workoutType(),
                    HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
                    HKObjectType.quantityType(forIdentifier: .distanceCycling)!,
                    HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!,
                    HKObjectType.quantityType(forIdentifier: .heartRate)!])

```

欲しいデータタイプのアクセス許可を得るためのこの部分を追加する．ただし，今回は歩数のデータのみ欲しいので，上記のコードを

```swift
 let allTypes = Set([HKObjectType.quantityType(forIdentifier: .stepCount)!])  //今回は歩数のみ
```

に変えてDataCollection.swift に書き加える．以下抜粋

```DataCollection.swift
struct DataCollection: View {
    // @Stateを使ってUIの状態と同期をとる
    @State var labelText = "Tap Here"
    @State var flag = false
    let healthStore = HKHealthStore()  // HealthKitのデータを格納するHealthStoreを定義
    // アクセス許可が欲しいデータタイプを指定
    let allTypes = Set([HKObjectType.quantityType(forIdentifier: .stepCount)!])  //今回は歩数のみ
    // View
    var body: some View {
```

ここまでで動作に問題ないか確認するため，本記事の上の方で書いた，ボタンがタップされたらSucceeded と表示する部分を少しいじります．

```DataCollection.swift
        // 縦にViewを並べる
        VStack() {
            Text(labelText)
                .font(.largeTitle)
                .padding(.bottom)
            // ボタンの作成(フラグを使って表示されるテキストを変える)
            Button(action: {
                // ボタンがタップされてない時
                if(self.flag){
                    self.labelText = "Get Data"
                    self.flag = false
                }else{
                    // ボタンがタップされた時
                    // もしHealthKitが利用可能なら
                    if HKHealthStore.isHealthDataAvailable() {
                        self.labelText = "HealthKit Available"
                    }else{
                        self.labelText = "Unavailable"}
                    self.flag = true
                }}){
                    // ボタンのテキスト
                    Text("Button")
                        .font(.largeTitle)
                        .foregroundColor(Color.white)
            }
            .padding(.all)
            .background(Color.blue)
        }
```

こんな感じ．ボタンがタップされた時に HealthKit が利用可能なら Available, 不可なら Unavailable と表示するようにしてみました．

![Screen Shot 2019-12-14 at 15.37.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76122/42a7c64d-dd73-53c2-c9cc-a73e5583c2f2.png)


#### Step4. データの読み取りの権限をリクエストする
次にアプリからデバイスにデータへのアクセス権限をリクエストします．Step3 までに書いたコードにチュートリアル通り以下のコードを追加します．

```Swift
healthStore.requestAuthorization(toShare: allTypes, read: allTypes) { (success, error) in
    if !success {
        // Handle the error here.
    }
}
```
ただし，このままコピペしてもうまくいかないので，自分のアプリに適した形に書き変えます．toShare は nil に変更し，self. を必要な場所に追加しています．また今回エラー処理を使わないので if 以下は削除しました．最後に } を閉じて以下のようになります．

```Swift
self.healthStore.requestAuthorization(toShare: nil, read: self.allTypes) { (success, error) in}
```
これを DataCollection.swift に追記します．

```DataCollection.swift
// ボタンがタップされた時
// もしHealthKitが利用可能なら
if HKHealthStore.isHealthDataAvailable() {
    self.labelText = "HealthKit Available" 
    self.healthStore.requestAuthorization(toShare: nil, read: self.allTypes) { (success, error) in}
}else{
```

こんな感じ．これでビルドするとエラーが出ないはず．ただしこのままボタンをタップするとアプリがクラッシュします．これはそういう仕様だそうです．

これを避けるために **info.plist** に新たな設定を追加します．info.plist を開き．右クリックで **add row** でプライバシーに関する設定を使いします．
![Screen Shot 2019-12-14 at 19.07.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76122/986d48f7-0d98-6614-8526-e21489a55d71.png)
この3つを追加します．さらにこれらの Value に用途を記述します．ここは任意の文章でいいです．

ここまで設定しアプリをビルドすると，ボタンをタップした時にアクセス権限を聞いてきます．
<img width="375" alt="IMG_8258.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76122/322125ff-da8e-3229-cb13-eebd352a892f.png">
これを許可して
<img width="375" alt="IMG_8259.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76122/6263b5fb-7aa1-eaa2-9ebf-837495e22279.png">

準備OKです！

今回は HealthKit の設定をひたすらしていきました．[SwiftUI×HealthKitでiOSデータ入門 (後編)](https://qiita.com/_kenken82/private/8201f6e7b6a07c61b8ff)へ続きます．次回はいよいよ HealthKit からデータを取得します．