iOS SDK Integration Guide

 

Issues during integration? See FAQ below.

Before You Begin: SDK Prerequisites

Follow the steps in Integrating a Singular SDK: Planning and Prerequisites.

These steps are prerequisites for any Singular SDK integration.

1. Installing the SDK

You can install the Singular SDK using CocoaPods, Swift Package Manager, or a Static Library.

Installing the SDK Using CocoaPods
  1. Download and install the latest version of CocoaPods.
  2. To create a podfile, navigate to the project root folder in the Terminal and type:

    pod init
  3. To add the Singular SDK dependency, add the following to your project's Podfile:

    pod 'Singular-SDK'

    For example:

    pod_for_swiftcocoapods.png

  4. In your Terminal, navigate to the project root folder and run:

    pod install
  5. From this point forward, open the Xcode workspace file .xcworkspace to open the project, instead of the .xcodeproj file.
  6. Create a Swift bridging header according to the instructions below.
Installing the SDK Using Swift Package Manager
  1. In Xcode, go to File > Add Packages and enter the Singular SDK GitHub repository:

    https://github.com/singular-labs/Singular-iOS-SDK

    update_sdk_version_1.png

  2. Update the Singular SDK version:

    update_sdk_version_2.png

  3. Click the Add Package button.
  4. Go to Build Phases > Link Binary with Libraries and add the AdServices.framework library. Be sure to mark it as Optional since it's only available for devices with iOS 14.3 and higher.

    link_binary_adservices.png

  5. Go to Build Settings > Linking > Other Linker Flags and update Other Linker Flags with the following items:

    $(inherited) -ObjC -l"sqlite3.0" -l"z" -framework "AdSupport" -framework "Security" -framework "Singular" -framework "StoreKit" -framework "SystemConfiguration" -framework "WebKit" -framework "iAd"

    frameworks.png

    other_linker_flags.png

  6. Create a Swift bridging header according to the instructions below.
Installing the Singular SDK Framework (Static Library)

Downloading and installing the Singular Framework is only required if you are NOT using the CocoaPods or SPM methods above.

Are you upgrading from Singular SDK 12.3.2 or lower?

Follow the steps below to remove the old Static Library

  1. Navigate to the Project files and remove the Singular Static Library. Typically, you should have a folder named something like Singular-iOS-sdk-v12.3.2. Right-Click the folder and delete it from the project.
  2. Proceed to the Next section to install the new Framework

Adding the Singular Framework for the first time

  1. Download and unzip the SDK Framework.

    Select the correct Framework for your implementation:

  2. Add the unzipped folder to a folder in your Xcode project:

    In Xcode, right-click Your App Name > Add Files To [Your Project Name]. In the dialog that opens, select Options > Create Groups and add the folder where you unzipped the SDK.





    The Singular framework should now be in your project

  3. To add the required libraries:

    • In Xcode, select Build Phases > Link Binary With Libraries.
    • Click + and add the following libraries:

      Libsqlite3.0.tbd
      SystemConfiguration.framework
      Security.framework
      Libz.tbd
      AdSupport.framework
      WebKit.framework
      StoreKit.framework
      AdServices.framework (mark as Optional since it's only
      available for devices with iOS 14.3 and higher).
  4. Embed & Sign the Singular Framework
    • Navigate to General > Frameworks, Libraries, and Embedded Content
    • Adjust the Singular Framework to be "Embed & Sign"

Adding the Swift Bridging Header

If you installed the SDK using CocoaPods or the Swift Package Manager, you must create a Bridging Header for Swift to use the Obj-C libraries from the Singular SDK.

  1. In your project, create a new file of type Header, and name it YourProjectName-Bridging-Header.

    new_header_file.png

  2. Open the file and add the following:

    #import <Singular/Singular.h>

    For example:

    swift_cocoapods_import_singular.png

  3. Go to Build Settings > Objective-C Bridging Header and add the file's relative path:

    objective_c_bridging_header.png

2. Setting Up a Basic SDK Integration

Important:

  • To use Swift, you must have a Bridging Header (see guide above).
  • If you added the Singular SDK using the Swift Package Manager, ensure you updated Build Settings > Other Linker Flags as explained above.

2.1. Importing the Singular Library

In the SceneDelegate, AppDelegate, or any file where Singular will be used, import the Singular class library to start using the Singular SDK.

// If installed with Cocoapods or Swift Package Manager
import Singular
          
// If installed manually in Objective-C (New Framework)
#import <Singular/Singular.h>

// If installed manually in Objective-C (Legacy)
#import "Singular.h"

2.2. Creating a Configuration Object

Before you initialize Singular functionality in your code, you have to create a Singular configuration object and set all your configuration options. This code block should be added in SceneDelegate (or if you are not using SceneDelegate, add it to AppDelegate).

The following code example creates a configuration object and sets some common configuration options, such as enabling SKAdNetwork in Managed Mode and setting a timeout to wait for an ATT response.

The following sections give more details about each of these options and how you can customize them.

Example: Creating a Configuration Object with Some Common Options

SwiftObjective-C
func getConfig() -> SingularConfig? {
     // Create the config object with the SDK Key and SDK Secret
     guard let config = SingularConfig(apiKey: Constants.APIKEY, andSecret:
       Constants.SECRET) else {
         return nil
         }
     
     // Set a 300 sec delay before initialization to wait for the user's ATT response
     // Remove this if you are not displaying an ATT prompt!
     config.waitForTrackingAuthorizationWithTimeoutInterval = 300
     
     // Support custom ESP domains
     config.espDomains = ["links.your-website-domain.com"]
     
     // Set a handler method for deep links
     config.singularLinksHandler = { params in
     self.handleDeeplink(params: params)
     }
     
     return config
      }

Note: Starting with Singular iOS SDK version 12.0.6, SKAdNetwork is enabled by default.

If you are still using an older version of the SDK, you need to enable SKAdNetwork using the following code when creating the configuration object:

