iOS Swift SDK

Version: 1.0.1

SDK Support and Compatibility

  • The minimum iOS version supported is iOS 13


Install SDK

Integrate with Swift Package Manager

Swift Package manager repo - https://gitlab.com/zeta-crm/zetakit-swift

Steps to integrate with Swift Package Manager

  • Enter our Swift Package Manager URL in Project Package Dependency
  • Select Product ZetaCore and ZetaNotification Service to add to your target app

Integrate with Cocoapods

  • Zeta Kit is available with two pods ZetaCore and ZetaNotificationService
  • Add both of these pods to your target app in Podfile
  • target 'YourAppTarget' do
      pod 'ZetaCore'
      pod ZetaNotificationService
    end
    



Initialize SDK

   import ZetaCore
  
  //create a zeta config object, pass in below details
  let zetaConfig = ZTConfig(
            isLoggingEnabled: true,
            clientSiteId: Environment.clientSiteId,
            clientSecret: Environment.clientSecret,
            region: .US,
            appGroupId: "group.xyz.app",
            optin: true
           )
  
  //call initialize method from shared instance of ZetaClient      
  ZetaClient.shared.initialize(config: zetaConfig)

ZTConfig parameters

isLoggingEnabled : set to true if we want logging enabled, logs will be present and can be viewed in console app (console app )
clientSecret: will be provided from ZMP portal post App registration
clientSiteId: will be provided from ZMP portal post App registration
region: Is the region your ZMP Account is configured for, it can be either US or EU, region cannot be interchanged for same ZMP Account
appGroupId: appGroupdId is used to track notification delivery status, please look into Push Notification for more details
optin: pass optin as true to continue SDK tracking and false to stop SDK from tracking

Fetch BSIN from SDK
BSIN uniquely identifies the user in ZMP
To get cached BSIN use ZetaClient.shared.getCachedBSIN()
To listen to lifecycle changes of BSIN, you can set delegate ZetaClient.shared.user?.setIdentityDelegate(delegate: self)

Notes

  • Intialisation can be called on main thread as heavy work is scheduled in background thread
  • clientSecret can be set separately via ZetaClient.shared.setClientSecret(Environment.clientSecret)

Tracking Opt-in and Opt-out

When the application does not require the SDK to track events and user properties, it can utilize the opt-out feature. The application sends the opt-out configuration as part of its initialization or can call a separate opt-out method. If the application is using SDK-exposed methods for opting in or out, the same status should be sent through the SDK initialization configuration during the next launch. Otherwise, the configuration value may overwrite the opt-in/out status. Once the SDK receives the opt-out request, it will immediately stop all communication with the back end, and any data cached in the database will also be cleared. Until the application opts back in, the SDK will neither collect data from the application nor send data to the server. The application can opt in or out at any time during its lifecycle. After opting out, if the application opts back in, the SDK will consider it a fresh launch.

Tracking opt-out

Tracking of SDK can be disabled in two ways:

  1. Pass opt-in as false during initialization
     //create a zeta config object, pass in below details
      let zetaConfig = ZTConfig(
                isLoggingEnabled: true,
                clientSiteId: Environment.clientSiteId,
                clientSecret: Environment.clientSecret,
                region: .US,
                appGroupId: "group.xyz.app",
                optin: false
               )
      
      //call initialize method from shared instance of ZetaClient      
      ZetaClient.shared.initialize(config: zetaConfig)
    
  2. Call stop tracking method
  3. ZetaClient.shared.optOutFromTracking()
    

Tracking opt-in

To start tracking again you can pass optin as true during ZetaClient.shared.initialize or call ZetaClient.shared.optInForTracking(userId: userId)
You can pass current user id to help SDK fetch correct profile of the user from ZMP.



User Identification

Update User Properties

ZTUserBuilder is used to update multiple properties of user at once

//ZTUserBuilder provides client app to update user details in form of
//builder pattern
//This will automaticall call API in background to update user details 

ZetaClient.shared.user.build { user in
     user.userId = "userid"
     user.firstName = "User1"
     user.lastName = "last-name"
     user.name = "name"
     user.signedUpAt = "2024-09-17T05:54:58.000Z"
     user.source = "source"
     user.email = ZTUserEmail(email: "[email protected]")
     user.phone = ZTUserPhone(phone: "1234")
}

// we can update contacts with additional properties if needed
ZetaClient.shared.user?.build({ user in
   
    let ztAdditionalInfo = ZTContactAdditionalInfo()
    ztAdditionalInfo.subscriptionStatus = ZTContactSubscriptionStatus.active
    ztAdditionalInfo.inactivityReason = "reason"
    ztAdditionalInfo.signedUpAt = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.lastOpened = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.doubleOptInStatus = "single"
    ztAdditionalInfo.phoneType = "mobile"
    ztAdditionalInfo.lastSent = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.lastOpened = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.lastClicked = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.properties = ["key": "value"]
    
    user.email = ZTUserEmail(email: value, additionalInfo: ztAdditionalInfo)
})

Notes

  • All date-time should be in ISO 8601 format in UTC
  • preference of contact defaults to standard but other preferences can be added/customized within the settings menu in ZMP. Only those defined values can be passed in preference
  • double_opt_in_status optional field to track double opt ins - default value is null, but will accept single or double values
  • phone_type optional field for phone contact types. default value is null, accepts mobile or landline values

