---
source_url: https://www.pubnub.com/docs/sdks/cocoa-swift
title: Cocoa Swift API & SDK Docs 10.1.6
updated_at: 2026-05-22T11:06:23.814Z
---

> 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


# Cocoa Swift API & SDK Docs 10.1.6

:::warning Unsupported SDK
PubNub no longer supports this SDK, but you are [welcome to contribute](https://github.com/pubnub/objective-c).
:::

This SDK has been replaced by a new PubNub Swift SDK written purely in Swift. See the [PubNub Swift SDK documentation](https://www.pubnub.com/docs/sdks/swift).

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

## Get code: CocoaPods

[CocoaPods](https://guides.cocoapods.org/using/getting-started.html) is a dependency manager and this is by far the easiest and quickest way to get started with PubNub SDK! (If you don't have pods installed yet you can check [CocoaPods installation](https://guides.cocoapods.org/using/getting-started.html#installation) section for installation guide).

Be sure you are running CocoaPods 1.0.0 or above! You can install the latest cocopods gem:

```bash
gem install cocoapods
```

If you've already installed you can upgrade to the latest CocoaPods gem using:

```bash
gem update cocoapods
```

## Get code: framework

The PubNub framework project allows you to build standalone framework bundles which can be copied right into your application.

To build the framework as a standalone bundle and integrate into project, perform the following:

1. Clone the PubNub master repository 1git clone git@github.com:pubnub/objective-c.git
2. Navigate to root of the cloned repository
3. Run the CocoaPods pod install command to pull out all required dependencies 1pod install
4. Open the PubNub.xcworkspace from the PubNub repository in Xcode.
5. Select the Framework or Universal Framework target for target platform and hit Cmd+B to build the selected type of framework.
6. Navigate to the Framework directory and find the Products directory.
7. Drag&Drop PubNub.framework bundle from the Products directory to your application.
8. Select the Copy items if needed checkbox and click Finish.
9. Open your project's General tab and scroll to Embedded Binaries.
10. Click on + and select PubNub.framework file.

:::note PubNub-Universal
If the `PubNub` target has been used, then the framework will be generated only for the selected platform (device or simulator.) If you try to use the framework to compile for another platform, it will crash during run-time Using the `PubNub-Universal` build target (which can be used on both device and simulator) will help mitigate any sort of crash scenarios during development.
:::

Now that these steps are complete, let's learn more about how to use the framework in your app.

### Using the framework with your app

At this point, you should have the framework added to your application project, we'll need to make your project aware of the PubNub framework.

You need to import the PubNub module in files where it will be used.

```swift
import PubNubSDK
```

## Get code: Carthage

To build the framework as a standalone bundle and integrate into project, perform the following:

1. Install latest Carthage (if required).
2. Create a Cartfile any location can be used) or use existing file. 1touch Cartfile
3. Add next line into a Cartfile which will allow to build framework bundles. 1github "pubnub/objective-c" ~> 4.1
4. Update and rebuild the project's dependencies (update command ensure what latest PubNub client code will be used to build framework). build can be used if frameworks has been built before and it will be much faster because git clone will be skipped. 1carthage update Command above will build framework for all configured platforms, but if only one required it can be called like this 1carthage update --platform ios Instead of ios can be used: mac, tvos or watchos
5. Navigate to the Carthage directory and find the Build directory.
6. Navigate to directory which represent your target platform. For example: iOS
7. Drag&Drop PubNub.framework bundle from the Products directory to your application.
8. Select the Copy items if needed checkbox and click Finish.
9. Open your project's General tab and scroll to Embedded Binaries.
10. Click on + and select PubNub.framework file.

### Using the framework with your app

At this point, you should have the framework added to your application project, we'll need to make your project aware of the PubNub framework.

You need to import the PubNub module in files where it will be used.

```swift
import PubNubSDK
```

## Get code: source

[https://github.com/pubnub/objective-c/](https://github.com/pubnub/objective-c)

[View Supported Platforms](https://www.pubnub.com/docs/sdks/cocoa-swift/platform-support)

## Hello World

To include PubNub SDK in your project you need to use CocoaPods.

Install the CocoaPods gem by following the procedure defined above. To add the PubNub SDK to your project with CocoaPods, there are four tasks to complete:

1. Create new Xcode project.
2. Create a Podfile in your newly created Xcode project root folder 1touch Podfile
3. The PubNub client can be added as module (only with a deployment target of OS X 10.9 and above) 1source 'https://github.com/CocoaPods/Specs.git'2platform :osx, "10.9"3use_frameworks!4 5target 'application-target-name' do6 pod "PubNub", "~> 4.1"7end If you have any other pods you'd like to include, or if you have other targets you'd to add (like a test target) add those entries to this Podfile as well. See the CocoaPods documentation for more information on Podfile configuration.
4. Install your pods by running pod install via the command line from the directory that contains your Podfile.

:::note Work within the workspace
After installing your Pods, you should only be working within the workspace generated by CocoaPods or specified by you in Podfile. Always open the newly generated workspace file, not the original project file!
:::

**OS X 10.9 and above**: Complete the application delegate configuration

If the project's deployment target is set to OS X 10.9 and above, you will need to import the module named `AppDelegate.swift`:

:::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. Not setting the `UUID` can significantly impact your billing if your account uses the [Monthly Active Users (MAUs)](https://www.pubnub.com/pricing/) based pricing model, and can also lead to unexpected behavior if you have Presence enabled.
:::

```swift
import Cocoa
import PubNubSDK // <- Here is our PubNub module import.
```

**Common to all OS X versions**: Add the `PNObjectEventListener` protocol to the AppDelegate

```swift
class AppDelegate: NSObject, NSApplicationDelegate, PNObjectEventListener {

    // Stores reference on PubNub client to make sure what it won't be released.
    var client: PubNub!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        ...
    }
}
```

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

```swift
// Initialize and configure PubNub client instance
let configuration = PNConfiguration(publishKey: "demo", subscribeKey: "demo")
self.client = PubNub.clientWithConfiguration(configuration)
self.client.addListener(self)

// Subscribe to demo channel with presence observation
self.client.subscribeToChannels(["my_channel"], withPresence: true)

// Handle new message from one of channels on which client has been subscribed.
func client(_ client: PubNub, didReceiveMessage message: PNMessageResult) {

    // Handle new message stored in message.data.message
    if message.data.channel != 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.
    }

    print("Received message: \(message.data.message) on channel \(message.data.channel) " +
          "at \(message.data.timetoken)")
}

// New presence event handling.
func client(_ client: PubNub, didReceivePresenceEvent event: PNPresenceEventResult) {

    // Handle presence event event.data.presenceEvent (one of: join, leave, timeout, state-change).
    if event.data.channel != 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 != "state-change" {

        print("\(event.data.presence.uuid) \"\(event.data.presenceEvent)'ed\"\n" +
              "at: \(event.data.presence.timetoken) on \(event.data.channel) " +
              "(Occupancy: \(event.data.presence.occupancy))");
    }
    else {

        print("\(event.data.presence.uuid) changed state at: " +
              "\(event.data.presence.timetoken) on \(event.data.channel) to:\n" +
              "\(event.data.presence.state)");
    }
}

// Handle subscription status change.
func client(_ client: PubNub, didReceive status: PNStatus) {

    if status.operation == .subscribeOperation {

        // Check whether received information about successful subscription or restore.
        if status.category == .PNConnectedCategory || status.category == .PNReconnectedCategory {

            let subscribeStatus: PNSubscribeStatus = status as! PNSubscribeStatus
            if subscribeStatus.category == .PNConnectedCategory {

                // This is expected for a subscribe, this means there is no error or issue whatsoever.

                // Select last object from list of channels and send message to it.
                let targetChannel = client.channels().last!
                client.publish("Hello from the PubNub Swift SDK", toChannel: targetChannel,
                               compressed: false, withCompletion: { (publishStatus) -> Void in

                    if !publishStatus.isError {

                        // Message successfully published to specified channel.
                    }
                    else {

                        /**
                         Handle message publish error. Check 'category' property to find out
                         possible reason because of which request did fail.
                         Review 'errorData' property (which has PNErrorData data type) of status
                         object to get additional information about issue.

                         Request can be resent using: publishStatus.retry()
                         */
                    }
                })
            }
            else {

                /**
                 This usually occurs if subscribe temporarily fails but reconnects. This means there was
                 an error but there is no longer any issue.
                 */
            }
        }
        else if status.category == .PNUnexpectedDisconnectCategory {

            /**
             This is usually an issue with the internet connection, this is an error, handle
             appropriately retry will be called automatically.
             */
        }
        // Looks like some kind of issues happened while client tried to subscribe or disconnected from
        // network.
        else {

            let errorStatus: PNErrorStatus = status as! PNErrorStatus
            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`
                 */
            }
        }
    }
}
```

## Copy and paste examples

In addition to the Hello World sample code, we also provide some copy and paste snippets of common API functions:

### Init

Instantiate a new Pubnub instance. Only the `subscribeKey` is mandatory. Also include `publishKey` if you intend to publish from this instance, and the `secret_key` if you wish to perform Access Manager administrative operations from this Swift instance.

:::note Secure your secretKey
It is not a best practice to include the secret key in client-side code for security reasons.
When you init with `secret_key`, you get root permissions for the Access Manager. With this feature you don't have to grant access to your servers to access channel data. The servers get all access on all channels.
:::

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

```swift
let configuration = PNConfiguration(publishKey: "demo", subscribeKey: "demo")
self.client = PubNub.clientWithConfiguration(configuration)
```

:::note PubNub instance as variable
The PubNub instance should be placed as a variable in a long-life object (otherwise the PubNub instance will be automatically removed as an autoreleased object.)
```swift
var client: PubNub!
```
:::

### Listeners

#### Add listeners

```swift
/**
 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: false)

// Handle a new message from a subscribed channel
func client(_ client: PubNub, didReceiveMessage message: PNMessageResult) {
    // Reference to the channel group containing the chat the message was sent to
    let subscription = message.data.subscription
    print("\(message.data.publisher) sent message to '\(message.data.channel)' at
           \(message.data.timetoken): \(message.data.message)")
}

// Handle a subscription status change
func client(_ client: PubNub, didReceive status: PNStatus) {

    if status.operation == .subscribeOperation {

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

            let subscribeStatus: PNSubscribeStatus = status as! PNSubscribeStatus
            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.
        }
            // Looks like some kind of issues happened while the client tried to subscribe,
            // or disconnected from the network.
        else {

            let errorStatus: PNErrorStatus = status as! PNErrorStatus
            if errorStatus.category == .PNAccessDeniedCategory {

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

                /**
                 More errors can be directly specified by creating explicit cases for other categories
                 of `PNStatusCategory` errors, such as:
                 - `PNDecryptionErrorCategory`
                 - `PNMalformedFilterExpressionCategory`
                 - `PNMalformedResponseCategory`
                 - `PNTimeoutCategory`
                 - `PNNetworkIssuesCategory`
                 */
            }
        }
    }
    else if status.operation == .unsubscribeOperation {

        if status.category == .PNDisconnectedCategory {

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

        /**
         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, consult http://www.pubnub.com/docs/swift/api-reference/configuration#configuration_basic_usage
         */

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

// Handle a new presence event
func client(_ client: PubNub, didReceivePresenceEvent event: PNPresenceEventResult) {

    // Handle presence event event.data.presenceEvent (one of: join, leave, timeout, state-change).
    if event.data.channel != event.data.subscription {

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

        // Presence event received on a channel stored in event.data.channel
    }

    if event.data.presenceEvent != "state-change" {

        print("\(event.data.presence.uuid) \"\(event.data.presenceEvent)'ed\"\n" +
              "at: \(event.data.presence.timetoken) on \(event.data.channel) " +
              "(Occupancy: \(event.data.presence.occupancy))");
    }
    else {

        print("\(event.data.presence.uuid) changed state at: " +
              "\(event.data.presence.timetoken) on \(event.data.channel) to:\n" +
              "\(event.data.presence.state)");
    }
}

// Handle a new signal from a subscribed channel
func client(_ client: PubNub, didReceiveSignal signal: PNSignalResult) {
    print("\(signal.data.publisher) sent signal to '\(signal.data.channel)' at
           \(signal.data.timetoken): \(signal.data.message)")
}

// Handle a new user event (update or delete) from a subscribed user channel
func client(_ client: PubNub, didReceiveUserEvent event: PNUserEventResult) {
    print("'\(event.data.identifier)' user has been \(event.data.event)'ed at
           \(event.data.timestamp)")
}

// Handle a new space event (update or delete) from a subscribed space channel
func client(_ client: PubNub, didReceiveSpaceEvent event: PNSpaceEventResult) {
    print("'\(event.data.identifier)' space has been \(event.data.event)'ed at
           \(event.data.timestamp)")
}

// Handle a new membership event from a subscribed user or space channel
func client(_ client: PubNub, didReceiveMembershipEvent event: PNMembershipEventResult) {
    print("Membership between '\(event.data.userId)' user '\(event.data.spaceId)' space has been
           \(event.data.event)'ed at \(event.data.timestamp)")
}

// Handle message reactions (added or removed) from one of channels on which client has been subscribed.
func client(_ client: PubNub, didReceiveMessageAction action: PNUserEventResult) {
    print("'\(action.type)' action with '\(action.value)' value has been \(event.data.event) by \(action.uuid) at
           \(action.actionTimetoken) to message with \(action.messageTimetoken) timetoken")
}
```

#### Listener status events

This is the list of categories which can be received by an event listener:

| Category | Description |
| --- | --- |
| `PNAccessDeniedCategory` | Subscribe request failed because of access error (active Access Manager). `status.errorData.channels` or `status.errorData.channelGroups` contain list of channels and / or groups to which user with specified `auth` key doesn't have access. |
| `PNDecryptionErrorCategory` | Subscribe request returned messages which can't be decrypted with configured `cipherKey`. Unencrypted message will be returned in `status.associatedObject` where `associatedObject` is `PNMessageData` which contain channel and message properties. |
| `PNNetworkIssuesCategory` | Subscribe request failed because there was no network connection at the moment when request has been sent. |
| `PNConnectedCategory` | Subscribe request successfully completed and client connected to real-time data channels. |
| `PNReconnectedCategory` | Subscribe request successfully completed and client has been reconnected to real-time data channels after unexpected disconnection (`PNUnexpectedDisconnectCategory`). |
| `PNDisconnectedCategory` | Client did unsubscribe from specified real-time data channels. |
| `PNUnexpectedDisconnectCategory` | Previously started subscribe loop did fail and at this moment client disconnected from real-time data channels. |
| `PNRequestMessageCountExceededCategory` | If *requestMessageCountThreshold* is set, this status event will arrive each time when client receive more messages when it has been specified for `PNConfiguration` property. |
| `PNMalformedFilterExpressionCategory` | Subscription request can't be processed by PubNub service because filter expression malformed and can't be evaluated. |
| `PNHeartbeatOperation` | In case if presence heartbeat value is set, client will sent this status category at specified periods. If `status.isError` set to **YES**, it mean what heartbeat request did fail (potentially because of network issues). |

This is list of categories which can be received in API completion blocks (non-subscribe):

| Category | Description |
| --- | --- |
| `PNAcknowledgmentCategory` | An API call was successful. This status has additional details based on the type of the successful operation. |
| `PNAccessDeniedCategory` | Request failed because of access error (active Access Manager). `status.errorData.channels` or `status.errorData.channelGroups` contain list of channels and / or groups to which user with specified `auth` key doesn't have access. |
| `PNTimeoutCategory` | Used API didn't received response from server in time. |
| `PNNetworkIssuesCategory` | API did fail because there was no network connection at the moment when request has been sent. |
| `PNMalformedResponseCategory` | Request received in response non-JSON data. It can be because of publish WiFi hotspot which require authorization or proxy server message. |
| `PNBadRequestCategory` | Request can't be completed because not all required values has been passed or passed values has unexpected data type. |
| `PNDecryptionErrorCategory` | Message Persistence API may return this status category in case if some messages can't be decrypted. Unencrypted message will be returned in `status.associatedObject` where `associatedObject` is `PNMessageData` which contain channel and message properties. |
| `PNTLSConnectionFailedCategory` | TLS handshake issues and in most cases because of poor connection quality and packets loss and delays. |
| `PNTLSUntrustedCertificateCategory` | Origin to which client tried to connect has untrusted certificate. |

### Subscribe

Subscribe (listen on) a channel (it's async!):

```swift
/**
 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: false)

// Handle a new message from a subscribed channel
func client(_ client: PubNub, didReceiveMessage message: PNMessageResult) {
    // Reference to the channel group containing the chat the message was sent to
    let subscription = message.data.subscription
    print("\(message.data.publisher) sent message to '\(message.data.channel)' at
           \(message.data.timetoken): \(message.data.message)")
}

// Handle a subscription status change
func client(_ client: PubNub, didReceive status: PNStatus) {

    if status.operation == .subscribeOperation {

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

            let subscribeStatus: PNSubscribeStatus = status as! PNSubscribeStatus
            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.
        }
            // Looks like some kind of issues happened while the client tried to subscribe,
            // or disconnected from the network.
        else {

            let errorStatus: PNErrorStatus = status as! PNErrorStatus
            if errorStatus.category == .PNAccessDeniedCategory {

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

                /**
                 More errors can be directly specified by creating explicit cases for other categories
                 of `PNStatusCategory` errors, such as:
                 - `PNDecryptionErrorCategory`
                 - `PNMalformedFilterExpressionCategory`
                 - `PNMalformedResponseCategory`
                 - `PNTimeoutCategory`
                 - `PNNetworkIssuesCategory`
                 */
            }
        }
    }
    else if status.operation == .unsubscribeOperation {

        if status.category == .PNDisconnectedCategory {

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

        /**
         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, consult http://www.pubnub.com/docs/swift/api-reference/configuration#configuration_basic_usage
         */

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

// Handle a new signal from a subscribed channel
func client(_ client: PubNub, didReceiveSignal signal: PNSignalResult) {
    print("\(signal.data.publisher) sent signal to '\(signal.data.channel)' at
           \(signal.data.timetoken): \(signal.data.message)")
}

// Handle a new user event (update or delete) from a subscribed user channel
func client(_ client: PubNub, didReceiveUserEvent event: PNUserEventResult) {
    print("'\(event.data.identifier)' user has been \(event.data.event)'ed at
           \(event.data.timestamp)")
}
```

:::note Event listeners
The response of the call is handled by adding a Listener. Please see the [Listeners section](#listeners) for more details. Listeners should be added before calling the method.
:::

### Publish

Publish a message to a channel:

```swift
self.client.publish("Hello from the PubNub Swift SDK", toChannel: "my_channel",
                    compressed: false, withCompletion: { (status) in

    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()
         */
    }
})
```

### Here now

Get occupancy of who's `here now` on the channel by UUID:

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

```swift
// With .UUID client will pull out list of unique identifiers and occupancy information.
self.client.hereNowForChannel("my_channel", withVerbosity: .UUID,
                              completion: { (result, status) in

    if status == nil {

        /**
         Handle downloaded presence information using:
            result.data.uuids - list of uuids.
            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()
         */
    }
})
```

### Presence

Subscribe to real-time Presence events, such as `join`, `leave`, and `timeout`, by UUID. Setting the presence attribute to a callback will subscribe to presents events on `my_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/).
:::

```swift
/**
 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.
func client(_ client: PubNub, didReceivePresenceEvent event: PNPresenceEventResult) {

    // Handle presence event event.data.presenceEvent (one of: join, leave, timeout, state-change).
    if event.data.channel != 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 != "state-change" {

        print("\(event.data.presence.uuid) \"\(event.data.presenceEvent)'ed\"\n" +
              "at: \(event.data.presence.timetoken) on \(event.data.channel) " +
              "(Occupancy: \(event.data.presence.occupancy))");
    }
    else {

        print("\(event.data.presence.uuid) changed state at: " +
              "\(event.data.presence.timetoken) on \(event.data.channel) to:\n" +
              "\(event.data.presence.state)");
    }
}
```

:::note Event listeners
The response of the call is handled by adding a Listener. Please see the [Listeners section](#listeners) for more details. Listeners should be added before calling the method.
:::

### History

Retrieve published messages from archival storage:

:::warning Requires Message Persistence
This method requires that Message Persistence 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/).
:::

```swift
self.client.historyForChannel("history_channel", withCompletion: { (result, status) in

    if status == nil {

        /**
         Handle downloaded history using:
            result.data.start - oldest message time stamp in response
            result.data.end - newest message time stamp in response
            result.data.messages - list of messages
         */
    }
    else {

        /**
         Handle message history download 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()
         */
    }
})
```

### Unsubscribe

Stop subscribing (listening) to a channel.

```swift
/**
 Subscription 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: false)

// Handle subscription status change.
func client(_ client: PubNub, didReceive status: PNStatus) {

    if status.operation == .unsubscribeOperation && status.category == .PNDisconnectedCategory {

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

:::note Event listeners
The response of the call is handled by adding a Listener. Please see the [Listeners section](#listeners) for more details. Listeners should be added before calling the method.
:::