IVS Chat Client Messaging iOS SDK を使用する
このドキュメントでは、Amazon IVS Chat Client Messaging iOS SDK を使用するためのステップについて説明します。
チャットルームに接続する
開始する前に、「Amazon IVS Chat の開始方法」を理解しておく必要があります。ウェブ
チャットルームに接続するには、アプリがバックエンドによって提供されたチャットトークンを取得する何らかの方法が必要です。アプリケーションはおそらく、バックエンドへのネットワークリクエストを使用してチャットトークンを取得します。
このフェッチされたチャットトークンを SDK と通信するには、SDK の ChatRoom モデルで、async 関数、または初期化の時点で提供された ChatTokenProvider プロトコルに準拠するオブジェクトのインスタンスを提供する必要があります。これらのメソッドのいずれかによって返される値は、SDK ChatToken モデルのインスタンスである必要があります。
注意: バックエンドから取得したデータを使用して、ChatToken モデルのインスタンスを設定します。ChatToken インスタンスの初期化に必要なフィールドは、CreateChatToken レスポンスのフィールドと同じです。ChatToken モデルのインスタンスの初期化の詳細については、「ChatToken のインスタンスの作成」を参照してください。バックエンドは、アプリへの CreateChatToken リスポンスでデータを提供する責任を担っていることに留意してください。バックエンドと通信してチャットトークンを生成する方法は、アプリとそのインフラストラクチャによります。
ChatToken を SDK へ提供する戦略を選んだ後、トークンプロバイダーと、接続しようとしているチャットルームを作成するためにバックエンドが使用した AWS リージョンで ChatRoom インスタンスを正常に初期化した後に .connect() を呼び出します。.connect() は次の非同期関数のスローであることに注意してください。
import AmazonIVSChatMessaging let room = ChatRoom( awsRegion: <region-your-backend-created-the-chat-room-in>, tokenProvider: <your-chosen-token-provider-strategy> ) try await room.connect()
ChatTokenProvider プロトコルに準拠
ChatRoom のイニシャライザの tokenProvider パラメータには、ChatTokenProvider のインスタンスを指定できます。ChatTokenProvider に準拠するオブジェクトの例を次に示します。
import AmazonIVSChatMessaging // This object should exist somewhere in your app class ChatService: ChatTokenProvider { func getChatToken() async throws -> ChatToken { let request = YourApp.getTokenURLRequest let data = try await URLSession.shared.data(for: request).0 ... return ChatToken( token: String(data: data, using: .utf8)!, tokenExpirationTime: ..., // this is optional sessionExpirationTime: ... // this is optional ) } }
次に、この準拠オブジェクトのインスタンスを取得し、それを ChatRoom のイニシャライザに渡すことができます。
// This should be the same AWS Region that you used to create // your Chat Room in the Control Plane let awsRegion = "us-west-2" let service = ChatService() let room = ChatRoom( awsRegion: awsRegion, tokenProvider: service ) try await room.connect()
Swift で非同期関数を提供する
アプリケーションのネットワークリクエストを管理するマネージャーが、既にあるとします。次のように指定します。
import AmazonIVSChatMessaging class EndpointManager { func getAccounts() async -> AppUser {...} func signIn(user: AppUser) async {...} ... }
マネージャーに別の関数を追加して、バックエンドから ChatToken を取得できます。
import AmazonIVSChatMessaging class EndpointManager { ... func retrieveChatToken() async -> ChatToken {...} }
次に、ChatRoom を初期化する際に、Swift でその関数への参照を使用します。
import AmazonIVSChatMessaging let endpointManager: EndpointManager let room = ChatRoom( awsRegion: endpointManager.awsRegion, tokenProvider: endpointManager.retrieveChatToken ) try await room.connect()
ChatToken のインスタンスの作成
SDK で提供されているイニシャライザを使用して、ChatToken のインスタンスを簡単に作成できます。ChatToken のプロパティの詳細について理解するには、Token.swift にあるドキュメントを参照してください。
import AmazonIVSChatMessaging let chatToken = ChatToken( token: <token-string-retrieved-from-your-backend>, tokenExpirationTime: nil, // this is optional sessionExpirationTime: nil // this is optional )
Decodable を使用する
IVS チャット API とのインターフェイス中に、バックエンドが CreateChatToken レスポンスをフロントエンドアプリケーションにシンプルに転送することを決定した場合、Swift の Decodable プロトコルへの ChatToken の準拠を利用できます。ただし、注意点があります。
CreateChatToken リスポンスペイロードは、インターネットタイムスタンプの ISO 8601 標準JSONDecoder.DateDecodingStrategy.iso8601 を JSONDecoder の .dateDecodingStrategy プロパティへ値として提供しますCreateChatToken は文字列で高精度の小数秒を使用しますが、これは JSONDecoder.DateDecodingStrategy.iso8601 ではサポートされていません。
便宜上、SDK は JSONDecoder.DateDecodingStrategy にパブリック拡張機能を提供し、追加の .preciseISO8601 ストラテジーを使用して、ChatToken のインスタンスをデコードするときに JSONDecoder を正常に使用できるようにします。
import AmazonIVSChatMessaging // The CreateChatToken data forwarded by your backend let responseData: Data let decoder = JSONDecoder() decoder.dateDecodingStrategy = .preciseISO8601 let token = try decoder.decode(ChatToken.self, from: responseData)
チャットルームの接続を切断する
正常に接続した ChatRoom インスタンスから手動で切断するには、room.disconnect() を呼び出します。デフォルトでは、チャットルームは割り当てが解除されると自動的にこの関数を呼び出します。
import AmazonIVSChatMessaging let room = ChatRoom(...) try await room.connect() // Disconnect room.disconnect()
チャットメッセージ/イベントを受信する
チャットルームでメッセージを送受信するには、ChatRoom のインスタンスを正常に初期化し、room.connect() を呼び出した後、ChatRoomDelegate プロトコルに準拠するオブジェクトを提供する必要があります。UIViewController を使用した典型的な例を次に示します。
import AmazonIVSChatMessaging import Foundation import UIKit class ViewController: UIViewController { let room: ChatRoom = ChatRoom( awsRegion: "us-west-2", tokenProvider: EndpointManager.shared ) override func viewDidLoad() { super.viewDidLoad() Task { try await setUpChatRoom() } } private func setUpChatRoom() async throws { // Set the delegate to start getting notifications for room events room.delegate = self try await room.connect() } } extension ViewController: ChatRoomDelegate { func room(_ room: ChatRoom, didReceive message: ChatMessage) { ... } func room(_ room: ChatRoom, didReceive event: ChatEvent) { ... } func room(_ room: ChatRoom, didDelete message: DeletedMessageEvent) { ... } }
接続が変更されたときに通知を受け取る
当然のことながら、ルームが完全に接続されるまで、ルームでメッセージを送信するなどのアクションは実行できません。SDK のアーキテクチャは、非同期 API を介してバックグラウンドスレッドの ChatRoom への接続を推奨しようとします。メッセージ送信ボタンなどを無効にする何かを UI に構築する場合、SDK は Combine または ChatRoomDelegate を使用して、チャットルームの接続状態が変化したときに通知を受け取るための 2 つの戦略を提供します。これについて以下に説明します。
重要: チャットルームの接続状態は、ネットワーク接続の切断などによっても変化する可能性があります。アプリをビルトするときには、この点を考慮してください。
Combine を使用する
ChatRoom のすべてのインスタンスには、state プロパティの形式で独自の Combine パブリッシャーが付属しています。
import AmazonIVSChatMessaging import Combine var cancellables: Set<AnyCancellable> = [] let room = ChatRoom(...) room.state.sink { state in switch state { case .connecting: let image = UIImage(named: "antenna.radiowaves.left.and.right") sendMessageButton.setImage(image, for: .normal) sendMessageButton.isEnabled = false case .connected: let image = UIImage(named: "paperplane.fill") sendMessageButton.setImage(image, for: .normal) sendMessageButton.isEnabled = true case .disconnected: let image = UIImage(named: "antenna.radiowaves.left.and.right.slash") sendMessageButton.setImage(image, for: .normal) sendMessageButton.isEnabled = false } }.assign(to: &cancellables) // Connect to `ChatRoom` on a background thread Task(priority: .background) { try await room.connect() }
ChatRoomDelegate を使用する
また、ChatRoomDelegate に準拠するオブジェクト内でオプション関数の roomDidConnect(_:)、roomIsConnecting(_:)、および roomDidDisconnect(_:) を使用することもできます。UIViewController を使用する例を次に示します。
import AmazonIVSChatMessaging import Foundation import UIKit class ViewController: UIViewController { let room: ChatRoom = ChatRoom( awsRegion: "us-west-2", tokenProvider: EndpointManager.shared ) override func viewDidLoad() { super.viewDidLoad() Task { try await setUpChatRoom() } } private func setUpChatRoom() async throws { // Set the delegate to start getting notifications for room events room.delegate = self try await room.connect() } } extension ViewController: ChatRoomDelegate { func roomDidConnect(_ room: ChatRoom) { print("room is connected!") } func roomIsConnecting(_ room: ChatRoom) { print("room is currently connecting or fetching a token") } func roomDidDisconnect(_ room: ChatRoom) { print("room disconnected!") } }
チャットルームでアクションを実行する
チャットルームで実行できる機能 (メッセージの送信、メッセージの削除、ユーザーの接続解除など) は、ユーザーごとに異なります。これらのアクションのいずれかを実行するには、接続されている ChatRoom で perform(request:) を呼び出し、SDK で提供されている ChatRequest オブジェクトのいずれかのインスタンスを渡します。対応しているリクエストは Request.swift にあります。
チャットルームで実行されるアクションの中には、バックエンドアプリケーションが CreateChatToken を呼び出す場合に、接続しているユーザーに特定の権限を付与する必要があるものがあります。設計上、SDK は接続しているユーザーの機能を識別できません。したがって、接続された ChatRoom のインスタンスでモデレーターアクションを実行しようとすることはできますが、最終的にそのアクションが成功するかどうかは、control-plane API が決定します。
room.perform(request:) を通過するすべてのアクションは、受信したモデルとリクエストオブジェクトの両方の requestId に一致するモデルの予期されたインスタンス (そのタイプはリクエストオブジェクト自体に関連付けられています) をルームが受信するまで待機します。リクエストに問題がある場合、ChatRoom は常に ChatError の形でエラーをスローします。ChatError の定義は Error.swift にあります。
メッセージの送信
チャットメッセージを送信するには、 SendMessageRequest インスタンスを使用してください。
import AmazonIVSChatMessaging let room = ChatRoom(...) try await room.connect() try await room.perform( request: SendMessageRequest( content: "Release the Kraken!" ) )
前述のとおり、room.perform(request:) は ChatRoom が対応する ChatMessage を受け取ると返されます。リクエストに問題がある場合 (ルームのメッセージの文字数制限を超えるなど)、代わりに ChatError のインスタンスがスローされます。その場合、次の有用な情報を UI に表示できます。
import AmazonIVSChatMessaging do { let message = try await room.perform( request: SendMessageRequest( content: "Release the Kraken!" ) ) print(message.id) } catch let error as ChatError { switch error.errorCode { case .invalidParameter: print("Exceeded the character limit!") case .tooManyRequests: print("Exceeded message request limit!") default: break } print(error.errorMessage) }
メッセージへのメタデータの追加
メッセージを送信する場合、関連するメタデータを追加できます。SendMessageRequest は attributes プロパティがあり、これを使用してリクエストを初期化できます。そこにアタッチしたデータは、他のユーザーがルームでそのメッセージを受信したときにメッセージにアタッチされます。
送信中のメッセージにエモートデータを添付する例を以下に示します。
import AmazonIVSChatMessaging let room = ChatRoom(...) try await room.connect() try await room.perform( request: SendMessageRequest( content: "Release the Kraken!", attributes: [ "messageReplyId" : "<other-message-id>", "attached-emotes" : "krakenCry,krakenPoggers,krakenCheer" ] ) )
SendMessageRequest で attributes を使用することは、チャット製品で複雑な機能を構築するのに非常に役立ちます。たとえば、SendMessageRequest の [String :
String] 属性ディクショナリを使用して、スレッド機能を構築できます。
attributes ペイロードは非常に柔軟で強力です。これを使うと、他の方法では得られないようなメッセージについての情報を導き出すことができます。属性を使用する方が、たとえばメッセージの文字列を解析してエモートなどの情報を取得するよりもはるかに簡単です。
メッセージの削除
チャットメッセージの削除は、チャットメッセージの送信と同じです。メッセージを削除するには、ChatRoom で room.perform(request:) 関数を利用して、DeleteMessageRequest インスタンスを作成します。
受信したチャットメッセージの以前のインスタンスに簡単にアクセスするために、message.id の値を DeleteMessageRequest のイニシャライザに渡します。
必要に応じて、DeleteMessageRequest へ理由文字列を提供し、UI で表示できます。
import AmazonIVSChatMessaging let room = ChatRoom(...) try await room.connect() try await room.perform( request: DeleteMessageRequest( id: "<other-message-id-to-delete>", reason: "Abusive chat is not allowed!" ) )
これはモデレーターのアクションであるため、ユーザーは実際には別のユーザーのメッセージを削除できない場合があります。Swift のスロー可能な関数メカニズムを使用すると、適切な権限のないユーザーがメッセージを削除しようとしたときに UI にエラーメッセージを表示することができます。
バックエンドがユーザーに対して CreateChatToken を呼び出す場合、接続されたチャットユーザーに対してその機能を有効にするには、capabilities フィールドに "DELETE_MESSAGE" を渡す必要があります。
適切な権限がない状態でメッセージを削除しようとした場合、スローされる機能エラーをキャッチする例を次に示します。
import AmazonIVSChatMessaging do { // `deleteEvent` is the same type as the object that gets sent to // `ChatRoomDelegate`'s `room(_:didDeleteMessage:)` function let deleteEvent = try await room.perform( request: DeleteMessageRequest( id: "<other-message-id-to-delete>", reason: "Abusive chat is not allowed!" ) ) dataSource.messages[deleteEvent.messageID] = nil tableView.reloadData() } catch let error as ChatError { switch error.errorCode { case .forbidden: print("You cannot delete another user's messages. You need to be a mod to do that!") default: break } print(error.errorMessage) }
他のユーザーの接続を切断する
チャットルームから他のユーザーの接続を切断するには、room.perform(request:) を使用します。具体的には、DisconnectUserRequest のインスタンスを使用してください。ChatRoom が受信するすべての ChatMessage には sender プロパティがあり、これには DisconnectUserRequest のインスタンスで適切に初期化する必要があるユーザー ID が含まれています。必要に応じて、接続解除リクエストの理由文字列を指定します。
import AmazonIVSChatMessaging let room = ChatRoom(...) try await room.connect() let message: ChatMessage = dataSource.messages["<message-id>"] let sender: ChatUser = message.sender let userID: String = sender.userId let reason: String = "You've been disconnected due to abusive behavior" try await room.perform( request: DisconnectUserRequest( id: userID, reason: reason ) )
これはモデレーターアクションであるため、DISCONNECT_USER の権限がない限り別のユーザーの接続を切断することはできません。バックエンドアプリケーションが CreateChatToken を呼び出し "DISCONNECT_USER" 文字列を capabilities フィールドに挿入すると、権限が設定されます。
ユーザーが別のユーザーとの接続を切断する権限がない場合は、他のリクエストと同様に room.perform(request:) は ChatError のインスタンスをスローします。エラーの errorCode プロパティを調べ、モデレーター権限がないためにリクエストが失敗したかどうかを判断できます。
import AmazonIVSChatMessaging do { let message: ChatMessage = dataSource.messages["<message-id>"] let sender: ChatUser = message.sender let userID: String = sender.userId let reason: String = "You've been disconnected due to abusive behavior" try await room.perform( request: DisconnectUserRequest( id: userID, reason: reason ) ) } catch let error as ChatError { switch error.errorCode { case .forbidden: print("You cannot disconnect another user. You need to be a mod to do that!") default: break } print(error.errorMessage) }