Quickstart
In this quickstart, you publish a message to a PubNub channel and receive it in the same program. That's your first end-to-end real-time exchange with PubNub. Pick your language above. Every step and code sample on this page follows it.
Before you begin
Make sure you have a PubNub account and your own keyset. A keyset is the set of publish, subscribe, and secret keys that identifies your application to the PubNub network.
If you don't have one yet, the fastest way to get one is using a Prototype:
- Sign up or log in to Admin Portal.
- In the Recent prototypes section, click the app. A new app is created automatically when you log in for the first time.
- Copy the publish key and subscribe keys.
Create a keyset manually
You can also follow Set up your account to create one, then come back here.
Set up the project
- JavaScript
- Python
- Java
- Kotlin
- C#
- Go
- Rust
- C-Core (legacy)
- Swift
- Objective-C
- Dart
- PHP
- Ruby
- Unity
- Unreal Engine
Make sure you have Node.js 22 or later installed, then create a new directory and install the PubNub JavaScript SDK:
mkdir pubnub-quickstart
cd pubnub-quickstart
npm init -y
npm install pubnub
Then create a file called index.js in that directory.
The PubNub Python SDK requires Python 3.9 or later. Create a new directory with a virtual environment and install the SDK:
mkdir pubnub-quickstart
cd pubnub-quickstart
python3 -m venv venv
source venv/bin/activate
pip install pubnub
Then create a file called main.py in that directory.
Make sure you have JDK 17 or later and Maven installed, then create the project layout:
mkdir -p pubnub-quickstart/src/main/java
cd pubnub-quickstart
Create pom.xml with the PubNub Java SDK dependency:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>pubnub-quickstart</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.pubnub</groupId>
<artifactId>pubnub-gson</artifactId>
show all 19 linesThen create a file called src/main/java/App.java.
Make sure you have JDK 17 or later and Gradle installed, then create the project layout:
mkdir -p pubnub-quickstart/src/main/kotlin
cd pubnub-quickstart
Create settings.gradle.kts:
rootProject.name = "pubnub-quickstart"
Create build.gradle.kts with the PubNub Kotlin SDK dependency:
plugins {
kotlin("jvm") version "2.2.20"
application
}
repositories {
mavenCentral()
}
dependencies {
implementation("com.pubnub:pubnub-kotlin:13.4.4")
}
kotlin {
jvmToolchain(17)
show all 20 linesThen create a file called src/main/kotlin/Main.kt.
Make sure you have the .NET SDK 9.0 or later installed, then create a console project and add the PubNub package. PubnubPCL is the package for .NET Standard 2.0 and later, which covers .NET console applications:
dotnet new console -o pubnub-quickstart
cd pubnub-quickstart
dotnet add package PubnubPCL
Then open the generated Program.cs.
Make sure you have Go 1.25 or later installed, then create a module and add the PubNub Go SDK:
mkdir pubnub-quickstart
cd pubnub-quickstart
go mod init pubnub-quickstart
go get github.com/pubnub/go/v9
Then create a file called main.go in that directory.
Make sure you have Rust and Cargo installed, then create a binary crate and add the PubNub Rust SDK along with the async runtime and JSON crates this tutorial uses:
cargo new pubnub-quickstart
cd pubnub-quickstart
cargo add pubnub tokio --features tokio/full
cargo add futures serde_json
Then open the generated src/main.rs.
New SDK available
C-Core (legacy) is still supported. If you are starting a new project, use the new C SDK.
The PubNub C-Core (legacy) SDK has no package to install. You clone the repository and build a static library from it. This tutorial uses the OpenSSL build, which gives you TLS, so you need OpenSSL and pkg-config available.
Clone the repository and build the library for the synchronous interface:
git clone https://github.com/pubnub/c-core.git
cd c-core/openssl
make -f posix.mk pubnub_sync.a
Then create a file called main.c in the c-core/openssl directory. Keeping it there means the compiler finds pubnub_sync.h and the built library without extra path configuration.
Make sure you have Xcode installed, then create a Swift package:
mkdir pubnub-quickstart
cd pubnub-quickstart
swift package init --type executable
Replace the generated Package.swift with the following, which adds the PubNub Swift SDK as a dependency:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "pubnub-quickstart",
platforms: [.macOS(.v11)],
dependencies: [
.package(url: "https://github.com/pubnub/swift.git", from: "10.2.0")
],
targets: [
.executableTarget(
name: "pubnub-quickstart",
dependencies: [.product(name: "PubNubSDK", package: "swift")]
)
]
show all 16 linesDelete the generated Sources/pubnub-quickstart/pubnub_quickstart.swift and create Sources/pubnub-quickstart/main.swift in its place. The name main.swift is what allows top-level code in the file.
The PubNub Objective-C SDK is distributed as a CocoaPods pod, so you build this program in Xcode.
-
Make sure you have Xcode and CocoaPods installed. Install CocoaPods with
gem install cocoapodsif you don't have it. -
In Xcode, select File > New > Project, choose macOS > Command Line Tool, set Product Name to
pubnub-quickstartand Language to Objective-C, and save the project. -
Close Xcode.
-
In the project directory, create a
Podfile:platform :osx, '11.0'
target 'pubnub-quickstart' do
pod 'PubNub', '~> 7.0.4'
end -
Install the pod:
pod install -
Open
pubnub-quickstart.xcworkspace. From now on, use the workspace rather than the.xcodeprojfile.
You'll edit the main.m file that Xcode generated.
Make sure you have the Dart SDK installed, then create a package and add the PubNub Dart SDK. PubNub must run inside a Dart package, not as a standalone script file:
dart create pubnub_quickstart
cd pubnub_quickstart
dart pub add pubnub
Then open the generated bin/pubnub_quickstart.dart.
Make sure you have PHP and Composer installed, then create a directory and require the PubNub PHP SDK:
mkdir pubnub-quickstart
cd pubnub-quickstart
composer require pubnub/pubnub
Then create a file called index.php in that directory.
The PubNub Ruby SDK requires Ruby 3.0 or later. Install the gem and create a directory to work in:
mkdir pubnub-quickstart
cd pubnub-quickstart
gem install pubnub
Then create a file called app.rb in that directory.
The PubNub Unity SDK is a Unity package, and you configure your keys through a scriptable object in the editor rather than in code.
- In Unity Hub, create a new Unity project.
- In the editor, open Window > Package Manager, click +, and select Add package from git URL.
- Paste
https://github.com/pubnub/unity.git?path=/PubNubUnity/Assets/PubNuband click Add. - In the Project window, right-click the
Assetsfolder and select Create > PubNub > PubNub Config Asset. This creates aPNConfigAssetscriptable object that holds your keys. - Select the new
PNConfigAssetand set Publish Key and Subscribe Key to your keys from the Admin Portal. Set Log Level to None so the Console shows only your own messages rather than the SDK's internal logs. - In the
Assetsfolder, create a C# script calledPubNubQuickstart.
You wire the script to a game object after you write it, in the Run the program step.
The PubNub Unreal Engine SDK is a plugin, and you configure your keys in Project Settings rather than in code. This tutorial uses C++.
-
Launch Unreal Engine 5.2 or later and create a new C++ project called
Quickstart. -
Install the PubNub Gaming SDK plugin from FAB and enable PubNubSDK in your project.
-
In
Source/Quickstart/Quickstart.Build.cs, add the PubNub module dependency:PrivateDependencyModuleNames.AddRange(new string[] { "PubnubLibrary" }); -
Select Edit > Project Settings, scroll to the Plugins section, and click Pubnub SDK. Set Publish Key and Subscribe Key to your keys from the Admin Portal, and leave Initialize Automatically checked so the plugin initializes itself.
-
Select Tools > New C++ Class..., inherit from GameInstance, and name the class
PubNubQuickstartGameInstance.
You set the class as the project's default game instance after you write it, in the Run the program step.
Initialize PubNub
- JavaScript
- Python
- Java
- Kotlin
- C#
- Go
- Rust
- C-Core (legacy)
- Swift
- Objective-C
- Dart
- PHP
- Ruby
- Unity
- Unreal Engine
Open index.js and add the following:
const PubNub = require('pubnub');
const pubnub = new PubNub({
publishKey: 'YOUR_PUBLISH_KEY',
subscribeKey: 'YOUR_SUBSCRIBE_KEY',
userId: 'quickstart-user',
});
Open main.py and add the following:
import threading
from pubnub.callbacks import SubscribeCallback
from pubnub.enums import PNStatusCategory
from pubnub.exceptions import PubNubException
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
config = PNConfiguration()
config.publish_key = 'YOUR_PUBLISH_KEY'
config.subscribe_key = 'YOUR_SUBSCRIBE_KEY'
config.user_id = 'quickstart-user'
pubnub = PubNub(config)
Open src/main/java/App.java and add the following:
import com.google.gson.JsonObject;
import com.pubnub.api.PubNubException;
import com.pubnub.api.UserId;
import com.pubnub.api.enums.PNStatusCategory;
import com.pubnub.api.java.PubNub;
import com.pubnub.api.java.v2.PNConfiguration;
import com.pubnub.api.java.v2.callbacks.StatusListener;
import com.pubnub.api.java.v2.entities.Channel;
import com.pubnub.api.java.v2.subscriptions.Subscription;
import com.pubnub.api.models.consumer.PNStatus;
public class App {
public static void main(String[] args) throws PubNubException, InterruptedException {
PNConfiguration config = PNConfiguration.builder(new UserId("quickstart-user"), "YOUR_SUBSCRIBE_KEY")
.publishKey("YOUR_PUBLISH_KEY")
show all 20 linesThe subscribe key is a constructor argument on the configuration builder, alongside the userId. The publish key is a separate builder method.
Open src/main/kotlin/Main.kt and add the following:
import com.google.gson.JsonObject
import com.pubnub.api.PubNub
import com.pubnub.api.UserId
import com.pubnub.api.enums.PNStatusCategory
import com.pubnub.api.models.consumer.PNStatus
import com.pubnub.api.v2.PNConfiguration
import com.pubnub.api.v2.callbacks.StatusListener
import com.pubnub.api.v2.entities.Channel
fun main() {
val config = PNConfiguration.builder(UserId("quickstart-user"), "YOUR_SUBSCRIBE_KEY").apply {
publishKey = "YOUR_PUBLISH_KEY"
}.build()
val pubnub = PubNub.create(config)
show all 16 linesThe subscribe key is a constructor argument on the configuration builder, alongside the userId. The publish key is set on the builder itself.
Replace the contents of Program.cs with the following:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PubnubApi;
PNConfiguration config = new PNConfiguration(new UserId("quickstart-user"))
{
PublishKey = "YOUR_PUBLISH_KEY",
SubscribeKey = "YOUR_SUBSCRIBE_KEY"
};
Pubnub pubnub = new Pubnub(config);
Open main.go and add the following:
package main
import (
"fmt"
"log"
pubnub "github.com/pubnub/go/v9"
)
func main() {
config := pubnub.NewConfigWithUserId(pubnub.UserId("quickstart-user"))
config.PublishKey = "YOUR_PUBLISH_KEY"
config.SubscribeKey = "YOUR_SUBSCRIBE_KEY"
pn := pubnub.NewPubNub(config)
show all 16 linesReplace the contents of src/main.rs with the following:
use futures::StreamExt;
use pubnub::subscribe::{ConnectionStatus, EventEmitter, EventSubscriber, Subscriber};
use pubnub::{Keyset, PubNubClientBuilder};
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pubnub = PubNubClientBuilder::with_reqwest_transport()
.with_keyset(Keyset {
publish_key: Some("YOUR_PUBLISH_KEY"),
subscribe_key: "YOUR_SUBSCRIBE_KEY",
secret_key: None,
})
.with_user_id("quickstart-user")
.build()?;
show all 18 linesOpen main.c and add the following:
#include <stdio.h>
#include "pubnub_sync.h"
int main(void)
{
pubnub_t *pubnub = pubnub_alloc();
if (NULL == pubnub) {
puts("Failed to allocate a PubNub context");
return -1;
}
pubnub_init(pubnub, "YOUR_PUBLISH_KEY", "YOUR_SUBSCRIBE_KEY");
pubnub_set_user_id(pubnub, "quickstart-user");
show all 20 linesC-Core (legacy) uses dynamic allocation for its contexts. Always create a context with pubnub_alloc(), check the return value, and release it with pubnub_free().
pubnub_sync.h selects the synchronous interface, where each PubNub call runs to completion before the next line executes.
Open Sources/pubnub-quickstart/main.swift and add the following:
import Foundation
import PubNubSDK
let pubnub = PubNub(configuration: PubNubConfiguration(
publishKey: "YOUR_PUBLISH_KEY",
subscribeKey: "YOUR_SUBSCRIBE_KEY",
userId: "quickstart-user"
))
Open main.m and replace its contents with the following:
#import <Foundation/Foundation.h>
#import <PubNub/PubNub.h>
@interface QuickstartListener : NSObject <PNEventsListener>
@property(nonatomic, strong) PubNub *client;
@end
@implementation QuickstartListener
@end
int main(int argc, const char *argv[]) {
@autoreleasepool {
PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:@"YOUR_PUBLISH_KEY"
subscribeKey:@"YOUR_SUBSCRIBE_KEY"
userID:@"quickstart-user"];
show all 21 linesQuickstartListener holds a strong reference to the client so it isn't released while the program runs, and it adopts PNEventsListener, the protocol through which the SDK delivers messages and connection status.
Replace the contents of bin/pubnub_quickstart.dart with the following:
import 'package:pubnub/pubnub.dart';
Future<void> main() async {
final pubnub = PubNub(
defaultKeyset: Keyset(
publishKey: 'YOUR_PUBLISH_KEY',
subscribeKey: 'YOUR_SUBSCRIBE_KEY',
userId: UserId('quickstart-user'),
),
);
}
Open index.php and add the following:
<?php
require_once 'vendor/autoload.php';
use PubNub\Callbacks\SubscribeCallback;
use PubNub\Enums\PNStatusCategory;
use PubNub\Exceptions\PubNubException;
use PubNub\PNConfiguration;
use PubNub\PubNub;
$config = new PNConfiguration();
$config->setPublishKey('YOUR_PUBLISH_KEY');
$config->setSubscribeKey('YOUR_SUBSCRIBE_KEY');
$config->setUserId('quickstart-user');
show all 16 linesOpen app.rb and add the following:
require 'pubnub'
pubnub = Pubnub.new(
publish_key: 'YOUR_PUBLISH_KEY',
subscribe_key: 'YOUR_SUBSCRIBE_KEY',
user_id: 'quickstart-user'
)
Open PubNubQuickstart.cs and replace its contents with the following:
using System.Collections.Generic;
using PubnubApi;
using PubnubApi.Unity;
using UnityEngine;
public class PubNubQuickstart : PNManagerBehaviour
{
private Subscription subscription;
private void Awake()
{
Initialize("quickstart-user");
}
}
PNManagerBehaviour is the SDK's base MonoBehaviour. Initialize() reads your keys from the PNConfigAsset you assign in the inspector and creates the pubnub client, so your keys never appear in code. You pass the userId here because it identifies this client rather than the keyset.
Open PubNubQuickstartGameInstance.h and replace its contents with the following:
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "PubnubSubsystem.h"
#include "PubNubQuickstartGameInstance.generated.h"
UCLASS()
class QUICKSTART_API UPubNubQuickstartGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
virtual void Init() override;
show all 24 linesReplace QUICKSTART_API with [YOUR_PROJECT_NAME]_API if your project isn't called Quickstart.
Open PubNubQuickstartGameInstance.cpp and replace its contents with the following:
#include "PubNubQuickstartGameInstance.h"
void UPubNubQuickstartGameInstance::Init()
{
Super::Init();
PubnubSubsystem = GetSubsystem<UPubnubSubsystem>();
PubnubSubsystem->SetUserID(TEXT("quickstart-user"));
}
UPubnubSubsystem is a game instance subsystem, so you get it from the game instance rather than constructing a client. Your keys come from Project Settings, so the only thing you set in code is the userId that identifies this client.
Replace YOUR_PUBLISH_KEY and YOUR_SUBSCRIBE_KEY with the keys from your keyset, wherever this tutorial shows them. The userId is a string that identifies this client on the PubNub network. Use any unique value for now.
Subscribe to a channel
- JavaScript
- Python
- Java
- Kotlin
- C#
- Go
- Rust
- C-Core (legacy)
- Swift
- Objective-C
- Dart
- PHP
- Ruby
- Unity
- Unreal Engine
Append this to index.js:
const channel = pubnub.channel('quickstart-channel');
const subscription = channel.subscription();
subscription.onMessage = (event) => {
console.log('Message received:', event.message.text);
};
pubnub.addListener({
status: (event) => {
if (event.category === 'PNConnectedCategory') {
publishMessage();
}
},
});
show all 16 linespubnub.channel() creates a channel entity, and .subscription() scopes a subscription to it. subscription.onMessage fires each time a message arrives on that channel. For more on entities and subscriptions, refer to Pub/Sub overview.
pubnub.addListener() reports connection status at the client level. When status reports PNConnectedCategory, the listener calls publishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Append this to main.py:
connected = threading.Event()
class StatusListener(SubscribeCallback):
def status(self, pubnub, status):
if status.category == PNStatusCategory.PNConnectedCategory:
connected.set()
pubnub.add_listener(StatusListener())
subscription = pubnub.channel('quickstart-channel').subscription()
def on_message(message):
show all 21 linespubnub.channel() creates a channel entity, and .subscription() scopes a subscription to it. subscription.on_message fires each time a message arrives on that channel. For more on entities and subscriptions, refer to Pub/Sub overview.
subscribe() runs the connection on a background thread and returns immediately. Connection status arrives through a SubscribeCallback added to the client, so the listener sets a threading.Event that the main thread waits on before publishing.
Add this inside main(), after creating the pubnub client:
Channel channel = pubnub.channel("quickstart-channel");
Subscription subscription = channel.subscription();
subscription.setOnMessage(event ->
System.out.println("Message received: "
+ event.getMessage().getAsJsonObject().get("text").getAsString()));
pubnub.addListener(new StatusListener() {
@Override
public void status(PubNub pubnub, PNStatus status) {
if (status.getCategory() == PNStatusCategory.PNConnectedCategory) {
publishMessage(channel);
}
}
});
show all 17 linespubnub.channel() creates a channel entity, and .subscription() scopes a subscription to it. The handler you pass to setOnMessage() runs each time a message arrives on that channel. For more on entities and subscriptions, refer to Pub/Sub overview.
A StatusListener on the client reports connection status. When status reports PNConnectedCategory, the listener calls publishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Add this inside main(), after creating the pubnub client:
val channel = pubnub.channel("quickstart-channel")
val subscription = channel.subscription()
subscription.onMessage = { event ->
println("Message received: ${event.message.asJsonObject["text"].asString}")
}
pubnub.addListener(object : StatusListener {
override fun status(pubnub: PubNub, status: PNStatus) {
if (status.category == PNStatusCategory.PNConnectedCategory) {
publishMessage(channel)
}
}
})
show all 16 linespubnub.channel() creates a channel entity, and .subscription() scopes a subscription to it. subscription.onMessage fires each time a message arrives on that channel. For more on entities and subscriptions, refer to Pub/Sub overview.
A StatusListener on the client reports connection status. When status reports PNConnectedCategory, the listener calls publishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Append this to Program.cs:
Subscription subscription = pubnub.Channel("quickstart-channel").Subscription();
subscription.onMessage += (Pubnub pn, PNMessageResult<object> message) =>
{
Dictionary<string, object> payload =
pubnub.JsonPluggableLibrary.DeserializeToDictionaryOfObject(message.Message.ToString());
Console.WriteLine($"Message received: {payload["text"]}");
};
pubnub.AddListener(new SubscribeCallbackExt(
delegate (Pubnub pn, PNStatus status)
{
if (status.Category == PNStatusCategory.PNConnectedCategory)
{
_ = PublishMessage();
show all 19 linespubnub.Channel() creates a channel entity, and .Subscription() scopes a subscription to it. subscription.onMessage fires each time a message arrives on that channel. For more on entities and subscriptions, refer to Pub/Sub overview.
Keep the subscription variable in scope for as long as you want to receive updates. If the garbage collector reclaims a subscription, that subscription stops delivering events.
A SubscribeCallbackExt added to the client reports connection status. When status reports PNConnectedCategory, the listener calls PublishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Add this inside main(), after creating the pn client:
listener := pubnub.NewListener()
go func() {
for {
select {
case status := <-listener.Status:
if status.Category == pubnub.PNConnectedCategory {
publishMessage(pn)
}
case message := <-listener.Message:
payload := message.Message.(map[string]interface{})
fmt.Println("Message received:", payload["text"])
}
}
}()
show all 21 linesThe Go SDK delivers events on channels rather than through callbacks. listener.Message receives messages and listener.Status receives connection status, so a goroutine selects over both. For more on subscriptions, refer to Pub/Sub overview.
When status reports PNConnectedCategory, the goroutine calls publishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Add this inside main(), after building the pubnub client and before Ok(()):
let subscription = pubnub.channel("quickstart-channel").subscription(None);
subscription.subscribe();
tokio::spawn(
subscription
.messages_stream()
.for_each(|message| async move {
let payload: Value = serde_json::from_slice(&message.data).unwrap();
println!("Message received: {}", payload["text"].as_str().unwrap());
}),
);
let mut status_stream = pubnub.status_stream();
while let Some(status) = status_stream.next().await {
if matches!(status, ConnectionStatus::Connected) {
show all 18 linespubnub.channel() creates a channel entity, and .subscription(None) scopes a subscription to it with no extra options. For more on entities and subscriptions, refer to Pub/Sub overview.
The Rust SDK exposes events as streams. messages_stream() yields messages, so a spawned task prints each one as it arrives. pubnub.status_stream() yields connection status, and the loop waits on it until the status is Connected, so the publish you add in the next step runs only once the subscription is confirmed active.
Keep the subscription binding alive for as long as you want to receive updates. Dropping it unsubscribes.
The synchronous interface has no callbacks. A pubnub_subscribe() call returns when PubNub responds, and the first call is what establishes the connection.
Add this inside main(), after pubnub_set_user_id():
if (PNR_STARTED == pubnub_subscribe(pubnub, "quickstart-channel", NULL)) {
pubnub_await(pubnub);
}
pubnub_subscribe() starts the transaction and pubnub_await() blocks until it finishes. This first call returns no messages. It connects to quickstart-channel and records the point in time from which the next call reads, so any message published after it is delivered to the next pubnub_subscribe() call. For more on subscriptions, refer to Pub/Sub overview.
Append this to main.swift:
let subscription = pubnub.channel("quickstart-channel").subscription()
subscription.onMessage = { message in
if let text = message.payload[rawValue: "text"] as? String {
print("Message received: \(text)")
}
}
pubnub.onConnectionStateChange = { newStatus in
if case .connected = newStatus {
publishMessage()
}
}
subscription.subscribe()
pubnub.channel() creates a channel entity, and .subscription() scopes a subscription to it. subscription.onMessage fires each time a message arrives on that channel. For more on entities and subscriptions, refer to Pub/Sub overview.
pubnub.onConnectionStateChange reports connection status at the client level. When the status becomes .connected, the handler calls publishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Add these two methods to the QuickstartListener implementation, between @implementation and @end:
- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
NSLog(@"Message received: %@", message.data.message[@"text"]);
}
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
if (status.category == PNConnectedCategory) {
[self publishMessage];
}
}
Then add this inside main(), after creating the client:
[listener.client addListener:listener];
[listener.client subscribeToChannels:@[@"quickstart-channel"] withPresence:NO];
The PNEventsListener protocol delivers both messages and connection status to the object you register with addListener:. didReceiveMessage: fires each time a message arrives, and didReceiveStatus: fires on connection changes. For more on subscriptions, refer to Pub/Sub overview.
When the status category is PNConnectedCategory, the listener calls publishMessage, which you add in the next step. That way the message publishes only once the subscription is confirmed active. Passing withPresence:NO keeps this program to messages only.
Add this inside main(), after creating the pubnub client:
final subscription = pubnub.subscribe(channels: {'quickstart-channel'});
subscription.messages.listen((envelope) {
print('Message received: ${envelope.payload['text']}');
});
await subscription.whenStarts;
pubnub.subscribe() returns a subscription that exposes incoming events as streams. Listening to subscription.messages runs your callback for each message that arrives on the channel. For more on subscriptions, refer to Pub/Sub overview.
subscription.whenStarts is a future that completes once the subscription is connected and listening, so awaiting it means the publish you add in the next step runs only after the channel is live.
Append this to index.php:
class QuickstartListener extends SubscribeCallback
{
public function status($pubnub, $status)
{
if ($status->getCategory() === PNStatusCategory::PNConnectedCategory) {
publishMessage($pubnub);
}
}
public function message($pubnub, $message)
{
echo 'Message received: ' . $message->getMessage()['text'] . PHP_EOL;
}
public function presence($pubnub, $presence)
show all 22 linesA SubscribeCallback subclass receives messages and connection status. message() runs each time a message arrives on the channel, and status() runs on connection changes. presence() is required by the base class, so it stays empty here. For more on subscriptions, refer to Pub/Sub overview.
execute() blocks and runs the subscribe loop, so it must be the last statement your script runs. When the status category is PNConnectedCategory, the listener calls publishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Append this to app.rb:
callback = Pubnub::SubscribeCallback.new(
message: ->(envelope) {
puts "Message received: #{envelope.result[:data][:message]['text']}"
},
presence: ->(envelope) {},
status: ->(envelope) {
publish_message(pubnub) if envelope.status[:category] == Pubnub::Constants::STATUS_ACK
}
)
pubnub.add_listener(callback: callback)
pubnub.subscribe(channels: ['quickstart-channel'])
A Pubnub::SubscribeCallback holds one lambda per event type. The message lambda runs each time a message arrives on the channel. The presence lambda is required, so it stays empty here. For more on subscriptions, refer to Pub/Sub overview.
subscribe runs the connection on a background thread and returns immediately. The status lambda receives one acknowledgment, Pubnub::Constants::STATUS_ACK, when the subscribe loop connects, and it calls publish_message, which you add in the next step. That way the message publishes only once the subscription is confirmed active.
Add the subscription and the listeners to PubNubQuickstart.cs, so the class looks like this:
public class PubNubQuickstart : PNManagerBehaviour
{
private Subscription subscription;
private void Awake()
{
listener.onStatus += OnStatus;
listener.onMessage += OnMessage;
Initialize("quickstart-user");
subscription = pubnub.Channel("quickstart-channel").Subscription();
subscription.Subscribe<object>();
}
show all 38 linespubnub.Channel() creates a channel entity, and .Subscription() scopes a subscription to it. Attach your handlers to the inherited listener before you call Initialize(). The Unity SDK dispatches those handlers on the main game loop, so you can touch scene objects and call Debug.Log() from them directly. For more on entities and subscriptions, refer to Pub/Sub overview.
When status reports PNConnectedCategory, OnStatus calls PublishMessage(), which you add in the next step. That way the message publishes only once the subscription is confirmed active. OnDestroy detaches the handlers when the object goes away.
Add the listener bindings and the subscribe call to Init() in PubNubQuickstartGameInstance.cpp, then add the two handlers:
void UPubNubQuickstartGameInstance::Init()
{
Super::Init();
PubnubSubsystem = GetSubsystem<UPubnubSubsystem>();
PubnubSubsystem->SetUserID(TEXT("quickstart-user"));
PubnubSubsystem->OnMessageReceived.AddDynamic(this, &UPubNubQuickstartGameInstance::OnMessageReceived);
PubnubSubsystem->OnSubscriptionStatusChanged.AddDynamic(this, &UPubNubQuickstartGameInstance::OnSubscriptionStatusChanged);
PubnubSubsystem->SubscribeToChannel(TEXT("quickstart-channel"));
}
void UPubNubQuickstartGameInstance::OnMessageReceived(FPubnubMessageData MessageData)
{
show all 17 linesOnMessageReceived and OnSubscriptionStatusChanged are dynamic multicast delegates on the subsystem, so you bind your UFUNCTION() handlers with AddDynamic. MessageData.Message holds the message payload as an FString. For more on subscriptions, refer to Pub/Sub overview.
Channels are created implicitly the first time they are used and do not require provisioning, so quickstart-channel becomes available the moment you use it.
Publish a message
- JavaScript
- Python
- Java
- Kotlin
- C#
- Go
- Rust
- C-Core (legacy)
- Swift
- Objective-C
- Dart
- PHP
- Ruby
- Unity
- Unreal Engine
Append this to index.js:
async function publishMessage() {
try {
await pubnub.publish({
channel: 'quickstart-channel',
message: { text: 'Hello, PubNub!' },
});
} catch (error) {
console.error('Publish failed:', error);
}
}
publish() sends a message to a channel. The message can be any JSON-serializable value. Here it's a plain object with a text field.
Append this to main.py:
connected.wait()
try:
pubnub.publish() \
.channel('quickstart-channel') \
.message({'text': 'Hello, PubNub!'}) \
.sync()
except PubNubException as error:
print('Publish failed:', error)
publish() sends a message to a channel, and sync() runs the request and blocks until it completes. The message can be any JSON-serializable value. Here it's a dictionary with a text key.
connected.wait() blocks the main thread until the status listener reports the connection, so the publish never races ahead of the subscription.
Add this method to the App class, after main():
static void publishMessage(Channel channel) {
JsonObject message = new JsonObject();
message.addProperty("text", "Hello, PubNub!");
channel.publish(message).async(result ->
result.onFailure(exception ->
System.out.println("Publish failed: " + exception.getMessage())));
}
publish() sends a message to a channel, and async() runs the request without blocking. The message can be any JSON-serializable value. Here it's a Gson JsonObject with a text property.
Add this function to Main.kt, after main():
fun publishMessage(channel: Channel) {
val message = JsonObject().apply { addProperty("text", "Hello, PubNub!") }
channel.publish(message).async { result ->
result.onFailure { exception -> println("Publish failed: ${exception.message}") }
}
}
publish() sends a message to a channel, and async() runs the request without blocking. The message can be any JSON-serializable value. Here it's a Gson JsonObject with a text property.
Append this to Program.cs:
async Task PublishMessage()
{
PNResult<PNPublishResult> result = await pubnub.Publish()
.Channel("quickstart-channel")
.Message(new Dictionary<string, object> { { "text", "Hello, PubNub!" } })
.ExecuteAsync();
if (result.Status.Error)
{
Console.WriteLine($"Publish failed: {result.Status.ErrorData.Information}");
}
}
Publish() sends a message to a channel, and ExecuteAsync() awaits the result. The message can be any JSON-serializable value. Here it's a dictionary with a text key.
Local functions can appear after the statements that call them, so this can go at the end of the file.
Add this function to main.go, after main():
func publishMessage(pn *pubnub.PubNub) {
_, _, err := pn.Publish().
Channel("quickstart-channel").
Message(map[string]interface{}{"text": "Hello, PubNub!"}).
Execute()
if err != nil {
log.Println("Publish failed:", err)
}
}
Publish() sends a message to a channel, and Execute() runs the request and returns when it completes. The message can be any JSON-serializable value. Here it's a map with a text key.
Add this inside main(), after the status loop and before Ok(()):
pubnub
.publish_message(json!({ "text": "Hello, PubNub!" }))
.channel("quickstart-channel")
.execute()
.await?;
publish_message() sends a message to a channel, and execute() returns a future you await. The message can be any serializable value. Here it's a serde_json value with a text field.
Add this inside main(), after the first pubnub_subscribe() block:
if (PNR_STARTED == pubnub_publish(pubnub, "quickstart-channel", "{\"text\":\"Hello, PubNub!\"}")) {
pubnub_await(pubnub);
}
pubnub_publish() sends a message to a channel, and pubnub_await() blocks until PubNub acknowledges it. C-Core (legacy) takes the message as a JSON string that you build yourself, so the quotes inside it are escaped.
One context runs one transaction at a time. Publishing here is safe because the subscribe transaction above already finished.
Append this to main.swift:
func publishMessage() {
pubnub.publish(channel: "quickstart-channel", message: ["text": "Hello, PubNub!"]) { result in
if case let .failure(error) = result {
print("Publish failed: \(error.localizedDescription)")
}
}
}
publish(channel:message:) sends a message to a channel and reports the outcome in its completion handler. The message can be any JSON-serializable value. Here it's a dictionary with a text key.
Add this method to the QuickstartListener implementation, alongside the other two:
- (void)publishMessage {
[self.client publish:@{@"text": @"Hello, PubNub!"}
toChannel:@"quickstart-channel"
withCompletion:^(PNPublishStatus *status) {
if (status.isError) {
NSLog(@"Publish failed: %@", status.errorData.information);
}
}];
}
publish:toChannel:withCompletion: sends a message to a channel and reports the outcome in its completion block. The message can be any JSON-serializable value. Here it's an NSDictionary with a text key.
Add this inside main(), after await subscription.whenStarts:
try {
await pubnub.publish('quickstart-channel', {'text': 'Hello, PubNub!'});
} catch (error) {
print('Publish failed: $error');
}
publish() sends a message to a channel and returns a future you await. The message can be any JSON-serializable value. Here it's a map with a text key.
Append this to index.php:
function publishMessage($pubnub)
{
try {
$pubnub->publish()
->channel('quickstart-channel')
->message(['text' => 'Hello, PubNub!'])
->sync();
} catch (PubNubException $error) {
echo 'Publish failed: ' . $error->getMessage() . PHP_EOL;
}
}
publish() sends a message to a channel, and sync() runs the request and blocks until it completes. The message can be any JSON-serializable value. Here it's an array with a text key.
PHP makes top-level functions available before the line that declares them, so this can go after the subscribe() call that uses it.
Append this to app.rb:
def publish_message(pubnub)
pubnub.publish(
channel: 'quickstart-channel',
message: { text: 'Hello, PubNub!' }
) do |envelope|
puts "Publish failed: #{envelope.status[:category]}" if envelope.status[:error]
end
end
publish sends a message to a channel and reports the outcome in the block you pass it. The message can be any JSON-serializable value. Here it's a hash with a :text key.
Add this method to the PubNubQuickstart class:
private async void PublishMessage()
{
PNResult<PNPublishResult> result = await pubnub.Publish()
.Channel("quickstart-channel")
.Message(new Dictionary<string, object> { { "text", "Hello, PubNub!" } })
.ExecuteAsync();
if (result.Status.Error)
{
Debug.LogError($"Publish failed: {result.Status.ErrorData.Information}");
}
}
Publish() sends a message to a channel, and ExecuteAsync() awaits the result without blocking the game loop. The message can be any JSON-serializable value. Here it's a dictionary with a text key.
Add this handler to PubNubQuickstartGameInstance.cpp:
void UPubNubQuickstartGameInstance::OnSubscriptionStatusChanged(EPubnubSubscriptionStatus Status, FPubnubSubscriptionStatusData StatusData)
{
if (Status == EPubnubSubscriptionStatus::PSS_Connected)
{
PubnubSubsystem->PublishMessage(TEXT("quickstart-channel"), TEXT("{\"text\":\"Hello, PubNub!\"}"));
}
}
PublishMessage() sends a message to a channel. The Unreal SDK takes the message as a JSON FString that you build yourself, so the quotes inside it are escaped.
Publishing from OnSubscriptionStatusChanged when the status is PSS_Connected means the message goes out only once the subscription is confirmed active.
Run the program
- JavaScript
- Python
- Java
- Kotlin
- C#
- Go
- Rust
- C-Core (legacy)
- Swift
- Objective-C
- Dart
- PHP
- Ruby
- Unity
- Unreal Engine
node index.js
You should see:
Message received: Hello, PubNub!
The program keeps running because the subscription holds the connection open. Press Ctrl+C to stop it.
python main.py
You should see:
Message received: Hello, PubNub!
The program keeps running because the subscription thread holds the connection open. Press Ctrl+C to stop it.
Add this as the last statement in main(), so the program doesn't exit while the subscription is still running:
Thread.currentThread().join();
Then run the program:
mvn -q compile exec:java -Dexec.mainClass=App
You should see:
Message received: Hello, PubNub!
The SDK logs through SLF4J, so a few SLF4J: notices about a missing logger binding may appear before that line. They are harmless.
Thread.currentThread().join() never returns, so the program keeps running with the connection open. Press Ctrl+C to stop it.
Add this as the last statement in main(), so the program doesn't exit while the subscription is still running:
Thread.currentThread().join()
Then run the program:
gradle run -q
You should see:
Message received: Hello, PubNub!
The SDK logs through SLF4J, so a few SLF4J: notices about a missing logger binding may appear before that line. They are harmless.
Thread.currentThread().join() never returns, so the program keeps running with the connection open. Press Ctrl+C to stop it.
Add this line to Program.cs, after subscription.Subscribe<object>() and before the PublishMessage local function, so the program doesn't exit while the subscription is still running:
await Task.Delay(-1);
Then run the program:
dotnet run
You should see:
Message received: Hello, PubNub!
Task.Delay(-1) waits forever, so the program keeps running with the connection open. Press Ctrl+C to stop it.
Add this as the last statement in main(), so the program doesn't exit while the subscription is still running:
select {}
Then run the program:
go run main.go
You should see:
Message received: Hello, PubNub!
An empty select {} blocks forever, so the program keeps running with the connection open. Press Ctrl+C to stop it.
Add this before Ok(()) in main(), so the program doesn't exit while the subscription is still running:
std::future::pending::<()>().await;
Then run the program:
cargo run
You should see:
Message received: Hello, PubNub!
std::future::pending() never completes, so the program keeps running with the connection open. Press Ctrl+C to stop it.
Add this second pubnub_subscribe() call and the read loop inside main(), after the publish and before pubnub_free():
if (PNR_STARTED == pubnub_subscribe(pubnub, "quickstart-channel", NULL)) {
pubnub_await(pubnub);
}
for (char const *message = pubnub_get(pubnub); NULL != message; message = pubnub_get(pubnub)) {
printf("Message received: %s\n", message);
}
This second pubnub_subscribe() call returns as soon as PubNub has a message for you. pubnub_get() then returns the messages one at a time and NULL when there are none left.
Compile and run the program from the c-core/openssl directory:
cc -I. -I.. -o pubnub-quickstart main.c pubnub_sync.a $(pkg-config --libs openssl)
./pubnub-quickstart
You should see:
Message received: {"text":"Hello, PubNub!"}
C-Core (legacy) hands you the raw JSON payload rather than a parsed object, so the whole message prints. Unlike the other languages on this page, this program then exits on its own, because the synchronous interface reads messages when you ask for them instead of holding a listener open.
Add this as the last line of main.swift, so the program doesn't exit while the subscription is still running:
RunLoop.main.run()
Then run the program:
swift run
You should see:
Message received: Hello, PubNub!
RunLoop.main.run() never returns, so the program keeps running with the connection open. Press Ctrl+C to stop it.
Add this as the last statement inside the @autoreleasepool block in main(), so the program doesn't exit while the subscription is still running:
[[NSRunLoop currentRunLoop] run];
Then build and run the program in Xcode with ⌘R.
In the Xcode console you should see:
Message received: Hello, PubNub!
NSLog prefixes each line with a timestamp and the process name, so the line appears after that prefix.
The run loop never returns, so the program keeps running with the connection open. Press the Stop button in Xcode to end it.
dart run
You should see:
Message received: Hello, PubNub!
The program keeps running after main() returns because the subscription stream is still active. Press Ctrl+C to stop it.
php index.php
You should see:
Message received: Hello, PubNub!
The program keeps running because execute() runs the subscribe loop indefinitely. Press Ctrl+C to stop it.
Add this as the last line of app.rb, so the program doesn't exit while the subscription is still running:
sleep
Then run the program:
ruby app.rb
You should see:
Message received: Hello, PubNub!
sleep with no argument blocks forever, so the program keeps running with the connection open. Press Ctrl+C to stop it.
Wire the script into your scene, then play it:
- Create an empty game object in your scene and drag
PubNubQuickstartonto it to add it as a component. - Drag your
PNConfigAssetonto the Pn Configuration field of the PubNubQuickstart (Script) component. - Enter Play mode.
In the Console window you should see:
Message received: Hello, PubNub!
If the Console shows PNConfigAsset is missing instead, the config asset is not assigned to the component.
The subscription stays open for as long as you remain in Play mode. Exit Play mode to stop it.
Make your game instance the one the project uses, then play it:
- Select Edit > Project Settings and open Maps & Modes.
- Set Game Instance Class to
PubNubQuickstartGameInstance. - Compile the project and click Play.
In the Output Log you should see:
Message received: {"text":"Hello, PubNub!"}
The Unreal SDK hands you the raw JSON payload rather than a parsed object, so the whole message prints.
The subscription stays open for as long as the game instance lives. Stop playing to end it.
The message travels from your program to the PubNub network and back to the same subscriber.
Troubleshooting
If the output does not appear:
- Verify your keys. Publish keys start with
pub-c-and subscribe keys start withsub-c-. Swapping them is a common mistake. - Check for Access Manager errors. If Access Manager is enabled on your keyset, the SDK requires a valid token to subscribe and publish. If your status handler reports an access-denied status, disable Access Manager on your keyset in the Admin Portal or refer to Access Manager.
- Check your network. If the SDK cannot reach PubNub, your status handler reports a connection failure instead of a connected status, so the publish step never runs. Verify that the machine has internet access and that outbound HTTPS to
*.pubnub.com,*.pndsn.com,*.pubnub.net, and*.pubnubapi.comisn't blocked by a firewall. If your network policy requires IP allowlisting instead of domain allowlisting, that requires a paid plan. Contact support for the current IP list.
What happened
Your program acted as both publisher and subscriber on quickstart-channel:
- You created a subscription to
quickstart-channeland attached a handler for incoming messages. - You activated the subscription, which opened a connection to PubNub and started listening.
- Once the subscription was active, your program published a message to the same channel.
- PubNub routed the message to every active subscriber on
quickstart-channel, including this program. - Your message handler ran and printed the text.
This is the pub/sub model in practice: publishers send messages to a channel, and PubNub delivers them to all current subscribers.
Complete file
- JavaScript
- Python
- Java
- Kotlin
- C#
- Go
- Rust
- C-Core (legacy)
- Swift
- Objective-C
- Dart
- PHP
- Ruby
- Unity
- Unreal Engine
const PubNub = require('pubnub');
const pubnub = new PubNub({
publishKey: 'YOUR_PUBLISH_KEY',
subscribeKey: 'YOUR_SUBSCRIBE_KEY',
userId: 'quickstart-user',
});
const channel = pubnub.channel('quickstart-channel');
const subscription = channel.subscription();
subscription.onMessage = (event) => {
console.log('Message received:', event.message.text);
};
show all 35 linesimport threading
from pubnub.callbacks import SubscribeCallback
from pubnub.enums import PNStatusCategory
from pubnub.exceptions import PubNubException
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
config = PNConfiguration()
config.publish_key = 'YOUR_PUBLISH_KEY'
config.subscribe_key = 'YOUR_SUBSCRIBE_KEY'
config.user_id = 'quickstart-user'
pubnub = PubNub(config)
show all 46 linesimport com.google.gson.JsonObject;
import com.pubnub.api.PubNubException;
import com.pubnub.api.UserId;
import com.pubnub.api.enums.PNStatusCategory;
import com.pubnub.api.java.PubNub;
import com.pubnub.api.java.v2.PNConfiguration;
import com.pubnub.api.java.v2.callbacks.StatusListener;
import com.pubnub.api.java.v2.entities.Channel;
import com.pubnub.api.java.v2.subscriptions.Subscription;
import com.pubnub.api.models.consumer.PNStatus;
public class App {
public static void main(String[] args) throws PubNubException, InterruptedException {
PNConfiguration config = PNConfiguration.builder(new UserId("quickstart-user"), "YOUR_SUBSCRIBE_KEY")
.publishKey("YOUR_PUBLISH_KEY")
show all 49 linesimport com.google.gson.JsonObject
import com.pubnub.api.PubNub
import com.pubnub.api.UserId
import com.pubnub.api.enums.PNStatusCategory
import com.pubnub.api.models.consumer.PNStatus
import com.pubnub.api.v2.PNConfiguration
import com.pubnub.api.v2.callbacks.StatusListener
import com.pubnub.api.v2.entities.Channel
fun main() {
val config = PNConfiguration.builder(UserId("quickstart-user"), "YOUR_SUBSCRIBE_KEY").apply {
publishKey = "YOUR_PUBLISH_KEY"
}.build()
val pubnub = PubNub.create(config)
show all 43 linesusing System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PubnubApi;
PNConfiguration config = new PNConfiguration(new UserId("quickstart-user"))
{
PublishKey = "YOUR_PUBLISH_KEY",
SubscribeKey = "YOUR_SUBSCRIBE_KEY"
};
Pubnub pubnub = new Pubnub(config);
Subscription subscription = pubnub.Channel("quickstart-channel").Subscription();
show all 47 linespackage main
import (
"fmt"
"log"
pubnub "github.com/pubnub/go/v9"
)
func main() {
config := pubnub.NewConfigWithUserId(pubnub.UserId("quickstart-user"))
config.PublishKey = "YOUR_PUBLISH_KEY"
config.SubscribeKey = "YOUR_SUBSCRIBE_KEY"
pn := pubnub.NewPubNub(config)
show all 50 linesuse futures::StreamExt;
use pubnub::subscribe::{ConnectionStatus, EventEmitter, EventSubscriber, Subscriber};
use pubnub::{Keyset, PubNubClientBuilder};
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pubnub = PubNubClientBuilder::with_reqwest_transport()
.with_keyset(Keyset {
publish_key: Some("YOUR_PUBLISH_KEY"),
subscribe_key: "YOUR_SUBSCRIBE_KEY",
secret_key: None,
})
.with_user_id("quickstart-user")
.build()?;
show all 44 lines#include <stdio.h>
#include "pubnub_sync.h"
int main(void)
{
pubnub_t *pubnub = pubnub_alloc();
if (NULL == pubnub) {
puts("Failed to allocate a PubNub context");
return -1;
}
pubnub_init(pubnub, "YOUR_PUBLISH_KEY", "YOUR_SUBSCRIBE_KEY");
pubnub_set_user_id(pubnub, "quickstart-user");
show all 36 linesimport Foundation
import PubNubSDK
let pubnub = PubNub(configuration: PubNubConfiguration(
publishKey: "YOUR_PUBLISH_KEY",
subscribeKey: "YOUR_SUBSCRIBE_KEY",
userId: "quickstart-user"
))
let subscription = pubnub.channel("quickstart-channel").subscription()
subscription.onMessage = { message in
if let text = message.payload[rawValue: "text"] as? String {
print("Message received: \(text)")
}
show all 34 lines#import <Foundation/Foundation.h>
#import <PubNub/PubNub.h>
@interface QuickstartListener : NSObject <PNEventsListener>
@property(nonatomic, strong) PubNub *client;
@end
@implementation QuickstartListener
- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
NSLog(@"Message received: %@", message.data.message[@"text"]);
}
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
if (status.category == PNConnectedCategory) {
show all 47 linesimport 'package:pubnub/pubnub.dart';
Future<void> main() async {
final pubnub = PubNub(
defaultKeyset: Keyset(
publishKey: 'YOUR_PUBLISH_KEY',
subscribeKey: 'YOUR_SUBSCRIBE_KEY',
userId: UserId('quickstart-user'),
),
);
final subscription = pubnub.subscribe(channels: {'quickstart-channel'});
subscription.messages.listen((envelope) {
print('Message received: ${envelope.payload['text']}');
show all 25 lines<?php
require_once 'vendor/autoload.php';
use PubNub\Callbacks\SubscribeCallback;
use PubNub\Enums\PNStatusCategory;
use PubNub\Exceptions\PubNubException;
use PubNub\PNConfiguration;
use PubNub\PubNub;
$config = new PNConfiguration();
$config->setPublishKey('YOUR_PUBLISH_KEY');
$config->setSubscribeKey('YOUR_SUBSCRIBE_KEY');
$config->setUserId('quickstart-user');
show all 51 linesrequire 'pubnub'
pubnub = Pubnub.new(
publish_key: 'YOUR_PUBLISH_KEY',
subscribe_key: 'YOUR_SUBSCRIBE_KEY',
user_id: 'quickstart-user'
)
callback = Pubnub::SubscribeCallback.new(
message: ->(envelope) {
puts "Message received: #{envelope.result[:data][:message]['text']}"
},
presence: ->(envelope) {},
status: ->(envelope) {
publish_message(pubnub) if envelope.status[:category] == Pubnub::Constants::STATUS_ACK
show all 32 linesusing System.Collections.Generic;
using PubnubApi;
using PubnubApi.Unity;
using UnityEngine;
public class PubNubQuickstart : PNManagerBehaviour
{
private Subscription subscription;
private void Awake()
{
listener.onStatus += OnStatus;
listener.onMessage += OnMessage;
Initialize("quickstart-user");
show all 56 linesPubNubQuickstartGameInstance.h:
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "PubnubSubsystem.h"
#include "PubNubQuickstartGameInstance.generated.h"
UCLASS()
class QUICKSTART_API UPubNubQuickstartGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
virtual void Init() override;
show all 24 linesPubNubQuickstartGameInstance.cpp:
#include "PubNubQuickstartGameInstance.h"
void UPubNubQuickstartGameInstance::Init()
{
Super::Init();
PubnubSubsystem = GetSubsystem<UPubnubSubsystem>();
PubnubSubsystem->SetUserID(TEXT("quickstart-user"));
PubnubSubsystem->OnMessageReceived.AddDynamic(this, &UPubNubQuickstartGameInstance::OnMessageReceived);
PubnubSubsystem->OnSubscriptionStatusChanged.AddDynamic(this, &UPubNubQuickstartGameInstance::OnSubscriptionStatusChanged);
PubnubSubsystem->SubscribeToChannel(TEXT("quickstart-channel"));
}
show all 27 linesNext steps
You've published and received a message with PubNub. From here, you can go deeper into pub/sub or start adding other capabilities to your application:
- Pub/Sub overview. Learn about signals, channel groups, and publish options.
- Presence. Track who is connected to a channel in real time.
- Data storage. Retrieve messages that subscribers missed while offline.
- Available SDKs. Find the SDK for your platform or language.