---
source_url: https://www.pubnub.com/docs/sdks/objective-c/api-reference/presence
title: Presence API for Objective-C SDK
updated_at: 2026-06-18T11:28:06.164Z
sdk_name: PubNub Objective-C SDK
sdk_version: 7.0.3
---

> 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


# Presence API for Objective-C SDK

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

Install:

```bash
pod install PubNub@7.0.3
```

Presence lets you track who is online or offline and store custom state information. Presence shows:

* When a user has joined or left a channel
* How many users are subscribed to a particular channel (occupancy)
* Which channels a user or device is subscribed to
* Presence state associated with these users

Learn more about our Presence feature in the [Presence overview](https://www.pubnub.com/docs/general/presence/overview).

## Here now

:::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).
:::

This method returns information about the current state of a channel, including a list of unique user IDs (universally unique identifiers, UUIDs) currently subscribed to the channel and the total occupancy count of the channel.

:::note Cache
This method has a 3-second response cache time.
:::

### Method(s)

To call `Here Now` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)hereNowWithRequest:(PNHereNowRequest *)request 
                completion:(PNHereNowCompletionBlock)block;
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| request | PNHereNowRequest | Yes |  | Request with information required to retrieve presence information. |
| block | PNHereNowCompletionBlock | Yes |  | Here now processing completion `block` which pass two arguments: result - in case of successful request processing data field will contain results of here now operation; status - in case if error occurred during request processing. |

#### PNHereNowRequest

| Property | Description |
| --- | --- |
| `channels`Type: `NSArray<NSString *>`Default: - | List of channels for which here now information should be received. |
| `channelGroups`Type: `NSArray<NSString *>`Default: - | List of channel groups for which here now information should be received. |
| `verbosityLevel`Type: `PNHereNowVerbosityLevel`Default: `PNHereNowState` | One of `PNHereNowVerbosityLevel` fields to instruct what exactly data it expected in response. |
| `limit`Type: `NSUInteger`Default: `1000` | Maximum number of occupants to return per channel. Valid range: `0-1000`. Use `0` to get occupancy counts without user details. |

### Sample code

#### Get a list of uuids subscribed to channel

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

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

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

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

// First, set a state for this user (optional)
NSString *userID = client.currentConfiguration.userID;
PNPresenceStateSetRequest *stateRequest = [PNPresenceStateSetRequest requestWithUserId:userID];
stateRequest.state = @{@"status": @"online", @"device": @"mobile"};
stateRequest.channels = @[@"presence-demo"];

[client setPresenceStateWithRequest:stateRequest completion:^(PNClientStateUpdateStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ State set successfully");
    }
}];

// Subscribe to a presence channel
PNSubscribeRequest *subscribeRequest = [PNSubscribeRequest requestWithChannels:@[@"presence-demo"] 
                                                                channelGroups:nil];
subscribeRequest.observePresence = YES;

[client subscribeWithRequest:subscribeRequest];

// Example 1: Basic Here Now (occupancy only)
PNHereNowRequest *basicRequest = [PNHereNowRequest requestForChannels:@[@"presence-demo"]];

[client hereNowWithRequest:basicRequest 
                completion:^(PNPresenceHereNowResult *result, PNErrorStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Basic Here Now successful!");
        NSLog(@"Channel: presence-demo");
        NSLog(@"Occupancy: %@", @(result.data.totalOccupancy));
    } else {
        NSLog(@"❌ Error getting basic Here Now: %@", status.errorData.information);
    }
}];

// Example 2: Here Now with UUID list
PNHereNowRequest *uuidRequest = [PNHereNowRequest requestForChannels:@[@"presence-demo"]];
uuidRequest.verbosityLevel = PNHereNowUUID;

[client hereNowWithRequest:uuidRequest 
                completion:^(PNPresenceHereNowResult *result, PNErrorStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Here Now with UUIDs successful!");
        NSLog(@"Channel: presence-demo");
        NSLog(@"Occupancy: %@", @(result.data.totalOccupancy));
        
        // Print list of UUIDs in the channel
        NSArray<PNPresenceUUIDData *> *uuids = result.data.channels[@"presence-demo"].uuids;
        NSLog(@"UUIDs in channel:");
        for (PNPresenceUUIDData *uuidData in uuids) {
            NSLog(@"- %@", uuidData.uuid);
        }
    } else {
        NSLog(@"❌ Error getting Here Now with UUIDs: %@", status.errorData.information);
    }
}];