SwiftObjective-C
// Enable SKAdNetwork in Managed Mode
config.skAdNetworkEnabled = true

2.3. Customizing SKAdNetwork Options

SKAdNetwork is Apple's framework for determining mobile install attribution without compromising the end user's privacy. SKAdNetwork lets you measure the performance of your app marketing campaigns without sharing the user's personally identifiable information.

By default, SKAdNetwork is enabled in Managed Mode, where the conversion value is managed directly by Singular from the server side. If using Managed Mode, you don't need to add any code to your app to handle SKAdNetwork.

This allows for maximum flexibility as you can set and change your conversion values through the Singular platform without modifying your client-side code.

This server-side managed mode also helps you deal with the SKAdNetwork timers. SKAdNetwork allows you to update the conversion value within 24 hours from the time of registration to SKAdNetwork. Any call to update the conversion value extends the timer by 24 more hours. Therefore, when choosing your conversion events, you'll have to make sure the events happen within that update window. In managed mode, you can change the conversion event configuration at any time without releasing a new version of your app.

Using SKAdNetwork in Manual Mode (Advanced)

Learn how to use SKAdNetwork in manual mode

If you want to update the conversion value on your own using the app code, you first have to set the manualSkanConversionManagement flag in the Singular Config. This lets you use several SDK methods to retrieve and update the conversion value manually.

To enable Manual Mode:

SwiftObjective-C
func getConfig() -> SingularConfig? {
     // Singular Config Options
     guard let config = SingularConfig(apiKey: Constants.APIKEY,
       andSecret: Constants.SECRET) else {
         return nil
         }
     //...
     config.manualSkanConversionManagement = true
     //...
     return config
}

To update the conversion value:

In Manual Mode, to update the conversion value, you need to use the skanUpdateConversionValue method. You can use it wherever needed in your app's lifecycle.

Note: The skanUpdateConversionValue method will not function if you have not enabled manualSkanConversionManagement.

skanUpdateConversionValue Method
Description Manually updates the SKAdNetwork conversion value.
Signature (BOOL)skanUpdateConversionValue:(NSInteger)conversionValue;
Usage Example
SwiftObjective-C
// Sending a Standard Event for Login
Singular.event(EVENT_SNG_LOGIN)
      
// Manually updating the conversion value to 7 after the Event
Singular.skanUpdateConversionValue(7)

Other SKAdNetwork methods:

To get the current conversion value, use the skanGetConversionValue method or conversionValueUpdatedCallback. Both work in Managed and Manual Mode.

skanGetConversionValue Method
Description Get the current conversion value tracked by the Singular SDK.
Signature (NSNumber *)skanGetConversionValue;
Usage Example
SwiftObjective-C
let conversionValue = Singular.skanGetConversionValue()
conversionValueUpdatedCallback Callback
Description Get the current conversion value tracked by the Singular SDK.
Signature void(^conversionValueUpdatedCallback)(NSInteger);
Usage Example
SwiftObjective-C
func getConfig() -> SingularConfig? {
     // Singular Config Options
     guard let config = SingularConfig(apiKey: Constants.APIKEY,
       andSecret: Constants.SECRET) else {         
         return nil
         }     
     //...
     config.conversionValueUpdatedCallback = { conversionValue in
     // Here you have access to the latest conversion value
     }
     //...
     return config
}

2.4. Handling ATT Consent (Setting an Initialization Delay)

Displaying an ATT (App Tracking Transparency) Prompt

Starting with iOS 14.5, apps are required to ask for user consent (using the App Tracking Transparency framework) before they can access and share some user data that is helpful for tracking purposes, including the device's IDFA.

Singular highly benefits from having the IDFA to identify devices and perform install attribution (although there are ways to perform attribution without the IDFA). We strongly recommend that you ask for the user's consent to get the IDFA.

Delaying Initialization to Wait for ATT Response

By default, the Singular SDK sends a user session when it's initialized. When a session is sent from a new device, it immediately triggers Singular's attribution process - which is performed based only on the data available to Singular at that point. Therefore, it's essential to ask for consent and retrieve the IDFA before the Singular SDK sends the first session.

To delay the firing of a user session, initialize the Singular SDK with the waitForTrackingAuthorizationWithTimeoutInterval option in the Config object. This option is already included in the code sample in 2.2. Creating a Configuration Object.

SwiftObjective-C
func getConfig() -> SingularConfig? {
     guard let config = SingularConfig(apiKey: Constants.APIKEY,
       andSecret: Constants.SECRET) else {
         return nil
         }
     //...
     config.waitForTrackingAuthorizationWithTimeoutInterval = 300
     //...
     return config
}

Tip: When you set an initialization delay, the app flow is as follows:

  1. When the app opens, the Singular SDK starts recording a session and user events but does not send them to the Singular server yet.
  2. When App Tracking Transparency consent is granted/denied, or the set time elapses, the SDK sends the session and any queued events to the Singular server (with or without the IDFA).
  3. Singular then starts the attribution process, taking advantage of the IDFA if it is available.
Learn About All Possible ATT Scenarios

The following table summarizes the possible scenarios using this integration:

Scenario IDFA Availability
The user sees the consent dialog and grants consent before the set time elapses. IDFA is available
The user sees the consent dialog and denies consent before the set time elapses. IDFA is not available
The set time expires, then the user is shown the consent dialog and grants consent. IDFA is available only for the user events that are reported after the consent is granted
The set time expires, then the user is shown the consent dialog and denies consent. IDFA is not available
The user is shown the consent dialog, exits the app without taking action, and later opens the app and grants consent after the set time has expired. Any queued events are sent to the Singular server when the app is reopened. The IDFA is not available for these events. Any events tracked after consent is granted do have IDFA associated with them.
The user is shown the consent dialog, exits the app without taking action, and later opens the app and denies consent. Any queued events are sent to the Singular servers when the app is reopened. The IDFA is not available for these events or any of the events tracked afterward.

2.5. Handling Deep Links

