MessageKitを使用したメッセージ機能の実装概要

# はじめに
仕事でiOSアプリのメッセージ機能をアーキテクチャから作り直す機会をいただいたので、その時にわかったMessageKitの概要について書いていこうと思います。
※自社の仕様に依存している技術が多々あります。

# この記事で説明する内容
- MessageKitのインストール
- メッセージ履歴の表示
- ソケットでのメッセージ送信
- メッセージのインサート

# MessageKitのインストール
```
pod 'MessageKit', '~> 3.7.0'
```
バージョンは3.7.0を使用しています。
上記をPodFileに記述してターミナルでpod install

# メッセージ履歴の表示
```swift
import MessageKit
```
メッセージ画面にしたいVCに上記を記述すれば、messagesCollectionViewとしてメッセージのセルをもつcollectionViewを管理できます。
```swift
// メッセージ入力部分の設定
messageInputBar.delegate = self
// メッセージ表示の設定
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
```
```swift
// MARK: - MessagesDataSource
extension YourViewController: MessagesDataSource {

    func currentSender() -> SenderType {
        return Sender(id: "your_user_id", displayName: "Your Name")
    }

    func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType {
        return messages[indexPath.section] //sectionで順番を管理しているのがミスしがちなポイント 
    }

    func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int {
        return messages.count
    }
}

// MARK: - MessagesLayoutDelegate
extension YourViewController: MessagesLayoutDelegate {
    // カスタムなレイアウト設定があればここに追加
}

// MARK: - MessagesDisplayDelegate
extension YourViewController: MessagesDisplayDelegate {
    // カスタムな表示設定があればここに追加
}

// MARK: - MessageInputBarDelegate
extension YourViewController: MessageInputBarDelegate {
    // メッセージ入力部分のデリゲートメソッドがあればここに追加
}
```
上記のコードが最低限あればメッセージの表示ができます。
messagesにはその時々のメッセージを、指定された型に合わせて配列で用意すればOKです。
以下が１つのメッセージの型です。
```swift
Message(sender: Sender(id: "1", displayName: "User1"), messageId: "1", sentDate: Date(), kind: .text("Hello, World!"))
```
# ソケットでのメッセージの送信
```swift
import Foundation
import Network

class SocketManager {

    private var connection: NWConnection?

    init() {
        // ソケットの設定
        let host = "your_server_host"
        let port = "your_server_port"

        // 接続先のエンドポイントを作成
        let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(host), port: NWEndpoint.Port(port)!)

        // ソケットの接続
        connection = NWConnection(to: endpoint, using: .tcp)

        // データの受信ハンドラを設定
        connection?.receiveMessage { (data, context, isComplete, error) in
            if let data = data, !data.isEmpty {
                // データを受信したときの処理
                let receivedString = String(data: data, encoding: .utf8)
                print("Received data: \(receivedString ?? "")")
            }

            if isComplete {
                // ソケットの受信が完了したときの処理
            }
        }
    }

    func start() {
        // ソケットの開始
        connection?.start(queue: .global())
    }

    func send(message: String) {
        // メッセージをデータに変換
        if let data = message.data(using: .utf8) {
            // ソケットにデータを送信
            connection?.send(content: data, completion: .contentProcessed({ (error) in
                if let error = error {
                    print("Error sending data: \(error)")
                } else {
                    print("Data sent successfully")
                }
            }))
        }
    }

    func stop() {
        // ソケットの停止
        connection?.cancel()
    }
}

// 使用例
let socketManager = SocketManager()
socketManager.start()
socketManager.send(message: "Hello, Server!")
```
上記でStringのメッセージを送ることができます。
String以外の送る方法があるかもしれませんが、現状使用している技術はStringの送信だけです。
&や\、；等の記号を用いて区切りを入れるなどして画像や動画のIDを送信している形をとっています。

# メッセージのインサート
メッセージのインサートは簡単で先ほどのメッセージの型
```swift
Message(sender: Sender(id: "1", displayName: "User1"), messageId: "1", sentDate: Date(), kind: .text("Hello, World!"))
```
をクライアントで作成してmessagesの配列に追加して
```swift
messagesCollectionView.reloadData()
```
でメッセージ画面をリロードするだけでインサートできます。

# 最後に
以上で簡易的ですが、メッセージ周りを実装する上での基本的な機能の紹介を終わります。

弊社では、経験の有無を問わず採用を行っています。
興味のある方は是非カジュアル面談しましょう！

https://jambo-support.com/recruit_engineer/