// Example 3: Here Now with state information
PNHereNowRequest *stateRequest = [PNHereNowRequest requestForChannels:@[@"presence-demo"]];
stateRequest.verbosityLevel = PNHereNowState;

[client hereNowWithRequest:stateRequest 
                completion:^(PNPresenceHereNowResult *result, PNErrorStatus *status) {
    if (!status.isError) {
        NSLog(@"✅ Here Now with state information successful!");
        NSLog(@"Channel: presence-demo");
        NSLog(@"Occupancy: %@", @(result.data.totalOccupancy));
        
        // Print list of UUIDs with their state in the channel
        NSArray<PNPresenceUUIDData *> *uuids = result.data.channels[@"presence-demo"].uuids;
        NSLog(@"UUID states in channel:");
        for (PNPresenceUUIDData *uuidData in uuids) {
            NSLog(@"- UUID: %@", uuidData.uuid);
            
            if (uuidData.state) {
                NSLog(@"  State: %@", uuidData.state);
            } else {
                NSLog(@"  State: (none)");
            }
        }
    } else {
        NSLog(@"❌ Error getting Here Now with state: %@", status.errorData.information);
    }
}];

// Required PNEventsListener methods
- (void)client:(PubNub *)client didReceiveStatus:(PNStatus *)status {
    // Checking connectivity only using subscribe operation.
    if (status.operation != PNSubscribeOperation) return;
    
    // 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 didReceivePresenceEvent:(PNPresenceEventResult *)event {
    NSLog(@"👀 Presence event: %@ for UUID: %@ on channel: %@", 
          event.data.presenceEvent, 
          event.data.uuid, 
          event.data.channel);
    
    if (event.data.presence.state) {
        NSLog(@"   with state: %@", event.data.presence.state);
    }
}
```

### Response

Response objects which is returned by client when Here Now API for channel used:

```objectivec
@interface PNPresenceGlobalHereNowData : PNServiceData

// Active channels list.
@property (nonatomic, readonly, strong) NSDictionary$lt;NSString *, NSDictionary *> *channels;
// Total number of active channels.
@property (nonatomic, readonly, strong) NSNumber *totalChannels;
// Total number of subscribers.
@property (nonatomic, readonly, strong) NSNumber *totalOccupancy;

@end

@interface PNPresenceChannelGroupHereNowData : PNPresenceGlobalHereNowData

@end

@interface PNPresenceChannelGroupHereNowResult : PNResult

// Stores reference on channel group presence request processing information.
@property (nonatomic, nonnull, readonly, strong) PNPresenceChannelGroupHereNowData *data;

@end
```

### Other examples

#### Returning 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).
:::

```objectivec
/**
    With PNHereNowState client aside from occupancy and unique identifiers will will pull out
    state information associated with them.
    */
