# iOS SDK

#### Things to keep handy before starting your integration with Apxor

Please have the following handy before beginning the integration:

* **Application identifier** generated on the Apxor dashboard for your app\
  \
  ([Read more on how to fetch the application identifier from Apxor dashboard](/getting-started-with-apxor/adding-a-new-app.md))
* **App Bundle Id :** Every app has a unique application ID that looks like `com.example.myapp`. This id uniquely identifies the app on the device and also on the app store.\
  \
  [(Know more about bundle ids here)](https://developer.apple.com/documentation/appstoreconnectapi/bundle-ids)
* **The list of events** to setup triggers and track goals, user properties that allows to personalize messages and to target better.\
  \
  ([Read more on how you can setup here](/getting-started-with-apxor/api-guides/ios.md))

{% hint style="info" %}
**Note**

If you are already using a separate analytics class where you are sending events to different platforms from a single place it is much faster to use our tracking guide to complete event logging to setup triggers and track goals.

In case you are logging events to third parties after each interaction, please use our [Third party API guide](broken://pages/6mI0HCleo5SS08RVwKwr) to migrate all the events that you are sending to one of the supported third parties as listed in the guide.
{% endhint %}

## Getting started with Apxor iOS

Apxor provides you easy to use plugins for your Actions. They are:

<table><thead><tr><th width="206.33333333333331">Plugin Name</th><th width="253">Description</th><th>Example</th></tr></thead><tbody><tr><td>Apxor-Core</td><td>The Core Plugin is responsible for the basic event tracking</td><td>How many of my users have clicked on the cart icon after showing them a nudge Sample Event : 'ViewCart'</td></tr><tr><td>APXSurveyPlugin</td><td>This Plugin helps you create contextual surveys to capture your users' feedback, ratings, etc.</td><td>An NPS survey that would ask the user to rate the app experience on a scale of 1-10.</td></tr><tr><td>APXRTAPlugin</td><td>This Plugin helps you create real time actions.</td><td>Show a tooltip on the cart icon with messaging "Tap here to view items"</td></tr><tr><td>APXWYSIWYGPlugin</td><td>The WYSIWYG Plugin allows you to preview your configured actions onto your device in real time</td><td>Casting your mobile screen to the dashboard and selecting the hamburger icon</td></tr><tr><td>APXPushPlugin</td><td>The Push Plugin allows you to track uninstalls and real-time serve/pause the nudges, while the app is opened</td><td>Show a campaign to users who land on the home screen and add an item to the cart.</td></tr></tbody></table>

[Check here for the latest release notes](/getting-started-with-apxor/release-notes.md#ios).

## Adding Apxor SDK to your project

{% tabs fullWidth="false" %}
{% tab title="Cocoa Pods  " %}
{% hint style="danger" %}

## Cocoa Pods has announched that going further there will no updates.&#x20;

Cocoa Pods will be moved to read only mode followed by archieve. Users should switch to SPM for any updates related to cocoa pods. \
\
Refer the blog :<https://blog.cocoapods.org/CocoaPods-Specs-Repo/>
{% endhint %}

* Install **CocoaPods**, if you don't already have it. CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has over 95 thousand libraries and is used in over 3 million apps. CocoaPods can help you scale your projects elegantly.&#x20;
* If this is your first pod, run **`pod init`**. Add the following to the corresponding target in your Podfile and run **`pod install`**.

```groovy
use_frameworks!
pod 'Apxor-Core', '2.10.46'
pod 'Apxor-CE', '1.05.33'
pod 'Apxor-RTA', '1.09.51'
pod 'Apxor-WYSIWYG', '1.02.76'
pod 'Apxor-Survey', '1.04.24'
```

{% endtab %}

{% tab title="Swift Package Manager" %}
Use Swift Package Manager to install and manage Apxor dependencies.

1. In Xcode, with your app project open, navigate to **File** > **Add Packages.**

<figure><img src="/files/XwSmGsFKUd3OuuTScyKl" alt=""><figcaption></figcaption></figure>

2. When prompted, add the Apxor Apple platforms SDK repository:

```
https://github.com/apxor/ApxorSDK
```

<figure><img src="/files/OEtW7uyZVsdl4of48WKP" alt=""><figcaption></figcaption></figure>

3. Click on "**Add to Target**" and select your project.

{% hint style="warning" %}
**Note:** All the Package Product should have the same project selected. This ensures complete SDK integration.
{% endhint %}

<figure><img src="/files/Nb4op3FPxn6N4cXoLHZB" alt=""><figcaption></figcaption></figure>

> <p align="center">When finished, Xcode will automatically begin resolving and downloading your dependencies in the background.</p>

{% endtab %}

{% tab title="Manual" %}

| Plugin Name                                                                                                   | Latest Version                                              |
| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| [ApxoriOSSDK-Core](https://repo.apxor.com/artifactory/libs-release-ios/core/21044/ApxoriOSSDK-Core-21044.zip) | The Core Plugin is responsible for the basic event tracking |

* Download the above and Unzip to find **`ApxorSDK.framework`** directory

### Adding Apxor SDK to your project

1. Add **`ApxorSDK.framework`** to your **"Frameworks, Libraries and Embedded Content"** list. (for xcode < 11, under **“Embedded Binaries”**)
2. Also add the **`libsqlite3.dylib`** (or **`libsqlite3.0.tbd`**) library to the same list.
3.

```
<figure><img src="/files/ifRI4C3D2xbM3YNwnMYp" alt=""><figcaption></figcaption></figure>
```

4. Under build settings tab, in **"Other Linker Flags"**, add **`-ObjC`** , if not already present.

<figure><img src="/files/3nOOyEXr8PHM65ZiJDlI" alt=""><figcaption></figcaption></figure>

### Add Apxor script

* Add the following in the **Run script** phase in your build phases.

```
# Script to strip unwanted architectures

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"

find "$APP_PATH" -name '*[A][Pp][Xx]*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"

EXTRACTED_ARCHS=()

for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done

echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"

echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"

done
```

<figure><img src="/files/BzL4mubpv0o0nqsEKqTU" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/JYTgQFmoyuypipHi8RS6" alt=""><figcaption></figcaption></figure>

### Plugins Integration

* Add the libraries to your application's **"Frameworks, Libraries and Embedded Content"** list. (Click on the plugin name to download)
* Apxor provides you two easy to use plugins for your Actions. They are:

| Plugin Name                                                                                                      | Description                                                                                    |
| ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [APXSurveyPlugin](https://repo.apxor.com/artifactory/libs-release-ios/survey/10424/ApxoriOSSDK-Survey-10424.zip) | This Plugin helps you create contextual surveys to capture your users' feedback, ratings, etc. |
| [APXRTAPlugin](http://repo.apxor.com/artifactory/libs-release-ios/rta/latest/ApxoriOSSDK-RTA.zip)                | This Plugin helps you create real time actions.                                                |

* Make sure to add the following dependency plugins as well,

| Plugin Name                                                                                                         | Description                                                                                    |
| ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [APXCEPlugin](https://repo.apxor.com/artifactory/libs-release-ios/ce/10531/ApxoriOSSDK-CE-10531.zip)                | The Secret Sauce                                                                               |
| [APXWYSIWYGPlugin](http://repo.apxor.com/artifactory/libs-release-ios/debug/wysiwyg/latest/ApxoriOSSDK-WYSIWYG.zip) | The WYSIWYG Plugin allows you to preview your configured actions onto your device in real time |
| {% endtab %}                                                                                                        |                                                                                                |
| {% endtabs %}                                                                                                       |                                                                                                |

## Initialize Apxor iOS SDK

### Auto initialize SDK (Recommended)

* To Auto initialize SDK (Recommended), add the following inside your **`application`** plist file.
* Open your application's **Info.plist** as source code.

<figure><img src="/files/A2eDZBOplAFoGRpOqnRa" alt=""><figcaption></figcaption></figure>

* Copy paste the below piece of code, to create an entry for ApxorSDK.

```groovy
<key>Apxor</key>
<dict>
    <key>Core</key>
    <string>YOUR_APP_ID</string>
    <key>APXSurveyPlugin</key>
    <true/>
    <key>APXRTAPlugin</key>
    <true/>
    <key>APXWYSIWYGPlugin</key>
    <true/>
</dict>
```

### Manually initialize SDK (Not Recommended)

* To manually initialize SDK, call **`ApxorSDK.initialize`** method in your **`Application`** class

```groovy
//...
#import "ApxorSDK/ApxorSDK.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    [ApxorSDK initializeApxorWithID:@"<YOUR_APP_ID>"];
    // ... your code
}
```

* And open your application's **Info.plist** as source code.

<figure><img src="/files/A2eDZBOplAFoGRpOqnRa" alt=""><figcaption></figcaption></figure>

* Copy paste the below piece of code, to create an entry for ApxorSDK.

```groovy
<key>Apxor</key>
<dict>
    <key>APXSurveyPlugin</key>
    <true/>
    <key>APXRTAPlugin</key>
    <true/>
    <key>APXWYSIWYGPlugin</key>
    <true/>
</dict>
```

{% hint style="info" %}
**Note**

To get your app ID, please email us at <mark style="color:blue;">**<contact@apxor.com>**</mark>
{% endhint %}

## Configuring Test Device

* First, you need to configure your app to ensure there is a URL Scheme with your application's bundle identifier as the value.
* If your app already has a URL Scheme with your application's bundle identifier as the value, you can skip this step.

### Configure URL Scheme

* To configure URL scheme, goto your project settings, select **`Targets`**. Click on the **`Info`** tab.
* Select the **`URL Types`**, and click on the **`+`** button to add a new URL Scheme.
* Add a new URL Scheme with your **`bundle identifier`** as the value.
* Your bundle identifer will be in the format, **`com.xxxx.xxxx`**
* Use the image below for reference.

<figure><img src="/files/h0DXDpIUwnWJcE5UUtIf" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Note**

Make sure the URL scheme has the value of your **bundle identifier** that was provided in the dashboard while registering with us. Also, the app must have same **bundle identifier.**
{% endhint %}

### Handling the deep link

<details>

<summary>Using AppDelegate</summary>

* You'd need to enable Apxor to handle Apxor specific deeplinks.
* In your application's **`AppDelegate`** file, in the function **`application(_:open:options:)`**, add the following code at the beginning,

```swift
// ObjC
NSString *urlStr = url.absoluteString;
if ([urlStr containsString:@"add-test-device"]) {
  [ApxorSDK handleDeeplink:url];
}
```

```objectivec
// Swift
/*
  Apxor's code to handle deeplinks
  */
let urlStr = url.absoluteString
if (urlStr.contains("add-test-device")) {
    ApxorSDK.handleDeeplink(url)
}
```

* This will ensure the Apxor specific deep links are handle by our SDK.

</details>

<details>

<summary>Using SceneDelegate</summary>

* You'd need to enable Apxor to handle Apxor specific deeplinks.
* In your application's **`SceneDelegate`** file, add the following code at the beginning,

```objectivec
// ObjC
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
  for(UIOpenURLContext *x in URLContexts) {
    NSURL *url = x.URL;
    if([[url absoluteString] containsString:@"add-test-device"])
    {
      [ApxorSDK handleDeeplink:url];
    }
    break;
  }
}
```

```swift
// Swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  let url = URLContexts.first?.url
  let urlStr = url?.absoluteString
  if (urlStr!.contains("add-test-device")) {
    ApxorSDK.handleDeeplink(url!)
  }
}
```

</details>

## Configuring Push Notifications

* To use the push notifications feature, make sure the following lines exist in your application plist file under Apxor section.

```
<key>APXPushPlugin</key>
<true/>
```

* To configure iOS Push notification via Apxor dashboard, you'd need to upload APNs Auth Key file along with it's ID (key ID), your Team ID, and your application's Bundle ID.
* The APNs Auth Key is the best way to configure pushes, as you don't need to regenerate a certificate every year and also, this key can be used to configure Push notifications to sever of your applications (under the same apple developer account)
* Things required to configure iOS Push notification:
* Auth Key file
  * Key ID (usuallly the name of the Auth Key file)
  * Team ID (the 10 digit alphanumeric key)
  * Your app’s bundle ID (in the format com.abc.xyz)
  * See here on how to get these, Push notifications
* Once you get those details, add the below code in your application's **`AppDelegate`** file in the **`application`** **`didRegisterForRemoteNotificationsWithDeviceToken`** function.

```
import APXPushPlugin

let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
APXPushPlugin.setPushDeviceToken(token)
```

```
[APXPushPlugin setPushDeviceToken:token];
```

* The token can be passed in either the **`NSData`** or **`NSString`** format

<figure><img src="/files/ar6RPB8we81YamczxL27" alt=""><figcaption></figcaption></figure>

* If you haven't already used the code to Ask User for notifications permission, add the following function in your **`AppDelegate`** file.

```
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  // your existing code ...

  registerForPushNotifications()

  // ...
}

func registerForPushNotifications() {
  UNUserNotificationCenter.current()
    .requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, error in
      print("Permission granted: \(granted)")
      guard granted else { return }
      self?.getNotificationSettings()
    }
}

func getNotificationSettings() {
  UNUserNotificationCenter.current().getNotificationSettings { settings in
    print("Notification settings: \(settings)")
    guard settings.authorizationStatus == .authorized else { return }
    DispatchQueue.main.async {
      UIApplication.shared.registerForRemoteNotifications()
    }
  }
}
```

## Embed Slot

{% hint style="info" %}
**Note**

For using Apxor Embedded Cards the versions of the following plugins should be greater than or equal the ones mentioned below

**Core: 2.10.26**

**RTA: 1.09.26**

**CE: 1.05.16**

**WYSIWYG: 1.02.67**
{% endhint %}

For using Apxor's immersive template embedded cards, this step is necessary.

In all areas of the app where you may want to show an in-line widget, insert the following code in the view controller:

```swift
// Swift

let EmbedCard = APXRTAPlugin.initEmbedCard(withId: <Tag>)!

// you can add it in a UIStackView (recommended)
let stackView = UIStackView()
stackView.addArrangedSubview(EmbedCard)

// add your other existing views if any
stackView.addArrangedSubview(existingView1)
.
.

// if you want to add it in a UIView
let sampleView = UIView()
sampleView.addSubview(EmbedCard)

// add constraints (optional)
EmbedCard.translatesAutoresizingMaskIntoConstraints = false

EmbedCard.topAnchor.constraint(equalTo: sampleView.topAnchor).isActive = true
EmbedCard.leadingAnchor.constraint(equalTo: sampleView.leadingAnchor).isActive = true
EmbedCard.bottomAnchor.constraint(equalTo: sampleView.bottomAnchor).isActive = true
EmbedCard.trailingAnchor.constraint(equalTo: sampleView.trailingAnchor).isActive = true
```

```objectivec
// Objective-C

APXEmbedCard *embedCard = [APXRTAPlugin initEmbedCardWithId: <Tag>];

// you can add it in a UIStackView (recommended)
UIStackView *stackView = [[UIStackView alloc] init];
[stackView addArrangedSubview:embedCard];

// add your other existing views if any
[stackView addArrangedSubview:existingView1];
.
.

// if you want to add it in a UIView
UIView *sampleView = [[UIView alloc] init];
[sampleView addSubview:embedCard];

// add constraints (optional)
embedCard.translatesAutoresizingMaskIntoConstraints = NO;

[NSLayoutConstraint activateConstraints:@[
    [embedCard.topAnchor constraintEqualToAnchor:sampleView.topAnchor],
    [embedCard.leadingAnchor constraintEqualToAnchor:sampleView.leadingAnchor],
    [embedCard.bottomAnchor constraintEqualToAnchor:sampleView.bottomAnchor],
    [embedCard.trailingAnchor constraintEqualToAnchor:sampleView.trailingAnchor]
]];

```

Rename <mark style="color:blue;">\<Tag></mark> to any unique ID (any positive Integer). Ensure that you keep the ID unique across different IDs and across different Embed Card instances.

## Story Slot

{% hint style="info" %}
**Note**

For using Apxor Stories the versions of the following plugins should be greater than or equal the ones mentioned below

**Core: 2.10.26**

**RTA: 1.09.26**

**CE: 1.05.16**

**WYSIWYG: 1.02.67**
{% endhint %}

In all areas of the app where you may want to show an in-line widget, insert the following code in the view controller:

```swift
// Swift

let StoryWidget = APXRTAPlugin.initStories(withId: <Tag>)!

// you can add it in a UIStackView (recommended)
let stackView = UIStackView()
stackView.addArrangedSubview(StoryWidget)

// add your other existing views if any
stackView.addArrangedSubview(existingView1)
.
.

// if you want to add it in a UIView
let sampleView = UIView()
sampleView.addSubview(StoryWidget)

// add constraints (optional)
StoryWidget.translatesAutoresizingMaskIntoConstraints = false

StoryWidget.topAnchor.constraint(equalTo: sampleView.topAnchor).isActive = true
StoryWidget.leadingAnchor.constraint(equalTo: sampleView.leadingAnchor).isActive = true
StoryWidget.bottomAnchor.constraint(equalTo: sampleView.bottomAnchor).isActive = true
StoryWidget.trailingAnchor.constraint(equalTo: sampleView.trailingAnchor).isActive = true
```

```objectivec
// Objective-C

APXStoryGroup *storyWidget = [APXRTAPlugin initStoriesWithId:<Tag>];

// you can add it in a UIStackView (recommended)
UIStackView *stackView = [[UIStackView alloc] init];
[stackView addArrangedSubview:storyWidget];

// add your other existing views if any
[stackView addArrangedSubview:existingView1];
.
.

// if you want to add it in a UIView
UIView *sampleView = [[UIView alloc] init];
[sampleView addSubview:storyWidget];

// add constraints (optional)
storyWidget.translatesAutoresizingMaskIntoConstraints = NO;

[NSLayoutConstraint activateConstraints:@[
    [storyWidget.topAnchor constraintEqualToAnchor:sampleView.topAnchor],
    [storyWidget.leadingAnchor constraintEqualToAnchor:sampleView.leadingAnchor],
    [storyWidget.bottomAnchor constraintEqualToAnchor:sampleView.bottomAnchor],
    [storyWidget.trailingAnchor constraintEqualToAnchor:sampleView.trailingAnchor]
]];

```

Rename <mark style="color:blue;">\<Tag></mark> to any unique ID (any positive Integer). Ensure that you keep the ID unique across different IDs and across different Stories instances.

## Ensuring Apxor SDK is initialized successfully

* Lookout for the following log

<figure><img src="/files/lZ7MDzf7ZBl4IbnUyCRW" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/jNo4ltENh343LgxdAxyz" alt=""><figcaption></figcaption></figure>

[Click here for guide](/getting-started-with-apxor/api-guides/ios.md) to log user properties, events and event properties.

## Add-Ons

Now that you have completed the basic integration, you can proceed to set up event triggers, capture data for targeting, add slots for embed cards and stories, and personalize messaging. Include the following sections as needed.

### Initializing the SDK

To start tracking with the Apxor iOS SDK, you must first initialize it. To initialize the SDK,

```objectivec
// ObjC
[ApxorSDK initializeApxorSDK];
```

```swift
// Swift
ApxorSDK.initializeApxorSDK()
```

### Identifying the Users

ApxorSDK uses Identifier for Advertisers (IFA) to uniquely identify users. In cases where IFA is not available, we use Identifier for Vendors (IFV) for the same. Apart from this you can log a custom user identifier that you use to uniquely identify users in your app.

This identifier would be very instrumental specially when exporting data of a certain campaign or survey to your analytics system to create a cohort and measure the results of the campaign.

Similarly, when you are importing data to Apxor from your system that your marketing / product / data science team has identified and want to run campaigns specifically to them the `custom user identifier` will serve as the bridge to communicate between your systems and Apxor effectively.

Here is how you can set your user identifier for Apxor to recognise your users :

```objectivec
// ObjC
[ApxorSDK setUserIdentifier:@"1729"];
```

```swift
// Swift
ApxorSDK.setUserIdentifier("CustomUserIdentifier")
```

### Setting up campaign triggers, capturing data for targetting and goal tracking[​](http://localhost:3000/docs/android-sdk/Tracking#setting-up-campaign-triggerscapturing-data-for-targetting-and-goal-tracking) <a href="#setting-up-campaign-triggerscapturing-data-for-targetting-and-goal-tracking" id="setting-up-campaign-triggerscapturing-data-for-targetting-and-goal-tracking"></a>

The product/marketing or the growth team lists out the use cases with an idea of when to launch and to whom to launch. To do this we need to capture data in the form of events. Let us consider the following use case as an example :

<figure><img src="/files/cjD16dAViQgcMYKKhg53" alt=""><figcaption></figcaption></figure>

### **App Events**[**​**](http://localhost:3000/docs/android-sdk/Tracking#app-events)

In the above scenario, we want to trigger the campaign for users who have spent 'x' seconds and haven't tapped on a product. To understand that if the user has tapped the product we should log an event along with its attributes as follows to capture data:

Similarly if you want to send promotions to users who have viewed at least five products of the category shoes in the last three days or you want to measure how many people added an item to a cart from the campaign as a goal all this information is captured in the form of events.

These types of events are classified as `app events` - the data that is transferred to the servers at Apxor where you can segment users based on historic behavior or measure your goals as specified above.

To track an event with the event name and properties.

```objectivec
// ObjC
NSDictionary *info = [[NSDictionary alloc] init];
[info setValue:@"Select Language" forKey:@"event_type"];
[info setValue:@"Valyrian" forKey:@"event_type"];
[ApxorSDK logAppEventForEvent:@"LANG_SELECT" withInfo:info];
```

<figure><img src="/files/pwqGbcAFBkZQWXuNvL64" alt=""><figcaption></figcaption></figure>

```swift
// Swift
let eventDict = ["event_type":"Select Language", "event_type":"Valyrian"] as [String : AnyObject]
ApxorSDK.logAppEvent(withName: "EventName", info: eventDict)
```

### User Attributes

#### Personalizing and targeting by user persona[​](http://localhost:3000/docs/android-sdk/Tracking#personalising-and-targetting-by-user-persona) <a href="#personalising-and-targetting-by-user-persona" id="personalising-and-targetting-by-user-persona"></a>

We can personalize the messaging copy in the experiences we build for the user or target based on his persona using information that is centric to individual users. Let us consider the following example where we know the user is an `English` with `Gold` membership.<br>

This information helps to tailor content in English to that specific user and gives us the flexibility to different messaging to different membership tiers. This is how the information captured here is used for segmenting.<br>

Similarly capturing attributes like `Name` can help to personalize your message copy where it reads **Hi {username} can't find your product?** where the username is replaced by the attribute value of the property from the nudges dashboard along with providing meaningful defaults in their absence.

<figure><img src="/files/kwd028HZ6jg2J3mAGyFH" alt=""><figcaption></figcaption></figure>

This is how you log user information to Apxor :

```objectivec
// ObjC
NSDictionary *info = [[NSDictionary alloc] init];
[info setValue:@"spock@vulcan.com" forKey:@"email"];
[ApxorSDK setUserCustomInfo:info];
```

```swift
// Swift
let userInfo = ["email": "spock@vulcan.com"] as [String : AnyObject]
ApxorSDK.setUserCustomInfo(userInfo)
```

### Session Attributes

A Session can be simply defined as a user journey as he opens the app, until he closes the app. There can be various pieces of information that can be very impactful when accumulated in a session. For example, location in a session can be useful to know precisely where the user is utilizing the app most.

To add session attributes that are specific to a session,

```objectivec
// ObjC
NSDictionary *info = [[NSDictionary alloc] init];
[info setValue:@"In a galaxy far far away" forKey:@"location"];
[ApxorSDK setSessionCustomInfo:info];
```

```swift
// Swift
let userInfo = ["location": "In a galaxy far far away"] as [String : AnyObject]
ApxorSDK.setSessionCustomInfo(userInfo)
```

### Client Events

In the below scenario, let's assume you want to launch a survey when the soft back button is pressed asking the user for product feedback. In this, we don't need to capture the data of how many people pressed the back button which is useless and it bloats your event storage as it is a high-frequency event which increases your cost unnecessarily. This data point doesn't potentially answer any of your product questions and hence there is no ROI in storing data from this event.<br>

So for such scenarios where we need the behavioral data to launch a campaign or to collect feedback, which doesn't provide ROI on storing for measuring goals, answering your product questions or segmenting your target audience, we log these events as `Client Events` which involves zero transfer of data and is used only to set up your triggers on behavioral information from the user.

Events that are logged to reside on the client application are called client events, the data captured is not transferred to Apxor.

These are typically logged to capture behavioral observations and interactions to nudge a user.

> Example:
>
> Soft back button, user reaching end of page, etc.

<figure><img src="/files/aJ3Mv29Z26IODheNEIVE" alt=""><figcaption></figcaption></figure>

```objectivec
// ObjC
NSDictionary *info = [[NSDictionary alloc] init];
[info setValue:@"com.example.app.SettingsViewController" forKey:@"Screen"];
[ApxorSDK logClientEventWithName:@"SoftBackPressed" info:info];
```

```swift
// Swift
let eventDict = ["Screen":"com.example.app.SettingsViewController"] as [String : AnyObject]
ApxorSDK.logClientEvent(withName: "SoftBackPressed", info: eventDict)
```

### Track Screens <a href="#track-screens" id="track-screens"></a>

In the scenario discussed in this guide, how will we know if the user has spent thirty seconds on the home screen and did not click on the product? For this reason, it is important to use track the screens to set them up as triggers and also to capture the time spent on the screens.

<figure><img src="/files/T7Xr6AIf4U1AWAnQruYJ" alt=""><figcaption></figcaption></figure>

ApxorSDK automatically captures screens and their names for most view controllers. In some cases where TabBarController is used, the OS won't be providing any notifications to capture the screens automatically.

For these cases, we encourage you to log screen events using the following API. Make sure to log the API inside the **viewWillAppear** function.

```objectivec
// ObjC
- (void)viewWillAppear:(BOOL)animated {

    [ApxorSDK logScreenWithName:@"FirstViewController"];
    /*
      ... your code here ...
     */
}
```

```swift
// Swift
override func viewWillAppear(animated: Bool) {

    ApxorSDK.logScreenWithName("FirstViewController")
    /*
      ... your code here ...
    */
}
```

### Reporting Custom Errors

Custom errors describe situations like LOGIN\_FAILED, NETWORK\_CALL\_FAILED and are to be treated differently compared to app events. So these are treated as errors and are shown on the issues page to let you know their impact.

A custom error takes the exception itself and some context (what? OR which?) to make it easy for you to identify. To report a custom error,

```objectivec
// ObjC
NSException* myException = [NSException
                            exceptionWithName:@"FileNotFoundException"
                            reason:@"File Not Found on System"
                            userInfo:nil];
[ApxorSDK reportCustomError:myException withContext:@"customException"];
```

```swift
// Swift
let errorInfo = ["reason": "File Not Found on System"]  as [String : AnyObject]
var customError = NSError(domain:"", code:httpResponse.statusCode, userInfo:nil)
ApxorSDK.reportCustomError(customError, withInfo: errorInfo)
```

### Custom Fonts

ApxorSDK supports two types of fonts namely, .ttf(TrueType fonts) and .otf(OpenType fonts). Custom fonts can be used in ApxorSDK's real time actions in two simple steps.

#### Adding custom fonts to your application

* Fonts of your choice and selection are to be added in the **`Project's`** **`Supporting files`** section

<figure><img src="/files/w8jLWT5vMd02WGI74Kqv" alt=""><figcaption></figcaption></figure>

* Edit the Info.plist file to add the font names in the **`Fonts provided by application`**

<figure><img src="/files/L8eOcjMpAti22U2GwKpE" alt=""><figcaption></figcaption></figure>

* Ensure the fonts are available in, **`Build phases`** --> **`Copy Bundle resources`**

<figure><img src="/files/EMf1gCrw2poTBAXZWbkq" alt=""><figcaption></figcaption></figure>

#### While configuring the campaigns in the dashboard

* Enable custom fonts
* Enter the same exact font file name along with the extenstion (.ttf or .otf)

<figure><img src="/files/6XEfFlBlAwEIaPGeiyNQ" alt=""><figcaption></figcaption></figure>

You're all set! 🎉

> **Note**
>
> Font properties like Bold, Italic cannot be used in the dashboard for custom fonts. The exact font file with those properties has to be added in your application.

#### Handle custom redirection using Key-Value pairs

If your app wants to redirect users based on simple key-value pairs instead using Deeplink URLs or Activity, you can follow below approach

```
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  // Your code here
  // ...
  NotificationCenter.default.addObserver(self, selector: #selector(self.onRedirectionClicked(notification:)), name: Notification.Name("APXRedirectionNotification"), object: nil)
}

@objc func onRedirectionClicked(notification: NSNotification) {
  if let kvPairs = notification.userInfo!["info"] {
      print(kvPairs)
      // ...
      // use kvPairs 
      // ...
  }
}
```

```
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Your code here
  // ...
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onRedirectionClicked:) name:@"APXRedirectionNotification" object:nil];
}

- (void) onRedirectionClicked:(NSNotification *) notification
{
  NSLog(@"kvPairs: %@", notification.userInfo["info"]);
  // ...
  // use kvPairs 
  // ...
}
```

### To get Apxor Device Identifier

Apxor SDK maintains a unique ID for every user. To get the Apxor Device ID,

```objc
NSString *deviceId = [ApxorSDK getDeviceID];
```

> **Note**
>
> If the deviceID is nil, please retry after a few seconds.

### Log Events inside WebView <a href="#log-events-inside-webview" id="log-events-inside-webview"></a>

It is suggested that you log events inside your WebView once the page is completely rendered using Apxor Javascript Interface methods. Based on these events, you can configure Tooltips.

* Methods exposed from Apxor Javascript Interface

  ```js
  window.webkit.messageHandlers.logAppEvent.postMessage({"name": "...", "info": "..."});
  window.webkit.messageHandlers.logClientEvent.postMessage({"name": "...", "info": "..."});
  ```

  > **Note**
  >
  > Make sure the keys of the dictionary in postMessage are "name" and "info", do not change them.
* Examples for logging App Event

  > Example:
  >
  > Log an event on page load event.

  ```html
  ...
  <head>
    ...
    <script>
      function logApxorEvent(eventName, attributes) {
        if (window.webkit) {
          window.webkit.messageHandlers.logAppEvent.postMessage({"name": eventName, "info": attributes});
        }
      }
    </script>
  </head>
  <body onload="logApxorEvent('PageLoaded')">
    ...
  </body>
  ```

  > Example (React based web pages):
  >
  > Log an event on componentDidMount.

  ```js
  componentDidMount() {
      if (window.webkit) {
          window.webkit.messageHandlers.logAppEvent.postMessage({"name": 'LoginPageLoaded'});
      }
  }
  ```

### Actions in WebView

Many native applications feature WebViews to display descriptive content and much more. Apxor iOS SDK provides a way to show real-time actions inside that WebView to make the most of it.

Following are the steps in order to show real-time actions in your WebView.

* Add a tag to the webview (which is to be later provided in the dashboard) as shown.

  ```objectivec
  // ObjC
  [self.webview setTag:007];
  ```

  <pre class="language-swift"><code class="lang-swift"><strong>// Swift
  </strong><strong>webview.tag = 007;
  </strong></code></pre>
* You have to init the APXWKScriptHandler and call registerEventsAndScripts method to make sure any the calls made in the webview are taken care by the native SDK. It's as follows,

  * If you don't already have a bridging header, [checkout how to create a bridging header.](https://developer.apple.com/documentation/swift/importing-objective-c-into-swift)
  * Add the following in the bridging header file.

  ```ObjectiveC
  #import "APXRTAPlugin/APXWKScriptHandler.h"
  ```

  * Now, add the following to the init method of your webview

  ```
  let apxHandler: APXWKScriptHandler = APXWKScriptHandler.init(handlerFor: webView)
  apxHandler.registerEventsAndScripts()
  ```
* Here's how to do the same thing in objective-C.

  * Make sure there's a proper WKUserContentController set to your WkWebView, if not please init it and use that config to initialise your WKWebView.

  ```ObjectiveC
  WKUserContentController *controller = [[WKUserContentController alloc] init];
  WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
  config.userContentController = controller;
  ```

  ```ObjectiveC
  #import "APXRTAPlugin/APXWKScriptHandler.h"
  ...
  // add apxor's script handler
  APXWKScriptHandler *scriptHandler = [[APXWKScriptHandler alloc] initWithHandlerForWebView:_webView];
  [scriptHandler registerEventsAndScripts];
  ```
* Also, make sure there’s an id for the web element that you want to show tooltip on.

<figure><img src="/files/AQ7OUJTGvaX95EPPbrgF" alt=""><figcaption></figcaption></figure>

* In the above example, the element button has an attribute id = change\_button, which will used to identify that particular element.

### Dynamic Script Text in actions

A new capability of writing dynamic text in actions (Tooltips & InApps) had been introduced in latest release of Apxor SDK plugins.

You can write a script (a new language that Apxor is created which somewhat looks like Javascript) instead of plain text to substitue user and session properties that you have already logged to Apxor SDK or you can substitute a text element from your application or hidden text that you set it as keyed tag (apx\_view\_tag).

#### The Apxor Language <a href="#the-apxor-language" id="the-apxor-language"></a>

The Apxor language looks similar to Javascript with some modifications.

We assume every dynamic param that you want to substitute in a text is a pre-defined variable that you can create upfront in the Script dialog that Apxor Dashboard provides to you.

We support following operators and keywords as part of our language specification

**Unary Operators**

> `!` (Negation)

**Logical Operators**

> `&&` (Logical AND)
>
> `||` (Logical OR)

**Mathematical Operators**

> `+` (Arithmetic Addition)
>
> `-` (Arithmetic Subtraction)
>
> `*` (Arithmetic Multiplication)
>
> `/` (Arithmetic Division)
>
> `%` (Arithmetic Modulo)

**Comparison Operators**

> `<` (Less than)
>
> `<=` (Less than or Equals)
>
> `>` (Greater than)
>
> `>=` (Greater than or Equals)
>
> `==` (Equality)
>
> `!=` (Not Equality)
>
> `contains` (Checks if a string contains another string)

**Keywords**

> `httpGet`, `onSuccess`, `onError` will be used to make a HTTP GET API call
>
> `format` will be used to format a string
>
> `if`, `else` will be used to write conditional evaluation
>
> `true`, `false` boolean keywords
>
> `toInt` will be helful to convert double/float values to integer

**Examples**

> **Note:**
>
> Assume the following variables are defined
>
> * UserName (User Property)
> * RewardPoints (User Property)
> * IsSubscribed (User Property)
> * Subscribed (API JSON response parameter `user.is_subscribed`)

* Simple formatting of string

```javascript
format(
  "Hello {}. We are exicted to give you {} reward points. You can see these points in Rewards section",
  UserName,
  toInt(RewardPoints)
);
```

* Conditional Dynamic Text

```javascript
if (!IsSubscribed && RewardPoints < 500) {
  format(
    "Hello {}, you are just {} points away to get free subscription",
    UserName,
    500 - RewardPoints
  );
} else {
  format("Hello {}, You are already subscribed", UserName);
}
```

* API call

```javascript
httpGet(format("https://your-server.com/your-api?userName={}", UserName))
  .onSuccess(() => {
    if (SubScribed) {
      format("Hello {}, you are already subscribed", UserName);
    } else {
      format("Hello {}, you are not subscribed yet", UserName);
    }
  })
  .onError(() => format("Something went wrong. Try again later"));
```

### Yeah! You are ready to create your first [Campaign](/product-guides/create-a-campaign/mobile.md) or [Survey](/product-guides/create-a-survey/legacy.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://guides.apxor.com/getting-started-with-apxor/sdk/ios-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
