Create custom events
Learn how Chat SDK handles events before creating your own custom chat events.
Event handling
PubNub events
With a standard PubNub SDK like the Swift SDK, building a chat app requires additional steps:
- 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() | Subscribe to objects events of type channel — notified on channel metadata changes or deletion. |
User | onUpdated() / onDeleted() | Subscribe to objects events of type uuid — notified on user metadata changes or deletion. |
Message | onUpdated() | Subscribe to messageAction events — notified on message edits, deletions, and reaction changes. |
Membership | onUpdated() / onDeleted() | Subscribe to objects events of type membership — notified on membership metadata changes or deletion. |
Channel | onMessageReceived() | Subscribe to message events — notified on new messages published to the channel. |
Channel | onTypingChanged() | Subscribe to signal events of type Typing — notified when users start or stop typing. |
Channel | onPresenceChanged() | Subscribe to presence events — notified when users join, leave, or time out from a channel. |
Channel | onReadReceiptReceived() | Subscribe to read receipt signals — notified when users mark messages as read. |
Channel | onMessageReported() | Subscribe to report events — notified when messages are flagged. |
User | onInvited() | Subscribe to invite events — notified when the user is invited to a channel. |
User | onMentioned() | Subscribe to mention events — notified when the user is mentioned in a message. |
User | onRestrictionChanged() | 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() (with history) or signal() (no history).
Historical events
Use getEventsHistory() 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. Published to PUBNUB_INTERNAL_MODERATION_{channel_id}.
- Trigger:
message.report(reason:) - Listener:
channel.onMessageReported(callback:) - History:
channel.getMessageReportsHistory()
1public struct Report {
2 public let reason: String
3 public let text: String?
4 public let messageTimetoken: Timetoken?
5 public let reportedMessageChannelId: String?
6 public let reportedUserId: String?
7 public let autoModerationId: String?
8}
Typing events
Emitted when a user starts or stops typing. Delivered via signal (no history).
- Trigger:
channel.startTyping()/channel.stopTyping() - Listener:
channel.onTypingChanged(callback:)
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()withusersToMention - Listener:
user.onMentioned(callback:) - History:
chat.getEventsHistory(channelId: userId)
1public struct Mention {
2 public let messageTimetoken: Timetoken
3 public let channelId: String
4 public let parentChannelId: String?
5 public let mentionedByUserId: String
6}
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:)
1public struct ReadReceipt {
2 public let userId: String
3 public let lastReadTimetoken: Timetoken
4}
Invite events
Emitted when a user is invited to a channel. Delivered to the invited user's personal channel.
- Trigger:
channel.invite()/channel.inviteMultiple() - Listener:
user.onInvited(callback:) - History:
chat.getEventsHistory(channelId: userId)
1public struct Invite {
2 public let channelId: String
3 public let channelType: ChannelType
4 public let invitedByUserId: String
5 public let invitationTimetoken: Timetoken
6}
Restriction events
Emitted when an admin changes a user's mute or ban status on a channel.
- Listener:
user.onRestrictionChanged(callback:)
1public struct Restriction {
2 public let userId: String
3 public let channelId: String
4 public let ban: Bool
5 public let mute: Bool
6 public let reason: String?
7}
Custom events
The custom event type carries custom payloads for additional business logic. Methods:
channel.emitCustomEvent()— create and send custom eventschannel.onCustomEvent()— listen for incoming channel-scoped custom eventschat.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:
1channel.emitCustomEvent(
2 payload: [String: JSONCodable],
3 messageType: String? = nil,
4 storeInHistory: Bool = true
5) async throws -> Timetoken
Input
| Parameter | Description |
|---|---|
payload *Type: [String: JSONCodable]Default: n/a | Arbitrary key-value payload to publish with the event. |
messageTypeType: String?Default: nil | Optional custom message type used for filtering events. |
storeInHistoryType: BoolDefault: 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.
1
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:
1channel.onCustomEvent(
2 messageType: String? = nil,
3 callback: @escaping (CustomEvent) -> Void
4) -> AutoCloseable
The CustomEvent struct contains:
1public struct CustomEvent {
2 public let timetoken: Timetoken
3 public let userId: String
4 public let type: String?
5 public let payload: [String: JSONCodable]
6}
Input
| Parameter | Description |
|---|---|
messageTypeType: String?Default: nil | Optional custom message type filter. If nil, all custom event types are accepted. |
callback *Type: (CustomEvent) -> VoidDefault: 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.
1
Get historical events
getEventsHistory() retrieves historical events from a channel, similar to getHistory() 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:
1chat.getEventsHistory(
2 channelId: String,
3 startTimetoken: Timetoken? = nil,
4 endTimetoken: Timetoken? = nil,
5 count: Int = 100
6) async throws -> (events: [EventWrapper<EventContent>], isMore: Bool)
Input
| Parameter | Description |
|---|---|
channelId *Type: StringDefault: n/a | Channel from which you want to pull historical messages. |
startTimetokenType: TimetokenDefault: n/a | Timetoken delimiting the start of a time slice (exclusive) to pull events from. For details, refer to the Fetch History section. |
endTimetokenType: TimetokenDefault: n/a | Timetoken delimiting the end of a time slice (inclusive) to pull events from. For details, refer to the Fetch History section. |
countType: IntDefault: 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. |
→ eventsType: Set<Event<EventContent>> | Array listing the requested number of historical events objects. |
→ isMoreType: Bool | Info whether there are more historical events to pull. |
Sample code
Fetch the last 10 historical events from the CUSTOMER-SATISFACTION-CREW channel.
1