JINS MEMEのデータをxx秒おきにGoogle SpreadSheetに流す

# What's JINS MEME

こちらをご参考ください。
http://qiita.com/mito_log/items/020a87996ed2b9d793e6

# Motivation
JINS MEMEを使って何か作るにもデータの傾向を見たり分析してからでないとなぁ。ということでxx秒ごとにgoogle spread sheetに投げてデータを見てみます。今回も[前回に引き続き](http://qiita.com/mito_log/items/7acd6f01bcabb801e708)RxSwiftで頑張っております。

# github
https://github.com/mitolog/JINSMEMESampler

# Spreadsheet
https://docs.google.com/spreadsheets/d/196kFM695twoL4b_a8dv-4AKrBZQW4jJk9MqcQyCgf4g/edit?usp=sharing

# Screenshots
iPhone
![MEME_spreadsheet_iphone.png](https://qiita-image-store.s3.amazonaws.com/0/46489/7b12d933-626f-33ec-7eea-925dcbee98e1.png)

spreadsheet
![MEME_spreadsheet_spreadsheet.png](https://qiita-image-store.s3.amazonaws.com/0/46489/d87ff6be-ff56-44cb-a90d-ea7d674dcbb5.png)

# How it works

ios側では、データを一旦配列に貯めておいて、一定時間毎にcsvでデータをpostで送信しています。

google spreadsheet側では、google app scriptの[Content Service](https://developers.google.com/apps-script/guides/content)という機能を使い、簡単なRestfulAPI(POSTのみ)を用意しています。そこで、csvをパースして、シートに転記しています。

google apps scriptを使ったAPIについては、[過去に書いた記事](http://qiita.com/mito_log/items/cc2283cac1e2b325f9bc)を見つつ作れるかと思います。注意すべきは、

 - スプレッドシート名を固有のものにすること
 - アプリの実行権限を得ること
 - ウェブアプリケーションとして導入のオプション

くらいかと思います。

# Recap

## Too much memory use while data retrieval and sending
今回もRxSwiftを使ってみたのですが、dispose関連のタイミングやら手法がまだカバーできていないせいか、データ取得時に大量にメモリを食ってしまっています。一応leaksでみてみるとリークは無いとの判定でしたが、工夫したいところです。

## takeUntil

今回、スタート/ストップボタンを押すと、「データの取得開始、タイマーの起動」をしています。つまり、
スタート:subscribeの開始
ストップ: dispose
としたかったのですが、disposeをどうやるべきか...紆余曲折を経てtakeUntilを使う手法にたどり着きました。

![takeuntil.png](https://qiita-image-store.s3.amazonaws.com/0/46489/71dde447-c706-c5c8-7923-8ccc897299d5.png)
画像: [RxSwiftのPlayground](https://github.com/ReactiveX/RxSwift/tree/master/Rx.playground)より

一番上をrealtimeDataを取得するシーケンス(`MEMELibAccess.sharedInstance.rx_realtimeDataReceived`)、そのすぐ下を、データストップシーケンス(`stopSequence`)とすると、stopSequenceに何かしら次のアイテムをセットした時点で一番上のシーケンスは完了扱いとなり、disposeされます。

コード上でいくと、

```swift
MEMELibAccess.sharedInstance.rx_realtimeDataReceived
            .takeUntil(self.stopSequence)
            .subscribeNext { [unowned self] realtimeData in
```

というふうにせっとしておいて、その後に、`stopSequence.on(.Next(true))`とかしてあげればOKです。

詳しくは、[GettingStartedのDisposingの箇所](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md#disposing)を見るといいかと思います。

## bi-directional binding

[RxSwiftで双方向データバインディング](http://qiita.com/cross-xross/items/c18744512cb37793b0a2)をみながらやってみました。

とても柔軟だなとおもったのは、例えばモデルがdoubleでビューがstringでもバインドできます。

```swift:view_to_model
self.intervalTextField.rx_text.subscribeNext { [unowned self] intervalStr in
    if intervalStr.utf16.count > 0 {
        self.vm.interval.value = atof(intervalStr)
    }
}.addDisposableTo(self.disposeBag)
```

```swift:model_to_view
self.vm.interval
    .subscribeOn(MainScheduler.sharedInstance)
    .map { interval in
        return String(interval)
    }
    .bindTo(self.intervalTextField.rx_text)
    .addDisposableTo(self.disposeBag)
```

mapを噛ませて、stringを返すようにすればバインドできました。

## Scheduler

今回のように通信が絡んだりタイマーが絡むと、UIスレッドを意識しないといけません。RxSwiftでもスケジューラーという概念でスレッドを管理できるようになっているので、スケジューラーは要チェックです。

https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Schedulers.md

1点気になったのが、ドキュメントでは、`.subscribeOn(MainScheduler.sharedInstance)`すればメインスレッドでsubscribeできるよみたいに書いてあったのですが、一部効かない場所があったので、`dispatch_async(dispatch_get_main_queue()) {}`で対応しました。

## 20Hz
いきなりJINS MEMEのことになりますが(ていうかほぼRxSwiftの勉強になっていますね...)、JINS MEMEのrealtimeモードでは、通信速度がBluetooth通信周期にのっとって約20Hzとなっています。
参照: https://developers.jins.com/ja/

ということは、1秒間に約20サンプルのデータがiPhoneには送られていることになり、10秒だと約200のサンプルになります。

google spreadsheetの最大セル数は、200万セルということなので、逆算すると、

2000000セル / 16(1行分のセル数) / 20サンプル = **6250秒(約104分)**

は貯められることになりますが、用途としては、突発的な動作や短いスパンでの変化を分析したいときに使えるかなと思いました。


ということで、引き続きデータを見ながらどんなことができそうかを探っていきます。