From a847368083702923f8eff2fd324ace9b24a59cbd Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 8 May 2014 12:59:30 +0200 Subject: [PATCH 1/5] Added test for backend url --- AdjustTests/AIActivityHandlerTests.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AdjustTests/AIActivityHandlerTests.m b/AdjustTests/AIActivityHandlerTests.m index 40cda79f3..e646c63ac 100644 --- a/AdjustTests/AIActivityHandlerTests.m +++ b/AdjustTests/AIActivityHandlerTests.m @@ -13,6 +13,7 @@ #import "AIActivityHandler.h" #import "AIActivityPackage.h" #import "AITestsUtil.h" +#import "AIUtil.h" @interface AIActivityHandlerTests : XCTestCase @@ -90,6 +91,9 @@ - (void)testFirstRun // check the Sdk version is being tested XCTAssertEqual(@"ios3.3.1", activityPackage.clientSdk, @"%@", activityPackage.extendedString); + // check the server url + XCTAssertEqual(@"https://app.adjust.io", AIUtil.baseUrl); + // packageType should be SESSION_START XCTAssertEqual(@"/startup", activityPackage.path, @"%@", activityPackage.extendedString); From 8f4fbe1f944357fcc37522fa94b2537d81e4ddf1 Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 22 May 2014 11:55:46 +0200 Subject: [PATCH 2/5] Convert log level from string --- Adjust/AILogger.h | 3 +++ Adjust/AILogger.m | 23 +++++++++++++++++++++++ AdjustTests/AIActivityHandlerTests.m | 10 ++++++++++ 3 files changed, 36 insertions(+) diff --git a/Adjust/AILogger.h b/Adjust/AILogger.h index c60377c72..00ec715bf 100644 --- a/Adjust/AILogger.h +++ b/Adjust/AILogger.h @@ -31,4 +31,7 @@ typedef enum { @end @interface AILogger : NSObject + ++ (AILogLevel) LogLevelFromString: (NSString *) logLevelString; + @end diff --git a/Adjust/AILogger.m b/Adjust/AILogger.m index 1a5b6eb7f..e49800300 100644 --- a/Adjust/AILogger.m +++ b/Adjust/AILogger.m @@ -71,4 +71,27 @@ - (void)logLevel:(NSString *)logLevel format:(NSString *)format parameters:(va_l } } ++ (AILogLevel)LogLevelFromString:(NSString *)logLevelString { + if ([logLevelString isEqualToString:@"verbose"]) + return AILogLevelVerbose; + + if ([logLevelString isEqualToString:@"debug"]) + return AILogLevelDebug; + + if ([logLevelString isEqualToString:@"info"]) + return AILogLevelInfo; + + if ([logLevelString isEqualToString:@"warn"]) + return AILogLevelWarn; + + if ([logLevelString isEqualToString:@"error"]) + return AILogLevelError; + + if ([logLevelString isEqualToString:@"assert"]) + return AILogLevelAssert; + + // default value if string does not match + return AILogLevelInfo; +} + @end diff --git a/AdjustTests/AIActivityHandlerTests.m b/AdjustTests/AIActivityHandlerTests.m index e646c63ac..595c212aa 100644 --- a/AdjustTests/AIActivityHandlerTests.m +++ b/AdjustTests/AIActivityHandlerTests.m @@ -14,6 +14,7 @@ #import "AIActivityPackage.h" #import "AITestsUtil.h" #import "AIUtil.h" +#import "AILogger.h" @interface AIActivityHandlerTests : XCTestCase @@ -584,4 +585,13 @@ - (void)testOpenUrl { XCTAssert([self.loggerMock containsMessage:AILogLevelDebug beginsWith:@"Reattribution {\n foo = bar;\n key = value;\n}"], @"%@", self.loggerMock); } +- (void)testConversions { + // check the logLevel conversions + XCTAssertEqual(AILogLevelVerbose, [AILogger LogLevelFromString:@"verbose"]); + XCTAssertEqual(AILogLevelDebug, [AILogger LogLevelFromString:@"debug"]); + XCTAssertEqual(AILogLevelInfo, [AILogger LogLevelFromString:@"info"]); + XCTAssertEqual(AILogLevelWarn, [AILogger LogLevelFromString:@"warn"]); + XCTAssertEqual(AILogLevelError, [AILogger LogLevelFromString:@"error"]); + XCTAssertEqual(AILogLevelAssert, [AILogger LogLevelFromString:@"assert"]); +} @end From e1b90f5e45ae2df85c3df5a5e420ffcc8d0699d9 Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 22 May 2014 11:58:13 +0200 Subject: [PATCH 3/5] Create native bridge --- Adjust.xcodeproj/project.pbxproj | 32 ++ Adjust/Adjust.h | 4 +- AdjustBridge/AdjustBridge.h | 20 + AdjustBridge/AdjustBridge.js.txt | 55 +++ AdjustBridge/AdjustBridge.m | 104 +++++ .../WebViewJavascriptBridge.h | 44 ++ .../WebViewJavascriptBridge.js.txt | 116 +++++ .../WebViewJavascriptBridge.m | 407 ++++++++++++++++++ 8 files changed, 780 insertions(+), 2 deletions(-) create mode 100644 AdjustBridge/AdjustBridge.h create mode 100644 AdjustBridge/AdjustBridge.js.txt create mode 100644 AdjustBridge/AdjustBridge.m create mode 100755 AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.h create mode 100755 AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.js.txt create mode 100755 AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.m diff --git a/Adjust.xcodeproj/project.pbxproj b/Adjust.xcodeproj/project.pbxproj index e07a33dcb..e08268f24 100644 --- a/Adjust.xcodeproj/project.pbxproj +++ b/Adjust.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 9679922018BBAE2800394606 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9679921018BBAE2800394606 /* Foundation.framework */; }; 9679922218BBAE2800394606 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9679922118BBAE2800394606 /* UIKit.framework */; }; 9679922518BBAE2800394606 /* libAdjust.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9679920D18BBAE2800394606 /* libAdjust.a */; }; + 96CD4478192A546F0029A1AA /* AdjustBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 96CD4473192A546F0029A1AA /* AdjustBridge.m */; }; + 96CD4479192A546F0029A1AA /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 96CD4477192A546F0029A1AA /* WebViewJavascriptBridge.m */; }; 96E5E38118BBB48A008E7B30 /* Adjust.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E5E34D18BBB48A008E7B30 /* Adjust.m */; }; 96E5E38B18BBB48A008E7B30 /* AIActivityHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E5E36318BBB48A008E7B30 /* AIActivityHandler.m */; }; 96E5E38C18BBB48A008E7B30 /* AIActivityKind.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E5E36518BBB48A008E7B30 /* AIActivityKind.m */; }; @@ -78,6 +80,12 @@ 9679921D18BBAE2800394606 /* AdjustTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AdjustTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9679921E18BBAE2800394606 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 9679922118BBAE2800394606 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 96ADB109192E0175006E1D9D /* AdjustBridge.js.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AdjustBridge.js.txt; sourceTree = ""; }; + 96CD4472192A546F0029A1AA /* AdjustBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdjustBridge.h; sourceTree = ""; }; + 96CD4473192A546F0029A1AA /* AdjustBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AdjustBridge.m; sourceTree = ""; }; + 96CD4475192A546F0029A1AA /* WebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge.h; sourceTree = ""; }; + 96CD4476192A546F0029A1AA /* WebViewJavascriptBridge.js.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = WebViewJavascriptBridge.js.txt; sourceTree = ""; }; + 96CD4477192A546F0029A1AA /* WebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge.m; sourceTree = ""; }; 96E5E34C18BBB48A008E7B30 /* Adjust.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Adjust.h; sourceTree = ""; }; 96E5E34D18BBB48A008E7B30 /* Adjust.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Adjust.m; sourceTree = ""; }; 96E5E36218BBB48A008E7B30 /* AIActivityHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIActivityHandler.h; sourceTree = ""; }; @@ -161,6 +169,7 @@ 9679920418BBAE2800394606 = { isa = PBXGroup; children = ( + 96CD4471192A546F0029A1AA /* AdjustBridge */, 96E5E39A18BBB49E008E7B30 /* AdjustTests */, 96E5E34B18BBB48A008E7B30 /* Adjust */, 9679920F18BBAE2800394606 /* Frameworks */, @@ -189,6 +198,27 @@ name = Frameworks; sourceTree = ""; }; + 96CD4471192A546F0029A1AA /* AdjustBridge */ = { + isa = PBXGroup; + children = ( + 96ADB109192E0175006E1D9D /* AdjustBridge.js.txt */, + 96CD4472192A546F0029A1AA /* AdjustBridge.h */, + 96CD4473192A546F0029A1AA /* AdjustBridge.m */, + 96CD4474192A546F0029A1AA /* WebViewJavascriptBridge */, + ); + path = AdjustBridge; + sourceTree = ""; + }; + 96CD4474192A546F0029A1AA /* WebViewJavascriptBridge */ = { + isa = PBXGroup; + children = ( + 96CD4475192A546F0029A1AA /* WebViewJavascriptBridge.h */, + 96CD4476192A546F0029A1AA /* WebViewJavascriptBridge.js.txt */, + 96CD4477192A546F0029A1AA /* WebViewJavascriptBridge.m */, + ); + path = WebViewJavascriptBridge; + sourceTree = ""; + }; 96E5E34B18BBB48A008E7B30 /* Adjust */ = { isa = PBXGroup; children = ( @@ -343,6 +373,7 @@ buildActionMask = 2147483647; files = ( 96E5E38118BBB48A008E7B30 /* Adjust.m in Sources */, + 96CD4479192A546F0029A1AA /* WebViewJavascriptBridge.m in Sources */, 96E5E38B18BBB48A008E7B30 /* AIActivityHandler.m in Sources */, 96E5E39618BBB48A008E7B30 /* AIRequestHandler.m in Sources */, 96E5E39918BBB48A008E7B30 /* AIUtil.m in Sources */, @@ -358,6 +389,7 @@ 96E5E39318BBB48A008E7B30 /* AILogger.m in Sources */, 96E5E39418BBB48A008E7B30 /* AIPackageBuilder.m in Sources */, 96E5E39218BBB48A008E7B30 /* AIAdjustFactory.m in Sources */, + 96CD4478192A546F0029A1AA /* AdjustBridge.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Adjust/Adjust.h b/Adjust/Adjust.h index f40eb8a57..07e1d4ea9 100644 --- a/Adjust/Adjust.h +++ b/Adjust/Adjust.h @@ -135,8 +135,8 @@ static NSString * const AIEnvironmentProduction = @"production"; */ + (void)setMacMd5TrackingEnabled:(BOOL)enabled; -// Special method used by SDK wrappers such as Adobe Air SDK. -+ (void)setSdkPrefix:(NSString *)sdkPrefix __attribute__((deprecated)); +// Special method used by wrapper JS bridge. Do not call directly. ++ (void)setSdkPrefix:(NSString *)sdkPrefix; /** * Tell adjust that the application resumed. diff --git a/AdjustBridge/AdjustBridge.h b/AdjustBridge/AdjustBridge.h new file mode 100644 index 000000000..af66d5acb --- /dev/null +++ b/AdjustBridge/AdjustBridge.h @@ -0,0 +1,20 @@ +// +// AdjustBridge.h +// Adjust +// +// Created by Pedro Filipe on 19/05/14. +// Copyright (c) 2014 adjust. All rights reserved. +// + +#import +#import "Adjust.h" +#import + +@interface AdjustBridge : NSObject + +- (void)adjustFinishedTrackingWithResponse:(AIResponseData *)responseData; + ++ (void) loadBridge:(NSObject *) webViewDelegate + webView:(UIWebView *) webView; + +@end diff --git a/AdjustBridge/AdjustBridge.js.txt b/AdjustBridge/AdjustBridge.js.txt new file mode 100644 index 000000000..e0c8ffd79 --- /dev/null +++ b/AdjustBridge/AdjustBridge.js.txt @@ -0,0 +1,55 @@ +window.AdjustBridge = (function () { + + var bridge = {}; + + var adjustPrefix = 'adjust_'; + + function setBridgePrivate(bridgeParam) { + bridge = bridgeParam; + bridge.init(function(message, callback) { }); + } + + function trackEventPrivate (eventToken, parameters) { + bridge.callHandler(adjustPrefix + 'trackEvent', + {'eventToken': eventToken, 'parameters': parameters}, + function(response) {}) + }; + + function trackRevenuePrivate (amountInCents, eventToken, parameters) { + bridge.callHandler(adjustPrefix + 'trackRevenue', + {'amountInCents': amountInCents, 'eventToken': eventToken, 'parameters': parameters}, + function(response) {}) + }; + + function setResponseDelegatePrivate(responseDelegate) { + bridge.callHandler(adjustPrefix + 'setResponseDelegate', {}, function(response) {}); + + bridge.registerHandler('responseDelegate', function(data, responseCallback) { + responseDelegate(data) + }); + }; + + function setEnabledPrivate(enabled) { + bridge.callHandler(adjustPrefix + 'setEnabled', {'enabled': enabled}, function(response) { }); + }; + + function isEnabledPrivate(isEnabledReturnCallBack) { + bridge.callHandler(adjustPrefix + 'isEnabled', {}, function(isEnabledString) { + isEnabledReturnCallBack(isEnabledString) + }); + }; + + function openUrlPrivate(url) { + bridge.callHandler(adjustPrefix + 'openUrl', {'url': url}, function(response) { }); + }; + + return { + setBridge: setBridgePrivate, + trackEvent: trackEventPrivate, + trackRevenue: trackRevenuePrivate, + setResponseDelegate: setResponseDelegatePrivate, + setEnabled: setEnabledPrivate, + isEnabled: isEnabledPrivate, + openUrl: openUrlPrivate + }; +})(); diff --git a/AdjustBridge/AdjustBridge.m b/AdjustBridge/AdjustBridge.m new file mode 100644 index 000000000..70099e30f --- /dev/null +++ b/AdjustBridge/AdjustBridge.m @@ -0,0 +1,104 @@ +// +// AdjustBridge.m +// Adjust +// +// Created by Pedro Filipe on 19/05/14. +// Copyright (c) 2014 adjust. All rights reserved. +// + +#import "AdjustBridge.h" +#import "WebViewJavascriptBridge.h" + +static NSString * const kAdjustJsPrefix = @"adjust_"; + +static WebViewJavascriptBridge* _AdjustBridge = nil; +static id adjustBridgeInstance = nil; + +@implementation AdjustBridge + +- (id) init { + self = [super init]; + return self; +} + ++ (void) loadBridge:(NSObject *) webViewDelegate + webView:(UIWebView *) webView { + if (_AdjustBridge) { return; } + + _AdjustBridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:webViewDelegate handler:^(id data, WVJBResponseCallback responseCallback) { + }]; + + [_AdjustBridge registerHandler:[NSString stringWithFormat:@"%@trackEvent", kAdjustJsPrefix] handler:^(id data, WVJBResponseCallback responseCallback) { + + NSString* eventToken = [data objectForKey:@"eventToken"]; + NSDictionary* parameters = [data objectForKey:@"parameters"]; + + if (parameters != nil) { + [Adjust trackEvent:eventToken withParameters:parameters]; + } else { + [Adjust trackEvent:eventToken]; + } + }]; + + [_AdjustBridge registerHandler:[NSString stringWithFormat:@"%@trackRevenue", kAdjustJsPrefix] handler:^(id data, WVJBResponseCallback responseCallback) { + + NSString* eventToken = [data objectForKey:@"eventToken"]; + NSDictionary* parameters = [data objectForKey:@"parameters"]; + double amountInCents = [[data objectForKey:@"amountInCents"] doubleValue]; + + if (parameters != nil) { + [Adjust trackRevenue:amountInCents forEvent:eventToken withParameters:parameters]; + } else if (eventToken != nil) { + [Adjust trackRevenue:amountInCents forEvent:eventToken]; + } else { + [Adjust trackRevenue:amountInCents]; + } + }]; + + [_AdjustBridge registerHandler:[NSString stringWithFormat:@"%@setResponseDelegate", kAdjustJsPrefix] handler:^(id data, WVJBResponseCallback responseCallback) { + + adjustBridgeInstance = [[AdjustBridge alloc] init]; + [Adjust setDelegate:adjustBridgeInstance]; + }]; + + [_AdjustBridge registerHandler:[NSString stringWithFormat:@"%@setEnabled", kAdjustJsPrefix] handler:^(id data, WVJBResponseCallback responseCallback) { + + BOOL enabled = [[data objectForKey:@"enabled"] boolValue]; + [Adjust setEnabled:enabled]; + }]; + + [_AdjustBridge registerHandler:[NSString stringWithFormat:@"%@isEnabled", kAdjustJsPrefix] handler:^(id data, WVJBResponseCallback responseCallback) { + + BOOL isEnabled = [Adjust isEnabled]; + + responseCallback([NSNumber numberWithBool:isEnabled]); + }]; + + [_AdjustBridge registerHandler:[NSString stringWithFormat:@"%@openUrl", kAdjustJsPrefix] handler:^(id data, WVJBResponseCallback responseCallback) { + + NSURL* url = [[NSURL alloc] initWithString:[data objectForKey:@"url"]]; + [Adjust appWillOpenUrl:url]; + }]; + + if (![[webView stringByEvaluatingJavaScriptFromString:@"typeof AdjustBridge == 'object'"] isEqualToString:@"true"]) { + NSBundle *bundle = [NSBundle mainBundle]; + NSString *filePath = [bundle pathForResource:@"AdjustBridge.js" ofType:@"txt"]; + NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; + [webView stringByEvaluatingJavaScriptFromString:js]; + } + + [Adjust setSdkPrefix:@"bridge"]; +} + +- (void)adjustFinishedTrackingWithResponse:(AIResponseData *)responseData { + NSMutableDictionary* dicResponseData = (NSMutableDictionary*) [responseData dictionary]; + + [dicResponseData removeObjectForKey:@"success"]; + [dicResponseData setObject:[NSNumber numberWithBool:responseData.success] forKey:@"success"]; + + [dicResponseData removeObjectForKey:@"willRetry"]; + [dicResponseData setObject:[NSNumber numberWithBool:responseData.willRetry] forKey:@"willRetry"]; + + [_AdjustBridge callHandler:@"responseDelegate" data:dicResponseData responseCallback:^(id response) {}]; +} +@end diff --git a/AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.h b/AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.h new file mode 100755 index 000000000..e70b0fba7 --- /dev/null +++ b/AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.h @@ -0,0 +1,44 @@ +// +// WebViewJavascriptBridge.h +// ExampleApp-iOS +// +// Created by Marcus Westin on 6/14/13. +// Copyright (c) 2013 Marcus Westin. All rights reserved. +// + +#import +#import + +#define kCustomProtocolScheme @"wvjbscheme" +#define kQueueHasMessage @"__WVJB_QUEUE_MESSAGE__" + +#if defined __MAC_OS_X_VERSION_MAX_ALLOWED + #import + #define WVJB_PLATFORM_OSX + #define WVJB_WEBVIEW_TYPE WebView + #define WVJB_WEBVIEW_DELEGATE_TYPE NSObject +#elif defined __IPHONE_OS_VERSION_MAX_ALLOWED + #define WVJB_PLATFORM_IOS + #define WVJB_WEBVIEW_TYPE UIWebView + #define WVJB_WEBVIEW_DELEGATE_TYPE NSObject +#endif + +typedef void (^WVJBResponseCallback)(id responseData); +typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback); + +@interface WebViewJavascriptBridge : WVJB_WEBVIEW_DELEGATE_TYPE + ++ (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView handler:(WVJBHandler)handler; ++ (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate handler:(WVJBHandler)handler; ++ (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate handler:(WVJBHandler)handler resourceBundle:(NSBundle*)bundle; ++ (void)enableLogging; + +- (void)send:(id)message; +- (void)send:(id)message responseCallback:(WVJBResponseCallback)responseCallback; +- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; +- (void)callHandler:(NSString*)handlerName; +- (void)callHandler:(NSString*)handlerName data:(id)data; +- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; +- (void)reset; + +@end diff --git a/AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.js.txt b/AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.js.txt new file mode 100755 index 000000000..86303713c --- /dev/null +++ b/AdjustBridge/WebViewJavascriptBridge/WebViewJavascriptBridge.js.txt @@ -0,0 +1,116 @@ +;(function() { + if (window.WebViewJavascriptBridge) { return } + var messagingIframe + var sendMessageQueue = [] + var receiveMessageQueue = [] + var messageHandlers = {} + + var CUSTOM_PROTOCOL_SCHEME = 'wvjbscheme' + var QUEUE_HAS_MESSAGE = '__WVJB_QUEUE_MESSAGE__' + + var responseCallbacks = {} + var uniqueId = 1 + + function _createQueueReadyIframe(doc) { + messagingIframe = doc.createElement('iframe') + messagingIframe.style.display = 'none' + doc.documentElement.appendChild(messagingIframe) + } + + function init(messageHandler) { + if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice') } + WebViewJavascriptBridge._messageHandler = messageHandler + var receivedMessages = receiveMessageQueue + receiveMessageQueue = null + for (var i=0; i 500) { + NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:500]); + } else { + NSLog(@"WVJB %@: %@", action, json); + } +} + + + +/* Platform specific internals: OSX + **********************************/ +#if defined WVJB_PLATFORM_OSX + +- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate handler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle{ + _messageHandler = messageHandler; + _webView = webView; + _webViewDelegate = webViewDelegate; + _messageHandlers = [NSMutableDictionary dictionary]; + + _webView.frameLoadDelegate = self; + _webView.resourceLoadDelegate = self; + _webView.policyDelegate = self; + + _resourceBundle = bundle; +} + +- (void) _platformSpecificDealloc { + _webView.frameLoadDelegate = nil; + _webView.resourceLoadDelegate = nil; + _webView.policyDelegate = nil; +} + +- (void)webView:(WebView *)webView didFinishLoadForFrame:(WebFrame *)frame +{ + if (webView != _webView) { return; } + + if (![[webView stringByEvaluatingJavaScriptFromString:@"typeof WebViewJavascriptBridge == 'object'"] isEqualToString:@"true"]) { + NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle]; + NSString *filePath = [bundle pathForResource:@"WebViewJavascriptBridge.js" ofType:@"txt"]; + NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; + [webView stringByEvaluatingJavaScriptFromString:js]; + } + + if (_startupMessageQueue) { + for (id queuedMessage in _startupMessageQueue) { + [self _dispatchMessage:queuedMessage]; + } + _startupMessageQueue = nil; + } + + if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)]) { + [_webViewDelegate webView:webView didFinishLoadForFrame:frame]; + } +} + +- (void)webView:(WebView *)webView didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + if (webView != _webView) { return; } + + if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:didFailLoadWithError:forFrame:)]) { + [_webViewDelegate webView:webView didFailLoadWithError:error forFrame:frame]; + } +} + +- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener +{ + if (webView != _webView) { return; } + + NSURL *url = [request URL]; + if ([[url scheme] isEqualToString:kCustomProtocolScheme]) { + if ([[url host] isEqualToString:kQueueHasMessage]) { + [self _flushMessageQueue]; + } else { + NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@://%@", kCustomProtocolScheme, [url path]); + } + [listener ignore]; + } else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) { + [_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener]; + } else { + [listener use]; + } +} + +- (void)webView:(WebView *)webView didCommitLoadForFrame:(WebFrame *)frame { + if (webView != _webView) { return; } + + if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:didCommitLoadForFrame:)]) { + [_webViewDelegate webView:webView didCommitLoadForFrame:frame]; + } +} + +- (NSURLRequest *)webView:(WebView *)webView resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource { + if (webView != _webView) { return request; } + + if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:resource:willSendRequest:redirectResponse:fromDataSource:)]) { + return [_webViewDelegate webView:webView resource:identifier willSendRequest:request redirectResponse:redirectResponse fromDataSource:dataSource]; + } + + return request; +} + + + +/* Platform specific internals: iOS + **********************************/ +#elif defined WVJB_PLATFORM_IOS + +- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(id)webViewDelegate handler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle{ + _messageHandler = messageHandler; + _webView = webView; + _webViewDelegate = webViewDelegate; + _messageHandlers = [NSMutableDictionary dictionary]; + _webView.delegate = self; + _resourceBundle = bundle; +} + +- (void) _platformSpecificDealloc { + _webView.delegate = nil; +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView { + if (webView != _webView) { return; } + + _numRequestsLoading--; + + if (_numRequestsLoading == 0 && ![[webView stringByEvaluatingJavaScriptFromString:@"typeof WebViewJavascriptBridge == 'object'"] isEqualToString:@"true"]) { + NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle]; + NSString *filePath = [bundle pathForResource:@"WebViewJavascriptBridge.js" ofType:@"txt"]; + NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; + [webView stringByEvaluatingJavaScriptFromString:js]; + } + + if (_startupMessageQueue) { + for (id queuedMessage in _startupMessageQueue) { + [self _dispatchMessage:queuedMessage]; + } + _startupMessageQueue = nil; + } + + __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; + if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { + [strongDelegate webViewDidFinishLoad:webView]; + } +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + if (webView != _webView) { return; } + + _numRequestsLoading--; + + __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; + if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) { + [strongDelegate webView:webView didFailLoadWithError:error]; + } +} + +- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { + if (webView != _webView) { return YES; } + NSURL *url = [request URL]; + __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; + if ([[url scheme] isEqualToString:kCustomProtocolScheme]) { + if ([[url host] isEqualToString:kQueueHasMessage]) { + [self _flushMessageQueue]; + } else { + NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@://%@", kCustomProtocolScheme, [url path]); + } + return NO; + } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { + return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; + } else { + return YES; + } +} + +- (void)webViewDidStartLoad:(UIWebView *)webView { + if (webView != _webView) { return; } + + _numRequestsLoading++; + + __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; + if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { + [strongDelegate webViewDidStartLoad:webView]; + } +} + +#endif + +@end From 32024f22ff0741f468b2299ead65a5b1fd3170b6 Mon Sep 17 00:00:00 2001 From: Pedro Filipe Date: Thu, 22 May 2014 16:46:42 +0200 Subject: [PATCH 4/5] Create Web App guide --- AdjustBridge/AdjustBridge.h | 2 +- README.md | 47 ++++---- doc/webApp.md | 234 ++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+), 23 deletions(-) create mode 100644 doc/webApp.md diff --git a/AdjustBridge/AdjustBridge.h b/AdjustBridge/AdjustBridge.h index af66d5acb..55e7c2a82 100644 --- a/AdjustBridge/AdjustBridge.h +++ b/AdjustBridge/AdjustBridge.h @@ -14,7 +14,7 @@ - (void)adjustFinishedTrackingWithResponse:(AIResponseData *)responseData; -+ (void) loadBridge:(NSObject *) webViewDelegate ++ (void)loadBridge:(NSObject *) webViewDelegate webView:(UIWebView *) webView; @end diff --git a/README.md b/README.md index f565764d5..83e9328b9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Summary This is the iOS SDK of adjust.io™. You can read more about adjust.io™ at -[adjust.io]. +[adjust.io]. If your app is a iOS Web App, consult our [iOS Web App][webApp] guide. ## Basic Installation @@ -219,7 +219,25 @@ state changed to `SKPaymentTransactionStatePurchased`: If you want to track all revenues in the same currency you might want to use [AEPriceMatrix][AEPriceMatrix] to do simple tier based currency conversion. -### 8. Receive delegate callbacks +### 8. Handle reattributions with deep linking + +You can also set up the adjust SDK to read deep links that come to your app, +also known as custom URL schemes in iOS. We will only read the data that is +injected by adjust tracker URLs. This is essential if you are planning to run +retargeting or re-engagement campaigns with deep links. + +In the Project Navigator open the source file your Application Delegate. Find +or add the method `openURL` and add the following call to adjust: + +```objc +- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication annotation:(id)annotation +{ + [Adjust appWillOpenUrl:url]; +} +``` + +### 9. Receive delegate callbacks Every time your app tries to track a session, an event or some revenue, you can be notified about the success of that operation and receive additional @@ -264,6 +282,7 @@ failed to track. Within the delegate function you have access to the AIActivityKindSession AIActivityKindEvent AIActivityKindRevenue + AIActivityKindReattribution ``` - `NSString activityKindString` human readable version of the activity kind. @@ -273,6 +292,7 @@ failed to track. Within the delegate function you have access to the session event revenue + reattribution ``` - `BOOL success` indicates whether or not the tracking attempt was @@ -286,7 +306,7 @@ failed to track. Within the delegate function you have access to the - `NSString trackerName` the tracker name of the current install. Is `nil` if request failed or response could not be parsed. -### 9. Enable event buffering +### 10. Enable event buffering If your app makes heavy use of event tracking, you might want to delay some HTTP requests in order to send them in one batch every minute. You can enable @@ -297,7 +317,7 @@ in the `didFinishLaunching` method of your Application Delegate: [Adjust setEventBufferingEnabled:YES]; ``` -### 10. Disable tracking +### 11. Disable tracking You can disable the adjust SDK from tracking by invoking the method `setEnabled` with the enabled parameter as `NO`. This setting is remembered @@ -311,24 +331,6 @@ You can verify if the adjust SDK is currently active with the method `isEnabled`. It is always possible to activate the adjust SDK by invoking `setEnabled` with the enabled parameter as `YES`. -### 11. Handle deep linking - -You can also set up the adjust SDK to read deep links that come to your app, -also known as custom URL schemes in iOS. We will only read the data that is -injected by adjust tracker URLs. This is essential if you are planning to run -retargeting or re-engagement campaigns with deep links. - -In the Project Navigator open the source file your Application Delegate. Find -or add the method `openURL` and add the following call to adjust: - -```objc -- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication annotation:(id)annotation -{ - [Adjust appWillOpenUrl:url]; -} -``` - [adjust.io]: http://adjust.io [cocoapods]: http://cocoapods.org [dashboard]: http://adjust.io @@ -341,6 +343,7 @@ or add the method `openURL` and add the following call to adjust: [delegate]: https://raw.github.com/adjust/sdks/master/Resources/ios/delegate3.png [run]: https://raw.github.com/adjust/sdks/master/Resources/ios/run3.png [AEPriceMatrix]: https://github.com/adjust/AEPriceMatrix +[webApp]: https://github.com/adjust/ios_sdk/blob/master/doc/webApp.md ## License diff --git a/doc/webApp.md b/doc/webApp.md new file mode 100644 index 000000000..239e1c058 --- /dev/null +++ b/doc/webApp.md @@ -0,0 +1,234 @@ +## Summary + +This is the guide to the iOS SDK of adjust.io™ for Web Apps. You can read more about adjust.io™ at +[adjust.io]. + +It provides a bridge from Javascript to native Objective-C calls using the [WebViewJavascriptBridge] plugin. +This plugin is a thin layer that allows to make calls from and to Javascript and Objective-C in iOS simple and easy. + +## Basic Installation + +### 1. Install the adjust iOS SDK + +To install the native iOS SDK of adjust, follow the `Basic Installation` chapter at our [GitHub page][ios_installation]. + +### 2. Add the Javascript bridge to your project + +In Xcode's Project Navigator locate the `Supporting Files` group (or any other +group of your choice). From Finder drag the `AdjustBridge` subdirectory into +Xcode's `Supporting Files` group. + +![][drag_bridge] + +In the dialog `Choose options for adding these files` make sure to check the +checkbox to `Copy items into destination group's folder` and select the upper +radio button to `Create groups for any added folders`. + +![][add_bridge] + +### 3. Integrate AdjustBridge into your app + +In the Project Navigator open the source file your View Controller. Add +the `import` statement at the top of the file. In the `viewDidLoad` or +`viewWillAppear` method of your Web View Delegate add the following +calls to `AdjustBridge`: + +```objc +#import "Adjust.h" +// ... +UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; +// ... +[AdjustBridge loadBridge:self webView:webView]; +// ... +``` + +![][delegate_bridge] + +### 4. Integrate AdjustBrige into your WebView + +To use the Javascript bridge of on your WebView, it must be configured like the [WebViewJavascriptBridge][js_setup] plugin in section `4)`. Include the following Javascript code to intialize the adjust iOS Javascript bridge: + +```js +function connectWebViewJavascriptBridge(callback) { + if (window.WebViewJavascriptBridge) { + callback(WebViewJavascriptBridge) + } else { + document.addEventListener('WebViewJavascriptBridgeReady', function() { + callback(WebViewJavascriptBridge) + }, false) + } +} + +connectWebViewJavascriptBridge(function(bridge) { + AdjustBridge.setBridge(bridge); + + // put calls to AdjustBridge here +}) +``` + +![][bridge_js] + +## Additional features + +Once you integrated the adjust iOS Javascript bridge SDK into your project, you can take advantage +of the following features. Remember that you have only access to the bridge inside the callback that initializes it. + +### 5. Add tracking of custom events. + +You can tell adjust about every event you want. Suppose you want to track +every tap on a button. You would have to create a new Event Token in your +[dashboard]. Let's say that Event Token is `abc123`. In your button's +`onClick` event you could then add the following line to track the click: + +```js +AdjustBridge.trackEvent('abc123'); +``` + +You can also register a callback URL for that event in your [dashboard] and we +will send a GET request to that URL whenever the event gets tracked. In that +case you can also put some key-value-pairs in a JSON object and pass it to the +`trackEvent` function. We will then append these named parameters to your +callback URL. + +For example, suppose you have registered the URL +`http://www.adjust.com/callback` for your event with Event Token `abc123` and +execute the following lines: + +```js +var parameters = {'key': 'value', 'foo': 'bar'}; +AdjustBridge.trackEvent('abc123', parameters); +``` + +In that case we would track the event and send a request to: + + http://www.adjust.com/callback?key=value&foo=bar + +It should be mentioned that we support a variety of placeholders like `{idfa}` +that can be used as parameter values. In the resulting callback this +placeholder would be replaced with the ID for Advertisers of the current +device. Also note that we don't store any of your custom parameters, but only +append them to your callbacks. If you haven't registered a callback for an +event, these parameters won't even be read. + +### 6. Add tracking of revenue + +If your users can generate revenue by clicking on advertisements or making +in-app purchases you can track those revenues. If, for example, a click is +worth one cent, you could make the following call to track that revenue: + +```js +AdjustBridge.trackRevenue(1.0); +``` + +The parameter is supposed to be in cents and will get rounded to one decimal +point. If you want to differentiate between different kinds of revenue you can +get different Event Tokens for each kind. Again, you need to create those Event +Tokens in your [dashboard]. In that case you would make a call like this: + +```js +AdjustBridge.trackRevenue(1.0, 'abc123'); +``` + +Again, you can register a callback and provide a JSON object of named +parameters, just like it worked with normal events. + +```js +var parameters = {'key': 'value', 'foo': 'bar'}; +AdjustBridge.trackRevenue(1.0, 'abc123', parameters); +``` + +If you want to track all revenues in the same currency you might want to use +[AEPriceMatrix][AEPriceMatrix] to do simple tier based currency conversion. + +### 7. Handle reattributions with deep linking + +You can also set up the adjust SDK to read deep links that come to your app, +also known as custom URL schemes in iOS. We will only read the data that is +injected by adjust tracker URLs. This is essential if you are planning to run +retargeting or re-engagement campaigns with deep links. + +Send the captured url to the function `openUrl` of our Javascript bridge. + +### 8. Receive delegate callbacks + +Every time your app tries to track a session, an event or some revenue, you can +be notified about the success of that operation and receive additional +information about the current install. In Javascript is as simple as passing a +callback function to out AdjustBridge: + +```js +AdjustBridge.setResponseDelegate(function (responseData) { + // ... +}); +``` + +The delegate callback will get called every time any activity was tracked or +failed to track. Within the delegate callback you have access to the +`responseData` parameter. Here is a quick summary of its attributes: + +- `activityKind` indicates what kind of activity was tracked. It + has one of these values: + + ``` + 'session' + 'event' + 'revenue' + 'reattribution' + ``` + +- `success` indicates whether or not the tracking attempt was + successful. +- `willRetry` is true when the request failed, but will be + retried. +- `error` an error message when the activity failed to track or + the response could not be parsed. Is `undefined` otherwise. +- `trackerToken` the tracker token of the current install. Is `undefined` if + request failed or response could not be parsed. +- `trackerName` the tracker name of the current install. Is `undefined` if + request failed or response could not be parsed. + +### 9. Enable event buffering + +If your app makes heavy use of event tracking, you might want to delay some +HTTP requests in order to send them in one batch every minute. You can enable +event buffering by adding the following line after your `setEnvironment:` call +in the `didFinishLaunching` method of your Application Delegate: + +```objc +[Adjust setEventBufferingEnabled:YES]; +``` + +### 10. Disable tracking + +You can disable the adjust SDK from tracking by invoking the function +`setEnabled` with the enabled parameter as `false`. This setting is remembered +between sessions, but it can only be activated after the first session. + +```js +AdjustBridge.setEnabled(false); +``` + +You can verify if the adjust SDK is currently active with the function +`isEnabled`. Pass a callback function with the parameter value as the boolean that +indicates whether adjust SDK is active or not. + +```js +AdjustBridge.isEnabled(function (isEnabledBool) { + if (isEnabledBool) + // ... +} +``` + +It is always possible to activate the adjust SDK by invoking +`setEnabled` with the enabled parameter as `true`. + +[adjust.io]: http://adjust.io +[dashboard]: http://adjust.io +[WebViewJavascriptBridge]: https://github.com/marcuswestin/WebViewJavascriptBridge +[ios_installation]: https://github.com/adjust/ios_sdk#basic-installation +[AEPriceMatrix]: https://github.com/adjust/AEPriceMatrix +[drag_bridge]: https://raw.githubusercontent.com/adjust/sdks/master/Resources/ios/drag_bridge.png +[add_bridge]: https://raw.githubusercontent.com/adjust/sdks/master/Resources/ios/add_bridge.png +[delegate_bridge]: https://raw.githubusercontent.com/adjust/sdks/master/Resources/ios/delegate_bridge.png +[bridge_js]: https://raw.githubusercontent.com/adjust/sdks/master/Resources/ios/bridge_js.png +[js_setup]: https://github.com/marcuswestin/WebViewJavascriptBridge#setup--examples-ios--osx From 1f2d6321dfcf1d6c444b9126ea052031b009cb40 Mon Sep 17 00:00:00 2001 From: Pedro Date: Tue, 27 May 2014 11:12:27 +0200 Subject: [PATCH 5/5] Update version --- Adjust.podspec | 4 ++-- Adjust/AIUtil.m | 2 +- AdjustTests/AIActivityHandlerTests.m | 2 +- README.md | 2 +- VERSION | 2 +- doc/migrate.md | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Adjust.podspec b/Adjust.podspec index 0922a6fa7..8a7558908 100644 --- a/Adjust.podspec +++ b/Adjust.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = "Adjust" - s.version = "3.3.1" + s.version = "3.3.2" s.summary = "This is the iOS SDK of Adjust. You can read more about it at http://adjust.io." s.homepage = "http://adjust.io" s.license = { :type => 'MIT', :file => 'MIT-LICENSE' } s.author = { "Christian Wellenbrock" => "welle@adjust.com" } - s.source = { :git => "https://github.com/adeven/adjust_ios_sdk.git", :tag => "v3.3.1" } + s.source = { :git => "https://github.com/adeven/adjust_ios_sdk.git", :tag => "v3.3.2" } s.platform = :ios, '4.3' s.framework = 'SystemConfiguration' s.weak_framework = 'AdSupport', 'iAd' diff --git a/Adjust/AIUtil.m b/Adjust/AIUtil.m index a5af099ae..d16fc6837 100644 --- a/Adjust/AIUtil.m +++ b/Adjust/AIUtil.m @@ -14,7 +14,7 @@ #include static NSString * const kBaseUrl = @"https://app.adjust.io"; -static NSString * const kClientSdk = @"ios3.3.1"; +static NSString * const kClientSdk = @"ios3.3.2"; static NSString * const kDateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'Z"; static NSDateFormatter * dateFormat; diff --git a/AdjustTests/AIActivityHandlerTests.m b/AdjustTests/AIActivityHandlerTests.m index 595c212aa..502f6360f 100644 --- a/AdjustTests/AIActivityHandlerTests.m +++ b/AdjustTests/AIActivityHandlerTests.m @@ -90,7 +90,7 @@ - (void)testFirstRun AIActivityPackage *activityPackage = (AIActivityPackage *) self.packageHandlerMock.packageQueue[0]; // check the Sdk version is being tested - XCTAssertEqual(@"ios3.3.1", activityPackage.clientSdk, @"%@", activityPackage.extendedString); + XCTAssertEqual(@"ios3.3.2", activityPackage.clientSdk, @"%@", activityPackage.extendedString); // check the server url XCTAssertEqual(@"https://app.adjust.io", AIUtil.baseUrl); diff --git a/README.md b/README.md index 83e9328b9..a13ef49dc 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ If you're using [CocoaPods][cocoapods], you can add the following line to your `Podfile` and continue with [step 3](#step3): ```ruby -pod 'Adjust', :git => 'git://github.com/adjust/ios_sdk.git', :tag => 'v3.3.1' +pod 'Adjust', :git => 'git://github.com/adjust/ios_sdk.git', :tag => 'v3.3.2' ``` ### 1. Get the SDK diff --git a/VERSION b/VERSION index bea438e9a..477254331 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.1 +3.3.2 diff --git a/doc/migrate.md b/doc/migrate.md index 6dec9a718..84b793349 100644 --- a/doc/migrate.md +++ b/doc/migrate.md @@ -1,4 +1,4 @@ -## Migrate your adjust SDK for iOS to v3.3.1 from v3.0.0 +## Migrate your adjust SDK for iOS to v3.3.2 from v3.0.0 We added an optional parameter `transactionId` to our `trackRevenue` methods. If you are tracking In-App Purchases you might want to pass in the transaction identifier provided by Apple to avoid duplicate revenue tracking. It should look roughly like this: @@ -36,14 +36,14 @@ all adjust SDK calls. ![][rename] -3. Download version v3.3.1 and drag the new folder `Adjust` into your Xcode +3. Download version v3.3.2 and drag the new folder `Adjust` into your Xcode Project Navigator. ![][drag] 4. Build your project to confirm that everything is properly connected again. -The adjust SDK v3.3.1 added delegate callbacks. Check out the [README] for +The adjust SDK v3.3.2 added delegate callbacks. Check out the [README] for details. @@ -99,7 +99,7 @@ meaningful at all times! Especially if you are tracking revenue. 1. The `appDidLaunch` method now expects your App Token instead of your App ID. You can find your App Token in your [dashboard]. -2. The adjust SDK for iOS 3.3.1 uses [ARC][arc]. If you haven't done already, +2. The adjust SDK for iOS 3.3.2 uses [ARC][arc]. If you haven't done already, we recommend [transitioning your project to use ARC][transition] as well. If you don't want to use ARC, you have to enable ARC for all files of the adjust SDK. Please consult the [README] for details.