iPhoneのウィジェットに歩数を表示してみる。

iOS10でウィジェットが新しくなりましたね、ということでウィジェットに歩数を表示してみます。
ウィジェットに歩数を出すアプリなんて既出だけど気にせず進みます。
Xcode 8.2
Swift 3.0.2

#やること
ウィジェットの追加や詳しい説明は最後にリンクがあるのでそちらを参考にすれば画像付きでわかりやすいと思います。

ウィジェットにのみ歩数を表示する際にやることは
・ウィジェット（Today Extension）の追加
・HealthKitをONにして歩数を取得する
の2つだけです。

アプリとウィジェットの両方に表示したりデータを共有する場合は
・Frameworkの追加（コードの共通化）
・App Groupeの追加（データの共有）
が必要になります。

##ウィジェット（App Extension）の追加
メニューバーの [File]->[New]->[Target...] から[Application Extension] の [Today Extension]を追加します。
追加するとProduct Nameと同じフォルダができるのでそこにウィジェット用のViewControllerとstoryboard等が追加されます。

##HealthKitをONにして歩数を取得する
プロジェクトファイルの[TARGETS]のアプリの[Capability]ページの[HealthKit]をONに変更します。
iOS10からユーザーデータを扱う時は目的を書く必要があるとのことですので、info.plistのプロパティに`Privacy - Motion Usage Description`を追加してダイアログに表示する文字列を追加するか、xmlに以下を追加します。

```
<key>NSMotionUsageDescription</key>
<string>ダイアログに表示する文字列</string>
```

![Screen Shot 2016-12-21 at 15.44.19.png](https://qiita-image-store.s3.amazonaws.com/0/149731/45c8e034-4b1e-45e1-fdb2-06cf0a537927.png)

これで歩数を取得できるようになったので、実際に取得してみます。
`import CoreMotion`の追加を忘れずに

```TodayViewController.swift
    @IBOutlet weak var stepsLabel: UILabel!

    func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {

        if CMPedometer.isStepCountingAvailable() {
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd"
            let midnight = formatter.string(from: Date())
            let fromDateString = midnight + " 00:00:00"
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            let fromDate = formatter.date(from: fromDateString)!
            let toDate = Date()
            CMPedometer().queryPedometerData(from: fromDate, to: toDate, withHandler: { data, error in
                if let stepsNumber = data?.numberOfSteps {
                    let steps = String(describing: stepsNumber)
                    DispatchQueue.main.async {
                        self.stepsLabel.text = "本日" + steps + "歩歩きました"
                    }
                }
            })
        }
        completionHandler(NCUpdateResult.newData)
    }
```
viewDidLoadはウィジェットが追加された時に呼ばれるので主な処理はwidgetPerformUpdateで行います。
取得するのは日付が変わってから表示するまでの歩数です。
そのままウィジェットのUILabelに反映すると時間がかかっていたのでMainスレッドで処理するようにしました。

#アプリとウィジェットの両方に表示したりデータを共有する場合
##Frameworkの追加（コードの共通化）
Frameworkの追加等の詳しい説明は[こちら](http://qiita.com/masahiro_kusumoto/items/8358ce3b63ab4907c6c3)が参考になります。
メニューバーの [File]->[New]->[Target...] から[Framework & Library] の [Cocoa Touch Framework]を追加します。
Frameworkを作成するとアプリ側は使えるようになりますが、ウィジェットの方は自分で追加しないと使えないので、プロジェクトファイルのウィジェットを開き[Linked Frameworks and Libraries]で+ボタンを押して作成したFrameworkを追加します。
後は作成したFrameworkをimportするとFrameworkに追加したメソッド等が使えるようになります。

##データの共有
データの共有方法はKeychainを使う方法とUserDefaultsを使う方法の2つがありますが今回はUserDefaultsを使ってデータを共有します。アプリとウィジェットは別のアプリのようなものなのでApp Groupsを使い同じデータにアクセスできるようにします。
プロジェクトファイルの[TAEGETS]のアプリの[Capability]ページの[HealthKit]をONに変更します。+ボタンを押して"group.hoge.appname"等の適切な名前をつけて追加します。

```swift
        let userDefaults = UserDefaults.init(suiteName: "group.hoge.appname")!
        // 保存
        userDefaults.set(steps, forKey: "steps")
        userDefaults.synchronize()
        // 取得
        let steps = userDefaults.string(forKey: "steps")
```
このようにしてデータの共有をします。

#参考サイト
http://blog.jeffsuke.com/entry/2014/07/28/121827
http://qiita.com/masahiro_kusumoto/items/8358ce3b63ab4907c6c3