---
title: "Connect to AWS AppSync Events"
section: "frontend/data"
platforms: ["android", "angular", "javascript", "nextjs", "react", "react-native", "swift", "vue"]
gen: 2
last-updated: "2026-03-25T17:40:00.000Z"
url: "https://docs.amplify.aws/react/frontend/data/connect-event-api/"
---

<!-- Platform: javascript, angular, react, vue, react-native, nextjs,swift -->
This guide walks through how you can connect to AWS AppSync Events using the Amplify library.
<!-- /Platform -->

<!-- Platform: android -->
This guide walks through how you can connect to AWS AppSync Events, with or without Amplify.
<!-- /Platform -->

AWS AppSync Events lets you create secure and performant serverless WebSocket APIs that can broadcast real-time event data to millions of subscribers, without you having to manage connections or resource scaling. With this feature, you can build multi-user features such as a collaborative document editors, chat apps, and live polling systems.

Learn more about AWS AppSync Events by visiting the [Developer Guide](https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html).

<!-- Platform: android -->
## Install the AWS AppSync Events library

#### [Without Amplify]

Add the `aws-sdk-appsync-events` dependency to your app/build.gradle.kts file.

```kotlin title="app/build.gradle.kts"
dependencies {
    // highlight-start
    // Adds the AWS AppSync Events library without adding any Amplify dependencies
    implementation("com.amazonaws:aws-sdk-appsync-events:ANDROID_APPSYNC_SDK_VERSION")    
    // highlight-end
}
```

#### [With Amplify]

Add the `aws-sdk-appsync-events` and `aws-sdk-appsync-amplify` dependencies to your app/build.gradle.kts file.

```kotlin title="app/build.gradle.kts"
dependencies {
    // highlight-start
    // Adds the AWS AppSync Events library
    implementation("com.amazonaws:aws-sdk-appsync-events:ANDROID_APPSYNC_SDK_VERSION")    
    // Contains AppSync Authorizers which connect to Amplify Auth
    implementation("com.amazonaws:aws-sdk-appsync-amplify:ANDROID_APPSYNC_SDK_VERSION")
    // highlight-end
}
```

### Providing AppSync Authorizers

#### [Without Amplify]

The AWS AppSync Events library imports a number of Authorizer classes to match the various authorization strategies that may be used for your Events API. You should choose the appropriate Authorizer type for your authorization strategy.

*  `apiKey` authorization, **APIKeyAuthorizer**
*  `identityPool` authorization, **IAMAuthorizer**
*  `userPool` authorization, **AuthTokenAuthorizer**

You can create as many Events clients as necessary if you require multiple authorization types.

#### API KEY

An `ApiKeyAuthorizer` can be used with a hardcoded API key or by fetching the key from some source.:

```kotlin
// highlight-start
// Use a hard-coded API key
val authorizer = ApiKeyAuthorizer("[API_KEY]")
//highlight-end
// or
// highlight-start
// Fetch the API key from some source. This function may be called many times,
// so it should implement appropriate caching internally.
val authorizer = ApiKeyAuthorizer { fetchApiKey() }
//highlight-end
```

#### AMAZON COGNITO USER POOLS

When working directly with AppSync, you must implement the token fetching yourself.

```kotlin
// highlight-start
// Use your own token fetching. This function may be called many times,
// so it should implement appropriate caching internally.
val authorizer = AuthTokenAuthorizer {
  fetchLatestAuthToken()
}
//highlight-end
```

#### AWS IAM

When working directly with AppSync, you must implement the request signing yourself.

```kotlin
// highlight-start
// Provide an implementation of the signing function. This function should implement the 
// AWS Sig-v4 signing logic and return the authorization headers containing the token and signature.
val authorizer = IamAuthorizer { appSyncRequest -> signRequestAndReturnHeaders(appSyncRequest) }
// highlight-end
```

#### [With Amplify]

The AWS AppSync Events library imports a number of Authorizer classes to match the various authorization strategies that may be used for your Events API. You should choose the appropriate Authorizer type for your authorization strategy.

*  `apiKey` authorization, **APIKeyAuthorizer**
*  `identityPool` authorization, **AmplifyIAMAuthorizer**
*  `userPool` authorization, **AmplifyUserPoolAuthorizer**

You can create as many Events clients as necessary if you require multiple authorization types.

#### API KEY