To pass device details and push token

  //pass device token to SDK
  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        ZetaClient.shared.user?.updateDeviceToken(token: deviceToken)
  }
  
  //pass IDFA
  ZetaClient.shared.user?.updateIDFA(idfa)
  
  //pass IDFV
  ZetaClient.shared.user?.updateIDFV(idfv)
  

To clear session when user logs out

ZetaClient.shared.user?.clear()


Event Tracking

Auto Tracked Events

  • first_open generate only once, when initialize is called for the first time
  • app_active generated when: the app enters active state
  • app_inactive generated when: the app enters inactive or background state
  • app_terminate generated when: the app is closed

Defined Events - pre defined event that can be called from App

  • screen event can be called from App when navigating to a new screen
    • this screen name is cached in-memory in sdk, and will be used in subsequent events as a property
ZetaClient.shared.event?.trackScreenName(screen: "Events Screen")

  • location event can be called by passing latitude and longitude in Double, with optional parameter if App is in foreground
ZetaClient.shared.event?.trackLocation(location: ZTLocation(
            latitude: 12.933663251837913,
            longitude: 77.62168405034637,
            isForeground: true))

Custom Events

  • We can send custom events with properties from app
    • name is string type
    • properties is a dictionary of type [String: Any], properties should be valid JSON object or event will not be tracked, an error will be logged to system logger if logging is enabled
ZetaClient.shared.event?.send(
  name: "EventTest",
  properties: ["events-array": ["screen1", "screen2"],
              "events-dict": ["screen": "events"]])


Push Notification Setup

To update device token

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  ZetaClient.shared.user?.updateDeviceToken(token: deviceToken)
}

To track Zeta notification clicked by user

  • Call ZetaClient.shared.push?.userNotificationCenter from did receive response delegate function of UNUserNotificationCenterDelegate
  • This helps in tracking of user click events
  • When it returns true than do not call completion handler as this is taken care by SDK
extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        if ZetaClient.shared.push?.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler) == true {
            //return without calling completion handler
            return
        }
        
        completionHandler()
    }
}

    ///
    /// ```
    /// userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
    /// ```
    /// call this func when same is called in UNUserNotificationCenterDelegate
    /// This returns true if Zeta can handle notification, in that case client app does not need to call completion handler
    /// ```
    /// extension AppDelegate: UNUserNotificationCenterDelegate {
    ///     func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive /// response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    ///        let info = response.notification.request.content.userInfo
    ///        if ZetaClient.shared.push?.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler) == true {
    ///          //return without calling completion handler
    ///          return
    ///       }
    ///
    ///       // handle not Zeta notification clicks
    ///       completionHandler()
    ///     }
    ///  }
    /// ```
    /// - Parameters:
    ///   - center: UNUserNotificationCenter
    ///   - response: UNNotificationResponse
    ///   - completionHandler: notification completion handler
    /// - Returns: true if Zeta SDK can handle notification
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: (() -> Void)?) -> Bool
        

We can also track notification clicked by directly calling below function

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        ZetaClient.shared.push?.trackNotificationClicked(response: response)
        completionHandler()
    }
}

To get notified when zeta notification with deeplink is clicked

  • Call ZetaClient.shared.push?.userNotificationCenter as highlighted above
  • Pass an instance that confirms to ZTDeeplinkDelegate
  • func receivedDeeplink(deeplink: String, extraInfo: [String: String]?)
    • deeplink param will be the deeplink in notification payload passed from Zeta Campaign
    • extraInfo param will be the extraInfo in notification payload passed from Zeta Campaign
 //set deeplink delegate after initializing Zeta SDK
ZetaClient.shared.push?.setDeeplinkDelegate(deeplinkDelegate: self)

// self in above needs to confirm to ZTDeeplinkDelegate
// you will get a callback with deeplink and any extra-info sent from ZMP Campaign
public protocol ZTDeeplinkDelegate: AnyObject {
    func receivedDeeplink(deeplink: String, extraInfo: [String: String]?)
}

To get delivery status of notification

  • Create notification service extension
  • Import ZetaNotificationService from ZetaKit Package and add it to notification service extension target
  • use ZTNotificationService as suggested below in documentation
  • Create a app group id and pass it in ZTNotificationService.trackNotificationDelivered(request, appGroupId: "group.com.abc.xyzproduct")
  • you can use ZTNotificationService.canHandle(request, withContentHandler: contentHandler) to check if ZetaNotificationService can handle notification

/**
 # ZTNotificationService is used to provide rich notification extension
 # There are 2 ways to use ZTNotificationService
 
 #1 - Make ZTNotificationService to super class of NotificationService extension class
 */
 
 import ZetaNotificationService
 import UserNotifications

 class NotificationService: ZTNotificationService {

 }
 
 
// 2 - Use ZTNotificationService only for Zeta Notification Message
//     and custom handling for other notifications
// To track notification delivery status, call trackNotificationDelivered with app group id,
// AppGroupId is used to store notification id in shared user default to be synced with server when app will be launched
 
 import ZetaNotificationService
 import UserNotifications
 
 class NotificationService: UNNotificationServiceExtension {

     public override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        
     if ZTNotificationService.canHandle(request, withContentHandler: contentHandler) {
            ZTNotificationService.trackNotificationDelivered(request, appGroupId: "group.com.abc.xyzproduct")
            ZTNotificationService.handleNotification(request, withContentHandler: contentHandler)
            return
     }
     
         //custom handling
         contentHandler(request.content)
     }
}