AWS AppSync Eventsに接続する
このガイドでは、Amplifyライブラリを使用してAWS AppSync Eventsに接続する方法について説明します。
AWS AppSync Eventsを使用すると、接続やリソーススケーリングを管理することなく、数百万のサブスクライバーにリアルタイムイベントデータをブロードキャストできるセキュアで高性能なサーバーレスWebSocket APIを作成できます。この機能を使用すると、共同ドキュメントエディタ、チャットアプリ、ライブポーリングシステムなどの複数ユーザー機能を構築できます。
AWS AppSync Eventsの詳細については、デベロッパーガイドをご覧ください。
AWS AppSync Eventsライブラリのインストール
-
Swift Package Managerを使用して、
AWS AppSync Events Library for Swiftをプロジェクトに追加します。 -
その GitHub URL (https://github.com/aws-amplify/aws-appsync-events-swift)を入力し、`Up to Next Major Version
を選択して、Add Package`をクリックします。 -
次のプロダクトを選択してターゲットに追加します:
AWSAppSyncEvents
-
Swift Package Managerを使用して、
AWS AppSync Events Library for Swiftをプロジェクトに追加します。- その GitHub URL (https://github.com/aws-amplify/aws-appsync-events-swift)を入力し、`Up to Next Major Version
を選択して、Add Package`をクリックします。 - 次のプロダクトを選択してターゲットに追加します:
AWSAppSyncEvents
- その GitHub URL (https://github.com/aws-amplify/aws-appsync-events-swift)を入力し、`Up to Next Major Version
-
Swift Package Managerを使用して、
Amplify Library for Swiftをプロジェクトに追加します。- その GitHub URL (https://github.com/aws-amplify/amplify-swift)を入力し、`Up to Next Major Version
を選択して、Add Package`をクリックします。 - 次のプロダクトを選択してターゲットに追加します:
AmplifyAWSCognitoAuthPlugin
- その GitHub URL (https://github.com/aws-amplify/amplify-swift)を入力し、`Up to Next Major Version
AppSync認可者の提供
AWS AppSync Eventsライブラリは、Events APIで使用される可能性のあるさまざまな認可戦略に対応する複数の認可者クラスをインポートします。使用する認可戦略に適切な認可者タイプを選択する必要があります。
- API KEY認可の場合、APIKeyAuthorizer
- AWS IAM認可の場合、IAMAuthorizer
- AMAZON COGNITO USER POOLS認可の場合、AuthTokenAuthorizer
複数の認可タイプが必要な場合は、必要に応じて複数のEventsクライアントを作成できます。
API KEY
APIKeyAuthorizerはハードコードされたAPIキーか、何らかのソースからキーを取得することで使用できます。
// ハードコードされたAPIキーを使用しますlet apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")// または// 何らかのソースからAPIキーを取得します。この関数は何度も呼び出される可能性があるため、// 適切なキャッシュを内部的に実装する必要があります。let authorizer = APIKeyAuthorizer(fetchAPIKey: { // APIキーをフェッチします })AMAZON COGNITO USER POOLS
AppSyncで直接作業する場合は、トークンフェッチを自分で実装する必要があります。
// 独自のトークンフェッチを使用します。この関数は何度も呼び出される可能性があるため、// 適切なキャッシュを内部的に実装する必要があります。let authorizer = AuthTokenAuthorizer(fetchLatestAuthToken: { // 認可トークンをフェッチします})AWS IAM
AppSyncで直接作業する場合は、リクエスト署名を自分で実装する必要があります。
// 署名関数の実装を提供します。この関数はAWS Sig-v4署名ロジックを実装し、// トークンと署名を含む認可ヘッダーを返す必要があります。let authorizer = IAMAuthorizer(signRequest: { // `URLRequest`署名ロジックを実装します})AWS AppSync Eventsライブラリは、Events APIで使用される可能性のあるさまざまな認可戦略に対応する複数の認可者クラスをインポートします。使用する認可戦略に適切な認可者タイプを選択する必要があります。
- API KEY認可の場合、APIKeyAuthorizer
- AWS IAM認可の場合、IAMAuthorizer
- AMAZON COGNITO USER POOLS認可の場合、AuthTokenAuthorizer
複数の認可タイプが必要な場合は、必要に応じて複数のEventsクライアントを作成できます。
API KEY
APIKeyAuthorizerはハードコードされたAPIキーか、何らかのソースからキーを取得することで使用できます。
// ハードコードされたAPIキーを使用しますlet apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")// または// 何らかのソースからAPIキーを取得します。この関数は何度も呼び出される可能性があるため、// 適切なキャッシュを内部的に実装する必要があります。let authorizer = APIKeyAuthorizer(fetchAPIKey: { // APIキーをフェッチします })AMAZON COGNITO USER POOLS
Amplify Authを使用している場合は、Cognito アクセストークンを取得するメソッドを作成できます。
import Amplify
func getUserPoolAccessToken() async throws -> String { let authSession = try await Amplify.Auth.fetchAuthSession() if let result = (authSession as? AuthCognitoTokensProvider)?.getCognitoTokens() { switch result { case .success(let tokens): return tokens.accessToken case .failure(let error): throw error } } throw AuthError.unknown("Did not receive a valid response from fetchAuthSession for get token.")}その後、このメソッドでAuthTokenAuthorizerを作成します。
let authorizer = AuthTokenAuthorizer(fetchLatestAuthToken: getUserPoolAccessToken)AWS IAM
Amplify Authを使用している場合は、以下のようにAWSCognitoAuthPluginのヘルパーメソッドでIAMAuthorizerを初期化できます:
let authorizer = IAMAuthorizer( signRequest: AWSCognitoAuthPlugin.createAppSyncSigner(region: "region"))既存のAmplifyバックエンドなしでEvent APIに接続する
開始する前に、以下が必要です:
- AWSコンソールで作成されたEvent API
- メモを取ってください: HTTPエンドポイント、リージョン、APIキー
完了です! クライアントライブラリ使用ガイドに進んでください。
既存のAmplifyバックエンドにEvent APIを追加する
このガイドでは、既存のAmplifyバックエンドにEvent APIを追加する方法について説明します。フロントエンドアプリケーションからEvent APIで認証するためにCognito User Poolsを使用します。サインインしているユーザーはEvent APIをサブスクライブしてイベントをパブリッシュできます。
開始する前に、以下が必要です:
- 既存のAmplifyバックエンド(クイックスタートを参照)
@aws-amplify/backendと@aws-amplify/backend-cliの最新バージョン(npm add @aws-amplify/backend@latest @aws-amplify/backend-cli@latest)
バックエンド定義の更新
まず、バックエンド定義に新しいEvent APIを追加します。
import { defineBackend } from '@aws-amplify/backend';import { auth } from './auth/resource';// CDKリソースをインポートします:import { CfnApi, CfnChannelNamespace, AuthorizationType,} from 'aws-cdk-lib/aws-appsync';import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
const backend = defineBackend({ auth,});
// Event APIリソースの新しいスタックを作成します:const customResources = backend.createStack('custom-resources');
// スタックに新しいEvent APIを追加します:const cfnEventAPI = new CfnApi(customResources, 'CfnEventAPI', { name: 'my-event-api', eventConfig: { authProviders: [ { authType: AuthorizationType.USER_POOL, cognitoConfig: { awsRegion: customResources.region, // Event APIをAmplifyによってプロビジョンされたCognito User Poolを使用するように設定します: userPoolId: backend.auth.resources.userPool.userPoolId, }, }, ], // Connect、Publish、および Subscribe操作の認可プロバイダーとしてUser Poolを設定します: connectionAuthModes: [{ authType: AuthorizationType.USER_POOL }], defaultPublishAuthModes: [{ authType: AuthorizationType.USER_POOL }], defaultSubscribeAuthModes: [{ authType: AuthorizationType.USER_POOL }], },});
// Event APIのデフォルトネームスペースを作成します:const namespace = new CfnChannelNamespace( customResources, 'CfnEventAPINamespace', { apiId: cfnEventAPI.attrApiId, name: 'default', });
// User Pool内の認証されたユーザーロールにポリシーをアタッチしてEvent APIへのアクセスを許可します:backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy( new Policy(customResources, 'AppSyncEventPolicy', { statements: [ new PolicyStatement({ actions: [ 'appsync:EventConnect', 'appsync:EventSubscribe', 'appsync:EventPublish', ], resources: [`${cfnEventAPI.attrApiArn}/*`, `${cfnEventAPI.attrApiArn}`], }), ], }));バックエンドのデプロイ
変更をテストするには、Amplify Sandboxをデプロイします。
npx ampx sandboxクライアントライブラリ使用ガイド
Eventsクラスの作成
エンドポイントはAWS AppSync Eventsコンソールで確認できます。httpsで始まり、/eventで終わる必要があります。
let eventsEndpoint = "https://abcdefghijklmnopqrstuvwxyz.appsync-api.us-east-1.amazonaws.com/event"let events = Events(endpointURL: eventsEndpoint)RESTクライアントの使用
EventsRestClientを作成してRESTでイベントをパブリッシュできます。クライアント内での任意のパブリッシュコールに対して使用される、パブリッシュ認可者を受け入れます。
RESTクライアントの作成
let events = Events(endpointURL: eventsEndpoint)let restClient = events.createRestClient( publishAuthorizer: APIKeyAuthorizer(apiKey: "apiKey"))さらに、カスタムオプションをRest Clientに渡すことができます。現在の機能には、カスタムURLSessionConfigurationオブジェクトの渡し方、URLRequestInterceptorの前置き、およびクライアントライブラリログの有効化が含まれます。
クライアントライブラリログの収集およびRestOptionsクラスの詳細については、を参照してください。
let restClient = events.createRestClient( publishAuthorizer: apiKeyAuthorizer, options: .init( urlSessionConfiguration: urlSessionConfiguration, // `URLSessionConfiguration`のインスタンス logger: AppSyncEventsLogger(), // `EventsLogger`の実装 interceptor: AppSyncEventsURLRequestInterceptor() // `URLRequestInterceptor`の実装 ))単一のイベントをパブリッシュする
let defaultChannel = "default/channel"let event = JSONValue(stringLiteral: "123")do { let result = try await restClient.publish( channelName: defaultChannel, event: event ) print("Publish success with result:\n status: \(result.status) \n, successful events: \(result.successfulEvents) \n, failed events: \(result.failedEvents)")} catch { print("Publish failure with error: \(error)")}複数のイベントをパブリッシュする
一度に最大5つのイベントをパブリッシュできます。
let defaultChannel = "default/channel"let eventsList = [ JSONValue(stringLiteral: "123"), JSONValue(booleanLiteral: true), JSONValue(floatLiteral: 1.25), JSONValue(integerLiteral: 37), JSONValue(dictionaryLiteral: ("key", "value"))]
do { let result = try await restClient.publish( channelName: defaultChannel, events: eventsList ) print("Publish success with result:\n status: \(result.status) \n, successful events: \(result.successfulEvents) \n, failed events: \(result.failedEvents)")} catch { print("Publish failure with error: \(error)")}異なる認可者でパブリッシュする
let defaultChannel = "default/channel"let event = JSONValue(stringLiteral: "123")let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")do { let result = try await restClient.publish( channelName: defaultChannel, event: event, authorizer: apiKeyAuthorizer ) print("Publish success with result:\n status: \(result.status) \n, successful events: \(result.successfulEvents) \n, failed events: \(result.failedEvents)")} catch { print("Publish failure with error: \(error)")}WebSocketクライアントの使用
EventsWebSocketClientを作成してチャネルにパブリッシュしたりサブスクライブしたりできます。WebSocket接続はライブラリによって管理され、最初のサブスクライブまたはパブリッシュ操作時に接続します。接続すると、WebSocketは開いたままになります。クライアントをサブスクライブまたはパブリッシュする必要がなくなった場合は、明示的に切断する必要があります。
WebSocketクライアントの作成
let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")let webSocketClient = events.createWebSocketClient( connectAuthorizer: apiKeyAuthorizer, publishAuthorizer: apiKeyAuthorizer, subscribeAuthorizer: apiKeyAuthorizer)さらに、カスタムオプションをWebSocket Clientに渡すことができます。現在の機能には、カスタムURLSessionConfigurationオブジェクトの渡し方、URLRequestInterceptorの前置き、およびクライアントライブラリログの有効化が含まれます。
クライアントライブラリログの収集およびWebSocketOptionsクラスの詳細については、を参照してください。
let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")let webSocketClient = events.createWebSocketClient( connectAuthorizer: apiKeyAuthorizer, publishAuthorizer: apiKeyAuthorizer, subscribeAuthorizer: apiKeyAuthorizer, options: .init( urlSessionConfiguration: urlSessionConfiguration, // `URLSessionConfiguration`のインスタンス logger: AppSyncEventsLogger(), // `EventsLogger`の実装 interceptor: AppSyncEventsURLRequestInterceptor() // `URLRequestInterceptor`の実装 ) )単一のイベントをパブリッシュする
let defaultChannel = "default/channel"let event = JSONValue(stringLiteral: "123")do { let result = try await websocketClient.publish( channelName: defaultChannel, event: event ) print("Publish success with result:\n status: \(result.status) \n, successful events: \(result.successfulEvents) \n, failed events: \(result.failedEvents)")} catch { print("Publish failure with error: \(error)")}複数のイベントをパブリッシュする
一度に最大5つのイベントをパブリッシュできます。
let defaultChannel = "default/channel"let eventsList = [ JSONValue(stringLiteral: "123"), JSONValue(booleanLiteral: true), JSONValue(floatLiteral: 1.25), JSONValue(integerLiteral: 37), JSONValue(dictionaryLiteral: ("key", "value"))]
do { let result = try await websocketClient.publish( channelName: defaultChannel, events: eventsList ) print("Publish success with result:\n status: \(result.status) \n, successful events: \(result.successfulEvents) \n, failed events: \(result.failedEvents)")} catch { print("Publish failure with error: \(error)")}異なる認可者でパブリッシュする
let defaultChannel = "default/channel"let event = JSONValue(stringLiteral: "123")let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")do { let result = try await websocketClient.publish( channelName: defaultChannel, event: event, authorizer: apiKeyAuthorizer ) print("Publish success with result:\n status: \(result.status) \n, successful events: \(result.successfulEvents) \n, failed events: \(result.failedEvents)")} catch { print("Publish failure with error: \(error)")}チャネルをサブスクライブする
チャネルをサブスクライブするときは、特定のネームスペース/チャネル(例: default/channel)をサブスクライブするか、チャネルパスの末尾にワイルドカード(*)を指定して、マッチするすべてのチャネルにパブリッシュされたイベントを受け取ります(例: default/*)。
let defaultChannel = "default/channel"let subscription = try websocketClient.subscribe(channelName: defaultChannel)let task = Task { for try await message in subscription { print("Subscription received message: \(message))" }}AsyncThrowingStreamの周囲のタスクをキャンセルして、チャネルをアンサブスクライブできます。
task.cancel()異なる認可者でチャネルをサブスクライブする
let defaultChannel = "default/channel"let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")let subscription = try websocketClient.subscribe( channelName: defaultChannel, authorizer: apiKeyAuthorizer)let task = Task { for try await message in subscription { print("Subscription received message: \(message))" }}WebSocketを切断する
WebSocketの使用を終了し、クライアントでサブスクライブまたはパブリッシュを呼び出すつもりがない場合は、WebSocketを切断する必要があります。これにより、すべてのチャネルがアンサブスクライブされます。
// 保留中のパブリッシュ操作をWebSocketにポストするのを待つ場合はflushEventsをtrueに設定します// 保留中のポストを破棄してすぐに切断する場合はflushEventsをfalseに設定しますtry await webSocketClient.disconnect(flushEvents: true) // またはfalseを指定してすぐに切断しますクライアントライブラリログの収集
RESTクライアントおよびWebSocketクライアントの例では、カスタムロガーへのログを示しました。以下は、ログをXcodeコンソールに書き込むカスタムロガーの例です。自由に独自のEventsLoggerタイプを実装できます。
import osimport Foundationimport AWSAppSyncEvents
public final class AppSyncEventsLogger: EventsLogger { static let lock: NSLocking = NSLock()
static var _logLevel = LogLevel.error public init() { } public var logLevel: LogLevel { get { AppSyncEventsLogger.lock.lock() defer { AppSyncEventsLogger.lock.unlock() }
return AppSyncEventsLogger._logLevel } set { AppSyncEventsLogger.lock.lock() defer { AppSyncEventsLogger.lock.unlock() }
AppSyncEventsLogger._logLevel = newValue } }
public func error(_ log: @autoclosure () -> String) { os_log("%@", type: .error, log()) }
public func error(_ error: @autoclosure () -> Error) { os_log("%@", type: .error, error().localizedDescription) }
public func warn(_ log: @autoclosure () -> String) { guard logLevel.rawValue >= LogLevel.warn.rawValue else { return }
os_log("%@", type: .info, log()) }
public func info(_ log: @autoclosure () -> String) { guard logLevel.rawValue >= LogLevel.info.rawValue else { return }
os_log("%@", type: .info, log()) }
public func debug(_ log: @autoclosure () -> String) { guard logLevel.rawValue >= LogLevel.debug.rawValue else { return }
os_log("%@", type: .debug, log()) }
public func verbose(_ log: @autoclosure () -> String) { guard logLevel.rawValue >= LogLevel.verbose.rawValue else { return }
os_log("%@", type: .debug, log()) }}