An `ApiKeyAuthorizer` can provide a hardcoded API key, or fetch the API key from some source:

```kotlin
// highlight-start
// Use a hard-coded API key
val authorizer = ApiKeyAuthorizer("[API_KEY]")
//highlight-end
// or
// highlight-start
// Fetch the API key from some source. This function may be called many times,
// so it should implement appropriate caching internally.
val authorizer = ApiKeyAuthorizer { fetchApiKey() }
//highlight-end
```

#### AMAZON COGNITO USER POOLS

The `AmplifyUserPoolAuthorizer` uses your configured Amplify instance to fetch AWS Cognito UserPool tokens and attach to the request.

```kotlin
// highlight-start
// Using the provided Amplify UserPool Authorizer
val authorizer = AmplifyUserPoolAuthorizer()
//highlight-end
```

#### AWS IAM

The `AmplifyIAMAuthorizer` uses your configured Amplify instance to sign a request with the IAM SigV4 protocol.

```kotlin
// highlight-start
// Using the provided Amplify IAM Authorizer
val authorizer = AmplifyIamAuthorizer("{REGION}")
//highlight-end
```

<!-- /Platform -->

<!-- Platform: swift -->
## Install the AWS AppSync Events library

#### [Without Amplify]

- Add `AWS AppSync Events Library for Swift` into your project using Swift Package Manager.

