Advanced Options
Optional and advanced configuration for the Singular iOS SDK. These settings are not required for a standard integration.
JavaScript Interface
Overview
Enable Singular SDK functionality from JavaScript code in WKWebView-based hybrid apps using the JavaScript interface.
Supported Methods:
- setCustomUserId: Set custom user identifier
- unsetCustomUserId: Remove custom user identifier
- event: Track events with or without attributes
- revenue: Track revenue
Setup WKWebView Integration
Configure the JavaScript interface in your view controller's WKNavigationDelegate to enable Singular SDK methods from JavaScript.
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.
import UIKit
import WebKit
class ViewController: UIViewController {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Create WKWebView
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
view.addSubview(webView)
// Load web content
if let url = Bundle.main.url(forResource: "index", withExtension: "html") {
webView.loadFileURL(url, allowingReadAccessTo: url)
}
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// Check if Singular JavaScript interface exists
let js = "typeof(Singular)"
webView.evaluateJavaScript(js) { (result, error) -> Void in
if let resultString = result as? String {
if resultString == "undefined" {
// Load Singular JavaScript interface
do {
if let path = Bundle.main.path(forResource: "Singular", ofType: "js") {
let contents = try String(contentsOfFile: path)
webView.evaluateJavaScript(contents, completionHandler: nil)
}
} catch {
print("Error loading Singular.js: \(error)")
}
} else {
// Process Singular SDK request
Singular.processJSRequestWK(webView, withURL: navigationAction.request)
}
}
}
// Allow navigation
decisionHandler(.allow)
}
}
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
#import <Singular/Singular.h>
@interface ViewController () <WKNavigationDelegate>
@property (strong, nonatomic) WKWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Create WKWebView
WKWebViewConfiguration *webConfiguration = [[WKWebViewConfiguration alloc] init];
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:webConfiguration];
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
// Load web content
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
if (url) {
[self.webView loadFileURL:url allowingReadAccessToURL:url];
}
}
- (void)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
// Check if Singular JavaScript interface exists
NSString *js = @"typeof(Singular)";
[webView evaluateJavaScript:js completionHandler:^(id result, NSError *error) {
if ([result isKindOfClass:[NSString class]]) {
NSString *resultString = (NSString *)result;
if ([resultString isEqualToString:@"undefined"]) {
// Load Singular JavaScript interface
NSString *path = [[NSBundle mainBundle] pathForResource:@"Singular" ofType:@"js"];
if (path) {
NSError *readError;
NSString *contents = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&readError];
if (contents) {
[webView evaluateJavaScript:contents completionHandler:nil];
} else {
NSLog(@"Error loading Singular.js: %@", readError);
}
}
} else {
// Process Singular SDK request
[Singular processJSRequestWK:webView withURL:navigationAction.request];
}
}
}];
// Allow navigation
decisionHandler(WKNavigationActionPolicyAllow);
}
@end
Setup Requirements:
-
Include the
Singular.jsfile in your app bundle - Set your view controller as the WKWebView's navigation delegate
-
Implement the
decidePolicyForNavigationActiondelegate method -
Call
processJSRequestWKto handle Singular SDK requests
JavaScript Usage
Call Singular methods from JavaScript code running in your WKWebView.
Track Events
// Simple event without attributes
Singular.event('level_completed');
// Event with attributes (pass as JSON string)
Singular.event('purchase_attempt',
JSON.stringify({
"item_name": "sword",
"item_category": "weapons",
"item_price": 9.99
})
);
Track Revenue
// Track revenue in USD
Singular.revenue('USD', 9.99);
// Track revenue in other currencies
Singular.revenue('EUR', 8.50);
Manage Custom User ID
// Set custom user ID
Singular.setCustomUserId('user_12345');
// Remove custom user ID
Singular.unsetCustomUserId();
JavaScript API Notes:
-
All methods are called on the global
Singularobject - Event attributes must be passed as JSON-stringified objects
-
Method names use camelCase (e.g.,
setCustomUserId) - The interface automatically bridges JavaScript calls to native SDK methods
BETA Features
Not generally available: The features in this section are in BETA. Singular must enable them for your account before they will work, and their behavior may change. Talk to your Customer Success Manager before building against them.
Attribution Callback
The Attribution Callback delivers Singular's attribution result for the install — such as the network and campaign that drove it — to your app on the device, during the user's first session after install.
Enablement is required: This feature is off by default. If it has not been enabled for your account, the callback never fires, even when your code is correct. Contact your Customer Success Manager to request it.
Setting it up
- Ask your Customer Success Manager to enable the Attribution Callback for your account.
- Register your handler on the SDK configuration object before initializing the SDK. For the signature and a code sample, see
deviceAttributionCallback. - Test on a device that has not opened the app before. The callback fires only on a first session.
What to expect
- First session only: The callback fires once, during the first session after install. It is not called on later app opens.
- Organic is a result, not a failure: When no touchpoint is found, the callback still fires and reports the network as "Organic". If the callback does not fire at all, the cause is a setup problem rather than an attribution one.
- A best guess, not the final attribution: The result reflects the last touchpoint available at the moment the session is processed. It will not always match the attribution shown in Singular's reports, which is decided later with more information.