When a user clicks a deep link on a device that has the app installed, the app opens and shows a specific product or experience.

Singular tracking links can include deep linking functionality as well as deferred deep linking (see our Deep Linking FAQ and the Singular Links FAQ for more information).

The Singular SDK Config, implemented in the previous step, references a callback function ("handleDeeplink"). The "handleDeeplink" function is required to enable deep link and deferred deep link support through the Singular SDK.

Prerequisites for Implementing Deep Links

Make sure you have completed the following steps:

  • Followed the instructions in Singular Links Prerequisites.
  • In Xcode, added a Singular Custom Subdomain to Signing & Capabilities > Associated Domains.
  • Added the app scheme to your URL Types at Info > URL Types.
  • Added your Apple Developer Team ID and Scheme in the Apps page in the Singular platform.

Notes:

  • If the app is already configured to use iOS Universal Links, the Universal Link domain already exists in Associated Domains and can remain. This domain should be added to the Supported Domains config option, as noted in the next section.
  • You must also include the Singular custom link domain, so that Singular can track attributions from marketing campaigns and handle deep links from these campaigns.

Creating the Callback Method for the Handler

The code example below creates a callback method called handleDeeplink (this method is referenced in the Config code sample above).

The block signature is void(^)(SingularLinkParams*). The SingularLinkParams contains the deep link destination, passthrough parameters, and whether the link is deferred or not.

SwiftObjective-C
func handleDeeplink(params: SingularLinkParams?) {
     // Get Deeplink data from Singular Link
     let deeplink = params?.getDeepLink()
     let passthrough = params?.getPassthrough()
     let isDeferredDeeplink = params?.isDeferred()
     let urlParams = params?.getUrlParameters()
     // Add deep link handling code here
     //...
}

Other Link Options

2.6. Initializing Singular

Tip: Before proceeding, make sure you have completed the steps below!

  • Added the Singular Library
  • If using swift: created a Swift Bridging Header
  • Added code to create the Singular Config object
  • Added a deep link handler
  • Enabled SKAdNetwork
  • If showing the ATT: added waitForTrackingAuthorizationWithTimeoutInterval
  • Test built the app successfully (the app should build without error at this stage)

The Singular SDK should be initialized every time your app is opened. This is a prerequisite to all Singular attribution functionality, and it also sends a new user session to Singular (sessions are used to calculate user retention). The SDK is initialized using the config object that you created in 2.2. Creating a Configuration Object.

Where to Add the Initialization Code?

You have to initialize the Singular SDK in every entry point to the app:

  • For iOS 13+ using the SwiftUI Interface with no SceneDelegate or AppDelegate, initialize the Singular SDK in the following ContentView().onOpenURL() and .onChange(of: scenePhase) (See code below for example).

  • For iOS 13+, initialize the Singular SDK in the following SceneDelegate functions: willConnectTo session, continue userActivity, openURLContexts URLContexts.

  • For older versions of iOS that don't support SceneDelegate, initialize the SDK in the following AppDelegate functions: didFinishLaunchingWithOptions, continueUserActivity, openURL.

Initialization Code Examples