- Enter its Github URL (https://github.com/aws-amplify/aws-appsync-events-swift), select `Up to Next Major Version` and click `Add Package`.

- Select the following product and add it to your target:
  - `AWSAppSyncEvents`

#### [With Amplify]

- Add `AWS AppSync Events Library for Swift` into your project using Swift Package Manager.
  - Enter its Github URL (https://github.com/aws-amplify/aws-appsync-events-swift), select `Up to Next Major Version` and click `Add Package`.
  - Select the following product and add it to your target:
    - `AWSAppSyncEvents`

- Add `Amplify Library for Swift` into your project using Swift Package Manager.
  - Enter its Github URL (https://github.com/aws-amplify/amplify-swift), select `Up to Next Major Version` and click `Add Package`.
  - Select the following product and add it to your target:
    - `Amplify`
    - `AWSCognitoAuthPlugin`

### Providing AppSync Authorizers

#### [Without Amplify]

The AWS AppSync Events library imports a number of Authorizer classes to match the various authorization strategies that may be used for your Events API. You should choose the appropriate Authorizer type for your authorization strategy.

*  API KEY authorization, **APIKeyAuthorizer**
*  AWS IAM authorization, **IAMAuthorizer**
*  AMAZON COGNITO USER POOLS authorization, **AuthTokenAuthorizer**

You can create as many `Events` clients as necessary if you require multiple authorization types.

#### API KEY

An `APIKeyAuthorizer` can be used with a hardcoded API key or by fetching the key from some source.

```swift
// highlight-start
// Use a hard-coded API key
let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")
//highlight-end
// or
// highlight-start
// Fetch the API key from some source. This function may be called many times,
// so it should implement appropriate caching internally.
let authorizer = APIKeyAuthorizer(fetchAPIKey: { 
    // fetch your api key
 })
//highlight-end
```

#### AMAZON COGNITO USER POOLS

When working directly with AppSync, you must implement the token fetching yourself.

```swift
// highlight-start
// Use your own token fetching. This function may be called many times,
// so it should implement appropriate caching internally.
let authorizer = AuthTokenAuthorizer(fetchLatestAuthToken: {
  // fetch your auth token
})
//highlight-end
```

#### AWS IAM

When working directly with AppSync, you must implement the request signing yourself.

```swift
// highlight-start
// Provide an implementation of the signing function. This function should implement the 
// AWS Sig-v4 signing logic and return the authorization headers containing the token and signature.
let  authorizer = IAMAuthorizer(signRequest: {
  // implement your `URLRequest` signing logic
})
// highlight-end
```

#### [With Amplify]

The AWS AppSync Events library imports a number of Authorizer classes to match the various authorization strategies that may be used for your Events API. You should choose the appropriate Authorizer type for your authorization strategy.

*  API KEY authorization, **APIKeyAuthorizer**
*  AWS IAM authorization, **IAMAuthorizer**
*  AMAZON COGNITO USER POOLS authorization, **AuthTokenAuthorizer**

You can create as many `Events` clients as necessary if you require multiple authorization types.

#### API KEY

An `APIKeyAuthorizer` can be used with a hardcoded API key or by fetching the key from some source.

```swift
// highlight-start
// Use a hard-coded API key
let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")
//highlight-end
// or
// highlight-start
// Fetch the API key from some source. This function may be called many times,
// so it should implement appropriate caching internally.
let authorizer = APIKeyAuthorizer(fetchAPIKey: { 
    // fetch your api key
 })
//highlight-end
```

#### AMAZON COGNITO USER POOLS

If you are using Amplify Auth, you can create a method that retrieves the Cognito access token.

```swift
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.")
}
```

Then create the `AuthTokenAuthorizer` with this method.

```swift
let authorizer = AuthTokenAuthorizer(fetchLatestAuthToken: getUserPoolAccessToken)
```

#### AWS IAM

If you are using Amplify Auth, you can initialize `IAMAuthorizer` with a helper method from `AWSCognitoAuthPlugin` like below:

```swift
let authorizer = IAMAuthorizer(
  signRequest: AWSCognitoAuthPlugin.createAppSyncSigner(region: "region")
)
```

<!-- /Platform -->

## Connect to an Event API without an existing Amplify backend

Before you begin, you will need:

- An Event API created via the AWS Console
- Take note of: HTTP endpoint, region, API Key

<!-- Platform: android, swift -->
Thats it! Skip to [Client Library Usage Guide](#client-library-usage-guide).
<!-- /Platform -->

<!-- Platform: javascript, angular, react, vue, react-native, nextjs -->
```tsx title="src/App.tsx"
import type { EventsChannel } from 'aws-amplify/data';
import { useState, useEffect, useRef } from 'react';
import { Amplify } from 'aws-amplify';
import { events } from 'aws-amplify/data';

Amplify.configure({
  API: {
    Events: {
      endpoint:
        'https://abcdefghijklmnopqrstuvwxyz.appsync-api.us-east-1.amazonaws.com/event',
      region: 'us-east-1',
      defaultAuthMode: 'apiKey',
      apiKey: 'da2-abcdefghijklmnopqrstuvwxyz',
    },
  },
});

export default function App() {
 const [myEvents, setMyEvents] = useState<unknown[]>([]);

  const sub = useRef<ReturnType<EventsChannel['subscribe']>>(null);

  useEffect(() => {
    let channel: EventsChannel;

    const connectAndSubscribe = async () => {
      channel = await events.connect('default/channel');

      if (!sub.current) {
        sub.current = channel.subscribe({
          next: (data) => {
            console.log('received', data);
            setMyEvents((prev) => [data, ...prev]);
          },
          error: (err) => console.error('error', err),
        });
      }
    };

    connectAndSubscribe();

    return () => {
      sub.current?.unsubscribe();
      sub.current = null;
      return channel?.close();
    };
  }, []);

  async function publishEvent() {
    // Publish via HTTP POST
    await events.post('default/channel', { some: 'data' });

    // Alternatively, publish events through the WebSocket channel
    const channel = await events.connect('default/channel');
    await channel.publish({ some: 'data' });
  }

  return (
    <>
      <button onClick={publishEvent}>Publish Event</button>
      <ul>
        {myEvents.map((data, idx) => (
          <li key={idx}>{JSON.stringify(data.event, null, 2)}</li>
        ))}
      </ul>
    </>
  );
}
```
<!-- /Platform -->

## Add an Event API to an existing Amplify backend

This guide walks through how you can add an Event API to an existing Amplify backend. We'll be using Cognito User Pools for authenticating with Event API from our frontend application. Any signed in user will be able to subscribe to the Event API and publish events.

Before you begin, you will need:

- An existing Amplify backend (see [Quickstart](/[platform]/start/quickstart/))
- Latest versions of `@aws-amplify/backend` and `@aws-amplify/backend-cli` (`npm add @aws-amplify/backend@latest @aws-amplify/backend-cli@latest`)

### Update Backend Definition

First, we'll add a new Event API to our backend definition.

<!-- Platform: javascript, angular, react, vue, react-native, nextjs -->
```ts title="amplify/backend.ts"
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
// highlight-start
// import CDK resources:
import {
  CfnApi,
  CfnChannelNamespace,
  AuthorizationType,
} from 'aws-cdk-lib/aws-appsync';
import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
// highlight-end

const backend = defineBackend({
	auth,
});

// highlight-start
// create a new stack for our Event API resources:
const customResources = backend.createStack('custom-resources');

// add a new Event API to the stack:
const cfnEventAPI = new CfnApi(customResources, 'CfnEventAPI', {
  name: 'my-event-api',
  eventConfig: {
    authProviders: [
      {
        authType: AuthorizationType.USER_POOL,
        cognitoConfig: {
          awsRegion: customResources.region,
          // configure Event API to use the Cognito User Pool provisioned by Amplify:
          userPoolId: backend.auth.resources.userPool.userPoolId,
        },
      },
    ],
    // configure the User Pool as the auth provider for Connect, Publish, and Subscribe operations:
    connectionAuthModes: [{ authType: AuthorizationType.USER_POOL }],
    defaultPublishAuthModes: [{ authType: AuthorizationType.USER_POOL }],
    defaultSubscribeAuthModes: [{ authType: AuthorizationType.USER_POOL }],
  },
});

// create a default namespace for our Event API:
const namespace = new CfnChannelNamespace(
  customResources,
  'CfnEventAPINamespace',
  {
    apiId: cfnEventAPI.attrApiId,
    name: 'default',
  }
);

// attach a policy to the authenticated user role in our User Pool to grant access to the 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}`],
      }),
    ],
  })
);

