---
source_url: https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/custom-events
title: Create custom events
updated_at: 2026-06-16T12:48:53.358Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Create custom events

Learn how Chat SDK handles events before creating your own [custom chat events](#create-and-send-events).

## Event handling

### PubNub events

With a standard PubNub SDK like the [Swift SDK](https://www.pubnub.com/docs/sdks/swift/api-reference/configuration#event-listeners), building a chat app requires [additional steps](https://www.pubnub.com/docs/general/basics/receive-messages):

* Subscribe to channels to receive messages
* Add event listeners to handle messages, signals, and events

Chat SDK handles this automatically. All listener methods return a function to stop receiving events and unsubscribe from the channel.

| Entity | Method | Events handled |
| --- | --- | --- |
| `Channel` | [onUpdated() / onDeleted()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/updates#get-channel-updates) | Subscribe to `objects` events of type `channel` — notified on channel metadata changes or deletion. |
| `User` | [onUpdated() / onDeleted()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/users/updates#get-user-updates) | Subscribe to `objects` events of type `uuid` — notified on user metadata changes or deletion. |
| `Message` | [onUpdated()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/updates#get-message-updates) | Subscribe to `messageAction` events — notified on message edits, deletions, and reaction changes. |
| `Membership` | [onUpdated() / onDeleted()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/membership#get-updates) | Subscribe to `objects` events of type `membership` — notified on membership metadata changes or deletion. |
| `Channel` | [onMessageReceived()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/watch) | Subscribe to `message` events — notified on new messages published to the channel. |
| `Channel` | [onTypingChanged()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/typing-indicator#get-typing-events) | Subscribe to `signal` events of type `Typing` — notified when users start or stop typing. |
| `Channel` | [onPresenceChanged()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/users/presence#get-presence-updates) | Subscribe to `presence` events — notified when users join, leave, or time out from a channel. |
| `Channel` | [onReadReceiptReceived()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/read-receipts#stream-read-receipts) | Subscribe to read receipt signals — notified when users mark messages as read. |
| `Channel` | [onMessageReported()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/moderation#listen-to-report-events) | Subscribe to report events — notified when messages are flagged. |
| `User` | [onInvited()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/invite#listen-to-invite-events) | Subscribe to invite events — notified when the user is invited to a channel. |
| `User` | [onMentioned()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/users/mentions#show-notifications-for-mentions) | Subscribe to mention events — notified when the user is mentioned in a message. |
| `User` | [onRestrictionChanged()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/users/moderation-user#listen-to-restriction-changes) | Subscribe to moderation events — notified when user restrictions change. |

Chat SDK wraps server responses into entities like `Channel`, `Message`, and `User` with methods and properties for building your app's UI.

### Chat events

Chat SDK automatically emits these event types and delivers them via entity-level listener methods. Each event type uses the PubNub Pub/Sub API internally — either [publish()](https://www.pubnub.com/docs/sdks/swift/api-reference/publish-and-subscribe#publish) (with history) or [signal()](https://www.pubnub.com/docs/sdks/swift/api-reference/publish-and-subscribe#signal) (no history).

:::note Historical events
Use [getEventsHistory()](#get-historical-events) to fetch past events. Results cannot be filtered by type — all events emitted via `publish()` on the channel are returned.
:::

#### Report events

Emitted when a user [reports a message](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/moderation#flagreport-messages). Published to `PUBNUB_INTERNAL_MODERATION_{channel_id}`.

* **Trigger:** `message.report(reason:)`
* **Listener:** [channel.onMessageReported(callback:)](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/moderation#listen-to-report-events)
* **History:** `channel.getMessageReportsHistory()`

```swift
public struct Report {
    public let reason: String
    public let text: String?
    public let messageTimetoken: Timetoken?
    public let reportedMessageChannelId: String?
    public let reportedUserId: String?
    public let autoModerationId: String?
}
```

#### Typing events

Emitted when a user starts or stops typing. Delivered via signal (no history).

* **Trigger:** [channel.startTyping()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/typing-indicator#start-typing) / [channel.stopTyping()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/typing-indicator#stop-typing)
* **Listener:** [channel.onTypingChanged(callback:)](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/typing-indicator#get-typing-events)

The callback receives `[String]` — the array of user IDs currently typing.

#### Mention events

Emitted when a user is mentioned in a message. Delivered to the mentioned user's personal channel (channel ID equals the user ID).

* **Trigger:** `channel.sendText()` with `usersToMention`
* **Listener:** [user.onMentioned(callback:)](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/users/mentions#show-notifications-for-mentions)
* **History:** `chat.getEventsHistory(channelId: userId)`

```swift
public struct Mention {
    public let messageTimetoken: Timetoken
    public let channelId: String
    public let parentChannelId: String?
    public let mentionedByUserId: String
}
```

#### Read receipt events

Emitted when a user marks messages as read. Delivered via signal on the channel.

* **Trigger:** `membership.setLastReadMessage()`, `membership.setLastReadMessageTimetoken()`, `chat.markAllMessagesAsRead()`
* **Listener:** [channel.onReadReceiptReceived(callback:)](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/read-receipts#stream-read-receipts)

```swift
public struct ReadReceipt {
    public let userId: String
    public let lastReadTimetoken: Timetoken
}
```

#### Invite events

Emitted when a user is invited to a channel. Delivered to the invited user's personal channel.

* **Trigger:** [channel.invite()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/invite#invite-one-user) / [channel.inviteMultiple()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/invite#invite-multiple-users)
* **Listener:** [user.onInvited(callback:)](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/channels/invite#listen-to-invite-events)
* **History:** `chat.getEventsHistory(channelId: userId)`

```swift
public struct Invite {
    public let channelId: String
    public let channelType: ChannelType
    public let invitedByUserId: String
    public let invitationTimetoken: Timetoken
}
```

#### Restriction events

Emitted when an admin changes a user's mute or ban status on a channel.

* **Listener:** [user.onRestrictionChanged(callback:)](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/users/moderation-user#listen-to-restriction-changes)

```swift
public struct Restriction {
    public let userId: String
    public let channelId: String
    public let ban: Bool
    public let mute: Bool
    public let reason: String?
}
```

## Custom events

The `custom` event type carries custom payloads for additional business logic. Methods:

* `channel.emitCustomEvent()` — create and send custom events
* `channel.onCustomEvent()` — listen for incoming channel-scoped custom events
* `chat.getEventsHistory()` — retrieve historical events

### Create and send events

#### channel.emitCustomEvent()

`emitCustomEvent()` sends a custom event directly on a `Channel` object. This is the preferred method for channel-scoped custom events.

##### Method signature

This method takes the following parameters:

```swift
channel.emitCustomEvent(
    payload: [String: JSONCodable],
    messageType: String? = nil,
    storeInHistory: Bool = true
) async throws -> Timetoken
```

##### Input

| Parameter | Description |
| --- | --- |
| `payload` *Type: `[String: JSONCodable]`Default: n/a | Arbitrary key-value payload to publish with the event. |
| `messageType`Type: `String?`Default: `nil` | Optional custom message type used for filtering events. |
| `storeInHistory`Type: `Bool`Default: `true` | If `true`, the event is stored in Message Persistence (if enabled in Admin Portal). |

##### Output

| Parameter | Description |
| --- | --- |
| `Timetoken` | The timetoken of the emitted event. |

##### Sample code

You want to monitor a high-priority channel with a keyword spotter that identifies dissatisfaction words like "annoyed," "frustrated," or "angry." Suppose a message sent by any of the customers present on this channel contains any of these words. In that case, you want to resend it (with relevant metadata) to a separate technical channel (`CUSTOMER-SATISFACTION-CREW`) that's monitored by the team responsible for customer satisfaction.

```swift
// Emit the custom event to the "CUSTOMER-SATISFACTION-CREW" channel.
// Assuming you have a reference of type "ChannelImpl" named "channel".
Task {
  try await channel.emitCustomEvent(
    payload: [
      "chatID": "chat1234",
      "timestamp": "2022-04-30T10:30:00Z",
      "customerID": "customer5678",
      "triggerWord": "frustrated"
    ],
    messageType: "customer-satisfaction",
    storeInHistory: true
  )
}
```

### Receive current events

#### channel.onCustomEvent()

`onCustomEvent()` listens for custom events published on a specific channel, replacing the deprecated `listenForEvents()` for channel-scoped custom events.

##### Method signature

This method takes the following parameters:

```swift
channel.onCustomEvent(
    messageType: String? = nil,
    callback: @escaping (CustomEvent) -> Void
) -> AutoCloseable
```

The `CustomEvent` struct contains:

```swift
public struct CustomEvent {
    public let timetoken: Timetoken
    public let userId: String
    public let type: String?
    public let payload: [String: JSONCodable]
}
```

##### Input

| Parameter | Description |
| --- | --- |
| `messageType`Type: `String?`Default: `nil` | Optional custom message type filter. If `nil`, all custom event types are accepted. |
| `callback` *Type: `(CustomEvent) -> Void`Default: n/a | A closure invoked for each matching custom event received on the channel. |

##### Output

| Parameter | Description |
| --- | --- |
| `AutoCloseable` | An object you must retain. When released or closed, the listener stops. Call `close()` to stop receiving events. |

##### Sample code

Monitor the `CUSTOMER-SATISFACTION-CREW` channel for frustrated customer events. When such an event occurs, the `handleFrustratedEvent` function responds to the customer.

```swift
// Function to handle the "frustrated" event and respond to the customer
func handleFrustratedEvent(customerID: String, triggerWord: String) {
  let response = "Thank you for reaching out. We're sorry to hear that you're \(triggerWord). Our team is here to help."
  // Send the response back to the customer's chat (mocked function)
  sendResponseToCustomerChat(customerID: customerID, response: response)
}

// Mocked function to send a response back to the customer's chat
func sendResponseToCustomerChat(customerID: String, response: String) {
  debugPrint("Sent response to customer \(customerID): \(response)")
}

// Listen for custom events on the "CUSTOMER-SATISFACTION-CREW" channel.
// Assuming you have a reference of type "ChannelImpl" named "channel".
// Hold a strong reference to the returned "AutoCloseable"; otherwise, it will be canceled.
let stopCustomEvents = channel.onCustomEvent(messageType: "customer-satisfaction") { event in
  if let triggerWord = event.payload["triggerWord"]?.rawValue as? String {
    if triggerWord == "frustrated" {
      if let customerID = event.payload["customerID"]?.rawValue as? String {
        handleFrustratedEvent(customerID: customerID, triggerWord: triggerWord)
      }
    }
  }
}

// AsyncStream equivalent
Task {
  for await event in channel.stream.customEvents(messageType: "customer-satisfaction") {
    if let triggerWord = event.payload["triggerWord"]?.rawValue as? String {
      debugPrint("Trigger word received: \(triggerWord)")
    }
  }
}

// To stop listening:
stopCustomEvents.close()
```

### Get historical events

`getEventsHistory()` retrieves historical events from a channel, similar to [getHistory()](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/history) for messages. Results cannot be filtered by type and include all events emitted via `publish()` in the specified timeframe.

#### Method signature

This method takes the following parameters:

```swift
chat.getEventsHistory(
    channelId: String,
    startTimetoken: Timetoken? = nil,
    endTimetoken: Timetoken? = nil,
    count: Int = 100
) async throws -> (events: [EventWrapper<EventContent>], isMore: Bool)
```

#### Input

| Parameter | Description |
| --- | --- |
| `channelId` *Type: `String`Default: n/a | Channel from which you want to pull historical messages. |
| `startTimetoken`Type: `Timetoken`Default: n/a | [Timetoken](https://www.pubnub.com/docs/sdks/swift/api-reference/misc#time) delimiting the start of a time slice (exclusive) to pull events from. For details, refer to the [Fetch History section](https://www.pubnub.com/docs/sdks/swift/api-reference/storage-and-playback#fetch-history). |
| `endTimetoken`Type: `Timetoken`Default: n/a | Timetoken delimiting the end of a time slice (inclusive) to pull events from. For details, refer to the [Fetch History section](https://www.pubnub.com/docs/sdks/swift/api-reference/storage-and-playback#fetch-history). |
| `count`Type: `Int`Default: `100` | Number of historical events to return for the channel in a single call. You can pull a maximum number of 100 events in a single call. |

#### Output

| Parameter | Description |
| --- | --- |
| `(events: [EventWrapper<EventContent>], isMore: Bool)`Type: `object` | Returned object containing two fields: `events` and `isMore`. |
| `> events`Type: `Set<Event<EventContent>>` | Array listing the requested number of historical events objects. |
| `> isMore`Type: `Bool` | Info whether there are more historical events to pull. |

#### Sample code

Fetch the last `10` historical events from the `CUSTOMER-SATISFACTION-CREW` channel.

```swift
// Pull historical events from the "CUSTOMER-SATISFACTION-CREW" channel with count of 10.
// Assuming you have a reference of type "ChatImpl" named "chat"
Task {
  let historyResponse = try await chat.getEventsHistory(
    channelId: "CUSTOMER-SATISFACTION-CREW",
    count: 10
  )
  historyResponse.events.forEach { wrapper in
    debugPrint("Event at \(wrapper.event.timetoken) by \(wrapper.event.userId)")
    debugPrint("Payload: \(wrapper.event.payload)")
  }
  if historyResponse.isMore {
    debugPrint("More events available")
  }
}
```