高级选项
Singular iOS SDK 的可选高级配置。标准集成无需这些设置。
JavaScript 接口
概述
使用 JavaScript 接口,从基于 WKWebView 的混合应用中的 JavaScript 代码启用 Singular SDK 功能。
支持的方法:
- setCustomUserId: 设置自定义用户标识符
- unsetCustomUserId: 移除自定义用户标识符
- event: 跟踪带或不带属性的事件
- revenue: 跟踪收入
设置 WKWebView 集成
在视图控制器的 WKNavigationDelegate 中配置 JavaScript 接口,以便从 JavaScript 中调用 Singular SDK 方法。
注意: 从 iOS 8.0+ 开始,Apple 建议使用 WKWebView 向您的应用添加网页内容。请勿使用 UIWebView 或 WebView 。有关详细信息,请参阅 Apple 的 WKWebView 文档 。
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
设置要求:
-
在您的应用包中包含
Singular.js文件 - 将您的视图控制器设置为 WKWebView 的导航代理
-
实现
decidePolicyForNavigationAction代理方法 -
调用
processJSRequestWK以处理 Singular SDK 请求
JavaScript 用法
从在 WKWebView 中运行的 JavaScript 代码中调用 Singular 方法。
跟踪事件
// 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 in USD
Singular.revenue('USD', 9.99);
// Track revenue in other currencies
Singular.revenue('EUR', 8.50);
管理自定义用户 ID
// Set custom user ID
Singular.setCustomUserId('user_12345');
// Remove custom user ID
Singular.unsetCustomUserId();
JavaScript API 注意事项:
-
所有方法都在全局
Singular对象上调用 - 事件属性必须作为 JSON 字符串化的对象传递
-
方法名使用 camelCase(例如,
setCustomUserId) - 该接口会自动将 JavaScript 调用桥接到原生 SDK 方法
BETA 功能
非正式发布: 本节中的功能处于 BETA 阶段。Singular 必须为您的账户启用后才能生效,且其行为可能会发生变化。在基于这些功能进行开发之前,请先与您的客户成功经理沟通。
归因回调
归因回调会在用户安装后的首次会话期间,将 Singular 对该安装的归因结果(例如带来该安装的网络和广告系列)传递给设备上的应用。
需要启用: 该功能默认处于关闭状态。如果尚未为您的账户启用,即使代码正确,回调也永远不会触发。如需启用,请联系您的客户成功经理。
设置方法
- 请联系您的客户成功经理,为您的账户启用归因回调。
- 请在初始化 SDK 之前,在 SDK 配置对象上注册您的处理程序。有关方法签名和代码示例,请参阅
deviceAttributionCallback。 - 请在此前从未打开过该应用的设备上测试。回调仅在首次会话时触发。
预期行为
- 仅首次会话: 回调在安装后的首次会话期间触发一次,之后再次打开应用时不会触发。
- Organic 是一种结果,而非失败: 未找到触点时,回调仍会触发并将网络报告为 "Organic"。如果回调完全没有触发,则原因是设置问题,而非归因问题。
- 最佳推测,而非最终归因: 结果反映的是处理该会话时可用的最后一个触点。它不一定与 Singular 报告中显示的归因一致,后者会在稍后结合更多信息得出。