【IT-DIY】AppleWatchを使って、インターネット回線を必要としないチャットツールの作成(Part.2)

※こちらの記事はPart2です。
　Part1はこちら↓
　https://qiita.com/nextvision-sugakir/private/d2c9222160284639a156

はじめまして、IT企業に入社して1年目のSEです。
Part1では開発までの道のり、DIYするアプリの概要までをご紹介しました。(Step1～Step3)
Part2では、DIYの実施から完成品の試用までをまとめました。


# 【Step4】いざ実装
まず仕様をおさらいすると、iPhoneからAppleWatchにメッセージを送信する。AppleWatchはメインのアプリ画面を更新し、画面の上部に新規のメッセージを表示する。
今回作る機能は以下の通りです。
- ボタン、ラベルの実装(AppleWatch側アプリ)
- メッセージの入力機能(iPhone側アプリ)
- データの送受信の機能(両者の疎通確認)


となります。それぞれ書籍等を参考に実装してみます。

まずはiOSのアプリからAppleWatchのアプリに接続したいと思います。既にOS側が提供してくれているWatchConnectivityライブラリをViewControllerにインポートするだけで接続が確立します。
```
//Connect,AppleWatch
import WatchConnectivity

//Add WCSessionDelegate
class ViewController:UIViewController,WCSessionDelegate{

    func session(_ session:WCSession, activationDidCompleteWith activatationState: WCSessionActivationState, error:Error?){
        
    }
    
    func sessionDidBecomeInactive(_ session:WCSession){
        
    }
    
    func sessionDidDeactivate(_ session:WCSession){
        
    }
    
```

**ボタン、ラベルの実装**
XcodeにおけるUIの実装はとても簡単です。iPhoneとAppleWatchに共通しているのですが、配置したいコントロールを選択してドラッグアンドドロップするだけです。あとはプロパティでちょちょいと細かい設定をするだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/46055f1d-0672-2589-5210-263f69de8c80.png)


**メッセージの入力機能**
メッセージはiPhone側のテキストボックスを用いて送信します。先ほどのUIの実装の要領で、テキストボックスをiPhone側に配置し、ViewControllerでそのテキストボックスにデータの送受信処理を記述します。


**データの送受信の機能**
iPhoneからメッセージを送るメソッドは以下のように設定しました。ボタンを押下、すなわちメッセージを送信後に、次の入力に向けてテキストボックスを初期化しています。
```
    //Describe send message
    
    @IBOutlet weak var messageBox: UITextField!
    @IBAction func sendButton(_ sender: Any) {

        
        //Set message
        if (messageBox.text == ""){
            return
        }
        let message = ["send" :messageBox.text]
        
        //Send Apple Watch
        WCSession.default.sendMessage(message as [String : Any], replyHandler: {reply in print(reply)}, errorHandler: {error in print(error.localizedDescription)})
        messageBox.text = ""
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        //Start to Session WatchConnectivity
        if (WCSession.isSupported()) {
            let session = WCSession.default
            session.delegate = self
            session.activate()
        }
        
        messageBox.text = ""
    }
```
受信側のAppleWatchはInterfaceControllerに以下のように設定しました。
```
    //Receive data
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
        
        addCount += 1
        let index = 0
        tableDataList.insert(message["send"] as! String, at: 0)
        table.insertRows(at: [index], withRowType: "Row")
        table.setNumberOfRows(tableDataList.count, withRowType: "Row")
        tableDataList.enumerated().forEach{index, value in
            let row = table.rowController(at: index) as! TableRowController
            row.configure(.init(index: index, text: value))
            row.delegate = self
        }
        replyHandler(["watch" : "OK"])
    }
```



# 【Step5】DIY中に気づいた最後の壁
今回のDIYはiPhoneで送信したメッセージをAppleWatchは通知で受け取ろうとしていました。しかし、DIYを始めてからAppleWatchの**とある**仕様に気づきました。
それは、AppleWatchの通知(Notification)は、iPhoneがスリープ状態の時のみ通知されるというものでした。
私は根本的なことを見落としていたのです。そもそもAppleWatchはあくまでもiPhoneの拡張機能であり、iPhoneがスリープ状態の時、すなわち携帯を見ていないときに知らせる、という仕様は至極当然のことでした。
しかし今回のDIYでは、ビジネスシーンでの利用を考慮して、iPhoneとAppleWatchの使用者を分ける(以下の図を参照)という発想で進めたかったので、ここで大きな課題となったのです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/60c4a21a-54fa-2533-5943-7c973323eb02.png)

# 【Step6】気づいた問題に対する対策
私はここでも諦めませんでした。今回の表題のアプリは、**オフラインで使える**、といった仕様です。オフラインで利用するためには、**通知の実装を避けなければなりません**。まずは今回のDIYで通知を実装出来ないとなった際のデメリットをあげてみました。
- **即時に気づけない**

ぱっと思いつくのは、意外にもこれだけでした。とはいっても即時に気づけないのはとても大きな課題です。次に私はこの課題の改善策を考えました。即時に気づけないと具体的に何が困るか、またそれに対する対策をあげていきます。

**1. 緊急性の高いメッセージにすぐに対応できない**
**2. アプリ画面を開いて、常に目視で確認しなければならない**
これらの問題を改善するために、以下のような機能を実装しました。メイン画面に新しいメッセージが表示されるタイミングで、バイブレーションを起動します。

