Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2C5F7A4E2D2A4D4C00A1B2C3 /* AppsflyerSdkPluginMultiEngineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C5F7A4D2D2A4D4C00A1B2C3 /* AppsflyerSdkPluginMultiEngineTests.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -45,6 +46,7 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2C5F7A4D2D2A4D4C00A1B2C3 /* AppsflyerSdkPluginMultiEngineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppsflyerSdkPluginMultiEngineTests.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -92,6 +94,7 @@
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
2C5F7A4D2D2A4D4C00A1B2C3 /* AppsflyerSdkPluginMultiEngineTests.m */,
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
Expand Down Expand Up @@ -394,6 +397,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2C5F7A4E2D2A4D4C00A1B2C3 /* AppsflyerSdkPluginMultiEngineTests.m in Sources */,
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
254 changes: 254 additions & 0 deletions example/ios/RunnerTests/AppsflyerSdkPluginMultiEngineTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#import <XCTest/XCTest.h>
#import <Flutter/Flutter.h>
#import <appsflyer_sdk/AppsflyerSdkPlugin.h>

@interface AppsflyerSdkPlugin (Testing)
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger;
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result;
+ (void)invokeCallbackWithId:(NSString *)callbackId arguments:(id)arguments;
@end

@interface RecordingBinaryMessenger : NSObject <FlutterBinaryMessenger>
@property(nonatomic, strong) NSMutableArray<NSString *> *sentChannels;
@property(nonatomic, strong) NSMutableArray<FlutterMethodCall *> *sentMethodCalls;
@end

@implementation RecordingBinaryMessenger

- (instancetype)init {
self = [super init];
if (self) {
_sentChannels = [NSMutableArray array];
_sentMethodCalls = [NSMutableArray array];
}
return self;
}

- (void)sendOnChannel:(NSString *)channel message:(NSData *)message {
[self recordChannel:channel message:message];
}

- (void)sendOnChannel:(NSString *)channel
message:(NSData *)message
binaryReply:(FlutterBinaryReply)callback {
[self recordChannel:channel message:message];
if (callback) {
callback(nil);
}
}

- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString *)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
return 0;
}

- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString *)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler
taskQueue:(NSObject<FlutterTaskQueue> *)taskQueue {
return 0;
}

- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
}

- (void)recordChannel:(NSString *)channel message:(NSData *)message {
[self.sentChannels addObject:channel];
if (message == nil) {
return;
}

FlutterMethodCall *methodCall = [[FlutterStandardMethodCodec sharedInstance] decodeMethodCall:message];
if (methodCall != nil) {
[self.sentMethodCalls addObject:methodCall];
}
}

@end

@interface AppsflyerSdkPluginMultiEngineTests : XCTestCase
@property(nonatomic, strong) NSMutableArray<AppsflyerSdkPlugin *> *pluginsToCancel;
@property(nonatomic, strong) NSMutableSet<NSString *> *callbackIdsToCancel;
@end

@implementation AppsflyerSdkPluginMultiEngineTests

- (void)setUp {
[super setUp];
self.pluginsToCancel = [NSMutableArray array];
self.callbackIdsToCancel = [NSMutableSet set];
}

- (void)tearDown {
for (AppsflyerSdkPlugin *plugin in self.pluginsToCancel) {
for (NSString *callbackId in self.callbackIdsToCancel) {
[self cancelListeningWithPlugin:plugin callbackId:callbackId];
}
}
self.pluginsToCancel = nil;
self.callbackIdsToCancel = nil;
[super tearDown];
}

