---
source_url: https://www.pubnub.com/docs/sdks/objective-c/api-reference/publish-and-subscribe
title: Publish/Subscribe API for Objective-C SDK
updated_at: 2026-06-05T11:12:43.052Z
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


# Publish/Subscribe API for Objective-C SDK

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

Install:

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

PubNub delivers messages worldwide in less than 30 ms. Send a message to one recipient or broadcast to thousands of subscribers.

For higher-level conceptual details on publishing and subscribing, refer to [Connection Management](https://www.pubnub.com/docs/general/setup/connection-management) and to [Publish Messages](https://www.pubnub.com/docs/general/messages/publish).

## Publish

`publish()` sends a message to all channel subscribers. A successfully published message is replicated across PubNub's points of presence and delivered to all subscribed clients on that channel.

:::warning ObjectNode
The new Jackson parser does not recognize JSONObject. Use ObjectNode instead.
:::

###### Prerequisites and limitations

* You must [initialize PubNub](https://www.pubnub.com/docs/sdks/python/api-reference/configuration#configuration) with the `publishKey`.
* You don't have to be subscribed to a channel to publish to it.
* You cannot publish to multiple channels simultaneously.

###### Security

You can secure the messages with SSL/TLS by setting `ssl` to `true` during [initialization](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration). You can also [encrypt](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration#cryptomodule) messages.

###### Message data

The message can contain any JSON-serializable data (Objects, Arrays, Ints, Strings) and shouldn't contain any special classes or functions. `data` should not contain special Objective-C classes or functions as these will not serialize. String content can include any single-byte or multi-byte UTF-8 character.

:::warning Don't JSON serialize
You should not JSON serialize the `message` and `meta` parameters when sending signals, messages, or files as the serialization is automatic. Pass the full object as the message/meta payload and let PubNub handle everything.
:::

###### Size and compression

The maximum message size is 32 KiB, including the final escaped character count and the channel name. An optimal message size is under 1800 bytes.

If the message you publish exceeds the configured size, you receive a `Message Too Large` error. If you want to learn more or calculate your payload size, refer to [Message Size Limit](https://www.pubnub.com/docs/general/messages/publish#message-size-limit).

Message compression can be helpful if you want to send data exceeding the default 32 KiB message size limit, or use bandwidth more efficiently by sending the message payload as the compressed body of an HTTP POST call.

:::note Compression and PubNub SDKs
The C-Core and Objective-C SDKs support compressed messages.
:::

Compressing messages is useful for scenarios that include high channel occupancy and quick exchange of information like ride hailing apps or multiplayer games.

| Compression Trade-off | Details |
| --- | --- |
| Using Compression | Compression methods and support vary between SDKs. If the receiving SDK doesn't support the sender's compression method, or even if it doesn't support compression at all, the PubNub server automatically changes the compressed message's format so that it is understandable to the recipient. No action is necessary from you. Messages are not compressed by default; you must always explicitly specify that you want to use message compression. |
| Small messages can expand | Compressed messages generally have a smaller size, and can be delivered faster, but only if the original message is over 1 KiB. If you compress a signal (whose size is limited to 64 bytes), the compressed payload exceeds the signal's initial uncompressed size. |
| CPU overhead can increase | While a smaller payload size is an advantage, working with compressed messages uses more CPU time than working with uncompressed messages. CPU time is required to compress the message on the sending client, and again to decompress the message on the receiving client. Efficient resource management is especially important on mobile devices, where increased usage affects battery life. Carefully consider the balance of lower bandwidth and higher speed versus any increased CPU usage. |

Refer to the code below for an example of sending a compressed message:

```objectivec
[self.client publish:@{@"message": @"This message will be compressed"}
           toChannel:@"channel_name" compressed:YES
      withCompletion:^(PNPublishStatus *status) {
    if (!status.isError) {
        // Message successfully published to specified channel.
    } else {
        // Handle error.
    }
}]
```

:::tip Need larger messages?
Our platform is optimized for payloads up to 32 KiB. PubNub supports larger messages, but increasing the limit requires a verification of compatibility with your use case.
Talk to [our team](https://www.pubnub.com/company/contact-sales/) to discuss increasing the message size limit for your use case.
:::

###### Publish rate

You can publish as fast as bandwidth conditions allow. There is a soft limit based on max throughput since messages will be discarded if the subscriber can't keep pace with the publisher.

For example, if 200 messages are published simultaneously before a subscriber has had a chance to receive any, the subscriber may not receive the first 100 messages because the message queue has a limit of only 100 messages stored in memory.

###### Custom message type

You can optionally provide the `customMessageType` parameter to add your business-specific label or category to the message, for example `text`, `action`, or `poll`.

###### Best practices

* Publish to any given channel in a serial manner (not concurrently).
* Check that the return code is success (for example, `[1,"Sent","136074940..."]`)
* Publish the next message only after receiving a success return code.
* If a failure code is returned (`[0,"blah","<timetoken>"]`), retry the publish.
* Avoid exceeding the in-memory queue's capacity of 100 messages. An overflow situation (aka missed messages) can occur if slow subscribers fail to keep up with the publish pace in a given period of time.
* Throttle publish bursts according to your app's latency needs, for example no more than 5 messages per second.

### Method(s)

To `Publish a message` you can use the following method(s) in the Objective-C SDK:

#### Publish a message with block

```objectivec
- (void)publish:(id)message 
         toChannel:(NSString *)channel 
    withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | Reference on Foundation object ( `NSString`, `NSNumber`, `NSArray`, `NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which `message` should be published. |
| `block`Type: PNPublishCompletionBlock | `Publish` processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with compression and block

```objectivec
- (void)publish:(id)message 
         toChannel:(NSString *)channel 
        compressed:(BOOL)compressed 
    withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | Reference on Foundation object (`NSString`, `NSNumber`, `NSArray`, `NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which `message` should be published. |
| `compressed` *Type: BOOL | Whether `message` should be `compressed` and sent with request body instead of URI part. Compression useful in case if large data should be published, in another case it will lead to packet size grow. |
| `block`Type: PNPublishCompletionBlock | Publish processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with storage and block

```objectivec
- (void)publish:(id)message 
         toChannel:(NSString *)channel 
    storeInHistory:(BOOL)shouldStore 
    withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | Reference on Foundation `object` (`NSString`, `NSNumber`, `NSArray`,`NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which `message` should be published. |
| `shouldStore` *Type: BOOL | With `NO` this `message` later won't be fetched using Message Persistence API. |
| `block`Type: PNPublishCompletionBlock | `Publish` processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with storage, compression, and block

```objectivec
- (void)publish:(id)message 
         toChannel:(NSString *)channel 
    storeInHistory:(BOOL)shouldStore 
        compressed:(BOOL)compressed 
    withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | Reference on Foundation object (`NSString`, `NSNumber`, `NSArray`,`NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which `message` should be published. |
| `shouldStore` *Type: BOOL | With `NO` this `message` later won't be fetched using Message Persistence API. |
| `compressed` *Type: BOOL | Compression useful in case if large data should be published, in another case it will lead to packet size grow. |
| `block`Type: PNPublishCompletionBlock | `Publish` processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with payload and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
       withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | Reference on Foundation `object` (`NSString`, `NSNumber`, `NSArray`, `NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which `message` should be published. |
| `payloads`Type: NSDictionary | Dictionary with payloads for different vendors (Apple with `apns` key and Google with `fcm`). |
| `block`Type: PNPublishCompletionBlock | Publish processing completion block which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with payload, compression, and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
           compressed:(BOOL)compressed 
       withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | Reference on Foundation `object` (`NSString`, `NSNumber`, `NSArray`, `NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which `message` should be published. |
| `payloads` *Type: NSDictionary | Dictionary with payloads for different vendors (Apple with `apns` key and Google with `fcm`). |
| `compressed` *Type: BOOL | Compression useful in case if large data should be published, in another case it will lead to packet size grow. |
| `block`Type: PNPublishCompletionBlock | Publish processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with payload, storage, and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
       storeInHistory:(BOOL)shouldStore 
       withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | Reference on Foundation `object` (`NSString`, `NSNumber`, `NSArray`,`NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which `message` should be published. |
| `payloads`Type: NSDictionary | Dictionary with payloads for different vendors (Apple with `apns` key and Google with `fcm`). |
| `shouldStore` *Type: BOOL | With `NO` this `message` later won't be fetched using Message Persistence API. |
| `block`Type: PNPublishCompletionBlock | Publish processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with payloads, storage, compression, and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
       storeInHistory:(BOOL)shouldStore 
           compressed:(BOOL)compressed 
       withCompletion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | Reference on Foundation `object` (`NSString`, `NSNumber`, `NSArray`,`NSDictionary`) which will be published. |
| `channel` *Type: NSString | Reference on ID of the `channel` to which message should be published. |
| `payloads`Type: NSDictionary | Dictionary with payloads for different vendors (Apple with "apns" key and Google with "fcm"). |
| `shouldStore` *Type: BOOL | With `NO` this `message` later won't be fetched using Message Persistence API. |
| `compressed` *Type: BOOL | Compression useful in case if large data should be published, in another case it will lead to packet size grow. |
| `block`Type: PNPublishCompletionBlock | Publish processing completion block which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Publish a message with metadata and block

```objectivec
- (void)publish:(id)message 
       toChannel:(NSString *)channel 
    withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
      completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | The `message` may be any valid foundation `object` (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to publish messages to. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

#### Publish a message with compression, metadata, and block

```objectivec
- (void)publish:(id)message 
       toChannel:(NSString *)channel 
      compressed:(BOOL)compressed 
    withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
      completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | The `message` may be any valid foundation `object` (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to publish messages to. |
| `compressed` *Type: BOOL | If `true` the `message` will be `compressed` and sent with request body instead of the URI. Compression useful in case of large data, in another cases it will increase the packet size. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

#### Publish a message with storage, metadata, and block

```objectivec
- (void)publish:(id)message 
         toChannel:(NSString *)channel 
    storeInHistory:(BOOL)shouldStore 
      withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
        completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | The `message` may be any valid foundation `object` (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to publish messages to. |
| `shouldStore` *Type: BOOL | If `false` the messages will not be stored in Message Persistence, default `true`. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

#### Publish a message with storage, compression, metadata, and block

```objectivec
- (void)publish:(id)message 
         toChannel:(NSString *)channel 
    storeInHistory:(BOOL)shouldStore 
        compressed:(BOOL)compressed 
      withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
        completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | The `message` may be any valid foundation `object` (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to publish messages to. |
| `shouldStore` *Type: BOOL | If `false` the messages will not be stored in Message Persistence, default `true`. |
| `compressed` *Type: BOOL | If `true` the `message` will be `compressed` and sent with request body instead of the URI. Compression useful in case of large data, in another cases it will increase the packet size. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

#### Publish a message with payload, metadata, and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
         withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
           completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | The `message` may be any valid foundation `object` (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to publish messages to. |
| `payloads`Type: NSDictionary | Dictionary with `payloads` for different vendors (Apple with `aps` key and Google with `fcm`). Either payloads or `message` should be provided.. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

#### Publish a message with payload, compression, metadata, and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
           compressed:(BOOL)compressed 
         withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
           completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | The `message` may be any valid foundation object (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to `publish` messages to. |
| `payloads`Type: NSDictionary | Dictionary with `payloads` for different vendors (Apple with `aps` key and Google with `fcm`). Either `payloads` or `message` should be provided. |
| `compressed` *Type: BOOL | If `true` the `message` will be compressed and sent with request body instead of the URI. Compression useful in case of large data, in another cases it will increase the packet size. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

#### Publish a message with payload, storage, metadata, and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
       storeInHistory:(BOOL)shouldStore 
         withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
           completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | The `message` may be any valid foundation `object` (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to publish messages to. |
| `payloads`Type: NSDictionary | Dictionary with `payloads` for different vendors (Apple with `a ps` key and Google with `fcm`). Either `payloads` or `message` should be provided. |
| `shouldStore` *Type: BOOL | If `false` the messages will not be stored in Message Persistence, default `true`. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

#### Publish a message with payload, storage, compression, metadata, and block

```objectivec
- (void)publish:(nullable id)message 
            toChannel:(NSString *)channel 
    mobilePushPayload:(nullable NSDictionary<NSString *, id> *)payloads 
       storeInHistory:(BOOL)shouldStore 
           compressed:(BOOL)compressed 
         withMetadata:(nullable NSDictionary<NSString *, id> *)metadata 
           completion:(nullable PNPublishCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | The `message` may be any valid foundation object (`String`, `NSNumber`, `Array`, `Dictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to `publish` messages to. |
| `payloads`Type: NSDictionary | Dictionary with `payloads` for different vendors (Apple with `aps` key and Google with `fcm`). Either `payloads` or `message` should be provided. |
| `shouldStore` *Type: BOOL | If `false` the messages will not be stored in Message Persistence, default `true`. |
| `compressed` *Type: BOOL | If `true` the `message` will be compressed and sent with request body instead of the URI. Compression useful in case of large data, in another cases it will increase the packet size. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

### Sample code

#### Publish a message to a channel

```objectivec
#import <Foundation/Foundation.h>
#import <PubNub/PubNub.h>

// Basic configuration
PNConfiguration *config = [PNConfiguration configurationWithPublishKey:@"demo"
                                                          subscribeKey:@"demo"
                                                                userID:@"publishUser"];

// Optional: Configure encryption for secure messages
config.cryptoModule = [PNCryptoModule AESCBCCryptoModuleWithCipherKey:@"enigma"
                                           randomInitializationVector:YES];

// Create a PubNub client instance
PubNub *client = [PubNub clientWithConfiguration:config];

// Add listener for PubNub events
[client addListener:self];

// Subscribe to receive messages
PNSubscribeRequest *subscribeRequest = [PNSubscribeRequest requestWithChannels:@[@"demo-channel"] 
                                                                 channelGroups:nil];

[client subscribeWithRequest:subscribeRequest];

// Example 1: Publish a basic message
PNPublishRequest *basicRequest = [PNPublishRequest requestWithChannel:@"demo-channel"];
basicRequest.message = @{@"text": @"Hello from Objective-C SDK!"};

[client publishWithRequest:basicRequest withCompletion:^(PNPublishStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Message successfully published!");
        NSLog(@"Timetoken: %@", status.data.timetoken);
    } else {
        NSLog(@"❌ Error publishing message: %@", status.errorData.information);
        NSLog(@"Error category: %@", @(status.category));
    }
}];

// Example 2: Publish with metadata
NSDictionary *metadata = @{
    @"sender": @"mobile-app",
    @"version": @"1.0.3",
    @"priority": @"high"
};

PNPublishRequest *metadataRequest = [PNPublishRequest requestWithChannel:@"demo-channel"];
metadataRequest.message = @{@"text": @"Message with metadata"};
metadataRequest.metadata = metadata;

[client publishWithRequest:metadataRequest withCompletion:^(PNPublishStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Message with metadata successfully published!");
        NSLog(@"Timetoken: %@", status.data.timetoken);
    } else {
        NSLog(@"❌ Error publishing message with metadata: %@", status.errorData.information);
    }
}];

// Example 3: Publish with storage option
PNPublishRequest *storageRequest = [PNPublishRequest requestWithChannel:@"demo-channel"];
storageRequest.message = @{@"text": @"This message won't be stored in history"};
storageRequest.store = NO;

[client publishWithRequest:storageRequest withCompletion:^(PNPublishStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Message published with storage option!");
        NSLog(@"Timetoken: %@", status.data.timetoken);
    } else {
        NSLog(@"❌ Error publishing message with storage option: %@", status.errorData.information);
    }
}];

// Example 4: Publish compressed message
NSMutableDictionary *largeMessage = [NSMutableDictionary dictionary];
NSString *repeatedText = [@"" stringByPaddingToLength:1000 withString:@"This is a long message that will benefit from compression. " startingAtIndex:0];
largeMessage[@"text"] = repeatedText;
largeMessage[@"timestamp"] = @([[NSDate date] timeIntervalSince1970]);

PNPublishRequest *compressedRequest = [PNPublishRequest requestWithChannel:@"demo-channel"];
compressedRequest.message = largeMessage;
compressedRequest.compress = YES;

[client publishWithRequest:compressedRequest withCompletion:^(PNPublishStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Compressed message successfully published!");
        NSLog(@"Timetoken: %@", status.data.timetoken);
    } else {
        NSLog(@"❌ Error publishing compressed message: %@", status.errorData.information);
    }
}];

// Example 5: Publish with mobile push payload
NSDictionary *apnsPayload = @{
    @"aps": @{
        @"alert": @{
            @"title": @"New Message",
            @"body": @"You have a new message!"
        },
        @"sound": @"default",
        @"badge": @1
    },
    @"custom_data": @"custom_value"
};

NSDictionary *fcmPayload = @{
    @"notification": @{
        @"title": @"New Message",
        @"body": @"You have a new message!"
    },
    @"data": @{
        @"custom_data": @"custom_value"
    }
};

NSDictionary *pushPayloads = @{
    @"apns": apnsPayload,
    @"fcm": fcmPayload
};

PNPublishRequest *pushRequest = [PNPublishRequest requestWithChannel:@"demo-channel"];
pushRequest.message = @{@"text": @"Message with push notification"};
pushRequest.mobilePushPayload = pushPayloads;

[client publishWithRequest:pushRequest withCompletion:^(PNPublishStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Message with push payload successfully published!");
        NSLog(@"Timetoken: %@", status.data.timetoken);
    } else {
        NSLog(@"❌ Error publishing message with push payload: %@", status.errorData.information);
    }
}];

// Required PNEventsListener methods
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
    // Checking connectivity only using subscribe operation.
    if (status.operation != PNSubscribeOperation) return;
    
    if (status.category == PNConnectedCategory) {
        NSLog(@"✅ Successfully connected to PubNub!");
    } else if (status.isError) {
        NSLog(@"❌ PubNub connection error: %@", status);
    }
}

- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
    NSLog(@"✅ Received message: %@ on channel: %@", message.data.message, message.data.channel);
}
```

:::note Subscribe to the channel
Before running the above publish example, either using the [Debug Console](https://www.pubnub.com/docs/console/) or in a separate script running in a separate terminal window, [subscribe to the same channel](#subscribe) that is being published to.
:::

### Response

Response objects which is returned by client when publish API is used:

```objectivec
@interface PNPublishData : PNServiceData

@property (nonatomic, readonly, strong) NSNumber *timetoken;
@property (nonatomic, readonly, strong) NSString *information;

@end

@interface PNPublishStatus : PNAcknowledgmentStatus

@property (nonatomic, readonly, strong) PNPublishData *data;

@end
```

### Other examples

#### Publish with metadata

```objectivec
[self publish: @"Hello from the PubNub Objective-C" toChannel:@"chat_channel"
    withMetadata: @{@"senderID" : @"bob"}  completion:^(PNPublishStatus *status) {

    if (!status.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: [status retry]
            */
    }
}];
```

#### Push payload helper

You can use the helper method as an input to the `Message` parameter, to format the payload for publishing [Push](https://www.pubnub.com/docs/general/push/send) messages. For more info on the helper method, check [Create Push Payload Helper Section](https://www.pubnub.com/docs/sdks/objective-c/api-reference/misc#pnapnsnotificationconfiguration)

## Publish (builder pattern)

This function publishes a message on a channel.

:::note
This method uses the builder pattern, you can remove the args which are optional.
:::

### Method(s)

To run `Publish Builder` you can use the following method(s) in the Objective-C SDK:

```objectivec
publish()
    .message(id)
    .channel(NSString *)
    .shouldStore(BOOL)
    .compress(BOOL)
    .ttl(NSUInteger)
    .payloads(NSDictionary *)
    .metadata(NSDictionary *)
    .customMessageType(NSString*)
    .performWithCompletion(PNPublishCompletionBlock);
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | The `message` may be any valid foundation object (`NSString`, `NSNumber`, `NSArray`, `NSDictionary`). |
| `channel` *Type: NSString* | Specifies `channel` ID to `publish` messages to. |
| `shouldStore`Type: BOOL | If `NO` the messages will not be stored in history. Default `YES`. |
| `compress`Type: BOOL | If `YES` the `message` will be compressed and sent with request body instead of the URI. Compression useful in case of large data, in another cases it will increase the packet size. |
| `ttl`Type: NSUInteger | Specify for how many hours published `message` should be stored. |
| `payloads`Type: NSDictionary | Dictionary with `payloads` for different vendors (Apple with `aps` key and Google with `fcm`). Either `payloads` or `message` should be provided. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `customMessageType`Type: NSString* | A case-sensitive, alphanumeric string from 3 to 50 characters describing the business-specific label or category of the message. Dashes `-` and underscores `_` are allowed. The value cannot start with special characters or the string `pn_` or `pn-`. Examples: `text`, `action`, `poll`. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

### Sample code

Publish message which will be stored in Message Persistence for next 16 hours.

```objectivec
self.client.publish()
    .channel(@"my_channel")
    .message(@"Hello from PubNub iOS!")
    .shouldStore(YES)
    .ttl(16)
    .customMessageType(@"text-message")
    .performWithCompletion(^(PNPublishStatus *status) {

    if (!status.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: [status retry];
         */
    }
});
```

## Fire (builder pattern)

The fire endpoint allows the client to send a message to BLOCKS Event Handlers. These messages will go directly to any Event Handlers registered on the channel that you fire to and will trigger their execution. The content of the fired request will be available for processing within the Event Handler. The message sent via `fire()` is not replicated, and so will not be received by any subscribers to the channel. The message is also not stored in history.

:::note
This method uses the builder pattern, you can remove the args which are optional.
:::

### Method(s)

To `Fire a message` you can use the following method(s) in the Objective-C SDK:

```objectivec
fire()
    .message(id)
    .channel(NSString *)
    .compress(BOOL)
    .payloads(NSDictionary *)
    .metadata(NSDictionary *)
    .performWithCompletion(PNPublishCompletionBlock);
```

| Parameter | Description |
| --- | --- |
| `message`Type: id | The `message` may be any valid foundation object (`NSString`, `NSNumber`, `NSArray`, `NSDictionary`). |
| `channel` *Type: NSString | Specifies `channel` ID to `publish` messages to. |
| `compress`Type: BOOL | If `YES` the `message` will be compressed and sent with request body instead of the URI. Compression useful in case of large data, in another cases it will increase the packet size. |
| `payloads`Type: NSDictionary | Dictionary with `payloads` for different vendors (Apple with `aps` key and Google with `fcm`). Either `payloads` or `message` should be provided. |
| `metadata`Type: NSDictionary | `NSDictionary` with values which should be used by `PubNub` service to filter messages. |
| `block`Type: PNPublishCompletionBlock | The completion `block` which will be called when the processing is complete, has one argument: - `request` status reports the publish was successful or not (`errorData` contains error information in case of failure). |

### Sample code

```objectivec
self.client.fire()
    .channel(@"my_channel")
    .message(@"Hello from PubNub iOS!")
    .shouldStore(YES)
    .performWithCompletion(^(PNPublishStatus *status) {

    if (!status.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: [status retry];
         */
    }
});
```

## Signal

The `signal()` function is used to send a signal to all subscribers of a channel.

By default, signals are limited to a message payload size of `64` bytes. This limit applies only to the payload, and not to the URI or headers. If you require a larger payload size, please [contact support](https://www.pubnub.com/docs/mailto:support@pubnub.com).

### Method(s)

To `Signal a message` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)signal:(id)message 
           channel:(NSString *)channel 
    withCompletion:(nullable PNSignalCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `message` *Type: id | Object (`NSString`, `NSNumber`, `NSArray`, `NSDictionary`) which will be sent with signal. |
| `channel` *Type: NSString | ID of the `channel` to which `signal` should be sent. |
| `block`Type: PNSignalCompletionBlock | Signal processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

### Sample code

#### Signal a message to a channel

```objectivec
[self.client signal:@{ @"Hello": @"world" } channel:@"announcement"
     withCompletion:^(PNSignalStatus *status) {

    if (!status.isError) {
        // Signal successfully sent to specified channel.
    } else {
        /**
         * Handle signal sending error. Check 'category' property to find out possible issue
         * because of which request did fail.
         *
         * Request can be resent using: [status retry];
         */
    }
}];
```

### Response

Response objects returned by the client when Signal API is used:

```objectivec
@interface PNSignalStatusData : PNServiceData

@property (nonatomic, readonly, strong) NSNumber *timetoken;
@property (nonatomic, readonly, strong) NSString *information;

@end

@interface PNSignalStatus : PNAcknowledgmentStatus

@property (nonatomic, readonly, strong) PNSignalStatusData *data;

@end
```

## Signal (builder pattern)

:::note
This method uses the builder pattern, you can remove the args which are optional.
:::

### Method(s)

To run `Signal Builder` you can use the following method(s) in the Objective-C SDK:

```objectivec
signal()
    .message(id)
    .channel(NSString *)
    .customMessageType(NSString *)
    .performWithCompletion(PNSignalCompletionBlock);
```

| Parameter | Description |
| --- | --- |
| `message` *Type: Array | Object (`NSString`, `NSNumber`, `NSArray`, `NSDictionary`) which will be sent with signal. |
| `channel` *Type: String | ID of the `channel` to which `signal` should be sent. |
| `customMessageType`Type: NSString* | A case-sensitive, alphanumeric string from 3 to 50 characters describing the business-specific label or category of the message. Dashes `-` and underscores `_` are allowed. The value cannot start with special characters or the string `pn_` or `pn-`. Examples: `text`, `action`, `poll`. |
| `block`Type: PNSignalCompletionBlock | Signal processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

### Sample code

#### Signal a message to a channel

```objectivec
self.client.signal().message(@{ @"Hello": @"world" }).channel(@"announcement").customMessageType(@"text-message-signal").performWithCompletion(^(PNSignalStatus *status) {

    if (!status.isError) {
        // Signal successfully sent to specified channel.
    } else {
        /**
         * Handle signal sending error. Check 'category' property to find out possible issue
         * because of which request did fail.
         *
         * Request can be resent using: [status retry];
         */
    }
});
```

##### Response

Response objects which is returned by client when Signal API is used::

```objectivec
@interface PNSignalStatusData : PNServiceData

@property (nonatomic, readonly, strong) NSNumber *timetoken;
@property (nonatomic, readonly, strong) NSString *information;

@end

@interface PNSignalStatus : PNAcknowledgmentStatus

@property (nonatomic, readonly, strong) PNSignalStatusData *data;

@end
```

## Subscribe

### Receive messages

Your app receives messages and events via event listeners. The event listener is a single point through which your app receives all the messages, signals, and events that are sent in any channel you are subscribed to.

For more information about adding a listener, refer to the [Event Listeners](https://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration#event-listeners) section.

:::note No built-in event throttling
The PubNub SDK delivers every incoming event to your listener as it arrives — there is no built-in throttling or rate-limiting on the subscriber side. If you need to control how often your application processes events, wrap your listener callback with a throttle or debounce utility from your language or framework ecosystem.
To reduce the number of messages delivered to your client in the first place, use [Subscribe Filters](https://www.pubnub.com/docs/general/channels/subscribe-filters) to filter messages server-side before they reach your listener.
:::

### Description

This function causes the client to create an open TCP socket to the PubNub Real-Time Network and begin listening for messages on a specified `channel` ID. To subscribe to a `channel` ID the client must send the appropriate `subscribeKey` at initialization. By default a newly subscribed client will only receive messages published to the channel after the `subscribeToChannels` call completes.

:::tip Connectivity notification
You can be notified of connectivity via the `connect` callback. By waiting for the connect callback to return before attempting to publish, you can avoid a potential race condition on clients that subscribe and immediately publish messages before the subscribe has completed.
:::

Using Objective-C SDK, if a client becomes disconnected from a channel, it can automatically attempt to reconnect to that channel and retrieve any available messages that were missed during that period by setting `restore` to `YES`. By default a client will attempt to reconnect after exceeding a `320` second connection timeout.

:::warning Unsubscribing from all channels
**Unsubscribing** from all channels, and then **subscribing** to a new channel Y is not the same as subscribing to channel Y and then unsubscribing from the previously-subscribed channel(s). Unsubscribing from all channels resets the last-received `timetoken` and thus, there could be some gaps in the subscription that may lead to message loss.
:::

### Method(s)

To `Subscribe to a channel` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)subscribeToChannels:(NSArray<NSString *> *)channels 
               withPresence:(BOOL)shouldObservePresence;
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: NSArray | List of `channel` IDs on which client should try to `subscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether presence observation should be enabled for `channels` or not. For information on how to receive presence events and what those events are, refer to [Presence Events](https://www.pubnub.com/docs/general/presence/presence-events#subscribe-to-presence-channel). |

```objectivec
- (void)subscribeToChannels:(NSArray<NSString *> *)channels 
               withPresence:(BOOL)shouldObservePresence 
                clientState:(nullable NSDictionary<NSString *, id> *)state;
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: NSArray | List of `channel` IDs on which client should try to `subscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether presence observation should be enabled for `channels` or not. |
| `state`Type: NSDictionary | Reference on dictionary which stores key-value pairs based on `channel` ID and value which should be assigned to it. |

```objectivec
- (void)subscribeToChannels:(NSArray<NSString *> *)channels 
               withPresence:(BOOL)shouldObservePresence 
             usingTimeToken:(nullable NSNumber *)timeToken;
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: NSArray | List of `channel` IDs on which client should try to `subscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether presence observation should be enabled for `channels` or not. |
| `timeToken`Type: NSNumber | Specifies time from which to start returning any available cached messages. `Message` retrieval with `timetoken` is not guaranteed and should only be considered a best-effort service. |

```objectivec
- (void)subscribeToChannels:(NSArray<NSString *> *)channels 
               withPresence:(BOOL)shouldObservePresence 
             usingTimeToken:(nullable NSNumber *)timeToken 
                clientState:(nullable NSDictionary<NSString *, id> *)state;
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: NSArray | List of `channel` IDs on which client should try to `subscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether presence observation should be enabled for `channels` or not. |
| `timeToken`Type: NSNumber | Specifies time from which to start returning any available cached messages. `Message` retrieval with `timetoken` is not guaranteed and should only be considered a best-effort service. |
| `state`Type: NSDictionary | Reference on dictionary which stores key-value pairs based on `channel` ID and value which should be assigned to it. |

### Sample code

Subscribe to a channel:

```objectivec
/**
 Subscription results arrive to a listener which should implement the
 PNObjectEventListener protocol and be registered as follows:
 */
[self.client addListener:self];
[self.client subscribeToChannels: @[@"my_channel1", @"my_channel2"] withPresence:NO];

// Handle a new message from a subscribed channel
- (void)client:(PubNub *)client didReceiveMessage:(PNMessageResult *)message {
    // Reference to the channel group containing the chat the message was sent to
    NSString *subscription = message.data.subscription;
    NSLog(@"%@ sent message to '%@' at %@: %@", message.data.publisher, message.data.channel,
            message.data.timetoken, message.data.message);
}

// Handle a subscription status change
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {

    if (status.operation == PNSubscribeOperation) {

        // Check to see if the message is about a successful subscription or restore
        if (status.category == PNConnectedCategory || status.category == PNReconnectedCategory) {

            // Status object for those categories can be cast to `PNSubscribeStatus` for use below.
            PNSubscribeStatus *subscribeStatus = (PNSubscribeStatus *)status;
            if (subscribeStatus.category == PNConnectedCategory) {

                // For a subscribe, this is expected, and means there are no errors or issues
            }
            else {

                // This usually occurs if there is a transient error. The subscribe fails but
                 // then reconnects, and 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, and retry will be called automatically.
        }
        // An issue occurred while the client tried to subscribe,
        // or the client was disconnected from the network
        else {

            PNErrorStatus *errorStatus = (PNErrorStatus *)status;
            if (errorStatus.category == PNAccessDeniedCategory) {

                // Access Manager prohibited this client from subscribing to this channel and channel group.
                // This is another explicit error.
            }
            else {

                /**
                 You can directly specify more errors by creating explicit cases
                 for other categories of `PNStatusCategory` errors, such as:
                 - `PNDecryptionErrorCategory`
                 - `PNMalformedFilterExpressionCategory`
                 - `PNMalformedResponseCategory`
                 - `PNTimeoutCategory`
                 - `PNNetworkIssuesCategory`
                 */
            }
        }
    }
    else if (status.operation == PNUnsubscribeOperation) {

        if (status.category == PNDisconnectedCategory) {

            // This is the expected category for an unsubscribe.
            // There were no errors in unsubscribing from everything.
        }
    }
    else if (status.operation == PNHeartbeatOperation) {

        /**
         Heartbeat operations can have errors, so check first for an error.
         For more information on how to configure heartbeat notifications through the status
         PNObjectEventListener callback, refer to
         http://www.pubnub.com/docs/sdks/objective-c/api-reference/configuration#configuration_basic_usage
         */

        if (!status.isError) { /* Heartbeat operation was successful */ }
        else { /* There was an error with the heartbeat operation: handle it here */ }
    }
}

// Handle a new signal from a subscribed channel
- (void)client:(PubNub *)client didReceiveSignal:(PNSignalResult *)signal {
    NSLog(@"%@ sent signal to '%@' at %@: %@", message.data.publisher, message.data.channel,
          message.data.timetoken, message.data.message);
}

// Handle new object event from one of channels on which client has been subscribed.
- (void)client:(PubNub *)client didReceiveObjectEvent:(PNObjectEventResult *)event {
    NSLog(@"'%@' has been %@'ed at %@", event.data.type, event.data.event,
        event.data.timestamp);
}
```

### Response

```objectivec
@interface PNSubscriberData : PNServiceData

// Name of channel on which subscriber received data.
@property (nonatomic, readonly, strong) NSString *channel;

// Name of channel or channel group (if not equal to channel name).
@property (nonatomic, nullable, readonly, strong) NSString *subscription;

// Time at which the event arrived.
@property (nonatomic, readonly, strong) NSNumber *timetoken;

// Stores a reference to the metadata information passed along with the received event.
@property (nonatomic, nullable, readonly, strong) NSDictionary<NSString *, id> *userMetadata;

@end

@interface PNSubscribeStatus : PNErrorStatus

// Timetoken used to establish the current subscription cycle.
@property (nonatomic, readonly, strong) NSNumber *currentTimetoken;

// Stores a reference to the previous key used in the subscription cycle to receive
// currentTimetoken along with other events.
@property (nonatomic, readonly, strong) NSNumber *lastTimeToken;

// List of channels to which the client is currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannels;

//  List of channel group names to which the client is currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannelGroups;

// Structured PNResult data field information.
@property (nonatomic, readonly, strong) PNSubscriberData *data;

@end

// Message arrives to event listeners as model described below

@interface PNMessageData : PNSubscriberData

// Message sender identifier.
@property (nonatomic, readonly, strong) NSString *publisher;

// Message delivered through the data object live feed.
@property (nonatomic, nullable, readonly, strong) id message;

@end

@interface PNMessageResult : PNResult

// Stores a reference to the message object from live feed.
@property (nonatomic, readonly, strong) PNMessageData *data;

@end

// Signal arrives to event listeners as model described below
@interface PNSignalData : PNMessageData

// Signal sender identifier.
@property (nonatomic, readonly, strong) NSString *publisher;

// Signal message delivered through the data object live feed.
@property (nonatomic, nullable, readonly, strong) id message;

@end

@interface PNSignalResult : PNResult

// Stores reference to signal object from live feed.
@property (nonatomic, readonly, strong) PNSignalData *data;

@end

// Object events arrive to event listeners as model described below
@interface PNObjectEventData : PNSubscriberData

// This property will be set only if event 'type' is 'channel' and represent channel metadata.
@property (nonatomic, nullable, readonly, strong) PNChannelMetadata *channelMetadata;

// This property will be set only if event 'type' is 'uuid' and represent uuid metadata.
@property (nonatomic, nullable, readonly, strong) PNUUIDMetadata *uuidMetadata;

// This property will be set only if event `type` is 'membership' and represent uuid membership.
@property (nonatomic, nullable, readonly, strong) PNMembership *membership;

// Time when object event has been triggered.
@property (nonatomic, readonly, strong) NSNumber *timestamp;

// Name of action for which object event has been sent.
@property (nonatomic, readonly, strong) NSString *event;

// Type of object which has been changed and triggered event.
@property (nonatomic, readonly, strong) NSString *type;

@end

@interface PNObjectEventResult : PNResult

// Object event object from live feed.
@property (nonatomic, readonly, strong) PNObjectEventData *data;

@end

// Message action events arrive to event listeners as model described below
@interface PNMessageActionData : PNSubscriberData

// Action for which event has been received.
@property (nonatomic, readonly, strong) PNMessageAction *action;

// Name of action for which message action event has been sent ('added') or ('removed')
@property (nonatomic, readonly, copy) NSString *event;

@end

@interface PNMessageActionResult : PNResult

// Message action object from live feed.
@property (nonatomic, readonly, strong) PNMessageActionData *data;

@end
```

### Other examples

#### Wildcard subscribe to channels

:::note Requires Stream Controller add-on
This method requires that the *Stream Controller* add-on is enabled for your key in the [Admin Portal](https://admin.pubnub.com/) (with Enable Wildcard Subscribe checked). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

Wildcard subscribes allow the client to subscribe to multiple channels using wildcard. For example, if you subscribe to `a.*` you will get all messages for `a.b`, `a.c`, `a.x`. The wildcarded `*` portion refers to any portion of the channel string name after the `dot (.)`.

```objectivec
[self.client subscribeToChannels: @[@"my_channel.*"] withPresence:YES];
```

:::note Wildcard grants and revokes
Only one level (`a.*`) of wildcarding is supported. If you grant on `*` or `a.b.*`, the grant will treat `*` or `a.b.*` as a single channel named either `*` or `a.b.*`. You can also revoke permissions from multiple channels using wildcards but only if you previously granted permissions using the same wildcards. Wildcard revokes, similarly to grants, only work one level deep, like `a.*`.
:::

#### Subscribing to a Presence channel

:::warning Requires Presence
This method requires that the Presence add-on is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). For information on how to receive presence events and what those events are, refer to [Presence Events](https://www.pubnub.com/docs/general/presence/presence-events#subscribe-to-presence-channel).
:::

For any given channel there is an associated Presence channel. You can subscribe directly to the channel by appending `-pnpres` to the channel name. For example the channel named `my_channel` would have the presence channel named `my_channel-pnpres`.

```objectivec
/**
    Subscription process results arrive to listener which should adopt to
    PNObjectEventListener protocol and registered using:
    */
[self.client addListener:self];
[self.client subscribeToPresenceChannels:@[@"my_channel"]];

// New presence event handling.
- (void)client:(PubNub *)client didReceivePresenceEvent:(PNPresenceEventResult *)event {

    if (![event.data.channel isEqualToString:event.data.subscription]) {

        // Presence event has been received on channel group stored in event.data.subscription.
    }
    else {

        // Presence event has been received on channel stored in event.data.channel.
    }

    if (![event.data.presenceEvent isEqualToString:@"state-change"]) {

        NSLog(@"%@ \"%@'ed\"\nat: %@ on %@ (Occupancy: %@)", event.data.presence.uuid,
                event.data.presenceEvent, event.data.presence.timetoken, event.data.channel,
                event.data.presence.occupancy);
    }
    else {

        NSLog(@"%@ changed state at: %@ on %@ to: %@", event.data.presence.uuid,
                event.data.presence.timetoken, event.data.channel, event.data.presence.state);
    }
}
```

#### Sample Responses

##### Join event

```json
{
    "action": "join",
    "timestamp": 1345546797,
    "uuid": "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
    "occupancy": 2
}
```

##### Leave event

```json
{
    "action" : "leave",
    "timestamp" : 1345549797,
    "uuid" : "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
    "occupancy" : 1
}
```

##### Timeout event

```json
{
    "action": "timeout",
    "timestamp": 1345549797,
    "uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    "occupancy": 0
}
```

##### State change event

```json
{
    "action": "state-change",
    "uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    "timestamp": 1345549797,
    "data": {
        "isTyping": true
    }
}
```

##### Interval event

```json
{
    "action":"interval",
    "timestamp":1474396578,
    "occupancy":2
}
```

When a channel is in interval mode with `presence_deltas` `pnconfig` flag enabled, the interval message may also include the following fields which contain an array of changed UUIDs since the last interval message.

* joined
* left
* timedout

For example, this interval message indicates there were 2 new UUIDs that joined and 1 timed out UUID since the last interval:

```json
{
    "action" : "interval",
    "occupancy" : <# users in channel>,
    "timestamp" : <unix timestamp>,
    "joined" : ["uuid2", "uuid3"],
    "timedout" : ["uuid1"]
}
```

If the full interval message is greater than `30 KB` (since the max publish payload is `∼32 KiB`), none of the extra fields will be present. Instead there will be a `here_now_refresh` boolean field set to `true`. This indicates to the user that they should do a `hereNow` request to get the complete list of users present in the channel.

```json
{
    "action" : "interval",
    "occupancy" : <# users in channel>,
    "timestamp" : <unix timestamp>,
    "here_now_refresh" : true
}
```

#### Subscribing with state

:::warning Requires Presence
This method requires that the Presence add-on is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). For information on how to receive presence events and what those events are, refer to [Presence Events](https://www.pubnub.com/docs/general/presence/presence-events#subscribe-to-presence-channel).
:::

:::note Required UUID
Always set the `UUID` to uniquely identify the user or device that connects to PubNub. This `UUID` should be persisted, and should remain unchanged for the lifetime of the user or the device. If you don't set the `UUID`, you won't be able to connect to PubNub.
:::

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

// Subscribe to demo channel with presence observation
[self.client subscribeToChannels: @[@"my_channel"] withPresence:YES];

- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {

    if (status.category == PNConnectedCategory) {
        [self.client setState: @{@"new": @"state"} forUUID:self.client.uuid onChannel: @"my_channel"
                withCompletion:^(PNClientStateUpdateStatus *status) {

            if (!status.isError) {
                // Client state successfully modified on specified channel.
            } else {
                /**
                    Handle client state modification 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: [status retry];
                    */
            }
        }];
    }
}

// Handle new message from one of channels on which client has been subscribed.
- (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,
            message.data.channel, message.data.timetoken);
}
```

## Subscribe channel group

:::note Requires Stream Controller add-on
This method requires that the *Stream Controller* add-on is enabled for your key in the [Admin Portal](https://admin.pubnub.com/). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

This functions subscribes to a channel group.

### Method(s)

To `Subscribe to a Channel Group` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)subscribeToChannelGroups:(NSArray<NSString *> *)groups 
                    withPresence:(BOOL)shouldObservePresence;
```

| Parameter | Description |
| --- | --- |
| `groups` *Type: NSArray | List of `channel` group names on which client should try to `subscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether presence observation should be enabled for `groups` or not. |

```objectivec
- (void)subscribeToChannelGroups:(NSArray<NSString *> *)groups 
                    withPresence:(BOOL)shouldObservePresence 
                     clientState:(nullable NSDictionary<NSString *, id> *)state;
```

| Parameter | Description |
| --- | --- |
| `groups` *Type: NSArray | List of `channel` group names on which client should try to `subscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether presence observation should be enabled for `groups` or not. |
| `state`Type: NSDictionary | Reference on dictionary which stores key-value pairs based on `channel` group name and value which should be assigned to it. |

### Sample code

Subscribe to a channel group

```objectivec
NSString *channelGroup = @"family";
```

```objectivec
/**
 Subscription process results arrive to listener which should adopt to
 PNObjectEventListener protocol and registered using:
 */
[self.client addListener:self];
[self.client subscribeToChannelGroups:@[channelGroup] withPresence:NO];

// Handle new message from one of channels on which client has been subscribed.
- (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,
          message.data.channel, message.data.timetoken);
}

// Handle subscription status change.
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {

    if (status.operation == PNSubscribeOperation) {

        // Check whether received information about successful subscription or restore.
        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.
            }
            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.
             */
        }
        // Looks like some kind of issues happened while client tried to subscribe or disconnected from
        // network.
        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`
                 */
            }
        }
    }
}
```

### Response

```objectivec
@interface PNSubscriberData : PNServiceData

// Name of channel for which subscriber received data.
@property (nonatomic, readonly, strong) NSString *channel;
// Name of channel or channel group (in case if not equal to channel).
@property (nonatomic, nullable, readonly, strong) NSString *subscription;
// Time at which event arrived.
@property (nonatomic, readonly, strong) NSNumber *timetoken;
// Stores reference on metadata information which has been passed along with received event.
@property (nonatomic, nullable, readonly, strong) NSDictionary<NSString *, id> *userMetadata;

@end

@interface PNSubscribeStatus : PNErrorStatus

// Timetoken which has been used to establish current subscription cycle.
@property (nonatomic, readonly, strong) NSNumber *currentTimetoken;
// Stores reference on previous key which has been used in subscription cycle to receive
// currentTimetoken along with other events.
@property (nonatomic, readonly, strong) NSNumber *lastTimeToken;
// List of channels on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannels;
//  List of channel group names on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannelGroups;
// Structured PNResult data field information.
@property (nonatomic, readonly, strong) PNSubscriberData *data;

@end

// Message arrive to event listeners as model described below

@interface PNMessageData : PNSubscriberData

// Message sender identifier.
@property (nonatomic, readonly, strong) NSString *publisher;
// Message which has been delivered through data object live feed.
@property (nonatomic, nullable, readonly, strong) id message;

@end

@interface PNMessageResult : PNResult

// Stores reference on message object from live feed.
@property (nonatomic, readonly, strong) PNMessageData *data;

@end
```

### Other examples

#### Subscribe to the Presence channel of a channel group

:::note Requires Stream Controller and Presence add-ons
This method requires both the *Stream Controller* and *Presence* add-ons are enabled for your key in the [Admin Portal](https://admin.pubnub.com/). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

```objectivec
/**
    Subscription process results arrive to listener which should adopt to
    PNObjectEventListener protocol and registered using:
    */
[self.client addListener:self];
[self.client subscribeToChannelGroups:@[channelGroup] withPresence:YES];

// Handle new message from one of channels on which client has been subscribed.
- (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,
            message.data.channel, message.data.timetoken);
}

// New presence event handling.
- (void)client:(PubNub *)client didReceivePresenceEvent:(PNPresenceEventResult *)event {

    if (![event.data.channel isEqualToString:event.data.subscription]) {

        // Presence event has been received on channel group stored in event.data.subscription.
    }
    else {

        // Presence event has been received on channel stored in event.data.channel.
    }

    if (![event.data.presenceEvent isEqualToString:@"state-change"]) {

        NSLog(@"%@ \"%@'ed\"\nat: %@ on %@ (Occupancy: %@)", event.data.presence.uuid,
                event.data.presenceEvent, event.data.presence.timetoken, event.data.channel,
                event.data.presence.occupancy);
    }
    else {

        NSLog(@"%@ changed state at: %@ on %@ to: %@", event.data.presence.uuid,
                event.data.presence.timetoken, event.data.channel, event.data.presence.state);
    }
}

// Handle subscription status change.
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {

    if (status.operation == PNSubscribeOperation) {

        // Check whether received information about successful subscription or restore.
        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.
            }
            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.
                */
        }
        // Looks like some kind of issues happened while client tried to subscribe or disconnected from
        // network.
        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`
                    */
            }
        }
    }
}
```

## Unsubscribe

When subscribed to a single channel, this function causes the client to issue a `leave` from the `channel` and close any open socket to the PubNub Network. For multiplexed channels, the specified `channel`(s) will be removed and the socket remains open until there are no more channels remaining in the list.

:::warning Unsubscribing from all channels
**Unsubscribing** from all channels, and then **subscribing** to a new channel Y is not the same as subscribing to channel Y and then unsubscribing from the previously-subscribed channel(s). Unsubscribing from all channels resets the last-received `timetoken` and thus, there could be some gaps in the subscription that may lead to message loss.
:::

### Method(s)

To `Unsubscribe from a channel` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)unsubscribeFromChannels:(NSArray<NSString *> *)channels 
                   withPresence:(BOOL)shouldObservePresence;
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: NSArray | List of `channel` IDs from which client should try to `unsubscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether client should disable presence observation on specified `channels` or keep listening for presence event on them. |

### Sample code

Unsubscribe from a channel:

```objectivec
/**
 Unsubscription process results arrive to listener which should adopt to
 PNObjectEventListener protocol and registered using:
 */
[self.client addListener:self];
[self.client unsubscribeFromChannels: @[@"my_channel1", @"my_channel2"] withPresence:NO];

// Handle subscription status change.
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {

    if (status.operation == PNUnsubscribeOperation && status.category == PNDisconnectedCategory) {

        /**
         This is the expected category for an unsubscribe. This means there was no error in
         unsubscribing from everything.
         */
    }
}
```

### Response

```objectivec
@interface PNSubscriberData : PNServiceData

// Name of channel for which subscriber received data.
@property (nonatomic, readonly, strong) NSString *channel;
// Name of channel or channel group (in case if not equal to channel).
@property (nonatomic, nullable, readonly, strong) NSString *subscription;
// Time at which event arrived.
@property (nonatomic, readonly, strong) NSNumber *timetoken;
// Stores reference on metadata information which has been passed along with received event.
@property (nonatomic, nullable, readonly, strong) NSDictionary<NSString *, id> *userMetadata;

@end

@interface PNSubscribeStatus : PNErrorStatus

// Timetoken which has been used to establish current subscription cycle.
@property (nonatomic, readonly, strong) NSNumber *currentTimetoken;
// Stores reference on previous key which has been used in subscription cycle to receive
// currentTimetoken along with other events.
@property (nonatomic, readonly, strong) NSNumber *lastTimeToken;
// List of channels on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannels;
//  List of channel group names on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannelGroups;
// Structured PNResult data field information.
@property (nonatomic, readonly, strong) PNSubscriberData *data;

@end
```

## Unsubscribe all

Unsubscribe from all channels and all channel groups

### Method(s)

```objectivec
- (void)unsubscribeFromAll;
```

### Sample code

```objectivec
/**
 Unsubscription process results arrive to listener which should adopt to
 PNObjectEventListener protocol and registered using:
 */
[self.client addListener:self];
[self.client unsubscribeFromAll];

// Handle subscription status change.
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {

    if (status.operation == PNUnsubscribeOperation && status.category == PNDisconnectedCategory) {

        /**
         This is the expected category for an unsubscribe. This means there was no error in unsubscribing
         from everything.
         */
    }
}
```

### Returns

None

## Unsubscribe from a channel group

This function lets you Unsubscribe from a Channel Group

### Method(s)

To run `Unsubscribe from a Channel Group` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)unsubscribeFromChannelGroups:(NSArray<NSString *> *)groups 
                        withPresence:(BOOL)shouldObservePresence;
```

| Parameter | Description |
| --- | --- |
| `groups` *Type: NSArray | List of `channel` group names from which client should try to `unsubscribe`. |
| `shouldObservePresence` *Type: BOOL | Whether client should disable presence observation on specified `channel` groups or keep listening for presence event on them. |

### Sample code

#### Unsubscribe from a channel group

```objectivec
/**
 Unsubscription process results arrive to listener which should adopt to
 PNObjectEventListener protocol and registered using:
 */
[self.client addListener:self];
[self.client unsubscribeFromChannelGroups: @[@"developers"] withPresence:YES];

// Handle subscription status change.
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {

    if (status.operation == PNUnsubscribeOperation && status.category == PNDisconnectedCategory) {

        /**
         This is the expected category for an unsubscribe. This means there was no error in unsubscribing
         from everything.
         */
    }
}
```

### Response

```objectivec
@interface PNSubscriberData : PNServiceData

// Name of channel for which subscriber received data.
@property (nonatomic, readonly, strong) NSString *channel;
// Name of channel or channel group (in case if not equal to channel).
@property (nonatomic, nullable, readonly, strong) NSString *subscription;
// Time at which event arrived.
@property (nonatomic, readonly, strong) NSNumber *timetoken;
// Stores reference on metadata information which has been passed along with received event.
@property (nonatomic, nullable, readonly, strong) NSDictionary<NSString *, id> *userMetadata;

@end

@interface PNSubscribeStatus : PNErrorStatus

// Timetoken which has been used to establish current subscription cycle.
@property (nonatomic, readonly, strong) NSNumber *currentTimetoken;
// Stores reference on previous key which has been used in subscription cycle to receive
// currentTimetoken along with other events.
@property (nonatomic, readonly, strong) NSNumber *lastTimeToken;
// List of channels on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannels;
//  List of channel group names on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannelGroups;
// Structured PNResult data field information.
@property (nonatomic, readonly, strong) PNSubscriberData *data;

@end
```

## Presence

This function is used to subscribe to the presence channel. Using Objective-C SDK, if a client becomes disconnected from a channel, it can automatically attempt to reconnect to that channel and retrieve any available messages that were missed during that period by setting `restore` to `true`. By default a client will attempt to reconnect after exceeding a `320` second connection timeout.

### Method(s)

To do `Presence` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)subscribeToPresenceChannels:(NSArray<NSString *> *)channels;
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: NSArray | List of `channel` IDs for which client should try to `subscribe` on presence observing `channels`. |

### Sample code

Subscribe to the presence channel: For any given channel there is an associated `Presence` channel. You can subscribe directly to the channel by appending `-pnpres` to the channel name. For example the channel named `my_channel` would have the presence channel named `my_channel-pnpres`

```objectivec
/**
 Subscription process results arrive to listener which should adopt to
 PNObjectEventListener protocol and registered using:
 */
[self.client addListener:self];
[self.client subscribeToPresenceChannels:@[@"my_channel"]];

// New presence event handling.
- (void)client:(PubNub *)client didReceivePresenceEvent:(PNPresenceEventResult *)event {

    if (![event.data.channel isEqualToString:event.data.subscription]) {

        // Presence event has been received on channel group stored in event.data.subscription.
    }
    else {

        // Presence event has been received on channel stored in event.data.channel.
    }

    if (![event.data.presenceEvent isEqualToString:@"state-change"]) {

        NSLog(@"%@ \"%@'ed\"\nat: %@ on %@ (Occupancy: %@)", event.data.presence.uuid,
              event.data.presenceEvent, event.data.presence.timetoken, event.data.channel,
              event.data.presence.occupancy);
    }
    else {

        NSLog(@"%@ changed state at: %@ on %@ to: %@", event.data.presence.uuid,
              event.data.presence.timetoken, event.data.channel, event.data.presence.state);
    }
}
```

### Response

```objectivec
@interface PNSubscriberData : PNServiceData

// Name of channel for which subscriber received data.
@property (nonatomic, readonly, strong) NSString *channel;
// Name of channel or channel group (in case if not equal to channel).
@property (nonatomic, nullable, readonly, strong) NSString *subscription;
// Time at which event arrived.
@property (nonatomic, readonly, strong) NSNumber *timetoken;
// Stores reference on metadata information which has been passed along with received event.
@property (nonatomic, nullable, readonly, strong) NSDictionary<NSString *, id> *userMetadata;

@end

@interface PNSubscribeStatus : PNErrorStatus

// Timetoken which has been used to establish current subscription cycle.
@property (nonatomic, readonly, strong) NSNumber *currentTimetoken;
// Stores reference on previous key which has been used in subscription cycle to receive
// currentTimetoken along with other events.
@property (nonatomic, readonly, strong) NSNumber *lastTimeToken;
// List of channels on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannels;
//  List of channel group names on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannelGroups;
// Structured PNResult data field information.
@property (nonatomic, readonly, strong) PNSubscriberData *data;

@end

// Presence events arrive to event listeners as model described below

@interface PNPresenceDetailsData : PNSubscriberData

// Time when presence event has been triggered.
@property (nonatomic, readonly, strong) NSNumber *timetoken;
// Reference on unique user identifier for which event has been triggered.
@property (nonatomic, nullable, readonly, strong) NSString *uuid;
// List of newly joined subscribers' UUID.
@property (nonatomic, nullable, readonly, strong) NSArray<NSString *> *join;
// List of recently leaved subscribers' UUID.
@property (nonatomic, nullable, readonly, strong) NSArray<NSString *> *leave;
// List of recently UUID of subscribers which leaved by timeout.
@property (nonatomic, nullable, readonly, strong) NSArray<NSString *> *timeout;
// Channel presence information.
@property (nonatomic, readonly, strong) NSNumber *occupancy;
// User changed client state.
@property (nonatomic, nullable, readonly, strong) NSDictionary<NSString *, id> *state;

@end

@interface PNPresenceEventData : PNSubscriberData

// Type of presence event.
@property (nonatomic, readonly, strong) NSString *presenceEvent;
// Additional presence information.
@property (nonatomic, readonly, strong) PNPresenceDetailsData *presence;

@end

@interface PNPresenceEventResult : PNResult

// Stores reference on presence event object from live feed.
@property (nonatomic, readonly, strong) PNPresenceEventData *data;

@end
```

## Presence unsubscribe

This function lets you stop monitoring the presence of the `channel`(s). The channel(s) will be removed and the socket remains open until there are no more channels remaining in the list.

### Method(s)

To `Unsubscribe from Presence of a channel` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)unsubscribeFromPresenceChannels:(NSArray<NSString *> *)channels;
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: NSArray | List of `channel` IDs for which client should try to `unsubscribe` from presence observing `channels` |

### Sample code

Unsubscribe from the presence channel:

```objectivec
[self.client unsubscribeFromChannelGroups:@[@"developers"] withPresence:YES];
```

### Response

```objectivec
@interface PNSubscriberData : PNServiceData

// Name of channel for which subscriber received data.
@property (nonatomic, readonly, strong) NSString *channel;
// Name of channel or channel group (in case if not equal to channel).
@property (nonatomic, nullable, readonly, strong) NSString *subscription;
// Time at which event arrived.
@property (nonatomic, readonly, strong) NSNumber *timetoken;
// Stores reference on metadata information which has been passed along with received event.
@property (nonatomic, nullable, readonly, strong) NSDictionary<NSString *, id> *userMetadata;

@end

@interface PNSubscribeStatus : PNErrorStatus

// Timetoken which has been used to establish current subscription cycle.
@property (nonatomic, readonly, strong) NSNumber *currentTimetoken;
// Stores reference on previous key which has been used in subscription cycle to receive
// currentTimetoken along with other events.
@property (nonatomic, readonly, strong) NSNumber *lastTimeToken;
// List of channels on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannels;
//  List of channel group names on which client currently subscribed.
@property (nonatomic, readonly, copy) NSArray<NSString *> *subscribedChannelGroups;
// Structured PNResult data field information.
@property (nonatomic, readonly, strong) PNSubscriberData *data;

@end
```

## Terms in this document

* **Access Manager** - A cryptographic, token-based permission administrator that allows you to regulate clients' access to PubNub resources, such as channels, channel groups, and user IDs.
* **Action** - The type of activity (procedure) to execute when a condition is satisfied (for example, sending a message).
* **Billing alert notification** - A means of informing a user that a billing alert has been triggered. Before notifications can happen, a billing alert must be triggered first.
* **Business Object** - A container for data fields and metrics that defines aggregations and data sources.
* **Channel** - A pathway for sending and receiving messages between devices, created automatically when you first use it, that can handle any number of users and messages for different communication needs, like 1-1 text chats, group conversations, and other data streaming.
* **Channel pattern** - A way to group and analyze channel data to track performance metrics like message counts and user engagement over time with PubNub Insights.
* **Condition** - A requirement that must be satisfied or evaluated to true for an action to be executed. Input in a decision table.
* **Cryptor** - An implementation of a specific cryptographic algorithm used for data encryption/decryption that adheres to a standard interface.
* **Dashboard** - A collection of widgets (charts) that give an overview of the metrics one is evaluating.
* **Data fields** - Data you want Illuminate to track. These can be quantitative (measures), like "Number" or "Timestamp" or qualitative (dimensions) values, like "String" that can be used to categorize and segment data. Data fields can be aggregated and calculated.
* **Decision** - A collection (or decision table) of conditions and actions. When conditions are satisfied, the corresponding actions are triggered as per defined rules.
* **End Customer** - A customer of a PubNub partner. End customers do not have direct access to the Admin Portal. Instead, they interact with PubNub products—such as Illuminate—through the partner’s portal, where PubNub services are embedded. They can create PubNub objects only within this partner-provided environment.
* **Entity** - A subscribable object within a PubNub SDK that allows you to perform context-specific operations.
* **Listener** - A function or objectthat reacts to events or messages, like new chat messages or connection updates, letting your app respond in real-time.
* **Mapped/Unmapped** - Whether the data source for a data field has been defined or the action has been configured.
* **MCP Server** - A Model Context Protocol server that coordinates communication and synchronization between AI agents, clients, or services, such as Cursor IDE and Windsurf.
* **Message** - A unit of data transmitted between clients or between a client and a server in PubNub, containing information such as text, binary data, or structured data formats like JSON. Messages are sent over channels and can be tracked for delivery and read status.
* **Metric** - What exactly is evaluated using measures and dimensions (collectively called data fields), as well as aggregation functions.
* **Module** - A Functions v1 container that groups related functions for configuration and deployment on an app’s keysets.
* **Origin** - The subdomain used to establish a connection to the PubNub network that allows your application's traffic to appear like it's coming from your own domain.
* **Package** - A Functions v2 container that groups Functions, tracks Revisions, and is deployed to keysets.
* **Partner** - A PubNub customer who resells PubNub products, such as Illuminate, to their own customers. Partners have access to the Admin Portal, enabling them to create and manage PubNub objects for themselves or on behalf of their end customers.
* **Publish Key** - A unique identifier that allows your application to send messages to PubNub channels. It's part of your app's credentials and should be kept secure.
* **PubNub** - PubNub is a real-time messaging platform that provides APIs and SDKs for building scalable applications. It handles the complex infrastructure of real-time communication, including: Message delivery and persistence, Presence detection, Access control, Push notifications, File sharing, Serverless processing with Functions and Events & Actions, Analytics and monitoring with BizOps Workspace, AI-powered insights with Illuminate.
* **Push token** - A device identifier issued by a push provider (APNs or FCM) used to register a device for receiving mobile push notifications.
* **Rule** - A definition (row in a decision table) stating which action should be triggered for which condition.
* **Service Integration** - A machine identity that represents a program or service consuming the Admin API, scoped to your account and authenticated using expirable API keys with configurable permissions.
* **Signal** - A non-persistent message limited to 64 bytes designed for high-volume usecases where the the most recent data is relevant, like GPS location updates.
* **Subscribe Key** - A unique identifier that allows your application to receive messages from PubNub channels. It's part of your app's credentials and should be kept secure.
* **Timetoken** - A unique identifier for each message that represents the number of 100-nanosecond intervals since January 1, 1970, for example, 16200000000000000.
* **Trigger details** - A set of predefined criteria for a given billing alert. When met, billing alert notifications are generated.
* **User** - An individual or entity that interacts with a system, application, or service. In PubNub, a user typically refers to someone who sends or receives messages through the platform, identified by a unique user ID or username.
* **User ID** - UTF-8 encoded, unique string of up to 92 characters used to identify a single client (end user, device, or server) that connects to PubNub.
* **Vibe Coding** - A way to build applications in an intuitive, relaxed, and improvisational manner, using AI tools and natural language descriptions.