ウィジェットでBitbankの自分の資産を確認できるようにした

# なぜ作ろうと思ったか
仮想通貨大好きな友だちから「ウィジェットで各通貨の値段は確認できるけど資産を確認できないのは不便」という声が。
ランチ1回で買収されて作ってみた。

↓は既存のビットバンクのウィジェット
<img width="489" alt="スクリーンショット 2018-05-25 15.45.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/97817/ae8da0e3-ea7c-a9b1-a20b-5fd4db3cf678.png">
確かに各コインの値段はわかるけど自分の資産情報はない

# 作ったもの
アプリ側で表示したいコインとBitbankのPrivateApiを叩くためのApiKey、ApiSecretを保存

<img width="375" alt="IMG_2679.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/97817/e368c3ba-584d-7fbc-a061-0c39b9a60ee1.png">

それをウィジェットで表示するもの

<img width="499" alt="スクリーンショット 2018-05-25 15.48.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/97817/e51c6831-b54c-ece9-fc85-b9f76dd2ca44.png">

１つのコインの資産を見たいとリクエストされたので今回は１つのコインの総資産、現在の価格を表示

# Today Extension
iOSではこのウィジェットのことをToday Extensionと呼ぶ。
ターゲットの追加や実装方法などは他の方もあげているので一旦スルー
自分が詰まったところ中心に書きます。

## ウィジェットのサイズを大きくするには
よくある表示を減らす、表示を増やすを押したら広がるもの。

```TodayViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()
        self.extensionContext?.widgetLargestAvailableDisplayMode = NCWidgetDisplayMode.expanded
    }
```
widgetLargestAvailableDisplayModeを設定して表示を増やせるようにし、

```TodayViewController.swift
    func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize){
        if (activeDisplayMode == NCWidgetDisplayMode.compact) {
            self.preferredContentSize = maxSize;
        }
        else {
            self.preferredContentSize = CGSize(width: 0, height: 100);
        }
    }
```
このように増やした際の高さを指定。

## どのタイミングでAPIを叩けばいいのか

```TodayViewController.swift
    func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
        completionHandler(NCUpdateResult.newData)
    }
```

ウィジェットが表示されるたびに`widgetPerformUpdate`が呼ばれる。（viewDidAppearてきな感じ。。？）
ここでAPI叩いた結果に対して`NCUpdateResult`に値を渡してあげる。

>NCUpdateResultNewData - 内容が入れ替わったのでビューの再描画が必要
NCUpdateResultNoData - 更新は不要
NCUpdateResultFailed - 更新処理中にエラーが発生

## UserDefaultsをアプリ間で共有
今回はアプリ側でコインとAPIの情報をUserDefaultsに保存。それをToday Extension側で読み込んでAPIを叩く必要があった。

Targetの`Capabilities`を選択
新しい`App Groups`を作成し、両方のTargetにそのApp Groupsを設定

![スクリーンショット_2018-05-25_17_13_53.png](https://qiita-image-store.s3.amazonaws.com/0/97817/8961587e-e126-4718-e39f-dd8b5c44e08b.png)

このApp GroupsでUserDefaultsのインスタンスを作成し、保存、読み込めばデータが共有される。

```swift
let defaults = UserDefaults(suiteName: "group.共通のID")
```

リリースする際などはApple DeveloperからApp Groupsを作成して〜〜などしないといけないので注意

# PrivateAPIを叩くためのHMAC-SHA256署名
個人的に今回一番詰まった。。
BitbankのProvateAPIを叩くには `「UNIXTIME、リクエストのパス、クエリパラメータ」 を連結させたもの`をAPI Secretと使ってHMAC-SHA256で署名しなくてはならない。

今回は以下のライブラリを使用

[CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift)

`cocoapods`でインストール

```
pod 'CryptoSwift'
```

以下のように署名して使用

```swift
    private func makeAccessSignWith(accessKey: String, unixtime: String, path: String, queryParam: String) -> String? {
        let unixTimeStr = self.makeHaederUnixTime(unixTime: unixtime)
        let linkedStr = unixTimeStr + path + queryParam
        
        var bytes: [UInt8] = []
        
        bytes += linkedStr.bytes
        
        let signedString = try? HMAC(key: "APISecret", variant: .sha256).authenticate(bytes)
        
        return signedString?.toHexString()
    }
```
しかし叩けど認証エラーが返ってくる。。。
署名の作成方法が間違ってるのかと思っていると意外なとこで原因判明
BitBankのAPIドキュメントのページで自分のAPIキーとAPIシークレットを入力すると実際にAPIを叩いて確認することができるのだけれどそこのUNIXTIMEが
![be0c74b4-c5a4-4b58-9bb7-db85ecb1ba0f.png](https://qiita-image-store.s3.amazonaws.com/0/97817/93a6918e-0b4f-b52f-4f79-ee56f4e81ee9.png)

なぜ１００年前。。。
この変なUNIXTIMEで一度APIを叩いたことが原因でその後も認証エラーがでてたよう。
クライアントから叩く際にUNIXTIMEに1000掛けして解決

# 今後
いまは一個のコインしか表示してないので全部表示して資産も全部足した資産にしよう