[self.client hereNowForChannel: @"my_channel" withVerbosity:PNHereNowState
                    completion:^(PNPresenceChannelHereNowResult *result,
                                    PNErrorStatus *status) {

    if (!status) {

        /**
            Handle downloaded presence information using:
            result.data.uuids - list of uuids. Each uuid entry will have next
                                fields: "uuid" - identifier and "state" if it
                                has been provided.
            result.data.occupancy - total number of active subscribers.
            */
    }
    else {

        /**
            Handle presence audit 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];
            */
    }
}];
```

##### Example response

```objectivec
{
    "status" : 200,
    "message" : "OK",
    "service" : "Presence",
    "uuids" : [
        {
            "uuid" : "myUUID0"
        },
        {
            "state" : {
                "abcd" : {
                    "age" : 15
                }
            },
            "uuid" : "myUUID1"
        },
        {
            "uuid" : "b9eb408c-bcec-4d34-b4c4-fabec057ad0d"
        },
        {
            "state" : {
                "abcd" : {
                    "age" : 15
                }
            },
            "uuid" : "myUUID2"
        },
        {
            "state" : {
                "abcd" : {
                    "age" : 24
                }
            },
            "uuid" : "myUUID9"
        }
    ],
    "occupancy" : 5
}
```

#### Return occupancy only

:::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).
:::

You can return only the `occupancy` information for a single channel by specifying the channel and setting `UUIDs` to false:

```objectivec
// With PNHereNowOccupancy client will pull out only occupancy information.
[self.client hereNowForChannel: @"my_channel" withVerbosity:PNHereNowOccupancy
                    completion:^(PNPresenceChannelHereNowResult *result,
                                    PNErrorStatus *status) {
    if (!status) {

        /**
            Handle downloaded presence information using:
            result.data.occupancy - total number of active subscribers.
            */
    }
    else {

        /**
            Handle presence audit 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];
            */
    }
}];
```

##### Example response

```objectivec
{
    "status": 200,
    "message": "OK",
    "payload": {
        "channels": {
            "81d8d989-b95f-443c-a726-04fac323b331": {
                "uuids": [ "70fc1140-22b5-4abc-85b2-ff8c17b24d59" ],
                "occupancy": 1
            },
            "81b7a746-d153-49e2-ab70-3037a75cec7e": {
                "uuids": [ "91363e7f-584b-49cc-822c-52c2c01e5928" ],
                "occupancy": 1
            },
            "c068d15e-772a-4bcd-aa27-f920b7a4eaa8": {
                "uuids": [ "ccfac1dd-b3a5-4afd-9fd9-db11aeb65395" ],
                "occupancy": 1
            }
        },
        "total_channels": 3,
        "total_occupancy": 3
    },
    "service": "Presence"
}
```

## Here now for channel groups

Request information about subscribers on specific channel group. This API method will retrieve the list of UUIDs along with their state for each remote data object and the number of subscribers.

### Method(s)

To do `Here Now for Channel Groups` you can use the following method(s) in the Objective-C SDK:

```objectivec
- (void)hereNowForChannelGroup:(NSString *)group 
                withCompletion:(PNChannelGroupHereNowCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `group` *Type: NSString | Reference on `channel` `group` for which here now information should be received. |
| `block` *Type: PNChannelGroupHereNowCompletionBlock | Here now processing completion `block` which pass two arguments: result - in case of successful request processing data field will contain results of here now operation; status - in case if `error` occurred during request processing. |

```objectivec
- (void)hereNowForChannelGroup:(NSString *)group 
                 withVerbosity:(PNHereNowVerbosityLevel)level 
                    completion:(PNChannelGroupHereNowCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `group` *Type: NSString | Reference on `channel` `group` for which here now information should be received. |
| `level` *Type: PNHereNowVerbosityLevel | Reference on one of `PNHereNowVerbosityLevel` fields to instruct what exactly data it expected in response. |
| `block` *Type: PNChannelGroupHereNowCompletionBlock | Here now processing completion `block` which pass two arguments: result - in case of successful request processing data field will contain results of here now operation; status - in case if `error` occurred during request processing. |

### Sample code

#### Here now for channel groups

```objectivec
[self.client hereNowForChannelGroup:@"my_group"
                      withCompletion:^(PNPresenceChannelGroupHereNowResult *result,
                                       PNErrorStatus *status) {

    if (!status) {

        /**
         Handle downloaded presence information using:
            result.data.channels - dictionary with active channels and presence information on
                                   each. Each channel will have next fields: "uuids" - list of
                                   subscribers; occupancy - number of active subscribers.
                                   Each uuids entry has next fields: "uuid" - identifier and
                                   "state" if it has been provided.
            result.data.totalChannels - total number of active channels.
            result.data.totalOccupancy - total number of active subscribers.
         */
    }
    else {

        /**
         Handle presence audit 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()
         */
    }
 }];
```

### Response

Response objects which is returned by client when Here Now API for channel group used:

```objectivec
@interface PNPresenceGlobalHereNowData : PNServiceData

// Active channels list.
@property (nonatomic, readonly, strong) NSDictionary$lt;NSString *, NSDictionary *> *channels;
// Total number of active channels.
@property (nonatomic, readonly, strong) NSNumber *totalChannels;
// Total number of subscribers.
@property (nonatomic, readonly, strong) NSNumber *totalOccupancy;

@end

@interface PNPresenceChannelGroupHereNowData : PNPresenceGlobalHereNowData

@end

@interface PNPresenceChannelGroupHereNowResult : PNResult

// Stores reference on channel group presence request processing information.
@property (nonatomic, nonnull, readonly, strong) PNPresenceChannelGroupHereNowData *data;

@end
```

## Where now

:::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).
:::

This method returns the list of channels a UUID is subscribed to.

:::note Timeout events
If the app restarts (or the page refreshes) within the heartbeat window, no timeout event is generated.
:::

### Method(s)

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

```objectivec
- (void)whereNowUUID:(NSString *)uuid 
      withCompletion:(PNWhereNowCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `uuid` *Type: NSString | Reference on `UUID` for which request should be performed. |
| `block` *Type: PNWhereNowCompletionBlock | Where now processing completion `block` which pass two arguments: result - in case of successful request processing data field will contain results of where now operation; status - in case if error occurred during request processing. |

### Sample code

You simply need to define the `uuid` and the `callback` function to be used to send the data to as in the example below.

#### Get a list of channels a UUID is subscribed to

```objectivec
[self.client whereNowUUID:self.client.uuid withCompletion:^(PNPresenceWhereNowResult *result,
                                                            PNErrorStatus *status) {

    if (!status) {

        // Handle downloaded presence 'where now' information using: result.data.channels
    }
    else {

        /**
         Handle presence audit 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];
         */
    }
 }];
```

### Response

Response objects which is returned by client when Where Now API is used:

```objectivec
@interface PNPresenceWhereNowData : PNServiceData

// List of channels on which client subscribed.
@property (nonatomic, readonly, strong) NSArray<NSString *> *channels;

@end

@interface PNPresenceWhereNowResult : PNResult

// Stores reference on client presence request processing information.
@property (nonatomic, readonly, strong) PNPresenceWhereNowData *data;

@end
```

## User 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).
:::

Clients can set a dynamic custom state (score, game state, location) for their users on one or more channels and store it on a channel as long as the user stays subscribed.

The state is not persisted, and when the client disconnects, the state data is lost. For more information, refer to [Presence State](https://www.pubnub.com/docs/general/presence/presence-state).

### Method(s)

#### Set state

```objectivec
- (void)setState:(nullable NSDictionary<NSString *, id> *)state 
         forUUID:(NSString *)uuid 
       onChannel:(NSString *)channel 
  withCompletion:(nullable PNSetStateCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `state`Type: NSDictionary | Reference on dictionary which should be bound to `uuid` on specified channel.`If value is nil, state will be removed for` uuid `on` channel. |
| `uuid` *Type: NSString | Reference on unique user identifier for which `state` should be bound. |
| `channel` *Type: NSString | Name of the `channel` which will store provided state information for `uuid`. |
| `block`Type: PNSetStateCompletionBlock | State modification for user on `channel` processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

```objectivec
- (void)setState:(nullable NSDictionary<NSString *, id> *)state 
           forUUID:(NSString *)uuid
    onChannelGroup:(NSString *)group
    withCompletion:(nullable PNSetStateCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `state`Type: NSDictionary | Reference on dictionary which should be bound to `uuid` on specified channel group.`If value is nil, state will be removed for` uuid `on` group. |
| `uuid` *Type: NSString | Reference on unique user identifier for which `state` should be bound. |
| `group` *Type: NSString | Name of channel group which will store provided state information for `uuid`. |
| `block`Type: PNSetStateCompletionBlock | State modification for user on `channel` processing completion `block` which pass only one argument - request processing status to report about how data pushing was successful or not. |

#### Get state

```objectivec
- (void)stateForUUID:(NSString *)uuid
           onChannel:(NSString *)channel
      withCompletion:(PNChannelStateCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `uuid` *Type: NSString | Reference on unique user identifier for which state should be retrieved. |
| `channel` *Type: NSString | Name of channel from which state information for `uuid` will be pulled out. |
| `block` *Type: PNChannelStateCompletionBlock | State audition for user on `cahnnel` processing completion `block` which pass two arguments: result - in case of successful request processing data field will contain results of client state retrieve operation; status - in case if `error` occurred during request processing. |

```objectivec
- (void)stateForUUID:(NSString *)uuid
      onChannelGroup:(NSString *)group
      withCompletion:(PNChannelGroupStateCompletionBlock)block;
```

| Parameter | Description |
| --- | --- |
| `uuid` *Type: NSString | Reference on unique user identifier for which state should be retrieved. |
| `group` *Type: NSString | Name of `channel` `group` from which state information for `uuid` will be pulled out. |
| `block` *Type: PNChannelGroupStateCompletionBlock | State audition for user on `cahnnel` `group` processing completion `block` which pass two arguments: result - in case of successful request processing data field will contain results of client state retrieve operation; status - in case if error occurred during request processing. |

### Sample code

#### Set state

```objectivec
[self.client setState: @{@"Key": @"Value"} 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];
         */
    }
}];
```

#### Get state

```objectivec
[self.client stateForUUID:self.client.uuid onChannel:@"chat"
            withCompletion:^(PNChannelClientStateResult *result, PNErrorStatus *status) {

    if (!status) {

        // Handle downloaded state information using: result.data.state
    }
    else{

        /**
         Handle client state audit 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];
         */
    }
 }];
```

### Response

#### Set state

Response objects which is returned by client when Set State API for channels used:

```objectivec
@interface PNClientStateUpdateData : PNChannelClientStateData

@end

@interface PNClientStateUpdateStatus : PNErrorStatus

// Stores reference on client state for channel request processing information.
@property (nonatomic, nonnull, readonly, strong) PNClientStateUpdateData *data;

@end
```

#### Get state

Response objects which is returned by client when Get State API for channel used:

```objectivec
@interface PNChannelClientStateData : PNServiceData

// User-provided client state information.
@property (nonatomic, readonly, strong) NSDictionary<NSString *, id> *state;

@end

@interface PNChannelClientStateResult : PNResult

// Stores reference on client state for channel request processing information.
@property (nonatomic, readonly, strong) PNChannelClientStateData *data;

@end
```

Response objects which is returned by client when Get State API for channel group used:

```objectivec
@interface PNChannelGroupClientStateData : PNServiceData

// Multi channel client state information.
@property (nonatomic, readonly, strong) NSDictionary<NSString *, NSDictionary *> *channels;

@end

@interface PNChannelGroupClientStateResult : PNResult

// Stores reference on client state for channel group request processing information.
@property (nonatomic, readonly, strong) PNChannelGroupClientStateData *data;

@end
```

## User state (builder pattern)

:::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).
:::

The state API is used to set/get key/value pairs specific to a subscriber `uuid`.

State information is supplied as a JSON object of key/value pairs.

### Method(s)

#### Set state

```objectivec
state()
    .set()
    .uuid(NSString *)
    .state(NSDictionary *)
    .channels(NSArray<NSString *> *)
    .channelGroups(NSArray<NSString *> *)
    .performWithCompletion(PNSetStateCompletionBlock);
```

| Parameter | Description |
| --- | --- |
| `uuid`Type: NSString | Unique user identifier for which state should be bound. Current `PubNub` user ID will be used by default if not set or set to `nil`. |
| `state`Type: NSDictionary | Data which should be bound to specified `uuid` on `channels` / `channelGroups`. |
| `channels`Type: `NSArray<NSString *>` | List of the channel names which will store provided `state` information for `uuid`. Not required if `channelGroups` is set. |
| `channelGroups`Type: `NSArray<NSString *>` | List of channel group names which will store provided `state` information for `uuid`. Not required if `channels` is set. |
| `completion`Type: PNSetStateCompletionBlock | State modification for user on channel / channel group completion block which pass only one argument - request status reports state change was successful or not (`errorData` contains error information in case of failure). |

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

#### Get state

```objectivec
state()
    .audit()
    .uuid(NSString *)
    .channels(NSArray<NSString *> *)
    .channelGroups(NSArray<NSString *> *)
    .performWithCompletion(PNGetStateCompletionBlock);
```

| Parameter | Description |
| --- | --- |
| `uuid`Type: NSString | Unique user identifier for which state should be retrieved. Current `PubNub` user ID will be used by default if not set or set to `nil`. |
| `channels`Type: `NSArray<NSString *>` | List of the channel names from which state information for `uuid` will be pulled out. Not required if `channelGroups` is set. |
| `channelGroups`Type: `NSArray<NSString *>` | List of channel group names from which state information for `uuid` will be pulled out. Not required if `channels` is set. |
| `completion` *Type: PNGetStateCompletionBlock | State audition for user on channel / channel group completion block which pass two arguments: result - in case of successful request processing data field will contain results of client state retrieve operation; status - in case of error occurred during request processing. |

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

### Sample code

#### Set state

```objectivec
self.client.state().set().state(@{@"state": @"test"})
    .channels(@[@"channel1", @"channel12"])
    .channelGroups(@[@"group1", @"group2"])
    .performWithCompletion(^(PNClientStateUpdateStatus *status) {

        if (!status.isError) {
            // Client state successfully modified on specified channels and groups.
        } 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]
            */
        }
    });
```

#### Get state

```objectivec
self.client.state().audit().uuid(@"PubNub")
    .channels(@[@"channel1", @"channel12"])
    .channelGroups(@[@"group1", @"group2"])
    .performWithCompletion(^(PNClientStateGetResult *result, PNErrorStatus *status) {

        if (!status.isError) {
            // Handle downloaded state information using: result.data.channels
        } else {
            /**
             Handle client state audit 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]
            */
        }
    });
```