// finally, add the Event API configuration to amplify_outputs:
backend.addOutput({
  custom: {
    events: {
      url: `https://${cfnEventAPI.getAtt('Dns.Http').toString()}/event`,
      aws_region: customResources.region,
      default_authorization_type: AuthorizationType.USER_POOL,
    },
  },
});
// highlight-end
```
<!-- /Platform -->

<!-- Platform: android, swift -->
```ts title="amplify/backend.ts"
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
// highlight-start
// import CDK resources:
import {
  CfnApi,
  CfnChannelNamespace,
  AuthorizationType,
} from 'aws-cdk-lib/aws-appsync';
import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
// highlight-end

const backend = defineBackend({
	auth,
});

// highlight-start
// create a new stack for our Event API resources:
const customResources = backend.createStack('custom-resources');

// add a new Event API to the stack:
const cfnEventAPI = new CfnApi(customResources, 'CfnEventAPI', {
  name: 'my-event-api',
  eventConfig: {
    authProviders: [
      {
        authType: AuthorizationType.USER_POOL,
        cognitoConfig: {
          awsRegion: customResources.region,
          // configure Event API to use the Cognito User Pool provisioned by Amplify:
          userPoolId: backend.auth.resources.userPool.userPoolId,
        },
      },
    ],
    // configure the User Pool as the auth provider for Connect, Publish, and Subscribe operations:
    connectionAuthModes: [{ authType: AuthorizationType.USER_POOL }],
    defaultPublishAuthModes: [{ authType: AuthorizationType.USER_POOL }],
    defaultSubscribeAuthModes: [{ authType: AuthorizationType.USER_POOL }],
  },
});

// create a default namespace for our Event API:
const namespace = new CfnChannelNamespace(
  customResources,
  'CfnEventAPINamespace',
  {
    apiId: cfnEventAPI.attrApiId,
    name: 'default',
  }
);

// attach a policy to the authenticated user role in our User Pool to grant access to the 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}`],
      }),
    ],
  })
);
// highlight-end
```
<!-- /Platform -->

### Deploy Backend

To test your changes, deploy your Amplify Sandbox.

```bash title="Terminal" showLineNumbers={false}
npx ampx sandbox
```

<!-- Platform: javascript, angular, react, vue, react-native, nextjs -->
### Connect your frontend application

After the sandbox deploys, connect your frontend application to the Event API. We'll be using the [Amplify Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator) to sign in to our Cognito User Pool.

If you don't already have the Authenticator installed, you can install it by running `npm add @aws-amplify/ui-react`.

```tsx title="src/App.tsx"
import { useEffect, useState } from 'react';
import { Amplify } from 'aws-amplify';
import { events, type EventsChannel } from 'aws-amplify/data';
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import outputs from '../amplify_outputs.json';

Amplify.configure(outputs);