- (void)testDeepLinkCallbackOwnerSurvivesSecondaryEngineRegistration {
RecordingBinaryMessenger *mainMessenger = [[RecordingBinaryMessenger alloc] init];
RecordingBinaryMessenger *backgroundMessenger = [[RecordingBinaryMessenger alloc] init];

AppsflyerSdkPlugin *mainPlugin = [self pluginWithMessenger:mainMessenger];
(void)[self pluginWithMessenger:backgroundMessenger];

[self startListeningWithPlugin:mainPlugin callbackId:afUDPCallback];

NSString *payload = @"{\"id\":\"onDeepLinking\",\"deepLinkStatus\":\"FOUND\",\"deepLinkObj\":{}}";
[AppsflyerSdkPlugin invokeCallbackWithId:afUDPCallback arguments:payload];

XCTAssertEqual(mainMessenger.sentMethodCalls.count, 1);
XCTAssertEqualObjects(mainMessenger.sentChannels.firstObject, afCallbacksMethodChannel);
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.method, @"callListener");
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.arguments, payload);
XCTAssertEqual(backgroundMessenger.sentMethodCalls.count, 0);
}

- (void)testSecondaryEngineCannotStealExistingDeepLinkListener {
RecordingBinaryMessenger *mainMessenger = [[RecordingBinaryMessenger alloc] init];
RecordingBinaryMessenger *backgroundMessenger = [[RecordingBinaryMessenger alloc] init];

AppsflyerSdkPlugin *mainPlugin = [self pluginWithMessenger:mainMessenger];
AppsflyerSdkPlugin *backgroundPlugin = [self pluginWithMessenger:backgroundMessenger];

[self startListeningWithPlugin:mainPlugin callbackId:afUDPCallback];
[self startListeningWithPlugin:backgroundPlugin callbackId:afUDPCallback];

NSString *payload = @"{\"id\":\"onDeepLinking\",\"deepLinkStatus\":\"FOUND\",\"deepLinkObj\":{}}";
[AppsflyerSdkPlugin invokeCallbackWithId:afUDPCallback arguments:payload];

XCTAssertEqual(mainMessenger.sentMethodCalls.count, 1);
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.method, @"callListener");
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.arguments, payload);
XCTAssertEqual(backgroundMessenger.sentMethodCalls.count, 0);
}

- (void)testCallbackOwnerCanMoveAfterOwnerCancelsListening {
RecordingBinaryMessenger *mainMessenger = [[RecordingBinaryMessenger alloc] init];
RecordingBinaryMessenger *backgroundMessenger = [[RecordingBinaryMessenger alloc] init];

AppsflyerSdkPlugin *mainPlugin = [self pluginWithMessenger:mainMessenger];
AppsflyerSdkPlugin *backgroundPlugin = [self pluginWithMessenger:backgroundMessenger];

[self startListeningWithPlugin:mainPlugin callbackId:afUDPCallback];
[self cancelListeningWithPlugin:mainPlugin callbackId:afUDPCallback];
[self startListeningWithPlugin:backgroundPlugin callbackId:afUDPCallback];

NSString *payload = @"{\"id\":\"onDeepLinking\",\"deepLinkStatus\":\"FOUND\",\"deepLinkObj\":{}}";
[AppsflyerSdkPlugin invokeCallbackWithId:afUDPCallback arguments:payload];

XCTAssertEqual(mainMessenger.sentMethodCalls.count, 0);
XCTAssertEqual(backgroundMessenger.sentMethodCalls.count, 1);
XCTAssertEqualObjects(backgroundMessenger.sentMethodCalls.firstObject.method, @"callListener");
XCTAssertEqualObjects(backgroundMessenger.sentMethodCalls.firstObject.arguments, payload);
}

- (void)testSecondaryEngineCancelDoesNotRemoveDeepLinkListenerOwner {
RecordingBinaryMessenger *mainMessenger = [[RecordingBinaryMessenger alloc] init];
RecordingBinaryMessenger *backgroundMessenger = [[RecordingBinaryMessenger alloc] init];

AppsflyerSdkPlugin *mainPlugin = [self pluginWithMessenger:mainMessenger];
AppsflyerSdkPlugin *backgroundPlugin = [self pluginWithMessenger:backgroundMessenger];

[self startListeningWithPlugin:mainPlugin callbackId:afUDPCallback];
[self startListeningWithPlugin:backgroundPlugin callbackId:afUDPCallback];
[self cancelListeningWithPlugin:backgroundPlugin callbackId:afUDPCallback];

XCTAssertTrue([AppsflyerSdkPlugin udpCallback]);

NSString *payload = @"{\"id\":\"onDeepLinking\",\"deepLinkStatus\":\"FOUND\",\"deepLinkObj\":{}}";
[AppsflyerSdkPlugin invokeCallbackWithId:afUDPCallback arguments:payload];

XCTAssertEqual(mainMessenger.sentMethodCalls.count, 1);
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.method, @"callListener");
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.arguments, payload);
XCTAssertEqual(backgroundMessenger.sentMethodCalls.count, 0);
}

