Migrating from C-Core v7
This guide is for anyone with an existing application built on C-Core SDK v7 (PUBNUB_SDK_VERSION reporting 7.3.1) who wants to move it to the new C SDK.
Read this before you start porting. This is a full rewrite, not an incremental upgrade. Every public symbol changed. The context lifecycle, the asynchronous model, the error model, the build system, and the platform-abstraction approach are all different from v7, and there is no mechanical find-and-replace path from one to the other. If you maintain a custom platform port, a custom build script, or code that inspects enum pubnub_res values, budget real engineering time for this migration. Don't treat it as a version bump.
This guide is for you if any of the following is true:
- You have application code that calls v7 functions such as
pubnub_alloc(),pubnub_publish(),pubnub_subscribe(), or anything undercore/pubnub_coreapi.h. - You maintain a custom platform port (a target other than POSIX, Windows, or FreeRTOS), a custom Makefile fragment, or a hand-edited
pubnub_config.h. - You rely on a specific
PNR_*result code, on DNS-server configuration functions, or on the SDK's built-in UUID generators.
If you are new to the SDK and have no existing v7 code, skip this guide and start with Getting Started instead. If you still need v7's own documentation for reference while you migrate, it lives under C-Core SDK (legacy).
What has changed
| Area | C-Core v7 | New SDK |
|---|---|---|
| Context lifecycle | pubnub_alloc() / pubnub_free(), heap only; the static-vs-malloc backend choice is invisible at the call site | pubnub_create() / pubnub_destroy() (heap) or pubnub_init() / pubnub_deinit() (caller-provided memory) |
| The async model | A header/build choice: pubnub_sync.h vs. pubnub_callback.h, optionally overridden at runtime with pubnub_enforce_api() | One pubnub_future_t per call; three consumption styles chosen per call site, no header or build choice |
| The error model | enum pubnub_res, 41 values | pubnub_res_t, 16 values, with server-side detail moved to a normalized pubnub_service_error_t |
| The provider model | A fixed per-platform file stack (posix/, windows/, freertos/, mbedtls/, microchip_harmony/, qt/, unreal/, cpp/) | Six swappable provider vtables under include/pubnub/providers/, validated at context-creation time |
| The build system | Makefiles plus a hand-edited pubnub_config.h per platform | CMake with full / minimal / embedded profiles and a generated config header |
pubnub_delete_messages()'s end bound | Inclusive | Exclusive, see Silent behavior changes |
| History entry point | pubnub_fetch_history() | pubnub_fetch_messages(), see Renames and restructuring |
Message Actions type | Closed 3-value enum on the deprecated call path; a free-form string, already, on the recommended v7 replacement | Always a free-form string, up to 15 characters |
| Access Manager | Hand-built JSON via sprintf; the issued token is raw CBOR you decode yourself | Typed options struct; the token is parsed for you into indexed accessors |
| Files, Mobile Push | Did not exist | Full APIs, see What's new |
| Platform support | Includes Microchip Harmony, Qt, Unreal Engine, mbedTLS-as-transport, and a working C++ class | POSIX, Windows, FreeRTOS, and ESP-IDF only; the C++ wrapper is an unimplemented placeholder |
Silent behavior changes
These six changes compile fine against the new SDK and produce a different result, with no compiler warning and no runtime diagnostic. Read this section before you port any code, not after something breaks in production.
Delete messages: end flipped from inclusive to exclusive
Ported delete code silently leaves one message undeleted
In C-Core v7, pubnub_delete_messages()'s end timetoken was inclusive. It deleted the message at that exact timetoken along with everything after it. In the new SDK, end is exclusive, so the message at that timetoken is kept. A v7 delete call ported without changing end compiles, runs, returns PUBNUB_OK, and silently leaves exactly one message in place. Nothing flags this as an error.
- Before (C-Core v7)
- After (new SDK)
/* C-Core v7 — end is INCLUSIVE: the message AT "end" is deleted too */
pubnub_t* pb = pubnub_alloc();
pubnub_init(pb, "demo", "demo");
pubnub_delete_messages(pb, "my_channel",
"17001234567890000", /* start, exclusive */
"17001234567890123"); /* end, deleted in v7 */
pubnub_free(pb);
#include <pubnub/client.h>
#include <pubnub/features/history.h>
#include <pubnub/future.h>
#include <pubnub/response.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.user_id = "my_unique_user_id";
pubnub_context_t* ctx = pubnub_create(&cfg);
show all 40 linesFull field reference and error handling: Delete messages. Note that pubnub_fetch_messages()'s own end field is inclusive, unchanged from v7. Only delete's boundary changed. Don't assume fetch and delete share one rule on either SDK.
Message action value no longer needs quote-wrapping — and the new SDK won't tell you if you still send it that way
danger
value stores a wrong value, silentlyC-Core v7 required the caller to manually wrap value in literal quote characters and rejected a value missing them with a parameter error. The new SDK's value field is a plain string with no such requirement and no such check. Code that still sends "\"thumbs_up\"" instead of "thumbs_up" compiles, sends successfully, and stores a literally-quoted string as the action value. Nothing flags the mismatch.
See Add message action for the current field reference.
The old 100-item cap on pubnub_get_message_actions()'s limit is gone, silently
A caller mistake that used to be caught now proceeds instead
C-Core v7 documented limit as valid only in the range 1–100 and returned an error for anything higher. The new SDK's limit is a plain uint32_t with no documented or enforced maximum. Code that relied on the old error to catch an out-of-range value passed in by mistake no longer gets that safety net. The call proceeds instead of failing.
See Get message actions.
JSON-tree ownership is not uniform across features
Applying one feature's ownership rule to another is a double free or a leak
This trap is not old-versus-new. It is new-SDK-internal, but it is the single highest-risk issue for anyone porting several features from v7 in one pass, so it belongs here. Several options structs accept a pubnub_json_value_t* tree, and the ownership rule differs by feature. publish.h's message_value, signal.h's message_value, and presence.h's state_value are all caller-retains-ownership: you destroy the tree yourself with pubnub_json_destroy() after the call returns. app_context.h's custom_value is the outlier. Its ownership transfers to the SDK, which consumes and frees the tree during the call, on both success and failure. A developer who ports Publish first, correctly learns "I own it, I free it," and then reaches App Context and applies the same rule gets a double free. Applying the App Context rule to message_value or state_value instead leaks the tree. Neither mistake produces a diagnostic. Check the ownership rule for every JSON-tree field individually. Never generalize from one feature to another.
Heartbeat can be silently off by default
A "config-driven" heartbeat is not the same as an automatic one
C-Core v7 required an explicit pubnub_heartbeat() call, or an opt-in compiled auto-heartbeat module with its own watcher threads, so v7 users expect to turn heartbeat on explicitly. The new SDK has no callable heartbeat function at all. It's driven entirely by the presence_timeout and heartbeat_interval config fields and starts automatically when a subscription activates. But leaving both fields at their zero defaults, which is exactly what pubnub_config_defaults() produces, computes a heartbeat interval of 0. That means no automatic heartbeat is ever sent, with no error or log line to say so. Set presence_timeout or heartbeat_interval explicitly to get automatic heartbeat.
- Before (C-Core v7)
- After (new SDK)
/* C-Core v7 — heartbeat was an explicit call, or an opt-in compiled
module (core/pbauto_heartbeat.h) with its own watcher threads */
pubnub_heartbeat(pb, "my_channel", NULL);
#include <pubnub/client.h>
static void enable_heartbeat(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.user_id = "my_unique_user_id";
/* Heartbeat has no callable function. Leaving presence_timeout and
heartbeat_interval at their zero defaults sends NO heartbeat at all. */
cfg.presence_timeout = 120; /* heartbeat_interval is then auto-computed */
}
Full explanation: Heartbeat.
Access Manager's TTL cap moved from server-side to client-side
Same limit, different failure point
The 43,200-minute (30-day) TTL cap is unchanged in value between v7 and the new SDK, but the new SDK enforces it client-side, before any network call, whereas the v7 documentation describes server-side enforcement. Not dangerous, but if you're debugging why an out-of-range ttl now fails instantly instead of after a round trip, that's why. See Grant token.
Context lifecycle
C-Core v7 had one lifecycle. pubnub_alloc() returned a heap-allocated context and pubnub_free() released it. Whether that heap allocation came from malloc or from a fixed-size static array was a link-time choice invisible at the call site. Application code couldn't tell which backend it was linked against, and there was no caller-provided-memory option at all.
The new SDK has two lifecycle models, selected by which pair of functions you call:
- Heap.
pubnub_create()/pubnub_destroy(). Deep-copies every string field in yourpubnub_config_t. Compiled out entirely whenPUBNUB_CFG_NO_HEAP=1(the default in theembeddedCMake profile). - Caller-provided memory.
pubnub_init()/pubnub_deinit(). Borrows every string field, so you must keep them valid for the context's lifetime.
A name collision to watch for
The new SDK's pubnub_init() is not a renamed continuation of v7's pubnub_init(). v7's version took a context already produced by pubnub_alloc() plus key strings. The new version takes a context pointer to memory you supply plus a pubnub_config_t*. The two have different parameter lists, so a straight port won't compile as-is. And once you do get it to compile, don't assume the identical function name means identical behavior.
- Before (C-Core v7)
- After (new SDK) — heap
/* C-Core v7 — one lifecycle, heap-only */
pubnub_t* pb = pubnub_alloc();
pubnub_init(pb, "demo", "demo");
/* ... use pb for publish/subscribe/etc ... */
pubnub_free(pb); /* cancels any in-progress transaction, then frees */
#include <stddef.h>
#include <pubnub/client.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.publish_key = "demo";
cfg.user_id = "my_unique_user_id";
pubnub_context_t* ctx = pubnub_create(&cfg);
if (ctx == NULL) {
/* config was NULL, failed validation, or allocation failed */
return 1;
}
show all 21 linesIf your v7 target was a no-heap embedded platform, use pubnub_init()/pubnub_deinit() on a caller-provided buffer instead. See Initialization for the full pattern, including the alignment requirement for a statically declared context buffer.
The async model
C-Core v7 selected sync-versus-callback at the header level, per platform. Including pubnub_sync.h compiled in the blocking model, and including pubnub_callback.h (with PUBNUB_CALLBACK_API defined) compiled in the callback model. pubnub_enforce_api() could also switch between them at runtime, but only when both were compiled in, and only if called in the right order relative to initialization.
The new SDK has exactly one model: every feature entry point returns a pubnub_future_t by value. You choose how to consume it per call site, with three functions from pubnub/future.h:
- Cooperative polling —
pubnub_process()+pubnub_future_is_ready() - Blocking —
pubnub_await() - Callback —
pubnub_async()
There is no header to pick and no build flag to set. pubnub_sync.h, pubnub_callback.h, and pubnub_enforce_api() have no counterpart in the new SDK.
- Before (C-Core v7)
- After (new SDK) — pick a style per call
/* C-Core v7 — the async model was a header choice, per platform */
#include "posix/pubnub_callback.h" /* or posix/pubnub_sync.h for blocking */
pubnub_t* pb = pubnub_alloc();
pubnub_init(pb, "demo", "demo");
pubnub_publish(pb, "my_channel", "\"hello\"");
/* result arrives via the model that pubnub_callback.h / pubnub_sync.h
compiled in — not chosen at this call site */
#include <pubnub/client.h>
#include <pubnub/features/publish.h>
#include <pubnub/future.h>
#include <stdio.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.publish_key = "demo";
cfg.user_id = "device-042";
pubnub_context_t* ctx = pubnub_create(&cfg);
if (ctx == NULL) {
show all 35 linesCooperative-polling and callback variants of the same call appear in Calling patterns.
The error model
C-Core v7's enum pubnub_res had 41 values, each mapping fairly directly to one failure cause. The new SDK's pubnub_res_t has 16 values, range-coded by class. Server-side cause detail moved into a separate, normalized pubnub_service_error_t you retrieve on demand.
Three groups of old codes map cleanly onto a single new value each:
- 11 old per-feature server-error codes (
PNR_PUBLISH_FAILED,PNR_OBJECTS_API_ERROR,PNR_GRANT_TOKEN_API_ERROR,PNR_ACCESS_DENIED, and seven others) all collapse intoPUBNUB_ERR_SERVER. - 3 old timeout variants (
PNR_WAIT_CONNECT_TIMEOUT,PNR_CONNECTION_TIMEOUT,PNR_TIMEOUT) collapse intoPUBNUB_ERR_TIMEOUT. - 5 old network-failure codes (
PNR_ADDR_RESOLUTION_FAILED,PNR_CONNECT_FAILED,PNR_ABORTED,PNR_IO_ERROR,PNR_HTTP_ERROR) collapse intoPUBNUB_ERR_TRANSPORT.
The remaining codes don't map one-to-one
Roughly 22 other v7 codes (for example PNR_INVALID_CHANNEL, PNR_OUT_OF_MEMORY, PNR_TX_BUFF_TOO_SMALL) don't each correspond to a single new value the way the three groups above do. Treat them at the class level shown above and in the full catalog at The pubnub_res_t result catalog. This guide doesn't provide a precise one-to-one table for those codes.
PNR_GOT_ALL_ACTIONS has no counterpart at all. Pagination for message actions is now a has_more field on the result, not a distinct status. The new SDK also has no per-feature error accessor anywhere: every operation reports through the same pubnub_res_t, and server-side detail comes from pubnub_response_service_error().
- Before (C-Core v7)
- After (new SDK) — 16 values, normalized detail
/* C-Core v7 — 41 distinct enum pubnub_res values */
enum pubnub_res result = pubnub_last_result(pb);
switch (result) {
case PNR_OK:
break;
case PNR_TIMEOUT:
case PNR_CONNECTION_TIMEOUT:
/* two of three old timeout variants */
break;
case PNR_PUBLISH_FAILED:
case PNR_GRANT_TOKEN_API_ERROR:
/* two of eleven old per-feature server errors */
break;
default:
break;
show all 16 lines#include <pubnub/error.h>
#include <pubnub/future.h>
static void handle_result(pubnub_res_t result)
{
switch (result) {
case PUBNUB_OK:
break;
case PUBNUB_ERR_TRANSPORT:
case PUBNUB_ERR_TIMEOUT:
/* likely transient — consider retrying */
break;
case PUBNUB_ERR_SERVER:
/* call pubnub_response_service_error() for normalized detail */
break;
show all 19 linesFull pattern for reading server-side detail out of pubnub_service_error_t: Retrieving server error detail.
The provider model
This is the single biggest structural gain for anyone who maintained a custom port under v7.
C-Core v7 had no provider or vtable concept. Transport, TLS, and JSON handling were selected by linking a fixed set of platform-specific .c files: posix/, windows/, freertos/, mbedtls/, microchip_harmony/, qt/, unreal/. Porting to an unsupported platform meant writing new files that matched the old tree's internal function names and file-layout conventions. No documented list said which functions were mandatory, and no build-time or load-time check confirmed a port was complete. An incomplete port surfaced as a link error or undefined runtime behavior, not a diagnosable status code.
The new SDK exposes six independently swappable provider families: allocator, transport, serialization, platform, crypto, and logger. Each is a public vtable struct under include/pubnub/providers/. You select a backend at CMake configure time with a PUBNUB_PROVIDER_<FAMILY> cache variable, or override any single family per context by pointing the matching pubnub_config_t field at a vtable instance you implement yourself. Four of the six families are validated at context-creation time. A missing mandatory vtable member returns PUBNUB_ERR_PROVIDER_MISSING instead of failing silently.
- Before (C-Core v7) — porting to a new platform
- After (new SDK) — porting is implementing a documented vtable
/* C-Core v7 — porting meant writing new .c files under the old tree's
internal naming and layout conventions, with no documented mandatory
list and no check that the port was complete. A gap surfaced as a
link error or undefined behavior at runtime, not a status code. */
#include <pubnub/client.h>
static void configure_providers(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.user_id = "my_unique_user_id";
/* Leave a field NULL to use the compiled-in default, or point it
at a vtable you implement yourself to port to a new platform. */
cfg.allocator = NULL;
cfg.transport = NULL;
cfg.serialization = NULL;
cfg.platform = NULL;
cfg.crypto_module = NULL;
show all 17 linesThe full six vtables, their exact member prototypes, and the validation/resolution order are documented in Providers and its custom-provider section. Backend-selection strings per family are in Provider backends.
The build system
C-Core v7 built with Makefiles (posix.mk, windows.mk, dispatching into per-platform make/*.mk fragments). Compile-time sizing and feature knobs lived in a hand-edited, per-platform header named identically across directories: posix/pubnub_config.h, windows/pubnub_config.h, freertos/pubnub_config.h, and so on. A representative knob was PUBNUB_CTX_MAX, sizing a fixed context array when the old SDK's static allocation backend was linked in.
The new SDK builds with CMake (3.16 or later). Instead of hand-editing a config header per platform, you select a named build profile, full, minimal, or embedded, that fixes a coherent combination of feature flags, provider backends, and buffer sizes. Then override individual PUBNUB_CFG_* cache variables as needed. There's no file-for-file translation from a v7 Makefile fragment or pubnub_config.h to the new system. You re-express your choices as CMake variables from a documented starting point (a profile), not by porting old build files directly.
- Before (C-Core v7)
- After (new SDK)
# C-Core v7 — Makefiles, dispatched per platform
make -C posix -f posix.mk
make -C openssl -f posix.mk
# Feature/sizing knobs were hand-edited directly in posix/pubnub_config.h,
# e.g. #define PUBNUB_CTX_MAX 4
# New SDK — CMake, profile-driven
cmake --preset full
cmake --build --preset full
ctest --preset full
# Override an individual tunable on top of the profile's defaults:
cmake --preset full -DPUBNUB_CFG_MAX_IN_FLIGHT_REQUESTS=8
There's no installable CMake package for the new SDK. The only integration pattern shown by its own examples is add_subdirectory() plus target_link_libraries(). Full profile, provider, and tunable reference: Build profiles and presets and Provider backends.
Renames and restructuring
These v7 capabilities still exist in the new SDK, restructured or renamed, rather than removed.
| C-Core v7 | New SDK | Notes |
|---|---|---|
pubnub_fetch_history() | pubnub_fetch_messages() | Also the multi-channel entry point; single-shot only in v7 |
Channel-group functions in pubnub_coreapi.h (no dedicated header) | Dedicated pubnub_channel_group_*() functions | See Channel groups |
pubnub_add_message_action() (deprecated, closed 3-value type enum) / pubnub_add_message_action_str() (recommended, free-form string) | One pubnub_add_message_action(), always a free-form type string, up to 15 characters | If you had already migrated off the deprecated v7 function, the type representation doesn't change here, see Add message action |
pubnub_get_message_actions_more() + PNR_GOT_ALL_ACTIONS | has_more / more_start / more_end / more_limit fields fed back into a repeated call to pubnub_get_message_actions() | No separate continuation function or status code |
pubnub_history_with_message_actions() + its own _more() pair | include_message_actions flag on pubnub_fetch_messages_opts_t | One flag on one function replaces two v7 functions and their continuation pair |
pubnub_grant_token(pb, char const* perm_obj), hand-sprintf'd JSON | pubnub_grant_token(ctx, const pubnub_grant_token_opts_t*), typed permission arrays | See Grant token |
pubnub_parse_token(), returns raw CBOR you Base64-decode and parse yourself | pubnub_parse_token(), returns a fully decoded pubnub_parsed_token_t | See Parse token |
pubnub_state_get() | pubnub_get_state() | Renamed, options-struct rewrite |
App Context (Objects v2), existed in v7 source under pubnub_objects_api.h, positional string/JSON arguments, never documented | Options-struct API, 12-flag include bitmask, pagination | Same 12 logical operations, see App Context |
Gone with no counterpart
These v7 symbols and capabilities have no equivalent anywhere in the new SDK's public headers:
pubnub_alloc(),pubnub_free(),pubnub_cancel(). See Context lifecycle for the replacement model. (pubnub_future_cancel()cancels one in-flight request, not the context as a whole.)pubnub_sync.h,pubnub_callback.h,pubnub_enforce_api(). See The async model.PUBNUB_ASSERTand the whole assert-handler subsystem.- The 14 individual DNS server configuration functions (
pubnub_dns_set_primary_server_ipv4and its siblings) are gone, replaced by a singlepubnub_set_dns_servers()call that takes a primary and secondary server string. See DNS servers. Every UUID-generation function (pubnub_generate_uuid_v1_timethrough_v5_name_sha1, plussrand_from_pubnub_time) has no counterpart at all. See No UUID-generation API. pubnub_publish_ex()and its GZIP-over-POST option. Message compression is compile-time only now (PUBNUB_ENABLE_COMPRESSION/PUBNUB_ENABLE_REQUEST_COMPRESSION), with no callable compression function.pubnub_subscribe_v2()andPUBNUB_USE_SUBSCRIBE_EVENT_ENGINE. There's exactly one subscribe model now; see Entities and Create a subscription.pubnub_global_here_now(), a distinct, working v7 function with no counterpart here.pubnub_here_now()requires at least one channel or channel group. Calling it with bothchannelsandchannel_groupsleftNULLfails withPUBNUB_ERR_INVALID_ARGUMENTinstead of returning occupancy across every channel. If you need that, query each channel or channel group explicitly.- Here-now pagination (
limit/offseton the options struct).pubnub_here_now_opts_thas no pagination fields at all.
What's new
Not everything about this rewrite is loss. Three areas are genuine gains for a v7 user:
- Files. Didn't exist in v7 at all. Send, list, get-URL, download, and delete, plus a publish-file-message recovery path. See Files.
- Mobile Push. Didn't exist in v7 at all. APNs (HTTP/2) and FCM device-channel registration management. See Mobile Push.
- The provider model. Covered in full above. Ports that required editing SDK-internal source under v7 can now be expressed as one or more documented vtables, validated at context-creation time instead of failing silently.
- Access Manager token parsing.
pubnub_parse_token()now decodes CBOR internally and hands back a typed, indexed-accessor result. v7 required an external CBOR parser. - CMake build profiles.
full/minimal/embeddedreplace hand-edited per-platformpubnub_config.hfiles with one declarative, overridable configuration surface.
Platform support
State this plainly before you invest porting effort. Some v7 targets have no counterpart in the new SDK.
| Platform / capability | C-Core v7 | New SDK |
|---|---|---|
| POSIX | Supported | Supported (PUBNUB_PROVIDER_PLATFORM=posix) |
| Windows | Supported | Supported (PUBNUB_PROVIDER_PLATFORM=windows) |
| FreeRTOS | Supported | Supported (PUBNUB_PROVIDER_PLATFORM=freertos) |
| ESP-IDF | Supported through the general-purpose mbedtls/ module, not as a first-class target | A dedicated, separate build path (cmake/PubnubESP.cmake), new capability, not carried forward from v7's approach |
| Microchip Harmony | Full platform port (microchip_harmony/) | No counterpart. No provider or directory targets it. |
| Qt | Full integration with its own C++ class (qt/) | No counterpart. |
| Unreal Engine | Integration as an Unreal module (unreal/), documented in its own README as not yet fully functional | No counterpart. |
| mbedTLS as transport TLS | Supported for embedded TLS (mbedtls/) | mbedTLS survives only as a crypto provider backend (payload encryption/HMAC). It doesn't perform transport-level TLS. The transport family is curl / socket / custom only, so a v7 user relying on mbedtls/ for embedded transport TLS needs to write a custom transport provider. |
| A working C++ API | A real wrapper class (cpp/) with per-platform futures backends | The new C++ wrapper is an unimplemented placeholder. Anyone depending on the C++ surface, as opposed to plain C, has nothing to migrate to yet. |
If you're on Microchip Harmony, Qt, or Unreal Engine, or you rely on mbedTLS for transport-level TLS rather than payload crypto, or you use the C++ wrapper rather than the C API, confirm there's a path forward for you before investing further porting effort. See Provider backends for exactly what a custom provider requires, and Platform Support (legacy) for what v7 supported on your platform today.
Migration steps
- Pick a CMake profile,
full,minimal, orembedded, before anything else. It fixes every feature flag and provider default your build starts from. There's no Makefile-fragment-by-fragment translation from v7; this is a from-scratch decision. See Build profiles and presets. - Decide your context lifecycle model: heap (
pubnub_create()/pubnub_destroy()) or caller-provided memory (pubnub_init()/pubnub_deinit(), mandatory if your profile setsPUBNUB_CFG_NO_HEAP=1, whichembeddeddoes). This decision sets the string-ownership rule, borrow versus deep-copy, that every config field you set next must respect. - Re-express your v7 platform configuration, a hand-edited
pubnub_config.hand, if you maintained a custom platform port, its file-level implementation, asPUBNUB_CFG_*CMake cache variables and one or more provider vtables. Do this before touching any feature code, since every feature call needs a working context first. - Rewrite context setup and your call pattern for one simple operation, such as publish, before porting any feature's business logic. Every other feature's call depends on the one-future, three-consumption-styles model, so validating your provider and config choices against a trivial publish first is cheaper than discovering a config mistake after porting several features.
- Port feature-by-feature using Renames and restructuring and the per-feature API reference pages linked throughout this guide, in whatever order matches your application. While doing so, check every call against Silent behavior changes, especially the
pubnub_delete_messages()boundary and the JSON-tree ownership rule if you touch more than one JSON-tree-accepting feature. - Re-check any platform you relied on that isn't POSIX, Windows, or FreeRTOS against Platform support before investing further porting effort. Microchip Harmony, Qt, and Unreal Engine users have no target to port to today. mbedTLS-as-transport users need a custom transport provider, not a drop-in replacement.
- Rebuild your error-handling code last. The 41-to-16 result-code collapse touches every call site uniformly, so it's mechanical once every call site itself compiles. Doing it last avoids editing error-handling code twice as feature signatures change underneath it.
Additional resources
- Environment Setup: build profiles, provider backends, feature flags, and the three async consumption styles in full.
- Configuration: the complete
pubnub_config_tfield reference. - Status Events: the full
pubnub_res_tcatalog and server-error detail pattern. - Providers: all six provider vtables and how to write a custom one.
- C-Core SDK documentation (legacy): v7's own reference, if you still need it during migration.
- For questions or issues, contact PubNub support.