export default function App() {
  const [myEvents, setMyEvents] = useState<Record<string, any>[]>([]);

  useEffect(() => {
    let channel: EventsChannel;

    const connectAndSubscribe = async () => {
      channel = await events.connect('default/channel');

      channel.subscribe({
        next: (data) => {
          console.log('received', data);
          setMyEvents((prev) => [data, ...prev]);
        },
        error: (err) => console.error('error', err),
      });
    };

    connectAndSubscribe();

    return () => channel && channel.close();
  }, []);

  async function publishEvent() {
    // Publish via HTTP POST
    await events.post('default/channel', { some: 'data' });

    // Alternatively, publish events through the WebSocket channel
    const channel = await events.connect('default/channel');
    await channel.publish({ some: 'data' });
  }

  return (
    <Authenticator>
      {({ signOut, user }) => (
        <>
          <div>
            <h1>Welcome, {user.username}</h1>
            <button onClick={signOut}>Sign Out</button>
          </div>
          <div>
            <button onClick={publishEvent}>Publish Event</button>
            <ul>
            {myEvents.map((data) => (
              <li key={data.id}>{JSON.stringify(data.event)}</li>
              ))}
            </ul>
          </div>
        </>
      )}
    </Authenticator>
  );
}
```
<!-- /Platform -->

<!-- Platform: android, swift -->
## Client Library Usage Guide
<!-- /Platform -->

<!-- Platform: android -->
### Create the Events class

You can find your endpoint in the AWS AppSync Events console. It should start with `https` and end with `/event`.

```kotlin
val endpoint = "https://abcdefghijklmnopqrstuvwxyz.appsync-api.us-east-1.amazonaws.com/event"
val events = Events(endpoint)
```

### Using the REST Client

An `EventsRestClient` can be created to publish event(s) over REST. It accepts a publish authorizer that will be used by default for any publish calls within the client.

#### Creating the REST Client

``` kotlin
val events: Events // Your configured Events
val restClient = events.createRestClient(
  publishAuthorizer = ApiKeyAuthorizer("da2-abcdefghijklmnopqrstuvwxyz")
)
```

Additionally, you can pass custom options to the Rest Client. Current capabilities include modifying the `OkHttp.Builder` the `EventsRestClient` uses, and enabling client library logs.
See [Collecting Client Library Logs](#collecting-client-library-logs) for the AndroidLogger class.

``` kotlin
val restClient = events.createRestClient(
    publishAuthorizer = ApiKeyAuthorizer("da2-abcdefghijklmnopqrstuvwxyz"),
    options = Events.Options.Rest(
        loggerProvider = { namespace -> AndroidLogger(namespace, logLevel) },
        okHttpConfigurationProvider = { okHttpBuilder ->
            // update OkHttp.Builder used by EventsRestClient
        }
    )
)
```

#### Publish a Single Event

```kotlin
val restClient: EventsRestClient // Your configured EventsRestClient
// kotlinx.serialization.json.[JsonElement, JsonPrimitive, JsonArray, JsonObject]
val jsonEvent = JsonObject(mapOf("some" to JsonPrimitive("data")))
val publishResult = restClient.publish(
  channelName = "default/channel", 
  event = jsonEvent
)
when(publishResult) {
  is PublishResult.Response -> {
    val successfulEvents = publishResult.successfulEvents // inspect successful events
    val failedEvents = publishResult.failedEvents // inspect failed events
  }
  is PublishResult.Failure -> {
    val error = result.error // publish failure, inspect error
  }
}
```

#### Publish multiple events

You can publish up to 5 events at a time.

```kotlin
val restClient: EventsRestClient // Your configured EventsRestClient
// List of kotlinx.serialization.json.[JsonElement, JsonPrimitive, JsonArray, JsonObject]
val jsonEvents = listOf(
  JsonObject(mapOf("some" to JsonPrimitive("data1"))),
  JsonObject(mapOf("some" to JsonPrimitive("data2")))
)
val publishResult = restClient.publish(
  channelName = "default/channel",
  events = jsonEvents
)
when(publishResult) {
  is PublishResult.Response -> {
    val successfulEvents = publishResult.successfulEvents // inspect successful events
    val failedEvents = publishResult.failedEvents // inspect failed events
  }
  is PublishResult.Failure -> {
    val error = result.error // publish failure, inspect error
  }
}
```

#### Publish with a different authorizer

```kotlin
restClient.publish(
  channelName = "default/channel", 
  event = JsonObject(mapOf("some" to JsonPrimitive("data"))),
  authorizer = ApiKeyAuthorizer("da2-abcdefghijklmnopqrstuvwxyz")
)
```

### Using the WebSocket Client

An `EventsWebSocketClient` can be created to publish and subscribe to channels. The WebSocket connection is managed by the library and connects on the first subscribe or publish operation. Once connected, the WebSocket will remain open. You should explicitly disconnect the client when you no longer need to subscribe or publish to channels.

#### Creating the WebSocket Client 

```kotlin
val events: Events // Your configured Events
val apiKeyAuthorizer = ApiKeyAuthorizer("da2-abcdefghijklmnopqrstuvwxyz")
val webSocketClient = events.createWebSocketClient(
  connectAuthorizer = apiKeyAuthorizer, // used to connect the websocket
  subscribeAuthorizer = apiKeyAuthorizer, // used for subscribe calls
  publishAuthorizer = apiKeyAuthorizer // used for publish calls
)
```

Additionally, you can pass custom options to the WebSocket Client. Current capabilities include modifying the `OkHttp.Builder` the `EventsWebSocketClient` uses, and enabling client library logs.
See [Collecting Client Library Logs](#collecting-client-library-logs) for the AndroidLogger class.

```kotlin
val events: Events // Your configured Events
val apiKeyAuthorizer = ApiKeyAuthorizer("da2-abcdefghijklmnopqrstuvwxyz")
val webSocketClient = events.createWebSocketClient(
  connectAuthorizer = apiKeyAuthorizer, // used to connect the websocket
  subscribeAuthorizer = apiKeyAuthorizer, // used for subscribe calls
  publishAuthorizer = apiKeyAuthorizer // used for publish calls,
  options = Events.Options.WebSocket(
    loggerProvider = { namespace -> AndroidLogger(namespace, logLevel) },
    okHttpConfigurationProvider = { okHttpBuilder ->
            // update OkHttpBuilder used by EventsWebSocketClient
    }
  )
)
```

#### Publish a Single Event

```kotlin
val webSocketClient: EventsWebSocketClient // Your configured EventsWebSocketClient
// kotlinx.serialization.json.[JsonElement, JsonPrimitive, JsonArray, JsonObject]
val jsonEvent = JsonObject(mapOf("some" to JsonPrimitive("data")))
val publishResult = webSocketClient.publish(
  channelName = "default/channel",
  event = jsonEvent
)
when(publishResult) {
  is PublishResult.Response -> {
    val successfulEvents = publishResult.successfulEvents // inspect successful events
    val failedEvents = publishResult.failedEvents // inspect failed events
  }
  is PublishResult.Failure -> {
    val error = result.error // publish failure, inspect error
  }
}
```

#### Publish multiple Events

You can publish up to 5 events at a time.

```kotlin
val webSocketClient: EventsWebSocketClient // Your configured EventsWebSocketClient
// List of kotlinx.serialization.json.[JsonElement, JsonPrimitive, JsonArray, JsonObject]
val jsonEvents = listOf(
  JsonObject(mapOf("some" to JsonPrimitive("data1"))),
  JsonObject(mapOf("some" to JsonPrimitive("data2")))
)
val publishResult = webSocketClient.publish(
  channelName = "default/channel",
  events = jsonEvents
)
when(publishResult) {
  is PublishResult.Response -> {
    val successfulEvents = publishResult.successfulEvents // inspect successful events
    val failedEvents = publishResult.failedEvents // inspect failed events
  }
  is PublishResult.Failure -> {
    val error = result.error // publish failure, inspect error
  }
}
```

#### Publish with a different authorizer

```kotlin
val webSocketClient: EventsWebSocketClient // Your configured EventsWebSocketClient
val jsonEvent = JsonObject(mapOf("some" to JsonPrimitive("data")))
val publishResult = webSocketClient.publish(
  channelName = "default/channel",
  event = jsonEvent,
  authorizer = ApiKeyAuthorizer("da2-abcdefghijklmnopqrstuvwxyz")
)
when(publishResult) {
  is PublishResult.Response -> {
    val successfulEvents = publishResult.successfulEvents // inspect successful events
    val failedEvents = publishResult.failedEvents // inspect failed events
  }
  is PublishResult.Failure -> {
    val error = result.error // publish failure, inspect error
  }
}
```

#### Subscribing to a channel.

When subscribing to a channel, you can subscribe to a specific namespace/channel (ex: "default/channel"), or you can specify a wildcard (`*`) at the end of a channel path to receive events published to all channels that match (ex: "default/*").

Choosing the proper Coroutine Scope for the subscription Flow is critical. You should choose a scope to live for the lifetime in which you need the subscription. For example, if you want a subscription to be tied to a screen, you should consider using `viewModelScope` from an AndroidX `ViewModel`. The subscription Flow would persist configuration changes and be cancelled when `onCleared()` is triggered on the ViewModel.
When the Flow's Coroutine Scope is cancelled, the library will unsubscribe from the channel.

```kotlin
coroutineScope.launch {
  // subscribe returns a cold Flow. The subscription is established once a terminal operator is invoked.
  val subscription: Flow<EventsMessage> = webSocketClient.subscribe("default/channel").onCompletion {
    // Subscription has been unsubscribed
  }.catch { throwable ->
    // Subscription encountered an error and has been unsubscribed
    // See throwable for cause
  }

  // collect starts the subscription
  subscription.collect { eventsMessage ->
    // Returns a JsonElement type. 
    // Use Kotlin Serialization to convert into your preferred data structure.
    val jsonData = eventsMessage.data
  }
}
```

#### Subscribing to a channel with a different authorizer.

```kotlin
coroutineScope.launch {
  // subscribe returns a cold Flow. The subscription is established once a terminal operator is invoked.
  val subscription: Flow<EventsMessage> = webSocketClient.subscribe(
      channelName = "default/channel".
      authorizer = ApiKeyAuthorizer("da2-abcdefghijklmnopqrstuvwxyz")
  ).onCompletion {
    // Subscription has been unsubscribed
  }.catch { throwable ->
    // Subscription encountered an error and has been unsubscribed
    // See throwable for cause
  }

  // collect starts the subscription
  subscription.collect { eventsMessage ->
    // Returns a JsonElement type.
    val jsonData = eventsMessage.data
    // Use Kotlin Serialization to convert into your preferred data structure.
  }
}
```

#### Disconnecting the WebSocket

When you are done using the WebSocket and do not intend to call publish/subscribe on the client, you should disconnect the WebSocket. This will unsubscribe all channels.

```kotlin
val webSocketClient: EventsWebSocketClient // Your configured EventsWebSocketClient
// set flushEvents to true if you want to wait for any pending publish operations to post to the WebSocket
// set flushEvents to false to immediately disconnect, discarding any pending posts to the WebSocket
webSocketClient.disconnect(flushEvents = true) // or false to immediately disconnect
```
<!-- /Platform -->

<!-- Platform: swift -->
### Create the Events class

You can find your endpoint in the AWS AppSync Events console. It should start with `https` and end with `/event`.

```swift
let eventsEndpoint = "https://abcdefghijklmnopqrstuvwxyz.appsync-api.us-east-1.amazonaws.com/event"
let events = Events(endpointURL: eventsEndpoint)
```

### Using the REST Client

An `EventsRestClient` can be created to publish event(s) over REST. It accepts a publish authorizer that will be used by default for any publish calls within the client.

#### Creating the REST Client

```swift
let events = Events(endpointURL: eventsEndpoint)
let restClient = events.createRestClient(
  publishAuthorizer: APIKeyAuthorizer(apiKey: "apiKey")
)
```

Additionally, you can pass custom options to the Rest Client. Current capabilities include passing a custom `URLSessionConfiguration` object, a prepend `URLRequestInterceptor` and enabling client library logs.
See [Collecting Client Library Logs](#collecting-client-library-logs) and `RestOptions` class for more details.

```swift
let restClient = events.createRestClient(
  publishAuthorizer: apiKeyAuthorizer,
  options: .init(
    urlSessionConfiguration: urlSessionConfiguration, // your instance of `URLSessionConfiguration`
    logger: AppSyncEventsLogger(), // your implementation of `EventsLogger`
    interceptor: AppSyncEventsURLRequestInterceptor() // your implementation of `URLRequestInterceptor`
  )
)
```

#### Publish a single event

```swift
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)")
}
```

#### Publish multiple events

You can publish up to 5 events at a time.

```swift
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)")
}
```

#### Publish with a different authorizer

```swift
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)")
}
```

### Using the WebSocket Client

An `EventsWebSocketClient` can be created to publish and subscribe to channels. The WebSocket connection is managed by the library and connects on the first subscribe or publish operation. Once connected, the WebSocket will remain open. You should explicitly disconnect the client when you no longer need to subscribe or publish to channels.

#### Creating the WebSocket Client 

```swift
let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")
let webSocketClient = events.createWebSocketClient(
  connectAuthorizer: apiKeyAuthorizer,
  publishAuthorizer: apiKeyAuthorizer,
  subscribeAuthorizer: apiKeyAuthorizer
)
```

Additionally, you can pass custom options to the WebSocket Client. Current capabilities include passing a custom `URLSessionConfiguration` object, a prepend `URLRequestInterceptor` and enabling client library logs.
See [Collecting Client Library Logs](#collecting-client-library-logs) and `WebSocketOptions` class for more details.

```swift
let apiKeyAuthorizer = APIKeyAuthorizer(apiKey: "apiKey")
let webSocketClient = events.createWebSocketClient(
  connectAuthorizer: apiKeyAuthorizer,
  publishAuthorizer: apiKeyAuthorizer,
  subscribeAuthorizer: apiKeyAuthorizer,
  options: .init(
    urlSessionConfiguration: urlSessionConfiguration, // your instance of `URLSessionConfiguration`
    logger: AppSyncEventsLogger(), // your implementation of `EventsLogger`
    interceptor: AppSyncEventsURLRequestInterceptor() // your implementation of `URLRequestInterceptor`
  ) 
)
```

#### Publish a Single Event

```swift
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)")
}
```

#### Publish multiple Events

You can publish up to 5 events at a time.

```swift
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)")
}
```

#### Publish with a different authorizer

```swift
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)")
}
```

#### Subscribing to a channel

When subscribing to a channel, you can subscribe to a specific namespace/channel (e.g. `default/channel`), or you can specify a wildcard (`*`) at the end of a channel path to receive events published to all channels that match (e.g. `default/*`).

```swift
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))"
    }
}
```
To unsubscribe from the channel, you can cancel the enclosing task for the `AsyncThrowingStream`.

```swift
task.cancel()
```

#### Subscribing to a channel with a different authorizer

```swift
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))"
    }
}
```

#### Disconnecting the WebSocket

When you are done using the WebSocket and do not intend to call publish/subscribe on the client, you should disconnect the WebSocket. This will unsubscribe all channels.

```swift
// set flushEvents to true if you want to wait for any pending publish operations to post to the WebSocket
// set flushEvents to false to immediately disconnect, discarding any pending posts to the WebSocket
try await webSocketClient.disconnect(flushEvents: true) // or false to immediately disconnect
```
<!-- /Platform -->

<!-- Platform: android, swift -->
### Collecting Client Library Logs
<!-- /Platform -->

<!-- Platform: android -->
In the Rest Client and WebSocket Client examples, we demonstrated logging to a custom logger. Here is an example of a custom logger that writes logs to Android's Logcat. You are free to implement your own `Logger` type.

```kotlin
class AndroidLogger(
    private val namespace: String,
    override val thresholdLevel: LogLevel
) : Logger {

    override fun error(message: String) {
        if (!thresholdLevel.above(LogLevel.ERROR)) {
            Log.e(namespace, message)
        }
    }

    override fun error(message: String, error: Throwable?) {
        if (!thresholdLevel.above(LogLevel.ERROR)) {
            Log.e(namespace, message, error)
        }
    }

    override fun warn(message: String) {
        if (!thresholdLevel.above(LogLevel.WARN)) {
            Log.w(namespace, message)
        }
    }

    override fun warn(message: String, issue: Throwable?) {
        if (!thresholdLevel.above(LogLevel.WARN)) {
            Log.w(namespace, message, issue)
        }
    }

    override fun info(message: String) {
        if (!thresholdLevel.above(LogLevel.INFO)) {
            Log.i(namespace, message)
        }
    }

    override fun debug(message: String) {
        if (!thresholdLevel.above(LogLevel.DEBUG)) {
            Log.d(namespace, message)
        }
    }

    override fun verbose(message: String) {
        if (!thresholdLevel.above(LogLevel.VERBOSE)) {
            Log.v(namespace, message)
        }
    }
}
```
<!-- /Platform -->

<!-- Platform: swift -->
In the Rest Client and WebSocket Client examples, we demonstrated logging to a custom logger. Here is an example of a custom logger that writes logs to Xcode console. You are free to implement your own `EventsLogger` type.

```swift
import os
import Foundation
import 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())
    }
}

```
<!-- /Platform -->
