iOS Advanced Features

Send Location Information to CleverTap

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation {
    [CleverTap setLocation: newLocation.coordinate];
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation,
                     fromLocation oldLocation: CLLocation) {
    CleverTap.setLocation(newLocation.coordinate)
}

Manually Enable Support for Universal (Deep) Link Tracking

Deep links are a way of launching a native app and providing additional information telling it to do some specific event or show specific content. CleverTap automatically tracks universal links that open your application. If you have universal (deep) links coming to your app, you can capture the incoming UTM parameters easily. Call handleOpenURL:sourceApplication: when the application:openURL:sourceApplication:annotation: message is sent to your app delegate.

- (BOOL) application:(UIApplication *)application
    openURL:(NSURL *)url
    sourceApplication:(NSString *)sourceApplication
    annotation:(id)annotation {
      [[CleverTap sharedInstance] handleOpenURL:url sourceApplication:sourceApplication];
      return YES;
}

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary *)options {
    [[CleverTap sharedInstance] handleOpenURL:url sourceApplication:nil];
    return YES;
}

- (void)openURL:(NSURL*)url options:(NSDictionary<NSString *, id> *)options
completionHandler:(void (^ __nullable)(BOOL success))completion {
    [[CleverTap sharedInstance] handleOpenURL:url sourceApplication:nil];
    if (completion) {
        completion(YES);
    }
}
func application(application: UIApplication, openURL url: NSURL,
                 sourceApplication: String?, annotation: AnyObject) -> Bool {
    CleverTap.sharedInstance()?.handleOpenURL(url, sourceApplication: sourceApplication)
    return true
}

// Swift 3
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
   CleverTap.sharedInstance()?.handleOpen(url, sourceApplication: nil)
   return true
}

func open(_ url: URL, options: [String : Any] = [:],
                   completionHandler completion: ((Bool) -> Swift.Void)? = nil) {
	CleverTap.sharedInstance()?.handleOpen(url, sourceApplication: nil)
	completion?(false)
}

Debugging

By default, CleverTap logs are set to CleverTapLogLevel.info. During development, we recommend that you set the SDK to DEBUG mode, in order to log warnings or other important messages to the iOS logging system. This can be done by setting the debug level to CleverTapLogLevel.debug. If you want to disable CleverTap logs for production environment, you can set the debug level to CleverTapLogLevel.off.

#ifdef DEBUG
   [CleverTap setDebugLevel:CleverTapLogDebug];
#else
   [CleverTap setDebugLevel:CleverTapLogOff];
#endif
#if DEBUG
    CleverTap.setDebugLevel(CleverTapLogLevel.debug.rawValue)
#else
    CleverTap.setDebugLevel(CleverTapLogLevel.off.rawValue)
#endif

To debug the device in the CleverTap dashboard and check if events and profiles are being logged successfully on dashboard:

  1. Copy the CleverTap ID from the logs.
  2. Open the CleverTap dashboard.
  3. Navigate to Segments> Find People > search By Identity.
1205

Search By Identity on CleverTap Dashboard

Setting Account Credentials

If you do not wish to insert your account credentials in your app’s Info.plist, or you want to set your account ID programmatically, you can do so in your app delegate, in application:didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [CleverTap setCredentialsWithAccountID:@"Your account ID here" andToken:@"Your account token here"];
    [CleverTap autoIntegrate];
    ...
    return YES;
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject:AnyObject]?) -> Bool {
    CleverTap.setCredentialsWithAccountID("Your account ID here", andToken: "Your account token here")
    CleverTap.autoIntegrate()
    ...
    return true
}

If you have set used this method to set your CleverTap Account ID and Token, please ensure that you do not make an entry for these values in your info.plist files.

Checking Push Notifications from CleverTap

If you wish to determine whether a notification originated from CleverTap, call this method:

  • (BOOL)isCleverTapNotification:(NSDictionary *)payload;

You must manually call the SDK as follows/add the following CleverTap code to your AppDelegate:

- (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {

    /**
     Use this method to perform the tasks associated with your app's custom actions. When the user responds to a notification, the system calls this method with the results. You use this method to perform the task associated with that action, if at all. At the end of your implementation, you must call the completionHandler block to let the system know that you are done processing the notification.

     You specify your app's notification types and custom actions using UNNotificationCategory and UNNotificationAction objects.
     You create these objects at initialization time and register them with the user notification center. Even if you register custom actions, the action in the response parameter might indicate that the user dismissed the notification without performing any of your actions.

     If you do not implement this method, your app never responds to custom actions.

     see https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter?language=objc
     **/

    if ([[CleverTap sharedInstance] isCleverTapNotification:response.notification.request.content.userInfo]) {

       ...
    }
    completionHandler();
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        
     /**
     Use this method to perform the tasks associated with your app's custom actions. When the user responds to a notification, the system calls this method with the results. You use this method to perform the task associated with that action, if at all. At the end of your implementation, you must call the completionHandler block to let the system know that you are done processing the notification.
     
     You specify your app's notification types and custom actions using UNNotificationCategory and UNNotificationAction objects.
     You create these objects at initialization time and register them with the user notification center. Even if you register custom actions, the action in the response parameter might indicate that the user dismissed the notification without performing any of your actions.
     
     If you do not implement this method, your app never responds to custom actions.
     
     see https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter
     **/

      print("APPDELEGATE: didReceiveResponseWithCompletionHandler \(response.notification.request.content.userInfo)")

      // If you wish to determine whether a notification originated from CleverTap, you must manually call the SDK as follows:
      if CleverTap.sharedInstance().isCleverTapNotification(response.notification.request.content.userInfo) {

          ...
      }
 }

Encryption of PII data

PII data is stored across the SDK and could be sensitive information. From CleverTap iOS SDK v5.2.0 onwards, you can enable encryption for PII data such as Email, Identity, Name, and Phone.

Currently, two levels of encryption are supported, i.e., None(0) and Medium(1). The encryption level is None by default.

  • None: All stored data is in plaintext
  • Medium: PII data is encrypted completely

Default Instance

The only way to set the encryption level for the default instance is from theinfo.plist. Add the CleverTapEncryptionLevel string key to info.plist file where value 1 means Medium and 0 means None. The encryption level will be None if any other value is provided.

Additional Instance

Different instances can have different encryption levels. To set an encryption level for an additional instance:

CleverTapInstanceConfig *ctConfig = [[CleverTapInstanceConfig alloc] initWithAccountId:@"ADDITIONAL_CLEVERTAP_ACCOUNT_ID" accountToken:@"ADDITIONAL_CLEVERTAP_ACCOUNT_TOKEN"];
[ctConfig setEncryptionLevel:CleverTapEncryptionMedium];
CleverTap *additionalCleverTapInstance = [CleverTap instanceWithConfig:ctConfig];
let ctConfig = CleverTapInstanceConfig.init(accountId: "ADDITIONAL_CLEVERTAP_ACCOUNT_ID", accountToken: "ADDITIONAL_CLEVERTAP_ACCOUNT_TOKEN")
ctConfig.encryptionLevel = CleverTapEncryptionLevel.medium
let cleverTapAdditionalInstance = CleverTap.instance(with: ctConfig)

SwiftUI Support

Integration

CleverTap iOS SDK can be integrated into the SwiftUI sample app. SwiftUI provides a way to use AppDelegate within the SwiftUI life cycle by using @UIApplicationDelegateAdaptor. Create a file, e.g.,AppDelegate.swift then create a class of AppDelegate and attach it with the main entry point struct by using the @UIApplicationDelegateAdaptor property wrapper. For more information, refer to sample app.

import UserNotifications
import CleverTapSDK

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        registerForPush()
        CleverTap.setDebugLevel(2)
        CleverTap.autoIntegrate()
        CleverTap.sharedInstance()?.enableDeviceNetworkInfoReporting(true)
        return true
    }
    
    func registerForPush() {
        // Register for Push notifications
        UNUserNotificationCenter.current().delegate = self
        // request Permissions
        UNUserNotificationCenter.current().requestAuthorization(options: [.sound, .badge, .alert], completionHandler: {granted, error in
            if granted {
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        })
    }
}

Refer to the AppDelegate in your entry point struct like so:

import SwiftUI

@main
struct SwiftUIStarterApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

App Inbox in SwiftUI

App Inbox controller can be added using UIViewControllerRepresentable and its callback methods can be used using makeCoordinator method. For more information, refer to sample app.

struct CTAppInboxRepresentable: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> CleverTapInboxViewController {
        let style = CleverTapInboxStyleConfig()
        let inboxVC: CleverTapInboxViewController = (CleverTap.sharedInstance()?.newInboxViewController(with: style, andDelegate: context.coordinator))!
        return inboxVC
    }
    
    func updateUIViewController(_ uiViewController: CleverTapInboxViewController, context: Context) {
        // Updates the state of the specified view controller with new information from SwiftUI.
    }
    
    func makeCoordinator() -> CTCallbackCoordinator {
        // Callback class
    }
}

Track Screen Views in SwiftUI

There is no direct replacement for viewDidLoad() method in SwiftUI, but we can achieve the same behavior using onAppear modifier. For more information, refer to sample app.

#if canImport(SwiftUI)
import SwiftUI
import CleverTapSDK

@available(iOS 13, *)
internal struct CTViewModifier: ViewModifier {
    @State private var viewDidLoad = false
    let screenName: String

    func body(content: Content) -> some View {
        content.onAppear {
            if viewDidLoad == false {
                // `viewDidLoad` eqivalent in SwiftUI
                viewDidLoad = true
                // Record any CleverTap events here.
                CleverTap.sharedInstance()?.recordEvent(screenName)
            }
        }
    }
}

@available(iOS 13, *)
public extension View {
    func recordScreenView(screenName: String) -> some View {
        self.modifier(CTViewModifier(screenName: screenName))
    }
}
#endif