---
source_url: https://www.pubnub.com/docs/sdks/objective-c
title: Objective-C API & SDK Docs 7.0.2
updated_at: 2026-05-29T11:11:36.025Z
sdk_name: PubNub Objective-C SDK
sdk_version: 7.0.2
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Objective-C API & SDK Docs 7.0.2

PubNub Objective-C SDK, use the latest version: 7.0.2

Install:

```bash
pod install PubNub@7.0.2
```

This guide walks you through a simple "Hello, World" application that demonstrates the core concepts of PubNub. You'll set up a connection to PubNub and send and receive messages.

## Setup

### Get your PubNub keys

First, get your PubNub keys:

* [Sign in](https://admin.pubnub.com/#/login) or [create an account](https://admin.pubnub.com/#/signup) on the PubNub Admin Portal.
* Create an app (or use an existing one).
* Find your publish and subscribe keys in the app dashboard.

When you create an app, PubNub automatically generates a keyset. You can use the same keyset for development and production, but we recommend separate keysets for each environment to improve security and management.

### Install the SDK

:::note SDK version
Always use the latest SDK version to have access to the newest features and avoid security vulnerabilities, bugs, and performance issues.
:::

Download the SDK from any of the following sources:

### Use CocoaPods

To integrate PubNub into your project using [CocoaPods](https://guides.cocoapods.org/using/getting-started.html)

1. Install the latest cocoapods gem by running the gem install cocoapods command. If you already have this gem, make sure to update to the latest version by running the gem update cocoapods command.
2. Create a new Xcode project and create a Podfile in the root folder of the project: 1pod init 1platform :ios, '14.0'2 3 target 'application-target-name' do4 use_frameworks!5 6 pod "PubNub", "~> 5"7 end If you want to include additional pods or add other targets, add their entries to this Podfile as well, refer to the CocoaPods documentation for more information on Podfile configuration.
3. Install your pods by running the pod install command from the directory which contains your Podfile. After installing your Pods, you should work with the CocoaPods-generated workspace and not the original project file.
4. Import the PubNub headers in the classes where you want to use PubNub: 1#import <PubNub/PubNub.h>

### Use Carthage

To build PubNub as a standalone bundle using [Carthage](https://github.com/Carthage/Carthage) and integrate it into your project:

1. Install the latest release of Carthage and either create a new file or open an existing Cartfile.
2. Add a new line in the Cartfile to build the PubNub framework bundle: 1github "pubnub/objective-c" ~> 4
3. Update and rebuild your project's dependencies. The update command ensures that the latest PubNub client is used to build the framework. You can also use the faster build command if you have already built the framework before. 1carthage update --no-use-binaries The preceding command builds the framework for all configured platforms. You can use the following command to specify the platform: 1carthage update --platform ios --no-use-binaries Available platforms include mac, tvos, and watchos.
4. Navigate to Carthage/Build and enter the directory which represents your target platform, for example: iOS.
5. Drag and drop the PubNub.framework bundle from the Products directory to your application.
6. Mark the Copy items if needed checkbox and click Finish.
7. Open your project's General tab, scroll to Embedded Binaries, click +, and select the PubNub.framework file.
8. At this point, you should have the framework added to your application project. Import the PubNub headers in the classes where you want to use PubNub: 1#import <PubNub/PubNub.h>

### Source code

Clone the [GitHub repository](https://github.com/pubnub/objective-c):

```bash
git clone https://github.com/pubnub/objective-c
```

View the [supported platforms](https://www.pubnub.com/docs/sdks/objective-c/platform-support).

## Steps

### Initialize PubNub

You will use the workspace you generated with [CocoaPods](#use-cocoapods) in this procedure.

1. In Xcode, open your workspace and add the following content to the AppDelegate class. 1@interface AppDelegate () <PNEventsListener>2 3// Stores reference on PubNub client to make sure what it won't be released.4@property (nonatomic, strong) PubNub *client;5 6@end
2. Below what you added in step 1, add the following code. This is the minimum configuration you need to send and receive messages with PubNub. Make sure to replace myPublishKey and mySubscribeKey with your app's publish and subscribe keys from the Admin Portal. 1// Initialize and configure PubNub client instance2PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey: @"myPublishKey" subscribeKey:@"mySubscribeKey"];3configuration.uuid = @"myUniqueUUID";4 5self.client = [PubNub clientWithConfiguration:configuration];

For more information, refer to the [Configuration](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration) section of the SDK documentation.

### Set up event listeners

Listeners help your app react to events and messages. You can implement custom app logic to respond to each type of message or event.

Copy the following code to configure your app such that when it receives an event of type `PNConnectedCategory`, it calls the `publish` function. Additionally, the code below prints out the content of every received message.

```objectivec
[self.client addListener:self];

- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
    // Handle new message stored in message.data.message

    if (![message.data.channel isEqualToString:message.data.subscription]) {
        // Message has been received on channel group stored in message.data.subscription.
    } else {
        // Message has been received on channel stored in message.data.channel.
    }

    NSLog(@"Received message: %@ on channel %@ at %@", message.data.message[@"msg"],
          message.data.channel, message.data.timetoken);
}

- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
    if (status.operation == PNSubscribeOperation) {
        if (status.category == PNConnectedCategory || status.category == PNReconnectedCategory) {
            // Status object for those categories can be casted to `PNSubscribeStatus` for use below.
            PNSubscribeStatus *subscribeStatus = (PNSubscribeStatus *)status;

            if (subscribeStatus.category == PNConnectedCategory) {
                // This is expected for a subscribe, this means there is no error or issue whatsoever.

                // Select last object from list of subscribed channels and send message to it.
                NSString *targetChannel = [client channels].lastObject;
                [self.client publish:@{ @"msg": @"hello" } toChannel:targetChannel
                      withCompletion:^(PNPublishStatus *publishStatus) {

                        if (!publishStatus.isError) {
                            // Message successfully published to specified channel.
                        } else {
                            /**
                             * Handle message publish error. Check 'category' property to find out
                             * possible reason because of which request did fail.
                             * Review 'errorData' property (which has PNErrorData data type) of status
                             * object to get additional information about issue.
                             *
                             * Request can be resent using: [publishStatus retry];
                             */
                        }
                }];
            } else {
                /**
                 * This usually occurs if subscribe temporarily fails but reconnects. This means there was
                 * an error but there is no longer any issue.
                 */
            }
        } else if (status.category == PNUnexpectedDisconnectCategory) {
            /**
             * This is usually an issue with the internet connection, this is an error, handle
             * appropriately retry will be called automatically.
             */
        } else {
            PNErrorStatus *errorStatus = (PNErrorStatus *)status;

            if (errorStatus.category == PNAccessDeniedCategory) {
                /**
                 * This means that Access Manager does allow this client to subscribe to this channel and channel group
                 * configuration. This is another explicit error.
                 */
            } else {
                /**
                 * More errors can be directly specified by creating explicit cases for other error categories
                 * of `PNStatusCategory` such as: `PNDecryptionErrorCategory`,
                 * `PNMalformedFilterExpressionCategory`, `PNMalformedResponseCategory`, `PNTimeoutCategory`
                 * or `PNNetworkIssuesCategory`
                 */
            }
        }
    }
}
```

For more information, refer to the [Listeners](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration#event-listeners) section of the SDK documentation.

### Publish and subscribe

To receive messages sent to a particular channel, you *subscribe* to it. When you *publish* a message to a channel, PubNub delivers that message to everyone subscribed to that channel.

In this app, publishing a message is triggered when the status listener you created in the previous step receives the `PNConnectedCategory` event. The `publish` method uses the `targetChannel` variable that you can see in the code below.

To subscribe, you send a `subscribe` call:

```objectivec
[self.client subscribeToChannels: @[@"hello-world-channel"] withPresence:YES];
```

For more information, refer to the [Publish and Subscribe](https://www.pubnub.com/docs/sdks/objective-c/api-reference/publish-and-subscribe#publish) section of the SDK documentation, and to [Publishing a Message](https://www.pubnub.com/docs/general/messages/publish).

## Complete example

Your `AppDelegate` class should now look similar to the following snippet. Note that it contains code for different platforms.

```objectivec
<!-- MACOS -->

@interface AppDelegate () <PNEventsListener>

// Stores reference on PubNub client to make sure what it won't be released.
@property (nonatomic, strong) PubNub *client;

@end

@implementation PNAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Initialize and configure PubNub client instance
    PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:@"myPublishKey"
                                                                     subscribeKey:@"mySubscribeKey"];
    configuration.uuid = @"myUniqueUUID";

    self.client = [PubNub clientWithConfiguration:configuration];
    [self.client addListener:self];
    [self.client subscribeToChannels: @[@"hello-world-channel"] withPresence:YES];}

- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
    // Handle new message stored in message.data.message

    if (![message.data.channel isEqualToString:message.data.subscription]) {
        // Message has been received on channel group stored in message.data.subscription.
    } else {
        // Message has been received on channel stored in message.data.channel.
    }

    NSLog(@"Received message: %@ on channel %@ at %@", message.data.message[@ "msg"],
          message.data.channel, message.data.timetoken);
}

- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
    if (status.operation == PNSubscribeOperation) {
        if (status.category == PNConnectedCategory || status.category == PNReconnectedCategory) {
            // Status object for those categories can be casted to `PNSubscribeStatus` for use below.
            PNSubscribeStatus *subscribeStatus = (PNSubscribeStatus *)status;

            if (subscribeStatus.category == PNConnectedCategory) {
                // This is expected for a subscribe, this means there is no error or issue whatsoever.

                // Select last object from list of subscribed channels and send message to it.
                NSString *targetChannel = [client channels].lastObject;
                [self.client publish: @{ @ "msg": @"hello" } toChannel:targetChannel
                      withCompletion:^(PNPublishStatus *publishStatus) {

                        if (!publishStatus.isError) {
                            // Message successfully published to specified channel.
                        } else {
                            /**
                             * Handle message publish error. Check 'category' property to find out
                             * possible reason because of which request did fail.
                             * Review 'errorData' property (which has PNErrorData data type) of status
                             * object to get additional information about issue.
                             *
                             * Request can be resent using: [publishStatus retry];
                             */
                        }
                }];
            } else {
                /**
                 * This usually occurs if subscribe temporarily fails but reconnects. This means there was
                 * an error but there is no longer any issue.
                 */
            }
        } else if (status.category == PNUnexpectedDisconnectCategory) {
            /**
             * This is usually an issue with the internet connection, this is an error, handle
             * appropriately retry will be called automatically.
             */
        } else {
            PNErrorStatus *errorStatus = (PNErrorStatus *)status;

            if (errorStatus.category == PNAccessDeniedCategory) {
                /**
                 * This means that Access Manager does allow this client to subscribe to this channel and channel group
                 * configuration. This is another explicit error.
                 */
            } else {
                /**
                 * More errors can be directly specified by creating explicit cases for other error categories
                 * of `PNStatusCategory` such as: `PNDecryptionErrorCategory`,
                 * `PNMalformedFilterExpressionCategory`, `PNMalformedResponseCategory`, `PNTimeoutCategory`
                 * or `PNNetworkIssuesCategory`
                 */
            }
        }
    }
}
@end

<!-- OTHER PLATFORMS -->

@interface AppDelegate () <PNEventsListener>

// Stores reference on PubNub client to make sure what it won't be released.
@property (nonatomic, strong) PubNub *client;

@end

@implementation PNAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize and configure PubNub client instance
    PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:@"myPublishKey"
                                                                     subscribeKey:@"mySubscribeKey"];
    configuration.uuid = @"myUniqueUUID";

    self.client = [PubNub clientWithConfiguration:configuration];
    [self.client addListener:self];
    [self.client subscribeToChannels: @[@"hello-world-channel"] withPresence:YES];
}

- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
    // Handle new message stored in message.data.message

    if (![message.data.channel isEqualToString:message.data.subscription]) {
        // Message has been received on channel group stored in message.data.subscription.
    } else {
        // Message has been received on channel stored in message.data.channel.
    }

    NSLog(@"Received message: %@ on channel %@ at %@", message.data.message[@ "msg"],
          message.data.channel, message.data.timetoken);
}

- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
    if (status.operation == PNSubscribeOperation) {
        if (status.category == PNConnectedCategory || status.category == PNReconnectedCategory) {
            // Status object for those categories can be casted to `PNSubscribeStatus` for use below.
            PNSubscribeStatus *subscribeStatus = (PNSubscribeStatus *)status;

            if (subscribeStatus.category == PNConnectedCategory) {
                // This is expected for a subscribe, this means there is no error or issue whatsoever.

                // Select last object from list of subscribed channels and send message to it.
                NSString *targetChannel = [client channels].lastObject;
                [self.client publish: @{ @ "msg": @"hello" } toChannel:targetChannel
                      withCompletion:^(PNPublishStatus *publishStatus) {

                        if (!publishStatus.isError) {
                            // Message successfully published to specified channel.
                        } else {
                            /**
                             * Handle message publish error. Check 'category' property to find out
                             * possible reason because of which request did fail.
                             * Review 'errorData' property (which has PNErrorData data type) of status
                             * object to get additional information about issue.
                             *
                             * Request can be resent using: [publishStatus retry];
                             */
                        }
                }];
            } else {
                /**
                 * This usually occurs if subscribe temporarily fails but reconnects. This means there was
                 * an error but there is no longer any issue.
                 */
            }
        } else if (status.category == PNUnexpectedDisconnectCategory) {
            /**
             * This is usually an issue with the internet connection, this is an error, handle
             * appropriately retry will be called automatically.
             */
        } else {
            PNErrorStatus *errorStatus = (PNErrorStatus *)status;

            if (errorStatus.category == PNAccessDeniedCategory) {
                /**
                 * This means that Access Manager does allow this client to subscribe to this channel and channel group
                 * configuration. This is another explicit error.
                 */
            } else {
                /**
                 * More errors can be directly specified by creating explicit cases for other error categories
                 * of `PNStatusCategory` such as: `PNDecryptionErrorCategory`,
                 * `PNMalformedFilterExpressionCategory`, `PNMalformedResponseCategory`, `PNTimeoutCategory`
                 * or `PNNetworkIssuesCategory`
                 */
            }
        }
    }
}
@end
```

Now, run your app to see if you did everything correctly.

You should see output similar to the following:

```bash
Received message: Hello on channel hello-world-channel at 15844898827972406
```

Congratulations! You've just subscribed to a channel and sent your first message.

### Walkthrough

Instead of focusing on the order in which you *wrote* the code, let's focus on the *order in which it runs*. The app you just created does a few things:

* configures a PubNub connection
* adds the `status` and `message` event listeners
* subscribes to a channel
* publishes a message

### Configuring PubNub

The following code is the minimum configuration you need to send and receive messages with PubNub. For more information, refer to the [Configuration](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration) section of the SDK documentation.

```objectivec
// Initialize and configure PubNub client instance
PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey: @"myPublishKey" subscribeKey: @"mySubscribeKey"];
configuration.uuid = @"myUniqueUUID";

self.client = [PubNub clientWithConfiguration:configuration];
```

### Add event listeners

Listeners help your app react to events and messages. You can implement custom app logic to respond to each type of message or event.

You added two listeners to the app: `status` and `message`. Status listens for status events and when it receives an event of type `PNConnectedCategory`, it publishes the message. The other listener, `message`, listens for incoming messages on a particular channel. When it receives a message, the app simply prints the received message. This is why you see "Received message: Hello on channel hello-world-channel at 15844898827972406" displayed in the console.

```objectivec
[self.client addListener:self];

- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
    // Handle new message stored in message.data.message

    if (![message.data.channel isEqualToString:message.data.subscription]) {
        // Message has been received on channel group stored in message.data.subscription.
    } else {
        // Message has been received on channel stored in message.data.channel.
    }

    NSLog(@"Received message: %@ on channel %@ at %@", message.data.message[@"msg"],
          message.data.channel, message.data.timetoken);
 }

 - (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
    if (status.operation == PNSubscribeOperation) {
        if (status.category == PNConnectedCategory || status.category == PNReconnectedCategory) {
             // Status object for those categories can be casted to `PNSubscribeStatus` for use below.
             PNSubscribeStatus *subscribeStatus = (PNSubscribeStatus *)status;

            if (subscribeStatus.category == PNConnectedCategory) {
                // This is expected for a subscribe, this means there is no error or issue whatsoever.

                // Select last object from list of subscribed channels and send message to it.
                NSString *targetChannel = [client channels].lastObject;
                [self.client publish:@{ @"msg": @"hello" } toChannel:targetChannel
                      withCompletion:^(PNPublishStatus *publishStatus) {

                        if (!publishStatus.isError) {
                            // Message successfully published to specified channel.
                        } else {
                            /**
                             * Handle message publish error. Check 'category' property to find out
                             * possible reason because of which request did fail.
                             * Review 'errorData' property (which has PNErrorData data type) of status
                             * object to get additional information about issue.
                             *
                             * Request can be resent using: [publishStatus retry];
                             */
                        }
                }];
            } else {
                /**
                 * This usually occurs if subscribe temporarily fails but reconnects. This means there was
                 * an error but there is no longer any issue.
                 */
            }
        } else if (status.category == PNUnexpectedDisconnectCategory) {
            /**
             * This is usually an issue with the internet connection, this is an error, handle
             * appropriately retry will be called automatically.
             */
        } else {
            PNErrorStatus *errorStatus = (PNErrorStatus *)status;

            if (errorStatus.category == PNAccessDeniedCategory) {
                /**
                 * This means that Access Manager does allow this client to subscribe to this channel and channel group
                 * configuration. This is another explicit error.
                 */
            } else {
                /**
                 * More errors can be directly specified by creating explicit cases for other error categories
                 * of `PNStatusCategory` such as: `PNDecryptionErrorCategory`,
                 * `PNMalformedFilterExpressionCategory`, `PNMalformedResponseCategory`, `PNTimeoutCategory`
                 * or `PNNetworkIssuesCategory`
                 */
            }
        }
    }
}
```

For more information, refer to the [Listeners](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration#event-listeners) section of the SDK documentation.

### Publishing and subscribing

PubNub uses the Publish/Subscribe model for real-time communication. This model involves two essential parts:

* Channels are transient paths over which your data is transmitted
* Messages contain the data you want to transmit to one or more recipients

When you want to receive messages sent to a particular channel, you *subscribe* to it. When you *publish* a message to a channel, PubNub delivers that message to everyone who is subscribed to that channel. In this example, you subscribe to a channel named `myChannel`.

A message can be any type of JavaScript Object Notation (JSON)-serializable data (such as objects, arrays, integers, strings) that is smaller than 32 KiB. PubNub will, in most cases, deliver your message to its intended recipients in fewer than 30 ms regardless of their location. You can also share files up to 5MB.

When your app successfully connects to a channel, it calls the `publish` method, which effectively sends the "Hello" message. This behavior is configured in the `status` listener.

```objectivec
[self.client publish: @{ @"msg": @"Hello" } toChannel:targetChannel
      withCompletion:^(PNPublishStatus *publishStatus) {
}];
```

You can subscribe to more than one channel with a single subscribe call but in this example, you subscribe to a single channel:

```objectivec
[self.client subscribeToChannels: @[@"hello-world-channel"] withPresence:YES];
```

For more information, refer to the [Publish and Subscribe](https://www.pubnub.com/docs/sdks/objective-c/api-reference/publish-and-subscribe) section of the SDK documentation, and to [Publishing a Message](https://www.pubnub.com/docs/general/messages/publish).

## Next steps

You have just learned how to use the Objective-C SDK to send and receive messages using PubNub. Next, take a look at the [SDK reference documentation](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration) for more details.