고급 옵션
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델리게이트 메서드를 구현하세요 -
Singular SDK 요청을 처리하려면
processJSRequestWK를 호출하세요
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 리포트의 어트리뷰션과 항상 일치하지는 않습니다.