```
// vibration
WKInterfaceDevice.current().play(WKHapticType.notification)
```

わざわざ通知画面を表示しなくとも、バイブレーションによって気づくことが出来るので、これらの課題はクリアです。

**3. メッセージを見落としたまま次のメッセージが送られてきた際に、確認漏れが発生する**
最後に3つ目の課題については、ログの機能によって対策しました。
```
extension InterfaceController: TableRowControllerDeligate {
    func tableRowController(_ tableRowController: TableRowController, didTapDeleteButtonAt index: Int){
        tableDataList.remove(at: index)
        table.removeRows(at: [index])
        tableDataList.enumerated().forEach{ index, value in
            let row = table.rowController(at: index) as! TableRowController
            row.configure(.init(index: index, text: value))
            row.delegate = self
        }
    }
}
protocol TableRowControllerDeligate: AnyObject {
    func tableRowController(_ tableRowController: TableRowController, didTapDeleteButtonAt index: Int)    
}
final class TableRowController: NSObject {    
    struct Config {
        let index: Int
        let text: String
    }    
    weak var delegate: TableRowControllerDeligate?
    private var index: Int = 0
    @IBOutlet weak var label: WKInterfaceLabel!    
    func configure(_ config: Config){
        label.setText(config.text)
        index = config.index
    }  
    @IBAction func buttonAction() {
        delegate?.tableRowController(self, didTapDeleteButtonAt: index)
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/eb52f79c-b7c4-c9d8-abe1-1ff3a34f3646.png)
次々とメッセージが送られてくる場合、逆に通知画面では確認しきれないこともありますし、今回の目的は「携帯を開かなくてもよい」というものなので、通知では把握しきれず、過去のメッセージを見るためにわざわざアプリを開くのは、AppleWatchのメリットを活かせていません。ならば最初からログとしてメイン画面の上部に新しいメッセージを追加することで常に最新の3件は確認することが可能です。(DigitalCrownを回すことでさらに確認できる)



# 【Step7】デザインを整える
まず、DIYにより完成したアプリがこちら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/4e902ed3-e311-8920-c489-4fc154059e2f.png)
うーん、iPhoneの画面をもっとアプリっぽくしたい！
ということで背景色等をイジって。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/e89d6bc5-77dd-2572-6811-52abc6a1d5c9.png)
それっぽいのが出来ました！

デザインに関しては、用途や使用する人に合わせて簡単に変更できます。これもXcodeのデザインの拡張性の強さですよね。

これらのDIYにより実現できたAppleWatchのDIYを次のStepでシミュレータにて試用してみたいと思います。

# 【Step8】完成品を使ってみた(内部シミュレータ)
最後に、完成したアプリを動かしてみたいと思います！
まずは、シミュレータで両方のアプリを起動します。今回の仮想端末は、iPhone12とAppleWatch Series7です。
操作手順を順を追って説明していきます。

➀テキストボックスにメッセージを入力して「Send request」ボタンを押す
![5807097b-f708-4eec-8a02-07e519de292d.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/d731727e-6b72-6b81-5574-7b4750bd145f.jpeg)

➁AppleWatch側のトップにメッセージが表示される
![3fd2b7dc-5f21-4382-a414-4e711c24ca28.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/1c7a8222-8b0c-acaa-f00e-e86fecdad5a1.jpeg)

➂AppleWatch側の✕ボタンを押すと
![0cc2aba5-59a5-44d7-a63b-24c9f16f41e9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/f0a6e9a2-97af-b737-4d80-0a70a976f51e.jpeg)

➃選択したメッセージが削除されます。
![5d1f3815-c326-4a4e-a7e1-65a354710bcd.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950891/d63a2374-7ffa-e7fb-e43c-3cf25c2d500a.jpeg)

# まとめ
今回は、アプリの構想を練るところから製造、テストまでをご紹介しました。AppleWatchをはじめとしたウェアラブルデバイスは、市場では最近よく見かけるようになったものの、ビジネスシーンではコスト面のこともあり、まだあまり見かけません。しかし、AppleWatchのバイブレーション機能のように、ウェアラブルデバイスは、人間と一体になることで効果を発揮する機能があふれています。この観点で考えると、働き方改革に直結するシステムとかも生み出せそうですね。
今回作ったアプリも、これで終わりにするのではなく、地道に改良を重ねてより良いものにしていきたいと思います！(面白い発見があれば、Part3以降で取り上げるかもです。。。)

いかがだったでしょうか？今後も本取り組みを続けていこうと思うので、実機テストが出来た、あるいはアプリに進展があった際は、都度展開しようと思います。
最後までお読みいただきありがとうございました！
ではさようなら～
# おまけ
**なぜ配信して実機でテストしないの？**
今回のアプリは弊社のMacで作成しました。実機でテストするとなると、私のAppleWatchをMacに接続しなければなりません。業界のルール的に、個人のデバイスを会社のデバイスに接続するのは、セキュリティ面でよくないですよね。。。なので今回は内部のシミュレータ止まりとなったのです。
あ、ちなみに、TestFlight等でアプリを配信して実機テストする際も、配信時にAppleWatchのUUIDを登録しなければいけないので、似たような取り組みをされる方は注意が必要ですね！
もっとも、私がMacを持っていれば話は別なんですけどね。。。