---
description: >-
  Detailed technical documentation on using RudderStack’s iOS SDK to send events
  from your iOS apps to various destinations.
---

# iOS

The **RudderStack iOS SDK** lets you track your customer event data from your iOS applications and send it to your specified destinations via RudderStack.

Check out the [**GitHub codebase**](https://github.com/rudderlabs/rudder-sdk-ios) to get a more hands-on understanding of the SDK.

Starting from version **1.1.0**, the iOS SDK supports the **tvOS** platform. RudderStack also supports the **watchOS** platform starting from version **1.3.1**. You can now integrate the iOS SDK with your tvOS and watchOS apps and seamlessly track user events without the need for any additional configuration.

[![Github Badge](https://img.shields.io/cocoapods/v/Rudder.svg?style=flat?style=flat)](https://cocoapods.org/pods/Rudder)

## SDK setup requirements

To set up the RudderStack iOS SDK, the following prerequisites must be met:

* You will need to set up a [**RudderStack account**](https://app.rudderstack.com).
* Once signed up, set up an iOS source in the dashboard. For more information, follow [**this guide**](https://www.rudderstack.com/docs/rudderstack-cloud/sources/#adding-a-source). You should then see a **Write Key** for this source, as shown below:

![iOS source write key](https://876606571-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lq5Ea6fHVg3dSxMCgyQ%2Fuploads%2Fgit-blob-7962fb705c8a66997a5f59a155403dae17e1ba67%2Fios-sdk-1.png?alt=media)

* You will also need a data plane URL. Follow [**this section**](https://rudderstack.com/docs/rudderstack-open-source/installing-and-setting-up-rudderstack/#what-is-a-data-plane-url-where-do-i-get-it) for more information on the data plane URL and where to get it.
* Finally, you will need a Mac with the latest version of [**Xcode**](https://developer.apple.com/xcode/).

## Installing the RudderStack iOS SDK

We distribute our iOS SDK through [**Cocoapods**](https://cocoapods.org/pods/Rudder) and [**Carthage**](https://github.com/Carthage/Carthage). The recommended and easiest way to add the SDK to your project is through `Podfile`. Follow these steps:

CocoaPods Carthage

*   Add the RudderStack SDK to your `Podfile`, as shown:

    ```ruby
    pod 'Rudder'
    ```
*   Then, run the following command:

    ```bash
    pod install
    ```
*   Add the RudderStack SDK to your `Cartfile`, as shown:

    ```ruby
    github "rudderlabs/rudder-sdk-ios"
    ```
*   Then, run the following command:

    ```bash
    carthage update
    ```

Remember to include the following code in all `.m` and `.h` files or your `.swift` files where you want to refer to or use RudderStack SDK classes.

Objective-C Swift

```objectivec
#import <Rudder/Rudder.h>
```

```swift
import Rudder
```

RudderStack uses [SQLite](https://sqlite.org/index.html) to store the events before sending them to RudderStack data plane. Making calls like `SQLite.shutdown()` which is not thread-safe might lead to unexpected crash.

## Initializing the RudderStack client

Put this code in your `AppDelegate.m` file under the method `didFinishLaunchingWithOptions`:

Objective-C Swift

```objectivec
RSConfigBuilder *builder = [[RSConfigBuilder alloc] init];
[builder withDataPlaneUrl:DATA_PLANE_URL];
[RSClient getInstance:WRITE_KEY config:[builder build]];
```

A shared instance of `RSClient` is accessible after the initialization by `[RSClient sharedInstance]`.

```swift
let builder: RSConfigBuilder = RSConfigBuilder()
    .withDataPlaneUrl(DATA_PLANE_URL)
RSClient.getInstance(WRITE_KEY, config: builder.build())
```

A shared instance of `RSClient` is accesible after the initialization by `RSClient.sharedInstance()`

We automatically track the following optional events:

1. `Application Installed`
2. `Application Updated`
3. `Application Opened`
4. `Application Backgrounded`

You can disable these events using the `withTrackLifecycleEvents` method of `RSConfigBuilder` and passing `false`. However, it is highly recommended to keep them enabled.

RudderStack supports all the major API calls across all iOS devices via the SDK. These include the `track`, `identify`, and `screen` calls.

## Enabling/disabling user tracking via the optOut API (GDPR support)

RudderStack gives the users (e.g., an EU user) the ability to opt out of tracking any user activity until the user gives their consent. You can do this by leveraging RudderStack's `optOut` API.

The `optOut` API takes `YES` or `NO` as a Boolean value to enable or disable tracking user activities. This flag persists across device reboots.

The following snippet highlights the use of the `optOut` API to disable user tracking:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] optOut:YES];
```

```swift
RSClient.sharedInstance()?.optOut(true)
```

Once the user grants their consent, you can enable user tracking once again by using the `optOut` API with `NO` or `false` as a parameter sent to it, as shown:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] optOut:NO];
```

```swift
RSClient.sharedInstance()?.optOut(false)
```

The `optOut` API is available in the RudderStack iOS SDK starting from version `1.0.24`.

## Chromecast

[**Google Chromecast**](https://store.google.com/in/product/chromecast?hl=en-GB) is a device that plugs into your TV or monitor with an HDMI port, and can be used to stream content from your phone or computer.

RudderStack supports integrating the iOS SDK with your Cast app. Follow [these instructions](https://developers.google.com/cast/docs/ios_sender) to build your iOS sender app. Then, add the iOS SDK to it.

Follow the [Google Cast developer guide](https://developers.google.com/cast/docs/developers) for more details.

## Track

You can record the users' activity through the `track` method. Every user action is called as an **event**.

A sample `track` event is as shown:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] track:@"simple_track_with_props" properties:@{
    @"key_1" : @"value_1",
    @"key_2" : @"value_2"
}];
```

```swift
RSClient.sharedInstance()?.track("test_user_id", properties: [
    "key_1": "value_1",
    "key_2": "value_2"
])
```

The `track` method accepts the following parameters:

| Name         | Data Type      | Required | Description                                                 |
| ------------ | -------------- | -------- | ----------------------------------------------------------- |
| `eventName`  | `NSString`     | Yes      | Name of the event you want to track                         |
| `properties` | `NSDictionary` | No       | Extra data properties you want to send along with the event |
| `options`    | `RudderOption` | No       | Extra event options                                         |

## Identify

We capture `deviceId` and use that as `anonymousId` for identifying the user. This helps in tracking the users across the application installation.

To attach more information to the user, you can use the `identify` method. Once you identify the user, the SDK persists all the user information and passes it to the subsequent `track` or `screen` calls. To reset the user identification, you can use the `reset` method.

According to the Apple [documentation](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor), if the device has multiple apps from the same vendors, all those apps will be assigned the same `deviceId`. If all the applications from a vendor are uninstalled, then on next install the app will be assigned a new `deviceId`.

An example `identify` event is as shown:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] identify:@"test_user_id"
traits:@&#123;@"foo": @"bar",
        @"foo1": @"bar1",
        @"email": @"test@gmail.com",
        @"key_1" : @"value_1",
        @"key_2" : @"value_2"
&#125;
];
```

```swift
RSClient.sharedInstance()?.identify("test_user_id", traits: [
    "key_1": "value_1",
    "key_2": "value_2",
    "email": "test@gmail.com"
])
```

The `identify` method accepts the following parameters:

| Name      | Data Type      | Required | Description                                                                                          |
| --------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `userId`  | `NSString`     | Yes      | Developer identity for the user.                                                                     |
| `traits`  | `NSDictionary` | No       | Traits information for user. Use `dict` method of `RudderTraits` to convert to `NSDictionary` easily |
| `options` | `RudderOption` | No       | Extra options for the `identify` event.                                                              |

## Screen

You can use the `screen` call to record whenever the user sees a screen on the mobile device. You can also send some extra properties along with this event.

An example of the `screen` event is as shown:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] screen:@"ViewController"];
```

```swift
RSClient.sharedInstance()?.screen("ViewController")
```

The `screen` method accepts the following parameters:

| Name         | Data Type      | Required | Description                                                               |
| ------------ | -------------- | -------- | ------------------------------------------------------------------------- |
| `screenName` | `NSString`     | Yes      | Name of the screen viewed by the user.                                    |
| `properties` | `NSDictionary` | No       | Extra property object that you want to pass along with the `screen` call. |
| `options`    | `RudderOption` | No       | Extra options to be passed along with the `screen` event.                 |

## Group

The `group` call associates a user to a specific organization. A sample `group` call for the API is below:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] group:@"sample_group_id"
  traits:@{@"foo": @"bar",
            @"foo1": @"bar1",
            @"email": @"ruchira@gmail.com"}
];
```

```swift
RSClient.sharedInstance()?.group("test_group_id", traits: [
    "key_1": "value_1",
    "key_2": "value_2"
])
```

Alternatively, you can use the following method signature

| Name      | Data Type      | Required | Description                                                                 |
| --------- | -------------- | -------- | --------------------------------------------------------------------------- |
| `groupId` | `String`       | Yes      | An ID of the organization with which you want to associate your user        |
| `traits`  | `NSDictionary` | No       | Any other property of the organization you want to pass along with the call |
| `options` | `RudderOption` | No       | Event level options                                                         |

RudderStack does not persist the traits for the group across the sessions.

## Alias

The `alias` call associates the user with a new identification. A sample `alias` call for the API is below:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] alias:@"new_user_id"];
```

```swift
RSClient.sharedInstance()?.alias("new_user_id")
```

Alternatively, you can use the following method signature

| Name      | Data Type      | Required | Description                                     |
| --------- | -------------- | -------- | ----------------------------------------------- |
| `newId`   | `String`       | Yes      | The new `userId` you want to assign to the user |
| `options` | `RudderOption` | No       | Event level option                              |

We replace the old `userId` with the `newUserId` and we persist that identification across the sessions.

## Reset

You can use the `reset` method to clear the persisted `traits` for the `identify` call. This is required for the `Logout` operations.

Objective-C Swift

```objectivec
[[RSClient sharedInstance] reset];
```

```swift
RSClient.sharedInstance()?.reset()
```

## Configuring the RudderStack client

You can configure your client based on the following parameters using `RudderConfigBuilder`:

| Parameter               | Type      | Description                                                                                                                                                                                                                                                                                                                                                                                     | Default Value                   |
| ----------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| `logLevel`              | `int`     | Controls how much of the log you want to see from the SDK.                                                                                                                                                                                                                                                                                                                                      | `RSLogLevelNone`                |
| `dataPlaneUrl`          | `string`  | Your Data Plane URL.                                                                                                                                                                                                                                                                                                                                                                            | `https://hosted.rudderlabs.com` |
| `flushQueueSize`        | `int`     | Number of events in a batch request sent to the server.                                                                                                                                                                                                                                                                                                                                         | `30`                            |
| `dbThresholdCount`      | `int`     | Number of events to be saved in the `SQLite` database. Once the limit is reached, older events are deleted from the DB.                                                                                                                                                                                                                                                                         | `10000`                         |
| `sleepTimeout`          | `int`     | Minimum waiting time to flush the events to the server. .                                                                                                                                                                                                                                                                                                                                       | `10 seconds`                    |
| `configRefreshInterval` | `int`     | Fetches the config from dashboard after the specified time.                                                                                                                                                                                                                                                                                                                                     | `2 hours`                       |
| `trackLifecycleEvents`  | `boolean` | Specify whether the SDK will capture application life cycle events automatically.                                                                                                                                                                                                                                                                                                               | `true`                          |
| `recordScreenViews`     | `boolean` | Specify whether the SDK will capture screen view events automatically.                                                                                                                                                                                                                                                                                                                          | `false`                         |
| `enableBackgroundMode`  | `boolean` | Specify whether the SDK should send the events for some time when the app is moved to the background. Currently it is available only for `iOS` & `tvOS`.                                                                                                                                                                                                                                        | `false`                         |
| `controlPlaneUrl`       | `string`  | This parameter should be changed **only if** you are self-hosting the control plane. Check the [**Self-hosted control plane**](https://rudderstack.com/docs/stream-sources/rudderstack-sdk-integration-guides/rudderstack-ios-sdk/#self-hosted-control-plane) section below for more information. The iOS SDK will add `/sourceConfig` along with this URL to fetch the required configuration. | `https://api.rudderlabs.com`    |

### Self-hosted control plane

If you are using a device mode destination like Adjust, Firebase, etc., the iOS SDK needs to fetch the required configuration from the control plane. If you are using the [**Control Plane Lite**](https://www.rudderstack.com/docs/rudderstack-open-source/control-plane-lite/) utility to host your own control plane, then follow [**this guide**](https://www.rudderstack.com/docs/rudderstack-open-source/control-plane-lite/#control-plane-url) and specify `controlPlaneUrl` in `RudderConfigBuilder` that points to your hosted source configuration file.

You shouldn't pass the `controlPlaneUrl` parameter during SDK initialization if you are using [RudderStack Cloud](https://app.rudderstack.com). This parameter is supported only if you are using our open-source [Control Plane Lite](https://www.rudderstack.com/docs/rudderstack-open-source/control-plane-lite/) utility to self-host your control plane.

## Setting the device token

You can pass your `device-token` for Push Notifications to be passed to the destinations which support Push Notification. We set the `token` under `context.device.token`.

Follow the instructions below:

```objectivec
[RSClient putDeviceToken:@"your_device_token"];
```

## Advertisement ID

We have kept IDFA collection completely separate from the Core library so that the developer has better control over the same. You can pass the IDFA to `putAdvertisementId` method to set it under `context.device.advertisingId`

Follow the instructions below:

```objectivec
#import <AdSupport/ASIdentifierManager.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    RSConfigBuilder *builder = [[RSConfigBuilder alloc] init];
    [builder withDataPlaneURL:[[NSURL alloc] initWithString:DATA_PLANE_URL]];
    [RSClient getInstance:WRITE_KEY config:[builder build]];

    [[[RSClient sharedInstance] getContext] putAdvertisementId:[self getIDFA]];

    return YES;
}

- (NSString*) getIDFA {
    return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}
```

## ATTrackingManager authorization consent

You can pass the [**ATTrackingManager.trackingAuthorizationStatus**](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/3547038-trackingauthorizationstatus) to RudderStack and we'll pass it along to the relevant destinations wherever needed. For example AppsFlyer accepts this parameter for the attribution to work in their [**S2S mode**](https://support.appsflyer.com/hc/en-us/articles/207034486-Server-to-server-events-API-for-mobile-S2S-mobile-#att-3).

Follow the instructions below:

```objectivec
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    RSConfigBuilder *builder = [[RSConfigBuilder alloc] init];
    [builder withDataPlaneURL:[[NSURL alloc] initWithString:DATA_PLANE_URL]];
    [RSClient getInstance:WRITE_KEY config:[builder build]];

    [[[RSClient sharedInstance] getContext] putAppTrackingConsent:RSATTAuthorize];

    return YES;
}
```

Following are the available options you can pass to the `putAppTrackingConsent` method.

* `RSATTNotDetermined`
* `RSATTRestricted`
* `RSATTDenied`
* `RSATTAuthorize`

## Anonymous ID

We use the `deviceId` as `anonymousId` by default. You can use the following method to override and use your own `anonymousId` with the SDK.

An example of setting the `anonymousId` is shown below:

```java
[RSClient putAnonymousId:<ANONYMOUS_ID>];
```

## Filtering events

When sending events to a destination via the [**device mode**](https://rudderstack.com/docs/rudderstack-cloud/rudderstack-connection-modes/#device-mode), you can explicitly specify which events should be discarded or allowed to flow through - by whitelisting or blacklisting them.

Refer to the [Client-side Event Filtering](https://rudderstack.com/docs/stream-sources/rudderstack-sdk-integration-guides/event-filtering/) guide for more information on this feature.

## Enabling/disabling events for specific destinations

The RudderStack iOS SDK allows you to enable or disable event flow to a specific destination or all the destinations to which the source is connected. You can specify these destinations by creating a `RSOption` object as shown:

Objective-C Swift

```objectivec
RSOption *option = [[RSOption alloc]init];
//default value for `All` is true
[option putIntegration:@"All" isEnabled:YES];
// specifying destination by its display name
[option putIntegration:@"Amplitude" isEnabled:YES];
[option putIntegration:@"&lt;destination display name&gt;" isEnabled:&lt;BOOL&gt;];
// specifying destination by its Factory instance
[option putIntegrationWithFactory:[RudderMoengageFactory instance] isEnabled:NO];
[option putIntegrationWithFactory:[&lt;RudderIntegrationFactory&gt; instance] isEnabled:&lt;BOOL&gt;];
```

```swift
let option:RSOption = RSOption();
//default value for `All` is true
option.putIntegration("All", isEnabled:true)
// specifying destination by its display name
option.putIntegration("Amplitude", isEnabled:true)
option.putIntegration(&lt;DESTINATION DISPLAY NAME&gt;, isEnabled:&lt;BOOL&gt;)
// specifying destination by its Factory instance
option.putIntegration(with: RudderMoengageFactory.instance(), isEnabled: true);
option.putIntegration(with: &lt;RudderIntegrationFactory&gt;.instance(), isEnabled:&lt;BOOL&gt;);
```

The keyword `All` in the above snippet represents all the destinations the source is connected to. Its value is set to `true` by default.

Make sure the `destination display name` you pass while specifying the custom destinations should exactly match the destination name as shown [here](https://app.rudderstack.com/directory).

You can pass the destination(s) specified in the above snippet to the SDK in two ways:

### 1. Passing destinations while initializing the SDK

This is helpful when you want to enable/disable sending the events across all the event calls made using the SDK to the specified destinations.

Objective-C Swift

```objectivec
RSConfigBuilder *builder = [[RSConfigBuilder alloc] init];
[builder withDataPlaneURL:[[NSURL alloc] initWithString:DATA_PLANE_URL]];
[builder withLoglevel:RSLogLevelDebug];
[builder withTrackLifecycleEvens:YES];
[builder withRecordScreenViews:YES;
[RSClient getInstance:WRITE_KEY config:[builder build] options:option]; // passing the rudderoption object containing the list of destinations you specified
```

```swift
let builder: RSConfigBuilder = RSConfigBuilder()
            .withLoglevel(RSLogLevelDebug)
            .withDataPlaneUrl(DATA_PLANE_URL)
            .withTrackLifecycleEvens(true)
            .withRecordScreenViews(true)
RSClient.getInstance(WRITE_KEY, config: builder.build(),options: option)// passing the rudderoption object containing the list of destination(s) you specified
```

### 2. Passing destinations while making event calls

This approach is helpful when you want to enable/disable sending only a particular event to the specified destination(s) or if you want to override the specified destinations passed with the SDK initialization for a particular event.

Objective-C Swift

```objectivec
[[RSClient sharedInstance] track:@"simple_track_with_props" properties:@&#123;
        @"key_1" : @"value_1",
        @"key_2" : @"value_2"
    &#125; options:option]; // passing the rudderoption object containing the list of destination(s) you specified
```

```swift
let rudder: RSClient? = RSClient.sharedInstance()
rudder?.track("track_with_props", properties: [
            "key_1": "value_1",
            "key_2": "value_2",
        ],options:option) // passing the rudderoption object containing the list of destination(s) you specified
```

If you specify the destinations both while initializing the SDK as well as making an event call, then the destinations specified at the event level only will be considered.

## External ID

You can pass your custom `userId` along with standard `userId` in your `identify` calls. We add those values under `context.externalId`. The following code snippet shows a way to add `externalId` to your `identify` request.

```objectivec
RSOption *identifyOptions = [[RSOption alloc] init];
[identifyOptions putExternalId:@"brazeExternalId" withId:@"some_external_id_1"];
[[RSClient sharedInstance] identify:@"testUserId"
                             traits:@{@"firstname": @"First Name"}
                            options:identifyOptions];
```

## Debugging

If you run into any issues regarding the RudderStack iOS SDK, you can turn on the `VERBOSE` or `DEBUG` logging to find out what the issue is. To turn on the logging, change your `RudderClient` initialization to the following:

Objective-C Swift

```objectivec
RSConfigBuilder *builder = [[RSConfigBuilder alloc] init];
[builder withDataPlaneUrl:DATA_PLANE_URL];
[builder withLoglevel:RudderLogLevelDebug];
[RSClient getInstance:WRITE_KEY config:[builder build]];
```

```swift
let builder: RSConfigBuilder = RSConfigBuilder()
builder.withDataPlaneUrl(&lt;DATA_PLANE_URL&gt;)
builder.withLoglevel(RudderLogLevelDebug)
RSClient.getInstance(&lt;WRITE_KEY&gt;, config: builder.build())
```

## Developing a device mode destination

You can easily develop a device mode destination in case RudderStack doesn't support it already. Follow the steps listed in this section to do so.

More information on the RudderStack device mode can be found in the [RudderStack Connection Modes](https://rudderstack.com/docs/rudderstack-cloud/rudderstack-connection-modes/) guide.

* Create a `CustomFactory.h` file by extending [`RSIntegrationFactory`](https://github.com/rudderlabs/rudder-sdk-ios/blob/master/Rudder/RSIntegrationFactory.h), as shown:

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

NS_ASSUME_NONNULL_BEGIN

@interface CustomFactory : NSObject<RSIntegrationFactory>

+ (instancetype) instance;

@end

NS_ASSUME_NONNULL_END
```

* Then, create a `CustomFactory.m` file, as shown:

```objectivec
#import <Foundation/Foundation.h>
#import <Rudder/Rudder.h>
#import "CustomFactory.h"
#import "CustomIntegration.h"


@implementation CustomFactory

+ (instancetype)instance {
    static CustomFactory *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

- (instancetype)init
{
    self = [super init];
    return self;
}

- (nonnull NSString *)key {
    return @"Custom Factory";
}

- (nonnull id<RSIntegration>)initiate:(NSDictionary *)config client:(nonnull RSClient *)client rudderConfig:(nonnull RSConfig *)rudderConfig {
    return [[CustomIntegration alloc] initWithConfig:config withAnalytics:client];
}


@end
```

* Next, create a `CustomIntegration.h` file by extending [`RSIntegration`](https://github.com/rudderlabs/rudder-sdk-ios/blob/master/Rudder/RSIntegration.h).

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

NS_ASSUME_NONNULL_BEGIN

@interface CustomIntegration : NSObject<RSIntegration>

@property (nonatomic, strong) NSDictionary *config;
@property (nonatomic, strong) RSClient *client;

- (instancetype)initWithConfig:(NSDictionary *)config withAnalytics:(RSClient *)client;

@end

NS_ASSUME_NONNULL_END
```

* Next, create a `CustomIntegration.m` file.

```objectivec
#import <Foundation/Foundation.h>
#import <Rudder/Rudder.h>
#import "CustomIntegration.h"

@implementation CustomIntegration

- (instancetype) initWithConfig:(NSDictionary *)config withAnalytics:(RSClient *)client {
    if (self == [super init]) {
    }
    return self;
}

- (void) processRuderEvent:(nonnull RSMessage *)message {
    NSString *type = message.type;
    if ([type isEqualToString:@"identify"]) {
//        Do something
    } else if ([type isEqualToString:@"track"]) {
//        Do something
    } else if ([type isEqualToString:@"screen"]) {
//        Do something
    } else {
        [RSLogger logWarn:@"MessageType is not supported"];
    }
}

- (void) dump:(nonnull RSMessage *)message {
    [self processRuderEvent:message];
}

- (void) reset {
}

- (void) flush {
}

@end
```

* Register the `CustomFactory` with the RudderStack iOS SDK during its initialization, as shown:

```objectivec
    RSConfigBuilder *builder = [[RSConfigBuilder alloc] init];
    [builder withDataPlaneURL:[[NSURL alloc] initWithString:DATA_PLANE_URL]];
    [builder withLoglevel:RSLogLevelDebug];
    [builder withTrackLifecycleEvens:NO];
    [builder withRecordScreenViews:NO];
    [builder withCustomFactory:[CustomFactory instance]];
    [RSClient getInstance:WRITE_KEY config:[builder build]];
```

Some pointers to keep in mind:

* RudderStack's iOS SDK dumps every event it receives to the `dump()` method of the `CustomFIntegration` class. From here, you can process the event and hand it over to the native SDK of the Device Mode destination.
* The SDK also triggers the `reset()` method of the `CustomFactory` class on every `reset()` call made via the SDK. You can use this to handle the destination-specific reset logic.
* Make sure you do not duplicate the value of `KEY` present inside `CustomFactory`, across multiple `CustomFactory` that you develop.
* RudderStack's iOS SDK also triggers the `flush()` method of the `CustomFactory` class on every `flush()` call made via the SDK, which you can use to handle the destination-specific reset logic. You can make a `flush` call using the SDK as shown:

```objectivec
[[RSClient sharedInstance] flush];
```

## FAQs

### I'm facing issues building with Carthage on XCode 12. What should I do?

If you're facing an issue with Carthage and XCode 12, you can follow [**this workaround**](https://github.com/Carthage/Carthage/blob/master/Documentation/Xcode12Workaround.md) suggested by the Carthage team.

### Does the SDK support tvOS ?

As of version `1.1.0`, the iOS SDK supports the [**tvOS**](https://developer.apple.com/tvos/) platform.

### Does the SDK support watchOS ?

As of version `1.3.0`, the iOS SDK supports the [**watchOS**](https://developer.apple.com/watchos/) platform.

### How do I migrate from v1.0.2?

Update the usage of the following classes as per the table below:

| **Previous Name**     | **Updated Name**                                                                                     |
| --------------------- | ---------------------------------------------------------------------------------------------------- |
| `RudderClient`        | `RSClient`                                                                                           |
| `RudderConfig`        | `RSConfig`                                                                                           |
| `RudderConfigBuilder` | `RSConfigBuilder`                                                                                    |
| `RudderLogLevelDebug` | <p><code>RSLogLevelDebug</code></p><p>Other <code>LogLevel</code> follows the same nomenclature.</p> |

### How do I ensure the events tracked just before closing/backgrounding the app are sent immediately and not on the next app launch?

To ensure that the events tracked just before closing/backgrounding the app are sent to RudderStack immediately, you can set `withEnableBackgroundMode` to `YES` while creating the `RSConfigBuilder` object as shown below:

Currently, this feature is available only for `iOS` & `tvOS` platforms.

```objectivec
RSConfigBuilder *builder = [[RSConfigBuilder alloc] init];
[builder withEnableBackgroundMode:YES];
[RSClient getInstance:WRITE_KEY config:[builder build] options:defaultOption];
```

By doing so, your app requests iOS for some additional background run time to run the app, which in turn allows the SDK to immediately send the events tracked just before the app is closed/backgrounded, instead of waiting till the next app launch.

This SDK feature relies on the background mode capability offered by the iOS. There is no set number on the background run time the apps get, as it is completely abstracted by iOS. For more information, refer to [**this guide**](https://www.raywenderlich.com/5817-background-modes-tutorial-getting-started#toc-anchor-008).

### How can I get the user `traits` after making the `identify` call?

You can get the user traits after making an `identify` call in the following way:

Swift Objective-C

```swift
let traits = RSClient.sharedInstance()?.getContext().traits
```

```
NSDictionary* traits = [[RSClient sharedInstance] getContext].traits;
```

### How does the SDK handle different client/server errors?

In case of client-side errors, e.g. if the source write key passed to the SDK is incorrect, RudderStack gives you a **400 Bad Request** response and aborts the operation immediately. For other types of network errors (e.g. Invalid Data Plane URL), the SDK tries to flush the events to RudderStack in an incremental manner (every 1 second, 2 seconds, 3 seconds, and so on).

### Why is there a larger difference between `timestamp` and `received_at` for iOS events vs. Android events?

This scenario is most likely caused by the default behavior of iOS apps staying open in the background for a shorter period of time after a user closes them.

When a user closes an iOS or Android app, events will still continue to be sent from the queue until the app closes in the background as well. Any events still in the queue will remain there until the user reopens the app. Due to this lag, there are some scenarios where there can be significant differences between `timestamp` (when the event was created) and `received_at` (when RudderStack actually receives the events).

For Android apps, events can be sent from the background after apps close for a longer period of time than iOS apps, therefore, more of the events coming from the Android SDK have closer `timestamp` and `received_at` times.

### Does RudderStack integrate with SKAdNetwork?

RudderStack does not integrate with SKAdNetwork. However, SKAdNetwork can be directly integrated into an iOS application alongside RudderStack.

### Can I disable event tracking until the user gives their consent?

Yes, you can.

RudderStack gives you the ability to disable tracking any user activity until the user gives their consent, by leveraging the `optOut` API. This is required in cases where your app is audience-dependent (e.g. minors) or where you're using the app to track the user events (e.g. EU users) to meet the data protection and privacy regulations.

The `optOut` API takes `true` / `false` (in case of Swift) or `YES` / `NO` (in case of Objective-C) as a value to enable or disable tracking user activities. So, to disable user tracking, you can use the `optOut` API as shown:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] optOut:YES];
```

```swift
RSClient.sharedInstance()?.optOut(true)
```

Once the user gives their consent, you can enable user tracking again, as shown:

Objective-C Swift

```objectivec
[[RSClient sharedInstance] optOut:NO];
```

```swift
RSClient.sharedInstance()?.optOut(false)
```

For more information on the `optOut` API, refer to the [**Enabling/Disabling User Tracking via optOut API (GDPR Support)**](https://rudderstack.com/docs/stream-sources/rudderstack-sdk-integration-guides/rudderstack-ios-sdk#enabling-disabling-user-tracking-via-the-optout-api-gdpr-support) section above.

You only need to call the `optOut` API with the required parameter only once, as the information persists within the device even if you reboot it.

## Contact us

For any queries on any of the sections covered in this guide, you can [**contact us**](mailto:%20docs@rudderstack.com), or start a conversation in our [**Slack**](https://rudderstack.com/join-rudderstack-slack-community/) community.

If you come across any issues while using the iOS SDK, you can also open a new issue on our [**GitHub Issues page**](https://github.com/rudderlabs/rudder-sdk-ios/issues/new).