- (void)testCallbackOwnerCanMoveAfterOwnerIsDeallocated {
RecordingBinaryMessenger *mainMessenger = [[RecordingBinaryMessenger alloc] init];
RecordingBinaryMessenger *backgroundMessenger = [[RecordingBinaryMessenger alloc] init];
__weak AppsflyerSdkPlugin *weakOwnerPlugin = nil;

@autoreleasepool {
AppsflyerSdkPlugin *ownerPlugin = [[AppsflyerSdkPlugin alloc] initWithMessenger:mainMessenger];
weakOwnerPlugin = ownerPlugin;
[self startListeningWithPlugin:ownerPlugin callbackId:afUDPCallback];
}

XCTAssertNil(weakOwnerPlugin);

AppsflyerSdkPlugin *backgroundPlugin = [self pluginWithMessenger:backgroundMessenger];
[self startListeningWithPlugin:backgroundPlugin callbackId:afUDPCallback];

NSString *payload = @"{\"id\":\"onDeepLinking\",\"deepLinkStatus\":\"FOUND\",\"deepLinkObj\":{}}";
[AppsflyerSdkPlugin invokeCallbackWithId:afUDPCallback arguments:payload];

XCTAssertEqual(mainMessenger.sentMethodCalls.count, 0);
XCTAssertEqual(backgroundMessenger.sentMethodCalls.count, 1);
XCTAssertEqualObjects(backgroundMessenger.sentMethodCalls.firstObject.method, @"callListener");
XCTAssertEqualObjects(backgroundMessenger.sentMethodCalls.firstObject.arguments, payload);
}

- (void)testInstallConversionCallbackUsesOwningEngine {
RecordingBinaryMessenger *mainMessenger = [[RecordingBinaryMessenger alloc] init];
RecordingBinaryMessenger *backgroundMessenger = [[RecordingBinaryMessenger alloc] init];

AppsflyerSdkPlugin *mainPlugin = [self pluginWithMessenger:mainMessenger];
AppsflyerSdkPlugin *backgroundPlugin = [self pluginWithMessenger:backgroundMessenger];

[self startListeningWithPlugin:mainPlugin callbackId:afGCDCallback];
[self startListeningWithPlugin:backgroundPlugin callbackId:afGCDCallback];

XCTAssertTrue([AppsflyerSdkPlugin gcdCallback]);

NSString *payload = @"{\"id\":\"onInstallConversionData\",\"data\":\"{}\",\"status\":\"success\"}";
[AppsflyerSdkPlugin invokeCallbackWithId:afGCDCallback arguments:payload];

XCTAssertEqual(mainMessenger.sentMethodCalls.count, 1);
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.method, @"callListener");
XCTAssertEqualObjects(mainMessenger.sentMethodCalls.firstObject.arguments, payload);
XCTAssertEqual(backgroundMessenger.sentMethodCalls.count, 0);
}

