Messages App Extensionについて

# Messages App Extension

個人的に一番熱かったメッセージのエクステンションの[リファレンス](https://developer.apple.com/reference/messages)を読んで少し触ってみました
※ iMessage Appsは後で追記したいと思います😎👉 *追記しました*

* Xcode Version 8.0 beta
* iOS Version 10.0 beta

> Create an app extension that interacts with the Messages app and lets the user send text, stickers, media files, and interactive messages. Update interactive messages, changing the message’s state as users take turns interacting with it.

Mwssages内でアプリを介してテキストはもちろん、ステッカー、メディアファイル、対話型のメッセージを送れるようになったようです
対話型メッセージ：ターン性の可変型メッセージ(Messagesで遊戯王ができるイメージです)

App Extensionの作成は主にiMessage appかSticker Packsの2種類に分かれます

# Sticker Packs
> Sticker packs simply provide static sets of stickers. They do not require any code. You add stickers by dragging image files into the Sticker Pack folder inside the Stickers assets catalog.

コードを書く必要がないただ画像をセットしたもの(クリエイターさんもXcodeを開くようになりますね☺️)
ただし以下の仕様を満たす必要があるみたいです

1. フォーマットはPNG,APNG,GIF,JPEG
2. サイズは500KB未満
3. 大きさは100x100[pt]から206x206[pt]

> Always provide @3x images (300 x 300 pixels to 618 x 618 pixels). The system generates the @2x and @1x versions by downscaling the @3x images at runtime.

@3xから@2xと@1xが生成されるってことは@3xだけ作ればいいのかな？

作った画像の大きさに合わせてインスペクタのサイズを選ぶ(表示される時の大きさになる)

* Small. 100 x 100 points.
* Medium. 136 x 136 points.
* Large. 206 x 206 points.

ちなみにGIFを使ってみたのですが、もちろんちゃんと動いてました

<img width="216" alt="スクリーンショット 2016-06-26 13.41.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/1ab99d2a-c72c-7db2-7de3-bd6d7b95819e.png">


# iMessage Apps
> iMessage apps leverage the full framework to interact with the Messages app.

Messagesの対話の中にアプリケーションを組み込めるフレームワークが提供されています
ちなみに[サンプル](https://developer.apple.com/library/prerelease/content/samplecode/IceCreamBuilder/Introduction/Intro.html#//apple_ref/doc/uid/TP40017329)が公開されているので見てみると分かりやすいです

1. [MSMessagesAppViewController](https://developer.apple.com/reference/messages/msmessagesappviewcontroller): Messages内の処理を判別しビューの表示をさせるなどするViewControllerの役割
2. [MSStickerBrowserViewController](https://developer.apple.com/reference/messages/msstickerbrowserviewcontroller): 上記のSticker Packsで提供されるステッカーブラウザをカスタマイズする時に使う
3. [MSConversation](https://developer.apple.com/reference/messages/msconversation): Messagesの入力フィールドに文字やステッカー、添付ファイルの入力が行える
4. [MSMessage](https://developer.apple.com/reference/messages/msmessage): 会話の中でインタラクティブなメッセージを作成できる(これが遊戯王ができるクラス)
5. [MSSession](https://developer.apple.com/reference/messages/mssession): 4で会話の相手も操作対象にする場合はこのセッションを管理しなくてはならない

## MSMessagesAppViewController

* 会話の内容やどのメッセージが選択されたかを判別したり画面遷移するメソッドが用意されている
* `MSMessagesAppPresentationStyle`というenumで表示する画面の大きさ(`compact`,`expanded`)を定義している

<img width="250" alt="スクリーンショット_2016-06-26_13_50_07.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/7fe18f99-1530-c49b-3ef6-dc8d058c49a4.png"><img width="231" alt="スクリーンショット_2016-06-26_13_48_29.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/6689c8db-4c8d-008e-5add-3555f57098d6.png">

サンプルコードでは`willBecomeActive:`と`willTransition:`で画面の大きさを判別して遷移先のViewControllerを変えてる

この`compact`と`expanded`はユーザーがいつでも切り替えられるようになっているので、画面設計時にはコンテンツの配置に注意が必要です(StackViewのありがたみが分かります)

### 画面サイズについて

これらの画面サイズがどのように使い分けられているかと変更の方法を簡単に調べてみました

#### compact

* Extensionが起動した時(`willBecomeActive:`が呼ばれる時)
* `expanded`状態でナビゲーションバー右のボタンをタップした時

#### expanded

* メッセージ(`MSMessage`)をタップした時
* `compact`状態でツールバー右のボタンをタップした時

#### requestPresentationStyle

`requestPresentationStyle:`を使用すれば画面サイズの変更を**リクエスト**できるようになります

※ ユーザーがもし上記のような画面サイズの変更が行われる操作をしていた場合はそちらが優先されます

## MSStickerBrowserViewController

* `MSStickerView`(ステッカー)を簡単に管理できるViewController
* デフォルトでドラッグ&ドロップができてステッカーを剥がして貼るようなアニメーションも付いてくる
* `MSStickerBrowserViewDataSource`プロトコルに準拠すること(TableViewみたいな感じ)

## MSConversation

* 会話の中で何が選択されたかどこに何を発言するかを管理するクラス
* `selectedMessage`が現在選択されているメッセージ(`MSMessage`)
* `insert:`や`insertAttachment:`でステッカーや添付ファイルを貼れる

サンプルコードでは`selectedMessage`から会話のセッションを見て作業しているアイスクリームの判別をしている

### 会話ユーザーの特定

`MSMessagesAppViewController`の`activeConversation`を使用すると、どのユーザーが会話に参加しているのか特定ができます

※ シミュレーターでは下記識別子がメッセージの変更や`MSMessagesAppViewController`の`willBecomeActive:`のタイミングで書き換わってしまいます😦バグ？

#### localParticipantIdentifier

* 自分自身の識別子です。これはデバイスの初期化かExtensionがアンインストールされない限り同じ値を持ち続けます

#### remoteParticipantIdentifiers

* 会話に参加している他の人の(このデバイスから定義した)識別子です。これも上記と同じで初期化かアンインストールで値が変わります

#### ユーザー名の表示

<img width="500" alt="スクリーンショット 2016-06-26 14.54.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/1bf6c688-0580-96f5-12a1-dc583ad0ae08.png">

上記の識別子を`"$\(localParticipantIdentifier.uuidString)"`のように使うとユーザー名を表示できるようになります　※ 現状できないです😐

## MSMessage

* インタラクティブなメッセージを作成できる
* 自分だけでなく会話の参加者が皆操作できるがセッション(`MSSession`)の管理が必須になる

`MSMessage`の中身は`URL`で外観は`MSMessageLayout`で構成されている

```swift
guard let components = NSURLComponents(string: myBaseURL) else {
    fatalError("Invalid base url")
}

let size = NSURLQueryItem(name: "Size", value: "Large")
let count = NSURLQueryItem(name: "Topping_Count", value: "2")
let cheese = NSURLQueryItem(name: "Topping_0", value: "Cheese")
let pepperoni = NSURLQueryItem(name: "Topping_1", value: "Pepperoni")
components.queryItems = [size, count, cheese, pepperoni]

guard let url = components.url  else {
    fatalError("Invalid URL components.")
}

message.url = url
```

### MSMessageTemplateLayout

大抵`MSMessageLayout`は直接サブクラス化することはなく`MSMessageTemplateLayout`をインスタンス化して使う

<img width="254" alt="スクリーンショット 2016-06-14 3.12.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/05fc646e-1d0d-151f-83fd-aafcf691c681.png">

このテンプレートの中のプロパティに値を入れれば良い

#### captionの非表示

<img width="500" alt="スクリーンショット 2016-06-26 14.53.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/83361/e4e5baf8-3d0f-2851-10d7-1e112412ba30.png">

セッションでは`caption`に何も入れなければ自動で画像だけのレイアウトになるとありますが、今の所灰色の背景が残ってしまいます😩

## MSSession

* `MSMessage`に持たせることができ、特定のメッセージを更新する時はこいつを指定する
* セッションを持たせたい場合は`init(session:)`でメッセージを生成する
* セッションを指定した場合、セッションが使用されて初めてメッセージは表示される
* 同じセッションのメッセージを更新した場合は会話の一番下に移動して内容が変更される(その場で変更もできるらしい)

### 特定のセッションのメッセージを更新する例

1. `selectedMessage`をKVOで監視してまたは`willTransition:`でタップされた`MSMessage`を特定する
2. 上記の通知が来たら、内容やレイアウトなりを変更した`MSMessage`を生成する(この時`init(session:)`でセッションを指定すること)
3. 送信する時に`insert:localizedChangeDescription:`メソッドを使えば`changeDescription`に元あった場所に変更の説明を残せる