iOS Swift SDK

Document Version: 1.0.4 | ZetaKit: 0.1.5-beta

SDK Support and Compatibility

ZetaKit: iOS version iOS 13 and above


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

  • clientSiteId - will be provided from ZMP console post successful app registration
  • clientSecret - will be provided from ZMP console post successful app registration
  • isLoggingEnabled - The value for enabling SDK logs, by default the parameter value will be disabled, logs can be viewed in console app (console app)
  • optIn- The value of enabling the SDK to track events and send data to the backend.
  • region- The SDK supports region configuration; the correct region should be referenced from the ZTRegion struct. Regions cannot be interchanged for the same ZMP account. Based on the region, the clientSecret and clientSiteId may change.
  • appGroupId: appGroupdId is used to track notification delivery status, please look into Push Notification for more details

How to initialize the ZetaClient

The ZetaClient should be initialized inside the didFinishLaunchingWithOptions of your app.

During this initialization, siteId, optIn and region are mandatory fields, and by default, logging will be disabled.

For the SDK to communicate with the server, an clientSecretKey is required. The auth key can be provided either during the SDK initialization or through a late setClientSecret method.

ZetaClient.shared.setClientSecret(Environment.clientSecret)

How get the BSIN from the 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

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(uid: uid)

uid: At the time of opting in, the uid field is optional for identifying the user from the SDK's perspective. If the application does not provide a user ID, the SDK will consider the user as an anonymous user.


User Identification

Update User Properties

The ZTUserManagable protocol is exposed from the ZetaClient for performing user operations. The client app can update the user data using the builder pattern or by creating a ZTUser object at the app level and calling the updateUser method to pass the data to the SDK.

//Update user via builder method
ZetaClient.shared.user.build { user in
     user.uid = "uid"
     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")
}

//Update user via passing ZTUser object to update user
var user = ZTUser(name: "John Wick")
ZetaClient.shared.user.updateUser(user)


For contact information (email, phone number), the client app can also pass the additional info to the SDK.

// 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)
})

//ZTContactAdditionalInfo values 

preferences: [String] -  the default value will be "standard"
subscriptionStatus: ZTContactSubscriptionStatus - value can be NEW, ACTIVE,INACTIVE, NONE
contactProperties: Additioncal contact properties can be added through this dictionary

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

How to start the User Identity Session

The SDK supports tracking user session-specific properties and events. Setting the uid property will facilitate session tracking

//update uid via builder pattern
ZetaClient.shared.user.build { user in
     user.uid = "uid"
}

//update uid via ZTUser 
var user = ZTUser(uid: "john123")
ZetaClient.shared.user.updateUser(user)

How to clear the User Identity Session

ZetaClient.shared.user?.clear()

To pass IDFA, IDFV 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)
  

Location Tracking

location: This method can be called by passing the location coordinates along with foreground / background state. The foreground / background state indicates whether the location data is collected when the app is in the foreground or background.The provided location will be cached in memory, and for subsequent events, the location details will be added as a property.

 ZetaClient.shared.user?.updateLocation(
    ZTLocation(
      latitude: 12.933663251837913,
      longitude: 77.62168405034637,
      isForeground: true))



Event Tracking

Auto Tracked Events

  • app_installed This event is generated only once when the app is launched for the first time.
  • app_opened This event is generated whenever the app transitions from the background to the foreground.
  • app_closed This event is generated whenever the app transitions from the foreground to the background.
  • app_terminated This event is generated whenever the app is closed

Defined Events

Screen Name Tracking

screen: This event is used for tracking user screen navigation. The provided screen name will be cached in memory, and for subsequent events, the screen will be added as a property.

deeplink: An optional field for specifying that this screen is opened through a deeplink URL.

properties: An optional field for specifying additional properties.

ZetaClient.shared.event?.trackScreenName(screen: "Events Screen", deeplink: "app://home", properties: ["screen" : "Home"])

Custom Events

The SDK accepts custom events. To send custom events, the application needs to pass the event name and properties to the SDK.

eventName: A meaningful name that describes the event.

properties: properties is a dictionary of type [String: Any], properties should be JSON encodable and decodable. If any property value is not JSON encodable, the entire event will be discarded.

ZetaClient.shared.event?.send(
  name: "EventTest",
  properties: ["events-array": ["screen1", "screen2"],
              "events-dict": ["screen": "events"]])


Push Notification

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()
    }
}

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

  • Set ZTDeeplinkDelegate delegate after initialising Zeta SDK using ZetaClient.shared.push?.setDeeplinkDelegate
  • Call ZetaClient.shared.push?.userNotificationCenter as highlighted above
 //set deeplink delegate after initializing Zeta SDK
ZetaClient.shared.push?.setDeeplinkDelegate(deeplinkDelegate: self)
// 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]?)
}

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

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
  • you can use ZTNotificationService.canHandle to check if ZetaNotificationService can handle notification
  • Create a app group id and pass it in ZTNotificationService.trackNotificationDelivered

ZTNotificationService is used to provide rich notification extension and track delivery of notification . There are 2 ways to use ZTNotificationService

  1. Make ZTNotificationService super class of NotificationService extension class
import ZetaNotificationService
import UserNotifications

class NotificationService: ZTNotificationService {
    
    public override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        if ZTNotificationService.canHandle(request, withContentHandler: contentHandler) {
            //If Zeta SDK can handle the notification 
            //call ZTNotificationService.trackNotificationDelivered to track delivery
            //super.didreceive to handle rich notification
            ZTNotificationService.trackNotificationDelivered(request, appGroupId: "group.com.xyzapp")
            super.didReceive(request, withContentHandler: contentHandler)
            return
        }
        
         //handle non Zeta notification
         contentHandler(request.content)
    }
}
  1. Use ZTNotificationService directly in your NotificationService class

  2. 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
            }
            
            //handle non Zeta notification
            contentHandler(request.content)
        }
    }
    
     
    

Rich Notification Support:

Rich Notifications allow for more customization in your push notifications by adding additional content.This capability allows the marketer to send media-enabled push messages without developer support.

A ZMP user can upload a single media file or specify a URL for upload into their app. The media is then shown in the preview window of campaign builder. Upon receiving , the SDK downloads the media and displays it in a standard push notification.

iOS push notifications can include GIFs, images, videos.

Supported Media Types for Rich Notification:
Image files formats: JPG, GIF and PNG
Video files formats: MP4

Guidelines
We recommend a 2:1 aspect ratio (landscape) for expanded push notifications.
Images will always span the full width of the notification, and the height will adjust accordingly.