- (void)testRequestScopedCallbackUsesLatestEngine {
RecordingBinaryMessenger *mainMessenger = [[RecordingBinaryMessenger alloc] init];
RecordingBinaryMessenger *backgroundMessenger = [[RecordingBinaryMessenger alloc] init];

AppsflyerSdkPlugin *mainPlugin = [self pluginWithMessenger:mainMessenger];
AppsflyerSdkPlugin *backgroundPlugin = [self pluginWithMessenger:backgroundMessenger];

[self startListeningWithPlugin:mainPlugin callbackId:afGenerateInviteLinkSuccess];
[self startListeningWithPlugin:backgroundPlugin callbackId:afGenerateInviteLinkSuccess];

NSString *payload = @"{\"id\":\"generateInviteLinkSuccess\",\"data\":\"{}\",\"status\":\"success\"}";
[AppsflyerSdkPlugin invokeCallbackWithId:afGenerateInviteLinkSuccess arguments:payload];

XCTAssertEqual(mainMessenger.sentMethodCalls.count, 0);
XCTAssertEqual(backgroundMessenger.sentMethodCalls.count, 1);
XCTAssertEqualObjects(backgroundMessenger.sentMethodCalls.firstObject.method, @"callListener");
XCTAssertEqualObjects(backgroundMessenger.sentMethodCalls.firstObject.arguments, payload);
}

- (AppsflyerSdkPlugin *)pluginWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger {
AppsflyerSdkPlugin *plugin = [[AppsflyerSdkPlugin alloc] initWithMessenger:messenger];
[self.pluginsToCancel addObject:plugin];
return plugin;
}

- (void)startListeningWithPlugin:(AppsflyerSdkPlugin *)plugin callbackId:(NSString *)callbackId {
[self.callbackIdsToCancel addObject:callbackId];
FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"startListening" arguments:callbackId];
[plugin handleMethodCall:call result:^(id result) {}];
}

- (void)cancelListeningWithPlugin:(AppsflyerSdkPlugin *)plugin callbackId:(NSString *)callbackId {
FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"cancelListening" arguments:callbackId];
[plugin handleMethodCall:call result:^(id result) {}];
}

@end
16 changes: 10 additions & 6 deletions ios/Classes/AppsFlyerStreamHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

#import "AppsFlyerStreamHandler.h"

@interface AppsflyerSdkPlugin (CallbackRouting)
+ (void)invokeCallbackWithId:(NSString*)callbackId arguments:(id)arguments;
@end

@implementation AppsFlyerStreamHandler {

}
Expand All @@ -23,7 +27,7 @@ - (void)onConversionDataSuccess:(NSDictionary *)installData {
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
[AppsflyerSdkPlugin invokeCallbackWithId:afGCDCallback arguments:JSONString];
return;
}else if (error) {
return;
Expand All @@ -45,7 +49,7 @@ - (void)onConversionDataFail:(NSError *)error {
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
[AppsflyerSdkPlugin invokeCallbackWithId:afGCDCallback arguments:JSONString];
return;
}

Expand All @@ -66,7 +70,7 @@ - (void)onAppOpenAttribution:(NSDictionary *)attributionData {
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
[AppsflyerSdkPlugin invokeCallbackWithId:afOAOACallback arguments:JSONString];
return;
}

Expand All @@ -83,7 +87,7 @@ - (void)onAppOpenAttributionFailure:(NSError *)error {
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
[AppsflyerSdkPlugin invokeCallbackWithId:afOAOACallback arguments:JSONString];
return;
}

Expand All @@ -108,7 +112,7 @@ - (void)didResolveDeepLink:(AppsFlyerDeepLinkResult* _Nonnull) deepLinkResult {

}
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
[AppsflyerSdkPlugin invokeCallbackWithId:afUDPCallback arguments:JSONString];
return;
}

Expand Down Expand Up @@ -136,7 +140,7 @@ - (void)sendResponseToFlutter:(NSString *)responseID status:(NSString *)status d
@"status": status
};
JSONdata = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONdata];
[AppsflyerSdkPlugin invokeCallbackWithId:responseID arguments:JSONdata];
}

- (NSString*) getStatusAsString:(AFSDKDeepLinkResultStatus)value{
Expand Down
1 change: 0 additions & 1 deletion ios/Classes/AppsflyerSdkPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,3 @@
#define afCallbacksMethodChannel @"callbacks"
#define afEventChannel @"af-events"
#define afValidatePurchaseChannel @"af-validate-purchase"

Loading