Updates to the Event Engine and Event Listeners
Published: 2024-04-18 09:31:02

We are working hard to ensure that all our SDKs behave consistently and provide PubNub developers with the best possible experience.  We have recently introduced a number of changes, collectively known as updates to the “Event Engine,” as well as a new set of “Event Listener” APIs that offer greater control over how you receive PubNub Events.

This article seeks to explain what these different updates are as well as give an overview of our new “Event Listeners.”

SDK Support
-----------

At the time of writing, the latest releases of our [Java](https://www.pubnub.com/docs/sdks/java/api-reference/publish-and-subscribe#subscription-scope?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), [Swift](https://www.pubnub.com/docs/sdks/swift/api-reference/publish-and-subscribe#subscription-scope?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), [Javascript](https://www.pubnub.com/docs/sdks/javascript/api-reference/publish-and-subscribe#subscription-scope?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), [Kotlin](https://www.pubnub.com/docs/sdks/kotlin/api-reference/publish-and-subscribe#subscription-scope?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), and [Rust](https://www.pubnub.com/docs/sdks/rust/api-reference/publish-and-subscribe#subscription-scope?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) SDKs support the new Event Engine and Event Listeners, with _support in additional SDKs coming soon._

What is Changing?
-----------------

At a high level, we are making the following changes to our SDKs:

-   Reworking our event listener APIs so you no longer have to pass a single, global `PubNub` object around your entire application.  Of the four bullets on this list, _the new event listener APIs will deliver the greatest benefit to PubNub developers, so most of this article is dedicated to these_.
    
-   Updating and harmonizing the logic that manages the communication with the PubNub backend, receiving events such as messages or presence.  This is the ‘Event Engine’.
    
-   Improved our ‘presence state’ management.  This has been made possible through improvements to the ‘Event Engine.’
    
-   Harmonizing our retry policy to make it consistent across all SDKs.  This has also been made possible through improvements to the ‘Event Engine.’
    

Change #1: Rework of the Event Listener APIs
--------------------------------------------

We are improving our SDKs to provide more granular event listeners, allowing you to scope to the specific _entity_ which _event(s)_ you want to receive.  

**What is an** [**entity**](https://www.pubnub.com/docs/general/entities?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en)**?**  An entity can be one of ‘Channel,’ ‘Channel Group,’ ‘User Metadata,’ and ‘Channel metadata’.  

**What is an event in this context?**  That is one of ‘message,’ ‘presence events,’ ‘signals,’ ‘object event,’ ‘message actions,’ or ‘file.’

These improvements allow you, amongst other benefits, to specify separate message handlers for different channels, meaning you no longer have to keep track of the global state of your application and maintain a single listener for all PubNub events.

At the time of writing, we are still rolling out these updates across our range of SDKs.  I have used JavaScript for the code samples in the following sections, but you will find the equivalent functionality implemented in each of the SDKs I listed at the top of this article.

### What is changing with Event Listeners?

Until recently, all of our SDKs contained a single monolithic object, usually called `PubNub`, at the center of the SDK, providing entry points to all possible interactions, e.g., `subscribe`, `unsubscribe`, `publish`, etc.  If you want to perform any of these actions, such as subscribing to a specific channel in a specific screen of your app, you need to pass a reference to this global `PubNub` object throughout your app.

In addition to initiating actions, the `PubNub` Object also allows the user to listen to status updates (related mostly to the subscribe connection state) and real-time events coming from the PN network, such as messages, signals, presence events, objects, files, and actions through the `addListener()` and `removeListener()` methods.  This `addListener()` code usually becomes bloated because of the large number of condition blocks to handle all the event types and sources - just look at [the code for one of our demo apps](https://github.com/PubNubDevelopers/PubNub-Showcase/blob/cb05df7c8fac9b8a07ad1e1ec5642ede10cce2f2/web/chat/chat.js#L140) as a case in point(!) 

Additionally, it is important to understand that everything happens in the global scope of the `PubNub` object, that is:

-   The `channel`/`channel group` subscription list is global (i.e., scoped to the `PubNub` object), and the global list is affected through `subscribe`/`unsubscribe` calls.
    
-   The status and event listeners are global and emit events related to all `channels`/`channel groups` currently subscribed to.
    

Consequently, if you have multiple screens in your application, each requiring a connection to different PubNub channels, you must track which connections are required manually as your user navigates around your app.  This process becomes prone to errors as you unsubscribe from channels that are no longer required. 

### New Architecture for Event Listeners

The new architecture, currently rolling out across our range of SDKs, introduces new, more narrowly scoped ways of dealing with subscriptions and listening to events. These updated SDKs are currently backwardly compatible though deprecation is discussed later in this article. The new architecture offers additional methods to create what we call ‘[entity](https://www.pubnub.com/docs/general/entities?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en)’ objects: 

-   Channels
    
-   Channel Groups
    
-   User Metadata
    
-   Channel Metadata
    

These entities contain a `subscription()` method that returns a `Subscription` object.  For example, our JavaScript API can be called as follows:

```js
const channel = pubnub.channel('channel_1');
const channelSubscription = channel.subscription({receivePresenceEvents: true});
channelSubscription.subscribe()
channelSubscription.onMessage = function (message) {
	console.log(message);
}
channelSubscription.onPresence = function (presenceEvent) {
	console.log(presenceEvent);
}
```

You can think of the returned `Subscription` object as a more narrowly scoped `PubNub` client; it contains `subscribe()` and `unsubscribe()` methods to activate or stop the subscription to its parent entity. There are two ways to receive events on a `Subscription`:

1\. The `on[Event]` handlers as shown in the above code snippet for `onMessage` and `onPresence`.  Other handlers exist for signals, objects, files, and message actions.

2\. The `addListener` method will receive all events within a single function and you **will find this syntax more familiar if you are upgrading from our previous APIs**.  The equivalent code in JavaScript to listen for messages and presence events using the `addListener` method will look as follows:

```js
channelSubscription.addListener({
	//  Messages
	message: (message) => {console.log(message)},
	//  Presence event
	presence: (presenceEvent) => {console.log(presenceEvent)}, 
	//  Other event handlers for signals, objects, files, message actions
});
```

### What are the benefits of the new Event Listeners?

You can create multiple `Subscription` objects for any entity (e.g., channel), and all of these `Subscription` objects will be independent of each other, **even ones created for the same entity**.  This means that each `Subscription` can be activated (`subscribe()`) or stopped (`unsubscribe()`) without affecting other `Subscriptions` and allows for a much more flexible structuring of client applications, compared with managing the global state through the single `PubNub` object.  

**Note**: Creating multiple `Subscription` objects is cheap in terms of resources since the SDK will take care of multiplexing all these subscriptions over a single server connection for you.  Contrast this with the ‘old’ approach, where creating multiple `PubNub` objects would cause each to maintain a separate server connection.

Finally, SDKs will still support a global `addListener()` method on the `PubNub` object, but this should only be used to listen for [status events](https://www.pubnub.com/docs/general/setup/connection-management#sdk-statuses?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), such as `Connected` or `Disconnected`.

```js
pubnub.addListener({
    status: (s) => {console.log('Status', s.category) }
});
```

Let’s consider a simple application that shows the values of various stocks.  When the user first launches the application they are shown the values of some popular stocks, but when the user logs in, they are able to select their favorite stock symbols and will also receive the current prices of these.

To implement this application in PubNub, you might choose to have two channels, a “commonStock” channel and a “favoriteStock” channel.

Now, let’s consider the user journey and how you would track which channels to listen for stock updates on:

**With the previous architecture for channel subscriptions, which depended on a global object**

1\. The user launches the application.

The application subscribes to the “commonStock” channel

2\. The user logs in

The application subscribes to the “favoriteStock” channel

The application is now subscribed to two channels: “commonStock” and “favoriteStock”

3\. The user logs out

The application now has to determine which channels to unsubscribe from, in this case, “favoriteStock”.  The application is now only subscribed to a single channel: “commonStock”

**Although trivial in this example, you can see how as the number of screens and channels increases, it becomes increasingly complicated to manage for large apps**.  You need to have some component in your application that has a global view of your subscribed channel list and understands what should happen to that list as users navigate through your application. 

**Now, let’s walk through that same scenario with the new Event Listeners**

1\. The user launches the application.

The application subscribes to the “commonStock” channel, as before

2\. The user logs in

Since we want to receive common and favorite stock prices, we create two subscriptions for the logged-in user.  We do not need any previous knowledge that the user was subscribed to the ‘commonStock’ channel.

```js
stocksSub2 = PN.channel(“commonStock”).subscription() 
stocksSub2.subscribe()
favoritesSub = PN.channel(“favoriteStock”).subscription() 
favoritesSub.subscribe()
```

The application is now subscribed to two channels: “commonStock” and “favoriteStock.”  Note that even though the user is subscribed to both stocksSub1 and stocksSub2, this does not use any additional resources, and they will not receive duplicate messages.

3\. The user logs out

As part of the user log-out logic, we unsubscribe from both the “commonStock” and “favoriteStock” channels.  We do not have to consider what channels a logged-out user has access to since that is outside of our scope.

```js
stocksSub2.unsubscribe()
favoritesSub.unsubscribe()
```

The application is now subscribed to a single channel: “commonStockPrices,” because of the existing `stocksSub1` subscription.

The key difference here is that the different screens of the application only had to take care of what happened within their own scope, meaning **you do not have to maintain a global view of whether “commonStock” should still be subscribed to; the new event handlers that all for you**. 

### Combining Subscriptions into a Subscription Set

The examples so far have only considered individual channels, but the `Subscription` object is not limited to a single Channel or Channel Group, which can be combined together into a `SubscriptionSet` to handle the events on all these channels together.

To extend the previous example, we could have handled both logged-in channels with a single SubscriptionSet, with a single event handler.

```js
subscriptionSet = pubnub.subscriptionSet({channels: ['commonStock', 'favoriteStock’]})
subscriptionSet.onMessage = function (message) {console.log(message.channel)}
subscriptionSet.subscribe()
```

Note that while the `Subscription` was created from the [entity](https://www.pubnub.com/docs/general/entities?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), the `SubscriptionSet` is called on the global `PubNub` object.

### When will PubNub-Scoped Subscription be deprecated?

As we introduce the new architecture for event listeners, the previous APIs are being deprecated on a per-SDK basis.  Take the [Javascript](https://www.pubnub.com/docs/sdks/javascript/api-reference/publish-and-subscribe#subscription-scope?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) SDK, for example, and notice that the “old” [pubnub.subscribe()](https://www.pubnub.com/docs/sdks/javascript/api-reference/publish-and-subscribe#subscribe-deprecated?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) and [pubnub.unsubscribe()](https://www.pubnub.com/docs/sdks/javascript/api-reference/publish-and-subscribe#subscribe-deprecated?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) methods have been marked as deprecated.  We aim to make the upgrade process as friction free for our developers as possible, so each of our SDKs supports a long end-of-life (EOL) period.  Please see the documented EOL period for your specific SDK in the docs, found towards the [bottom of the SDK feature list](https://www.pubnub.com/docs/sdks/javascript/end-of-life?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en).

Change #2: Updates to our Event Engine
--------------------------------------

At the heart of our SDKs, there is a logic loop to handle the connection with our PubNub infrastructure to receive events such as messages or presence updates - this is what we are referring to as our event engine.  In general, this is not something you configure when using our SDKs and will ‘just work’ out of the box for 99.9% of our customers, but as we developed our SDK family over the years (as with any software engineering project), you accumulate technical debt.  While all of our SDKs provided a consistent experience to the user, behind the scenes, different SDKs had implementation quirks within the event engine that made it challenging for us to add new features and track down rare edge cases. 

We are updating the event engine in each of our SDKs so they all handle state transitions in a predictable and consistent manner; this will allow us to add enhancements that provide additional functionality while reducing the number of edge cases our customers will experience.

### What code changes are required for the new Event Engine?

A new configuration option has been added to the SDK configuration, `enableEventEngine` which the documentation states will “use the standardized workflows for subscribe and presence.”  By ‘standardized,’ this reflects our efforts to standardize the implementation of the engine across all of our SDKs described in the previous section.

This is a **non-breaking change**, meaning that if you set this option to true, you will not have to make any changes to your application **unless otherwise documented**. The only documented example so far is the Kotlin [status events for Subscribe](https://www.pubnub.com/docs/sdks/kotlin/status-events#subscribe?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) have been updated to make them consistent with other SDKs (detailed in our matching our [generic connection workflow](https://www.pubnub.com/docs/general/setup/connection-management#sdk-statuses?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en)).  These updated statuses are used in Kotlin by the [connection status listener](https://www.pubnub.com/docs/sdks/kotlin/api-reference/publish-and-subscribe#add-connection-status-listener?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en).  Please consult the SDK documentation for additional exceptions and required changes when enabling the new Event Engine.

Change #3: Updates to our Presence State Management
---------------------------------------------------

Presence State refers to the [custom user state](https://www.pubnub.com/docs/general/presence/presence-state?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) that can be set but will only persist as long as the user is subscribed to the channel.  Typical use cases for presence state include maintaining the player score, game state, or location.  If you are familiar with our [collaboration (whiteboard) demo](https://www.pubnub.com/demos/codoodler-collaboration-demo/?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), that app uses ‘presence state’ to register and update the user’s cursor position.

We have identified some edge cases with specific customers who would benefit from handling presence state updates differently. However, this is on a case-by-case basis, and most customers can continue to use the existing presence state logic without change.  Specifically, the ‘existing’ presence state logic is [setPresenceState](https://www.pubnub.com/docs/general/presence/presence-state#set-presence-state?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), [getPresenceState](https://www.pubnub.com/docs/general/presence/presence-state#set-presence-state?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), and listening for the ‘[state-change](https://www.pubnub.com/docs/general/presence/presence-events#presence-event-types?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en)’ presence event.

### What code changes are required for the new Presence State Management?

A new configuration option has been added to the SDK configuration, `maintainPresenceState`.  Most customers, unless otherwise advised, should retain the default value for this parameter, ‘true’.  Setting this value to ‘true’ will retain the current behavior, as described above.

Change #4: Harmonization of our SDK Retry policy 
-------------------------------------------------

In the event of client disconnection from PubNub, the automatic reconnection policy has been historically inconsistent between our different SDKs.  

Most of our SDKs would offer some way to configure how to recover from a connectivity issue, most commonly by exposing a ‘retry policy’ configuration and allowing the developer to select between ‘none,’ ‘linear,’ or ‘exponential.’ However, the timeout values associated with each policy would be hardcoded and specific to the SDK.

**The retry policy is being improved and made more customizable**, as documented on the [Connection Management Reconnection Policy page](https://www.pubnub.com/docs/general/setup/connection-management#reconnection-policy?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en).  As we continue to roll out these changes throughout our SDKs, the retry policy will become harmonized.  You can now configure:

-   [Linear retry policy](https://www.pubnub.com/docs/general/setup/connection-management#linear?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), with a customizable delay interval and configurable maximum retry count.
    
-   [Exponential retry policy](https://www.pubnub.com/docs/general/setup/connection-management#exponential?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en), with a customizable minimum and maximum delay, along with a maximum retry count.
    
-   [Endpoints that should be **excluded**](https://www.pubnub.com/docs/general/setup/connection-management#exclude-endpoints-from-auto-retry?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) from the retry policy, for example, if the data is time-sensitive and does not make sense to retry, you can exclude it.  Consider a live event where a large audience is reacting to many messages; you might choose to exclude the ‘message\_reaction’ event type.
    

Please consult the configuration section of your SDK for the new global `retryConfiguration` option and any SDK-specific limitations.

More Resources:
---------------

The changes described in this article, especially the changes to the Event Listeners, represent a fundamental change to how developers interact with our APIs. Still, we strongly believe that the long-term benefits outweigh the initial learning curve.  We want to make your transition to the new Event Listener APIs as easy as possible, so if you need help or support, feel free to contact our [dedicated support team](https://support.pubnub.com/hc/en-us?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) or email our developer relations team at [devrel@pubnub.com](mailto:devrel@pubnub.com).

We are also interested in your feedback. Is there something we could do or provide to make your upgrade easier?  Please let us know. 

How can PubNub help you?
========================

This article was originally published on [PubNub.com](https://www.pubnub.com/blog/updates-to-the-event-engine-and-event-listeners/?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en)

Our platform helps developers build, deliver, and manage real-time interactivity for web apps, mobile apps, and IoT devices.

The foundation of our platform is the industry's largest and most scalable real-time edge messaging network. With over 15 points-of-presence worldwide supporting 800 million monthly active users, and 99.999% reliability, you'll never have to worry about outages, concurrency limits, or any latency issues caused by traffic spikes.

Experience PubNub
-----------------

Check out [Live Tour](https://www.pubnub.com/tour/introduction/?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) to understand the essential concepts behind every PubNub-powered app in less than 5 minutes

Get Setup
---------

Sign up for a [PubNub account](https://admin.pubnub.com/signup/?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) for immediate access to PubNub keys for free

Get Started
-----------

The [PubNub docs](https://www.pubnub.com/docs?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en) will get you up and running, regardless of your use case or [SDK](https://www.pubnub.com/docs?utm_source=devto&utm_medium=social&utm_campaign=off_domain&utm_content=en)