LoginSignup
0
1

More than 1 year has passed since last update.

NCMBのSwift SDKとWebSocketを使ってチャットアプリを作る(その3:チャットの表示とチャットデータの取得)

Last updated at Posted at 2021-09-08

ハンズオン開催

こちらの内容を元にオンラインハンズオンを開催します。1時間程度になりますので、ご興味があればぜひご参加ください。

NCMBのSwift SDKを使ってデモアプリを作ってみます。リアルタイム通信系は人気があるのですが、NCMBでは残念ながらWebSocketは使えません。そこで今回はPieSocketというWebSocketを提供するサービスと組み合わせて、Swift製のチャットアプリを作ってみます。

前回はチャットメッセージの送信について解説しました。今回はチャットメッセージの表示と、既存データの取得について解説します。

コードについて

今回のコードはNCMBMania/Swift_Chat_Demoにアップロードしてあります。実装時の参考にしてください。

チャットメッセージの表示

チャットメッセージの表示は ChatView にて行います。WebSocketの接続やメッセージの送受信を管理している ChatScreenModelObservedObject として定義しています。

struct ChatView: View {
    @ObservedObject var chat = ChatScreenModel()

そして chat.messages にメッセージが追加されたらリストを更新しています。この時、ForEach の id は objectId になります。

ScrollView {
    ScrollViewReader { proxy in
        LazyVStack(spacing: 8) {
            ForEach(chat.messages, id: \.objectId ) { message in
                ChatMessageRow(message: message)
            }
        }
        .onChange(of: chat.messages.count) { _ in
            scrollToLastMessage(proxy: proxy)
        }
    }
}

scrollToLastMessage はリストの最後のメッセージを追いかける形で自動スクロールするためのメソッドです。

// 自動スクロール用
private func scrollToLastMessage(proxy: ScrollViewProxy) {
    if let lastMessage = chat.messages.last {
        withAnimation(.easeOut(duration: 0.4)) {
            proxy.scrollTo(lastMessage.objectId, anchor: .bottom)
        }
    }
}

自分と相手のメッセージで表示を分ける

自分のメッセージか否かで表示(色や配置)を分けています。これは ChatMessageRow で実装しています。自分のメッセージかどうかはNCMBObjectにあるuserId(チャット投稿者のobjectId)を使って判定しています。

struct ChatMessageRow: View {
    @State var message: NCMBObject
    // 日付のフォーマット(時刻のみ)
    static private let dateFormatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateStyle = .none
            formatter.timeStyle = .short
            return formatter
        }()

    var body: some View {
        HStack {
            if isMe() {
                Spacer()
            }
            VStack(alignment: .leading, spacing: 6) {
                HStack {
                    Text(message["displayName"]! as String)
                        .fontWeight(.bold)
                        .font(.system(size: 12))

                    Text(Self.dateFormatter.string(from: createDate()))
                        .font(.system(size: 10))
                        .opacity(0.7)
                }

                Text(message["body"]! as String)
            }
            .foregroundColor(isMe() ? .white : .black)
            .padding(10)
            .background(isMe() ? Color.blue : Color(white: 0.95))
            .cornerRadius(5)

            if !isMe() {
                Spacer()
            }
        }
    }

    // 自分宛かどうか判定する関数
    func isMe() -> Bool {
        if let userId: String = message["userId"] {
            let user = NCMBUser.currentUser
            return userId == user!.objectId
        }
        return false
    }

    // 日付をフォーマットに沿って返す
    func createDate() -> Date {
        if let createDate: String = message["createDate"] {
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
            return dateFormatter.date(from: createDate)!
        }
        return Date()
    }
}

既存メッセージの取得

Simulator Screen Shot - iPod touch (7th generation) - 2021-09-08 at 13.54.42.png

過去にやり取りされたメッセージは NCMB のChatクラスにあります。チャット画面が表示されたタイミングで、そのデータを取得します。データを受け取ったらメインスレッドで更新する必要があるので注意してください。メッセージはそのまま chat.messages に入れてしまえば、表示に反映されます。

// NCMBに保存されているメッセージを取得する関数
func getPastMessages() {
    // データ取得用のクエリオブジェクトを用意
    var query = NCMBQuery.getQuery(className: "Chat")
    // 並び順はcreateDateの昇順
    query.order = ["createDate"]
    // 20件取得
    query.limit = 20
    // 検索実行
    query.findInBackground(callback: { result in
        // 結果判定
        switch result {
        case let .success(ary):
            // 取得できた場合は結果をチャットメッセージとして反映
            DispatchQueue.main.async {
                chat.messages = ary
            }
            break
        case .failure(_): break // エラーの場合
        }
    })
}

反映をリアルタイムで行う

メッセージが追加された際に表示への反映をリアルタイムで行うため ChatScreenModelmessages@Published を付けておきます。

final class ChatScreenModel: ObservableObject {
    private var webSocketTask: URLSessionWebSocketTask?
    @Published var messages: [NCMBObject] = []

これで過去のメッセージも表示できるようになります。

まとめ

今回はNCMBの匿名認証とデータストア、そしてPieSocketを使ってチャットアプリを作りました。今回はチャンネルIDを1固定にしていますが、これを変えることで複数のチャットルームにも対応します。ぜひカスタマイズして、Swiftアプリの中にチャット機能を組み込んでください。

ドキュメント : 開発者向けドキュメント | ニフクラ mobile backend

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1