Subscriptions and subscription sets
In PubNub, a Subscription or SubscriptionSet is the object your code uses to control what a client receives. What a subscription addresses and how one connection carries many of them is the subscribe model. This page covers:
- which entity each kind of subscription is built from, and what that entity scopes it to
- what actually differs between a
Subscriptionand aSubscriptionSet, beyond how many entities each one covers - why some SDKs let you skip holding either object, and what that trade-off costs
- the two ways to build a
SubscriptionSet, and how it relates to the subscriptions inside it - why calling
subscribe()orunsubscribe()changes the object's state rather than sending a server-side registration - the one option you set when you create a subscription, and what it controls
- which parts of the subscribe model are deliberately not properties of the object
One rule holds throughout. A subscription is a local object, not a server-side record. The PubNub network knows only the channels and channel groups named in the SDK's current subscribe request. It has no concept of the Subscription object your code is holding. So creating, discarding, or reassigning one has no effect until your code calls subscribe() or unsubscribe() on it.
What entity a subscription is built from
Current SDKs build a subscription by calling .subscription() on an entity, and the entity decides what the resulting subscription is scoped to:
| Entity | .subscription() returns a subscription scoped to | Refer to |
|---|---|---|
Channel | One channel's messages, signals, and other channel-level events | Channels |
ChannelGroup | Every channel currently in that group, as one unit | Channel group |
UserMetadata | App Context events for that user's own metadata and memberships | App Context |
ChannelMetadata | App Context events for that channel's own metadata and memberships | App Context |
Each entity is created from the PubNub client, and .subscription() on it returns a plain object. Constructing it does nothing on the network until you call subscribe() on the result.
SDKs that predate entities have no .subscription() call at all. Refer to SDK entities for that split, and to Listener scope follows the subscription, not the channel name for what the split means for the handlers you attach.
Whether to use a subscription or a set
Both objects deliver the same event types through the same handlers. The difference is where a handler's boundary sits.
- A
Subscriptionscoped to one entity keeps that entity's events isolated from every other entity's. A handler registered on it never has to inspect the channel name to know what it's looking at, because nothing else can reach that handler. - A
SubscriptionSetremoves that isolation on purpose. One handler runs for every member's events, so logic that should behave identically across entities needs to exist only once, instead of copied onto severalSubscriptionobjects.
Subscription | SubscriptionSet | |
|---|---|---|
| Built from | one entity | several entities, or several existing Subscription objects |
| A handler registered on it fires for | only that entity's events | every member's events |
Calling subscribe() or unsubscribe() on it | activates or deactivates that one entity | activates or deactivates every member at once |
Every event still carries the channel it arrived on, so a handler on a set can recover per-entity behavior by branching on that field. A handler on a single Subscription has no such branch to write, because nothing else ever reaches it.
Whether to let the client manage subscriptions for you
Some SDKs also expose a client-level subscribe() that takes channel and channel group names directly paired with a client-level addListener() for handlers. This isn't a remnant from before entities existed. All three fully support the Subscription and SubscriptionSet objects described on this page, and keep the client-level call as a second, permanent way to reach the same result.
Check your SDK's API reference before assuming either holds for your specific SDK version.
Choosing it trades away the granularity described in Whether to use a subscription or a set, on purpose. Every channel and channel group you name this way lands in one scope the client keeps for you. Every handler you register at the client level then fires for all of it, the same way one ever-growing SubscriptionSet would. That's the point: no entity to create first, no Subscription variable to hold onto, no set to assemble by hand. The cost is the same one a SubscriptionSet carries: you can't isolate one channel's handling from another's without branching on the event's channel field yourself inside a shared handler.
Two ways to build a subscription set
A SubscriptionSet groups several subscriptions so that one subscribe() call, one unsubscribe() call, and one handler registration cover all of them. There are two ways to build one, and they start from opposite ends:
- From names or entities directly. Some SDKs let you ask the client for a set in one call, without creating each member subscription yourself first, for example
pubnub.subscriptionSet({ channels: ['ch1', 'ch2'] })from channel names. A few SDKs accept a mixed list of entities the same way, so one call can build a set covering aChannel, aChannelGroup, and aUserMetadataobject together. - From subscriptions you already created. Combine existing
Subscriptionobjects into a set, for example by calling.addSubscription()on one and passing another. This is the path that fits an application built incrementally. One component created a subscription for its own channel earlier, and a second component now needs to fold it into a shared set.
The examples below build two entity-scoped subscriptions and combine them into one set before subscribing. Method names differ between SDKs, and not every SDK that supports entities exposes this combine operation the same way, so check your platform's API reference for the exact call.
- JavaScript
- Java
- Kotlin
- C#
- Swift
- Unity
- Unreal Engine
The following JavaScript is illustrative rather than a working application:
1const subscription1 = pubnub.channel('chats.room1').subscription({ receivePresenceEvents: true });
2const subscription2 = pubnub.channel('chats.room2').subscription();
3
4// Combine two existing subscriptions into a set
5const subscriptionSet = subscription1.addSubscription(subscription2);
6
7subscriptionSet.subscribe();
1
subscription1.plus(subscription2) combines two subscriptions, built from a Channel and a ChannelGroup entity, into one SubscriptionSet. .add() and .remove() manage its membership afterward.
1
subscription1 + subscription2 combines two subscriptions into a SubscriptionSet. += and .add() are equivalent ways to add a member afterward, and -= and .remove() are equivalent ways to remove one.
1
subscription1.Add(subscription2) combines two subscriptions, built from a Channel and a ChannelGroup entity, into one SubscriptionSet.
1
pubnub.subscription(entities:) builds a set directly from a list of entities. subscriptionSet.add(subscription:) and .remove(subscription:) then manage the membership of a subscription created separately.
1
subscription1.Add(subscription2) combines two subscriptions, built from a Channel and a ChannelGroup entity, into one SubscriptionSet, the same API as the C# SDK.
1
CreateSubscriptionSet() builds a set from channel and channel group names. AddSubscription() and RemoveSubscription() then manage the membership of subscriptions created separately from entities.
A Subscription combined into a set this way keeps working on its own: both it and the set it now belongs to can be active at once, independently of each other. Refer to Listener scope follows the subscription, not the channel name for how handler scope follows this structure.
What subscribe and unsubscribe change
subscribe() and unsubscribe() are the switch on a Subscription or SubscriptionSet, not a message sent about that specific object. Calling subscribe() adds the object's channels and channel groups to the single list the SDK's subscribe loop is currently requesting. Calling unsubscribe() removes them from that list. What the network sees is only the resulting list, never the objects that produced it.
Three consequences follow from that:
- The object survives
unsubscribe(). Unsubscribing stops events from arriving through that object, but the object itself, and every handler still registered on it, remains. Callingsubscribe()on the same object again resumes it. That is what makes "pause and later resume the same scope" cheaper than discarding the object and rebuilding it from the entity. - A
SubscriptionSet'ssubscribe()andunsubscribe()apply to every member at once. There is no partial subscribe of a set: activating or deactivating it activates or deactivates every subscription it currently contains. unsubscribeAll()is a separate, client-wide operation. Some SDKs expose it on the PubNub client itself. It stops every subscription and subscription set that client holds in one call, regardless of how many separate objects created them. For the procedure and the calls each SDK exposes, refer to Stop receiving messages.
Whether a subscription receives presence events
Creating a subscription or subscription set takes an options argument alongside the entity, a SubscriptionOptions object in SDKs that name the type, or a plain object in SDKs that don't. receivePresenceEvents is the one subscription option verified across SDKs today. It decides whether that object's handlers also receive the presence join, leave, and timeout events for its scope. It is not something you can add to an already-created object. Setting it happens at the same call that produces the subscription, as in pubnub.channel('chats.room1').subscription({ receivePresenceEvents: true }).
A subscribe filter is not a second subscription option, even though older documentation grouped it with receivePresenceEvents this way. Refer to What a subscription is not for where it actually lives. Refer to Receive presence events for what the resulting join, leave, and timeout events carry.
What a subscription is not
- Not where a subscribe filter lives. A subscribe filter expression is a property of the client, applying to every channel and channel group that client subscribes to. Creating a subscription with different intentions per channel does not give each one a different filter. That needs either a separate client instance or client-side filtering in the handler. Refer to Filter received messages.
- Not where connection status lives. The status handler reports the shared subscribe connection, so it is registered on the PubNub client object, never on a
SubscriptionorSubscriptionSet. Refer to The status handler is the exception. - Not available in every SDK. SDKs that predate entities have no
SubscriptionorSubscriptionSetobject at all. They subscribe channels and channel groups directly on the client and register every handler there. That is the only model they offer. Check your platform's API reference before assuming the object model in this page applies. Refer to SDK entities. - Not the only path, even where it's fully supported. Some entity-capable SDKs keep a client-level
subscribe()andaddListener()alongsideSubscriptionandSubscriptionSet, as a deliberate second option. Refer to Whether to let the client manage subscriptions for you for more information.
Next steps
- Subscribe. What a subscription addresses, multiplexing, and filtering.
- Event listeners. The handler model and how scope follows the subscription.
- Receive messages. Create a subscription, register handlers, and start receiving.
- Stop receiving messages. Unsubscribe, remove handlers, and unsubscribe from everything at once.
- Core concepts. The entity model a subscription is built from.
- Channels and channel naming. The
Channelentity's own naming model.