For iOS 13+ (Swift SceneDelegate)
// INITIALIZE THE SDK IN THE FOLLOWING SCENEDELEGATE FUNCTIONS]
// willConnectTo session
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let userActivity = connectionOptions.userActivities.first // Print IDFV to Console for use in Singular SDK Console print(Date(), "-- Scene Delegate IDFV:", UIDevice().identifierForVendor!.uuidString as Any)
//Initialize the Singular SDK here: if let config = self.getConfig() { config.userActivity = userActivity Singular.start(config) } } // continue userActivity func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { // Starts a new Singular session on continueUserActivity if let config = self.getConfig() { config.userActivity = userActivity Singular.start(config) } } //openURLContexts URLContexts func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { // Starts a new Singular session on cold start from deeplink scheme if let config = self.getConfig() { config.openUrl = openurlString Singular.start(config) }
// Add custom code here to Redirect to non-Singular deep links //... }
For iOS 13+ (SwiftUI Interface)
// INITIALIZE THE SDK IN THE FOLLOWING WINDOWGROUP FUNCTIONS
var body: some Scene { WindowGroup { ContentView() .onOpenURL(perform: { url in openURL = url // Initialize Singular from an openURL if let config = self.getConfig() { config.openUrl = url Singular.start(config) } }) } .onChange(of: scenePhase) { oldValue, phase in // The SwiftUI ScenePhases replaces the old SceneDelegate lifecycle events switch phase { case .background: print("App Scene: backgrounded") case .inactive: print("App Scene: inactive") case .active: print("App Scene: active") // Initialize Singular if let config = self.getConfig() { Singular.start(config) } @unknown default: print("App Scene: unknown") } } }
For iOS 13+ (Objective-C SceneDelegate)
// INITIALIZE THE SDK IN THE FOLLOWING SCENEDELEGATE FUNCTIONS
     
// willConnectToSession
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions { NSUserActivity* userActivity = [[[connectionOptions userActivities] allObjects]
firstObject]; // Print identifier for Vendor (IDFV) to Xcode Console for use in Singular SDK Console NSLog(@"-- Scene Delegate IDFV: %@", [[[UIDevice currentDevice] identifierForVendor] UUIDString]);
// Start a new Singular session from a backgrounded app SingularConfig *config = [self getConfig]; config.userActivity = userActivity; [Singular start:config]; } // continueUserActivity
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity{ // Starts a new Singular session from a backgrounded App SingularConfig *config = [self getConfig]; config.userActivity = userActivity; [Singular start:config]; } // openURLContexts
- (void)scene:(UIScene *)scene openURLContexts:(nonnull NSSet *)URLContexts { // Starts a new Singular session on cold start from deeplink scheme SingularConfig *config = [self getConfig]; config.openUrl = url; [Singular start:config]; // Add custom code here to Redirect to Non-Singular deep links //... }
For Older iOS Versions (Objective-C AppDelegate)
// INITIALIZE THE SDK IN THE FOLLOWING APPDELEGATE FUNCTIONS
    
// didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Starts new session when user opens the app if session timeout passed/opened using Singular Link SingularConfig *config = [self getConfig]; config.launchOptions = launchOptions; [Singular start:config]; return YES; } // continueUserActivity
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id> *restorableObjects))restorationHandler { // Starts a new session when the user opens the app using a Singular Link while it was in the background SingularConfig *config = [self getConfig]; config.userActivity = userActivity; [Singular start:config]; return YES; } // openURL
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options{ // Starts new session when user opens the app using a non-Singular link, like a traditional app scheme. SingularConfig *config = [self getConfig]; config.openUrl = url; [Singular start:config]; // Add custom code here to Redirect to non-Singular deep links //... return YES; }

Notes:

  • When creating the config object, be careful to pass the correct option - userActivity or openUrl. See the sample code below and refer to the sample apps if needed.
  • Remember to comply with the various privacy laws enacted in regions where you conduct business, including GDPR, CCPA, and COPPA. For more information, see SDK Opt-In and Opt-Out Practices and review the Singular SDK functions that help you comply with data privacy laws.
  • To initialize the SDK, you need your Singular SDK Key and SDK Secret. You can get them in the Singular platform at "Developer Tools > SDK Integration > SDK Keys".

2.7. Sending the User ID to Singular (Optional)

You may send your internal User ID to Singular using a Singular SDK method.

Note: If you use Singular's Cross-Device solution, you must collect the User ID across all platforms.

  • The User ID can be any identifier and should not expose PII (Personally Identifiable Information). For example, you should not use a User's email address, username, or phone number. Singular recommends using a hashed value unique only to your first-party data.
  • The User ID value passed to Singular should also be the same internal User ID you capture across all platforms (Web/Mobile/PC/Console/Offline).
  • Singular will include the User ID in user-level exports, ETL, and Internal BI postbacks (if configured). The User ID is first-party data, and Singular does not share it with other parties.
  • The User ID value, when set with the Singular SDK Method, will persist until it is unset using the unsetCustomUserId method or until the app is uninstalled. Closing or restarting the app does not unset the User ID.

To set the User ID, use the setCustomUserId method. To unset it (for example, if the User "logs out" of the account), call unsetCustomUserId.

Note: If multiple Users use a single device, we recommend implementing a logout flow to set and unset the User ID for each login and logout.

If you already know the user ID when the app opens, call setCustomUserId before initializing the Singular SDK. This way, Singular can have the User ID from the first Session. However, the User ID is typically unavailable until the User registers or performs a login. In that case, call setCustomUserId after the registration flow is complete.

setCustomUserId and unsetCustomUserId Methods
Description Sets and Unsets the user ID.
Signature (void)setCustomUserId:(NSString*)customUserId
(void)unsetCustomUserId;
Usage Example
SwiftObjective-C
setCustomUserId("custom_user_id")
unsetCustomUserId()
Optional: Custom User ID Device Mapping

Important: This advanced Enterprise feature is only available in exceptional cases. Please consult with one of Singular’s Solution Engineers before implementing it.

Singular can receive additional mobile event tracking data via a server-to-server integration. To utilize this feature, you must map the User ID to Singular’s Mobile Device tracking identifier.

Note: Call this method as soon as possible after initializing the Singular SDK or once you have the User ID.

setDeviceCustomUserId Method
Description Sets the Custom User Id the same as login and maps it to Singular’s tracking identifier.
Signature static void setDeviceCustomUserId(String customUserId)
Usage Example
SwiftObjective-C
setDeviceCustomUserId("custom_user_id")

2.8. Implementing Global Properties (Optional)

The Singular SDK lets you define custom properties to be sent to the Singular servers along with every session and event sent from the app. These properties can represent any information you want about the user, the app mode/status, or anything else.

You can define up to 5 global properties. They persist between app runs (with the latest value you gave them) until you unset them or the user uninstalls the app.

Use Cases

Some use cases for global properties are:

  • Pass an identifier from a third-party SDK and then use it in a postback from Singular to said third party for matching purposes.
  • In a gaming app, you can define a property called "Level" and set it initially to "0". Any session and event sent from the app will be sent with "Level": "0". Once the user levels up, you reset the property to "1" and so on.

Notes:

  • Global properties are currently reflected in Singular's user-level event logs (see Exporting Attribution Logs) and in postbacks. Support for global properties in Singular's aggregate reporting (the Reports page or the reporting API) will be added in the future. If you have questions about this feature or are interested in updates to global properties support, contact your Singular customer success manager.
  • Each property name and value can be up to 200 characters long. If you pass a longer property name or value, it will be truncated to 200 characters.

Setting Global Properties through the Config Object

To set global properties before initializing the SDK, use the setGlobalProperty method in the Config object.

Note that since global properties and their values persist between app runs, the property you are setting may already be set to a different value. Use the overrideExisting parameter to tell the SDK whether to override an existing property with the new value or not.

setGlobalProperty Method
Description Set a global property.
Signature (void)setGlobalProperty:(NSString*)key withValue:(NSString*)value overrideExisting:(BOOL)overrideExisiting;
Usage Example
SwiftObjective-C
func getConfig() -> SingularConfig? {         
  // (Optional) Get 3rd-party identifiers to set in Global Properties:     
  // If 3rd-party SDKs are providing any identifiers to Singular, the
  // respective SDK must be initialized before Singular.     
  let thirdPartyKey = "anonymousID"     
  let thirdPartyID = "2ed20738-059d-42b5-ab80-5aa0c530e3e1"     
  
  // Singular Config Options     
  guard let config = SingularConfig(apiKey: Constants.APIKEY, 
    andSecret: Constants.SECRET) else {
return nil } //... // Using Singular Global Properties feature to capture third party identifiers config.setGlobalProperty(thirdPartyKey, withValue: thirdPartyID, overrideExisting: true) //... return config }

Setting Global Properties After Initialization

Use the following methods to set, unset, and retrieve global properties at any time in the app's run.

Notes:

  • If the property does not exist yet, and there are already 5 other global properties, the property will not be added.
  • If the property has already been set, the overrideExisting parameter determines whether the existing value will be overridden.
  • The method returns true if the property was set successfully or false otherwise.
setGlobalProperty Method
Description Set a global property to a given value.
Signature (BOOL) setGlobalProperty:(NSString*)key andValue:(NSString*)value overrideExisting:(BOOL)overrideExisting
Usage Example
SwiftObjective-C
var result = Singular.setGlobalProperty("propertyName",
  andValue: "propertyValue", overrideExisting: true)
getGlobalProperties Method
Description Retrieve all the global properties and their current values as a Map.
Signature NSDictionary*) getGlobalProperties
Usage Example
SwiftObjective-C
var globalProperties = Singular.getGlobalProperties()
unsetGlobalProperty Method
Description Remove a global property.
Signature (void) unsetGlobalProperty:(NSString*)key
Usage Example
SwiftObjective-C
Singular.unsetGlobalProperty("propertyName")
clearGlobalProperties Method
Description Remove all global properties.
Signature (void) clearGlobalProperties
Usage Example
SwiftObjective-C
Singular.clearGlobalProperties()

2.9. Modifying the Session Timeout (Optional)

By default, if the app runs in the background for 60 seconds or more before returning to the foreground, the SDK will register a new session. To change this timeout value, use the setSessionTimeout method and add it to the Config.

setSessionTimeout Method
Description Change the session timeout value.
Signature (void)setSessionTimeout:(int)timeout
Usage Example
SwiftObjective-C
func getConfig() -> SingularConfig? {       
  // Singular Config Options     
  guard let config = SingularConfig(apiKey: Constants.APIKEY, andSecret:
    Constants.SECRET) else {         
      return nil     
      }     
  //...
  Singular.setSessionTimeout(120)      
  //...
  return config
}

3. Tracking Events and Revenue

Note: For details on planning user events, see Will the App Track User Events? in the SDK Planning and Prerequisites guide.

Note: We recommend sending all in-app events to the Singular server using Singular SDK methods in your app. If you plan to send events to Singular from another provider or an internal server, see the Hybrid Event Tracking section below.

3.1. Tracking Events

Singular can collect data about in-app events to help analyze the performance of your campaigns and measure KPIs. For example, your organization may want to collect data about user logins, registrations, tutorial completions, or leveling up in a gaming app.

The list of events sent to Singular (with the accompanying attributes) should be compiled by the UA/marketing/business team based on your Marketing KPIs.

For more details on planning user events, see Will the App Track User Events? in the SDK Planning and Prerequisites guide.

In your code, send standard events to Singular using the event or eventWithArgs methods.

Note: For standard events, use the event's iOS name as it appears in the iOS SDK List of Standard Events and Attributes, e.g., EVENT_SNG_LOGIN.

For custom events, events that your organization wants to measure that do not match any of Singular's standard events, use any custom name (maximum of 32 characters). We recommend using names in English for compatibility with any ad network partners that may receive the event from Singular for optimization purposes.

event Method
Description Send a user event to Singular for tracking.
Signature (void)event:(NSString *)name
Usage Example
SwiftObjective-C
// Using Standard Event
Singular.event(EVENT_SNG_LOGIN)

// Using Custom Event
Singular.event("signup")
eventWithArgs Method
Description Send a user event to Singular for tracking, with additional information.
Signature (void)eventWithArgs:(NSString *)name, ...
Usage Example
SwiftObjective-C
//Using Standard Event
var dic: [AnyHashable : Any] = [:]
dic[ATTRIBUTE_SNG_ATTR_CONTENT_TYPE] = "PrivacyController"
dic[ATTRIBUTE_SNG_ATTR_CONTENT_ID] = "0"
dic[ATTRIBUTE_SNG_ATTR_CONTENT] = "GDPR and CCPA Opt-Out Options"
Singular.event(EVENT_SNG_CONTENT_VIEW, withArgs: dic)
//Using Custom Event var bonusdata: [AnyHashable: Any] = [ "level": 10, "points": 500 ] Singular.event("Bonus Points Earned", withArgs: bonusdata)

3.2. Tracking Revenue

Singular can collect data about revenue gained through the app to help analyze the performance and ROI of your campaigns. Singular will make the data available in reports, log export, and postbacks.

Note:If you app supports IAP tracking through the App Store, this method is recommended. If you are not using App Store IAP, see the alternative methods below.

Reporting Revenue Through IAP Tracking (Recommended)

To report revenue events to Singular, use the iapComplete SDK method. This method sends an IAP (Apple's In-App Purchase) revenue event to Singular with:

  • All the transaction details, which Singular will use to enrich reports.
  • The transaction receipt, which can be used to validate the transaction and analyze or prevent fraud attempts.

Notes:

  • When using the iapComplete, you must include the SKPaymentTransaction object in the event.
  • Any revenue reported in a different currency will be auto-converted to your organization's preferred currency, as set in your Singular account.
iapComplete Method
Description Send a revenue event to Singular with the transaction receipt.
Signature

(void)iapComplete:(id)transaction

(void)iapComplete:(id)transaction withName:(NSString *)name;

Usage Example
SwiftObjective-C
// *** Get the SKPaymentTransaction* transaction object ***
let transaction:SKPaymentTransaction = ...
      
// Send a transaction event to Singular without a custom event name
Singular.iapComplete(transaction)
      
// Send a transaction event to Singular with a custom event name
Singular.iapComplete(transaction, withName:"MyCustomRevenue")

Alternative Revenue Event Reporting

While Singular recommends using iapComplete, we also provide two alternative methods to report revenue events to Singular, in case your app doesn't use App Store IAP tracking.

The Revenue and CustomRevenue Methods

The revenue and customRevenue methods let you specify the transaction amount and currency manually, as well as optional additional details, such as the product serial number and quantity, etc. The customRevenue method also lets you pass a custom event name.

Note that if you use these methods, Singular does not get the transaction receipt and cannot validate the transaction.

Note: Pass currency as a three-letter ISO 4217 currency code, e.g., "USD," "EUR", "INR".

revenue Method
Description Send a revenue event to Singular with the revenue amount and currency and optional additional details.
Signature

(void)revenue:(NSString *)currency amount:(double)amount;

(void)revenue:(NSString *)currency amount:(double)amount productSKU:(NSString *)productSKU productName:(NSString *)productName productCategory:(NSString *)productCategory productQuantity:(int)productQuantity productPrice:(double)productPrice;

(void)revenue:(NSString *)currency amount:(double)amount withAttributes:(NSDictionary*)attributes;

Usage Example
SwiftObjective-C
// Revenue with no product details
Singular.revenue("USD",amount:1.99)
      
// Revenue with product details
Singular.revenue("EUR",amount:5.00, productSKU:"SKU1928375", 
  productName:"Reservation Fee",productCategory:"Fee", 
productQuantity:1, productPrice:5.00) // Send a Revenue Event with attributes in a dictionary var dic: [AnyHashable : Any] = [:] dic[ATTRIBUTE_SNG_ATTR_ITEM_DESCRIPTION] = "100% Organic Cotton Mixed Plaid Flannel Shirt" dic[ATTRIBUTE_SNG_ATTR_ITEM_PRICE] = "$69.95" dic[ATTRIBUTE_SNG_ATTR_RATING] = "5 Star" dic[ATTRIBUTE_SNG_ATTR_SEARCH_STRING] = "Flannel Shirt" Singular.revenue("USD", amount: 19.95, withAttributes: dic)
customRevenue Method
Description Send a revenue event to Singular with an event name as well as the revenue amount and currency and optional additional details.
Signature

(void)customRevenue:(NSString *)eventName currency:(NSString *)currency amount:(double)amount;

(void)customRevenue:(NSString *)eventName currency:(NSString *)currency amount:(double)amount productSKU:(NSString *)productSKU productName:(NSString *)productName productCategory:(NSString *)productCategory productQuantity:(int)productQuantity productPrice:(double)productPrice;

(void)customRevenue:(NSString*)eventname currency:(NSString *)currency amount:(double)amount withAttributes:(NSDictionary*)attributes;

Usage Example
SwiftObjective-C
// Revenue with a custom name and no product details
Singular.customRevenue("MyCustomRevenue", currency:"USD", amount:1.99)
      
// Revenue with a custom name and product details
Singular.customRevenue("MyCustomRevenue", currency:"EUR", amount:5.00, 
productSKU:"SKU1928375", productName:"Reservation Fee", productCategory:"Fee", productQuantity:1, productPrice:5.00) // Send a Custom Revenue Event with attributes in a dictionary var dic: [AnyHashable : Any] = [:] dic[ATTRIBUTE_SNG_ATTR_ITEM_DESCRIPTION] = "100% Organic Cotton Mixed Plaid Flannel Shirt" dic[ATTRIBUTE_SNG_ATTR_ITEM_PRICE] = "$69.95" dic[ATTRIBUTE_SNG_ATTR_RATING] = "5 Star" dic[ATTRIBUTE_SNG_ATTR_SEARCH_STRING] = "Flannel Shirt" Singular.customRevenue("CustomRevenueWithArgsDic", currency: "USD",
amount: 44.99, withAttributes: dic)

3.3. Hybrid Event Tracking (Advanced)

Singular recommends sending all events and revenue through the Singular SDK integrated into your app. However, Singular can collect events and revenue from other sources.

Any event NOT sent from the Singular SDK must comply with Singular's Server-to-Server Event documentation requirements and provide the matching device identifier to correctly attribute an event.

Important:

Discrepancies will occur if device identifiers used on Server-to-Server event requests do not have a matching device identifier in Singular. Be aware of the following possibilities:

  • If an event request is received "before" the Singular SDK has recorded the device identifier, from an App Session, then the event request will be considered the "first session" for the unknown device, and Singular will attribute the device as an organic attribution.
  • If the Singular SDK did record a device identifier, but the Singular SDK identifier differs from the device identifier specified in the Server-to-Server Event request then the event will be attributed incorrectly.

Hybrid Event Tracking Guides

Sending Events from an Internal Server

Singular can collect data about revenue from your Server to help analyze the performance and ROI of your campaigns.

Requirements:

  • From an in-app Registration or Login Event, capture and pass the device identifiers and store this data with the User ID on your server. Because device identifiers may change for a user, be sure to update the identifiers when a user generates an app session. This guarantees the server-side event will be attributed to the correct device.
  • Server-side events are platform specific and should only be sent with the device identifier matching the device platform (e.g., IDFA or IDFV for iOS devices).
  • You can use the Singular Internal BI postback mechanism to push an event in real time to your internal endpoint so that you can update the data set on the server side. See the Internal BI Postback FAQ.
  • Review the "Tracking Revenue" section in the Server-to-Server Integration guide for details.
Sending Events from a Revenue Provider
Third-party providers like RevenueCat or adapty can provide Purchase and Subscription Revenue to Singular.

Follow the links below for details on how to enable these partners.

Sending Events from Segment

To enable Segment to send events to Singular, in parallel with the Singular SDK, you must add a "Cloud-Mode" Destination in Segment. Follow our guide HERE.

4. Advanced Options

4.1. Creating Short Referrer Links

Note: This functionality is available in SDK version 11.0.8+.

Use the createReferrerShortLink method to generate a shortened share link for the User to share the app with friends. Define the referring user details in your app code when the link is created. This allows for tracking referrer attributions in reporting.

To create a short link:

  1. Build a Singular Custom Source Link with defined deep links, that leads to your app download (see the Singular Links FAQ). This link will be referred to as a base link in the code below.
  2. Any campaign override parameters to add to the link dynamically (see Tracking Link Parameters for the list of options).
  3. The Name and ID of the referring user, in order to track new app installs back to the user who shared the link.

Use the createReferrerShortLink method to generate the short link as in the example below.

createReferrerShortLink Method
Description Use the createReferrerShortLink method to generate a shortened share link for the User to share the app with friends.
Signature (void)createReferrerShortLink:(NSString *)baseLink referrerName:(NSString *)referrerName referrerId:(NSString *)referrerId passthroughParams:(NSDictionary *)passthroughParams completionHandler:(void(^)(NSString *, NSError *))completionHandler;
Usage Example
SwiftObjective-C
// 1. Define variables for the referrer short link
            
// Define your Singular tracking link to be used as a base link:
let referrerBaseLink = "https://yourdomain.sng.link/Csfge/aknl?_dl=myscheme%3A%2F%2Fmydeeplink/referrer&_smtype=3";
    
// Add your Referrer ID and Name
let referrerID = referrerIDField.text;
let referrerName = referrerNameField.text;
    
// Customize any Passthrough Parameters
let passthroughParams = ["channel": "sms"]
        
// 2. Call  ReferrerShortLink to get your shortlink to share on social media
Singular.createReferrerShortLink(referrerBaseLink, 
referrerName: referrerName, referrerId: referrerID,
passthroughParams: passthroughParams,
completionHandler: {(shortLink, error) in if error != nil { // Logic to retry/abort/modify the params passed to the function,
// based on the cause of the error } if (shortLink != nil || shortLink != "") { // Add your share logic here: //... } })

4.2. Adding Ad Revenue Attribution Support

Note:  Starting in SDK version 11.0.0, Singular added the option to set up ad revenue attribution through the Singular SDK. Ad revenue attribution can still be set up using API calls, without updating the Singular SDK in the apps. However, if you want to measure ad revenue for SKAdNetwork campaigns, it is required to set up ad revenue attribution through the SDK.

To add ad revenue attribution support in the Singular SDK:

  1. Make sure you are using the latest version of the Singular SDK.
  2. Add the appropriate code snippet to your Singular SDK integration, depending on the mediation platform used for ad revenue data.

Note: Pass currency as a three-letter ISO 4217 currency code, e.g., "USD," "EUR", "INR".

AdMob
  1. Note: This feature needs to be enabled in your Admob account.

    See https://support.google.com/admob/answer/11322405#getstarted

SwiftObjective-C
var rewardedAd: GADRewardedAd?
GADRewardedAd.load(withAdUnitID: "AD_UNIT_ID", request: request) { 
[weak self] ad, error in guard let self = self else { return } if let error = error { print("Rewarded ad failed to load with error: \(error.localizedDescription)") return } self.rewardedAd = ad self.rewardedAd?.paidEventHandler = { adValue in let impressionData = adValue let data = SingularAdData(
adPlatform: "Admob", currency: impressionData.currencyCode, revenue: impressionData.value) }
}
AppLovinMax
  1. Retrieve the object received from AppLovin MAX's event, didReceivedMessage
SwiftObjective-C
if let message = message {
   let data = SingularAdData(
adPlatform: "AppLovin", currency: "USD", revenue: message.data["revenue"] as? Double ?? 0.0) Singular.adRevenue(data) }
IronSource
  1. Retrieve the object received from IronSource's event, impressionDataDidSucceed
  2. Ensure that the ARM SDK Postbacks Flag in IronSource is turned on
  3. See https://developers.is.com/ironsource-mobile/general/ad-revenue-measurement-postbacks/#step-1
SwiftObjective-C
if let impressionData = impressionData {
   let data = SingularAdData(
adPlatform: "IronSource", currency: "USD", revenue: impressionData.revenue) Singular.adRevenue(data) }
TradPlus
  1. Set the impressionDelegate
  2. Add Singular to the TradPlusAdImpression Callback
SwiftObjective-C
TradPlus.sharedInstance().impressionDelegate = self

func tradPlusAdImpression(_ adInfo: [AnyHashable: Any]?) { guard let adInfo = adInfo else { return } let currency = "USD" if let ecpm = adInfo["ecpm"] as? Float { let revenue = CGFloat(ecpm) / 1000.0 let data = SingularAdData(adPlatform: "TradPlus", currency: currency, revenue: revenue) Singular.adRevenue(data) }
}
Other (Generic)
  1. Initialize the SingularAdData object with the relevant data
  2. Report the data to Singular
SwiftObjective-C
let data = SingularAdData(
adPlatform: "YOUR_AD_PLATFORM", currency: "CURRENCY_CODE", revenue: 9.90) Singular.adRevenue(data)

4.3. Tracking Uninstalls

Note: Uninstall tracking is only available to Enterprise customers. Additionally, uninstall tracking requires the app to support push notifications. See Apple's guide to implementing APNS.

To configure uninstall tracking:

  1. Enable the app in Singular by following the guide: Setting Up iOS Install Tracking.
  2. In the app, send Singular the device token returned from the Apple Push Notification Service (APNS). To pass the device token to Singular use the registerDeviceTokenForUninstall or registerDeviceToken method. Do this before the Singular SDK is Initialized. This must be called from the AppDelegate didRegisterForRemoteNotificationsWithDeviceToken method.

Note: If you are already retrieving a device token from an existing push notification implementation, you can use that value.

The APNS token is usually binary data in the native form. Pass the token as received from APNS. If the app alters the token data type pass it as a hex-encoded string, e.g.: b0adf7c9730763f88e1a048e28c68a9f806ed032fb522debff5bfba010a9b052

registerDeviceTokenForUninstall Method
Description Pass the device token returned from APNS.
Signature + (void)registerDeviceTokenForUninstall:(NSData*)deviceToken;
Usage Example
SwiftObjective-C
func application(_ application: UIApplication, 
  didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    // Send the deviceToken to Singular for uninstall tracking     
    Singular.registerDeviceToken(forUninstall: deviceToken) 
}

4.4. Singular SDK JavaScript Interface

Singular provides a JavaScript interface that you can use to call Singular in your app.

For example, if you set up the JavaScript interface, you can send events to Singular from JavaScript code as follows:

JavaScript
Singular.event('event');
Singular.event('test', JSON.stringify({"a1":"bar", "a2":"boo", "a3":"baz"}));

Supported Methods in JavaScript

The interface supports the following SDK methods:

  • setCustomUserID
  • unsetCustomUserID
  • event
  • revenue

Enabling the JavaScript Interface

Note: Starting in iOS 8.0+ Apple recommends using WKWebView to add web content to your app. Do not use UIWebView or WebView. See Apple's WKWebView documentation for more information.

To enable the JavaScript interface when using WKWebView, you need to add some code to the webView method of the WKNavigationDelegate protocol (this protocol helps you implement custom behaviors triggered when a web view handles a navigation request).

Swift
extension ViewController: WKNavigationDelegate { 
  func webView(_: WKWebView, decidePolicyFor: WKNavigationAction, 
    decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 
      // Singular handler 
      let js = "typeof(Singular)" 
      webView.evaluateJavaScript(js) { (result, error) -> Void in 
        if let resultString = result as? String { 
          if resultString.isEqual("undefined") { 
            do { 
              let contents = try String(contentsOfFile: 
                Bundle.main.path(forResource: "Singular", ofType: "js")!) 
              self.webView.evaluateJavaScript(contents, completionHandler: nil) 
            } catch { } 
          } else { 
            print(decidePolicyFor.request) 
            Singular.processJSRequestWK(self.webView, withURL:decidePolicyFor.request) 
          } 
        }  
      }
      // rest of your code goes here   
    }
}

5. Complying with Data Privacy Laws

Singular provides privacy-safeguarding functionality to help you cooperate with any partners who may be complying with consumer privacy laws such as GDPR and CCPA (California Consumer Privacy Act). These partners want to be notified if the end-user has consented to share their private information.

5.1. LimitDataSharing

If you have implemented a way to ask users for consent to share their information, use the limitDataSharing method to notify Singular of the user's choice:

  • Use limitDataSharing:NO to indicate that the user consented (opted in) to share their information.
  • Use limitDataSharing:YES if the user did not consent.

Singular uses LimitDataSharing in "User Privacy Postbacks" as well as passing this information on to partners who require it in order to comply with relevant regulations. See "User Privacy and Limit Data Sharing" for more information.

Note: The use of the method is optional, but there may be attribution information that the partner will share with Singular only if specifically notified that the user has opted in.

limitDataSharing Method
Description Notify Singular of user consent (opt-in) for sharing private data.
Signature (void)limitDataSharing:(BOOL)shouldLimitDataSharing;
Usage Example
SwiftObjective-C
// User has opted into sharing data
Singular.limitDataSharing(false) 

5.2. Additional Methods for GDPR Compliance

The Singular SDK provides several methods to help you comply with GDPR policies and let Singular know about user consent or non-consent for tracking.

trackingOptIn Method
Description Notify Singular of user consent for tracking (opt-in).
Signature (void)trackingOptIn;
Usage Example
SwiftObjective-C
Singular.trackingOptIn() 
stopAllTracking Method
Description Stop all tracking activities for this user on this app.
Signature (void)stopAllTracking;
Usage Example

Important: Calling this method disables the SDK, even between app restarts (the state is persistent). The only way to turn it off is by calling the resumeAllTracking method.

SwiftObjective-C
Singular.stopAllTracking() 
resumeAllTracking Method
Description Resume tracking activities for this user on this app.
Signature (void)resumeAllTracking;
Usage Example
SwiftObjective-C
Singular.resumeAllTracking() 
isAllTrackingStopped Method
Description Check the status of tracking activities for this user on this app.
Signature (BOOL)isAllTrackingStopped;
Usage Example
SwiftObjective-C
Singular.isAllTrackingStopped()

6. Frequently Asked Questions and Issues

Refer to this section if you encounter any issues or errors when building your test app.

Why am I receiving a "Sandbox: rsync.samba(15813) deny(1) file-read-data..." error?

In Xcode 15, there’s a new option called "User Script Sandboxing" that plays a crucial role in the build The purpose is to prevent scripts from making unintended changes to the system, thereby enhancing the stability and security of the build. When enabled, the build system restricts user scripts to disallow undeclared input/output dependencies. This is problematic for the Singular SDK, as it needs to run scripts to dynamiclly link dependencies.

To resolve the issue:

  1. Navigate to Build Settings > Build Options.
  2. Adjust "User Script Sandboxing" to a value "No"
Why am I receiving a "No module named Singular" error?
  1. Check that the Bridging Header was created.
  2. Validate the Bridging Header file is linked in Build Settings > Objective-C Bridging Header.
Why am I receiving an "Arm64" build error?

In some cases, the iOS Simulator requires arm64 to be excluded in Build Settings > Excluded Architectures.

excluded_architectures_arm64.png

I implemented Global Properties. Why am I not seeing them in the Testing Console?

Global Properties are currently not displayed in the Testing Console. They will be added in the future. Use Export Logs to validate this functionality.

Why am I getting a logging error?

The following common logging errors can be ignored:

  • [logging] duplicate column name: singular_link in "ALTER TABLE sessions ADD COLUMN singular_link TEXT DEFAULT NULL"
  • [logging] duplicate column name: payload in "ALTER TABLE sessions ADD COLUMN payload TEXT DEFAULT NULL"
  • [logging] duplicate column name: sequence in "ALTER TABLE events ADD COLUMN sequence INTEGER DEFAULT -1"
  • [logging] duplicate column name: payload in "ALTER TABLE events ADD COLUMN payload TEXT DEFAULT NULL"