【Swift】MessageKitを解剖する

こんにちは。
まだまだ雑魚ですがプログラミングのことしか頭にありません。
そんな中でMessageKitの理解がプログラミングへの理解を深めてくれたので
この記事を書きました。

チャットアプリなどのポートフォリオ作成で使用するMessageKitについて
解説していく記事。
特に型に注視していただけると視野が広がるかと思う。

##MessageKitのコード概要
チャットを行うにあたって必要な情報は
・送信者の情報
・メッセージのタイプ,情報
たったこの2つで、MessageKitではこの2つが揃ってしまえば実装できる。

####送信者の情報とは
・userId(固有のID)
　ユーザー登録させても良いし、
　登録させないなら端末の固有ID(UDID)で仮実装。(非実用的)
・userName(ディスプレイに表示する)

####メッセージのタイプ,情報とは
・messageId(固有IDでメッセージ削除などに使う)
・messageType(テキストか、写真か、動画か等)
・日付(チャットでは必須。ソート用にも)
・messageContent(メッセージの内容)

MessageKitは、大体これくらいの情報で実装することができる。

##SenderType
これは送信者の情報に関するプロトコルである。
プロトコルは、準拠した時にそこに記述されたプロパティや関数を
書かないと怒られるもので、コードの保守性をあげるのに役立つ。

```Swift
public protocol SenderType {

    /// The unique String identifier for the sender.
    ///
    /// Note: This value must be unique across all senders.
    var senderId: String { get }

    /// The display name of a sender.
    var displayName: String { get }
}
```
SenderTypeプロトコルの中身はこんな感じ。
import MessageKitとして SenderTypeを記述し、
command + カーソルを合わせる
↓
Jump to Definition
で記述場所にジャンプすると確認できる。

SenderTypeプロトコルの中身には、
senderIdとdisplayNameが記述されている。
このプロトコルに準拠した構造体Senderをこちらで実装する。

```Swift
struct Sender: SenderType {
    var senderId: String
    var displayName: String
}
```
これで送信者情報の構造体ができた。
チャットの実装において、送信者に関する処理では
この構造体を型にして使っていく。
あ、チャット実装するならこのコード書いてね。

##MessageType
冒頭に述べた最低限必要な情報のもう一方、メッセージのタイプ・情報。
中身を覗いてみる。

```Swift
public protocol MessageType {

    /// The sender of the message.
    var sender: SenderType { get }

    /// The unique identifier for the message.
    var messageId: String { get }

    /// The date the message was sent.
    var sentDate: Date { get }

    /// The kind of message and its underlying kind.
    var kind: MessageKind { get }

}
```

senderの型がSenderType型になっている。
先ほど実装したSenderを、MessageTypeに準拠した構造体の
senderプロパティに代入して利用するわけだ。

messageIdは既に説明したが、メッセージのID。
個別にメッセージを削除したりするのに使う。

sentDateは日付情報。

kindはメッセージが文字なのか、写真なのか、動画なのかを判別する。
中身を覗いてみる。

```Swift
public enum MessageKind {

    /// A standard text message.
    ///
    /// - Note: The font used for this message will be the value of the
    /// `messageLabelFont` property in the `MessagesCollectionViewFlowLayout` object.
    ///
    /// Using `MessageKind.attributedText(NSAttributedString)` doesn't require you
    /// to set this property and results in higher performance.
    case text(String)
    
    /// A message with attributed text.
    case attributedText(NSAttributedString)

    /// A photo message.
    case photo(MediaItem)

    /// A video message.
    case video(MediaItem)

    /// A location message.
    case location(LocationItem)

    /// An emoji message.
    case emoji(String)
    //以下省略
```

enumでケースごとに分けられているのがわかる。
text、attributedText、photo、などなど。

MessageTypeに準拠した構造体をかくとこうなる。

```Swift
struct Message: MessageType {
    var sender: SenderType
    var messageId: String
    var sentDate: Date
    var kind: MessageKind
    
    private init(kind: MessageKind, sender: Sender, messageId: String, date: Date) {
        self.kind = kind
        self.sender = sender
        self.messageId = messageId
        self.sentDate = date
    }
    init(attributedText: NSAttributedString, sender: Sender, messageId: String, date: Date) {
        self.init(kind: .attributedText(attributedText), sender: sender, messageId: messageId, date: date)
    }
}
```

最低限これだけあれば実装可能。
private initとprivateのついていないinitがある。
これは、MessageKindによって外部からアクセスする初期化処理を分け、
private initを通してMessage構造体を初期化する。

写真や動画も送信する想定なら、このprivateのついていないinitを
その数だけ書き足すことになるが、テキストのみの想定なので1つしか用意していない。


##次回：ChatViewControllerの実装
特に反響がなければ次回の記事は書かない予定。
いや、MessageKitだとどんなのが需要あるんだろう。
装飾系の説明？