Skip to content
Closed
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,7 @@ coverage/

.af-e2e/reports/
.af-smoke/reports/

# Swift Package Manager
.build/
.swiftpm/
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Versions

## 6.18.0+1

- Added iOS Swift Package Manager (SPM) support via `ios/appsflyer_sdk/Package.swift`
- Added SPM ObjC target mirroring `ios/Classes/` sources (ObjC-only, no mixed-language issues)
- CocoaPods path unchanged — full backward compatibility maintained
- Updated `.gitignore` with `.build/` and `.swiftpm/` entries

## 6.18.0

- Updated Android SDK from 6.17.6 to 6.18.0
Expand Down
31 changes: 31 additions & 0 deletions ios/appsflyer_sdk/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
name: "appsflyer_sdk",
platforms: [
.iOS("12.0")
],
products: [
.library(name: "appsflyer-sdk", targets: ["appsflyer_sdk"])
],
dependencies: [
.package(
url: "https://github.com/AppsFlyerSDK/AppsFlyerFramework",
from: "6.18.0"
)
],
targets: [
.target(
name: "appsflyer_sdk",
dependencies: [
.product(name: "AppsFlyerLib", package: "AppsFlyerFramework")
],
path: "Sources/appsflyer_sdk",
publicHeadersPath: "include",
cSettings: [
.headerSearchPath("include/appsflyer_sdk")
]
)
]
)
86 changes: 86 additions & 0 deletions ios/appsflyer_sdk/Sources/appsflyer_sdk/AppsFlyerAttribution.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// AppsFlyerAttribution.m
// flutter-appsflyer
//
// Created by Amit Kremer on 11/02/2021.
//

#import <Foundation/Foundation.h>
#import "AppsFlyerAttribution.h"

@implementation AppsFlyerAttribution

+ (id)shared {
static AppsFlyerAttribution *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[self alloc] init];
});
return shared;
}

- (id)init {
if (self = [super init]) {
self.options = nil;
self.restorationHandler = nil;
self.url = nil;
self.userActivity = nil;
self.annotation = nil;
self.sourceApplication = nil;
self.isBridgeReady = NO;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveBridgeReadyNotification:)
name:AF_BRIDGE_SET
object:nil];
}
return self;
}

- (void) continueUserActivity: (NSUserActivity*_Nullable) userActivity restorationHandler: (void (^_Nullable)(NSArray * _Nullable))restorationHandler{
if(self.isBridgeReady == YES){
[[AppsFlyerLib shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
}else{
[AppsFlyerAttribution shared].userActivity = userActivity;
[AppsFlyerAttribution shared].restorationHandler = restorationHandler;
}
}

- (void) handleOpenUrl:(NSURL *)url options:(NSDictionary *)options{
if(self.isBridgeReady == YES){
[[AppsFlyerLib shared] handleOpenUrl:url options:options];
}else{
[AppsFlyerAttribution shared].url = url;
[AppsFlyerAttribution shared].options = options;
}
}

- (void) handleOpenUrl:(NSURL *)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation{
if(self.isBridgeReady == YES){
[[AppsFlyerLib shared] handleOpenURL:url sourceApplication:sourceApplication withAnnotation:annotation];
}else{
[AppsFlyerAttribution shared].url = url;
[AppsFlyerAttribution shared].sourceApplication = sourceApplication;
[AppsFlyerAttribution shared].annotation = annotation;
}

}

- (void) receiveBridgeReadyNotification:(NSNotification *) notification
{
NSLog (@"AppsFlyer Debug: handle deep link");
if(self.url && self.sourceApplication && self.annotation){
[[AppsFlyerLib shared] handleOpenURL:self.url sourceApplication:self.sourceApplication withAnnotation:self.annotation];
self.url = nil;
self.sourceApplication = nil;
self.annotation = nil;
}else if(self.options && self.url){
[[AppsFlyerLib shared] handleOpenUrl:self.url options:self.options];
self.options = nil;
self.url = nil;
}else if(self.userActivity){
[[AppsFlyerLib shared] continueUserActivity:self.userActivity restorationHandler:nil];
self.userActivity = nil;
self.restorationHandler = nil;
}
}
@end
156 changes: 156 additions & 0 deletions ios/appsflyer_sdk/Sources/appsflyer_sdk/AppsFlyerStreamHandler.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//
// AppsFlyerStreamHandler.m
// appsflyer_sdk
//
// Created by Shahar Cohen on 05/09/2019.
//

#import "AppsFlyerStreamHandler.h"

@implementation AppsFlyerStreamHandler {

}

- (void)onConversionDataSuccess:(NSDictionary *)installData {
NSError *error;

//use callbacks
if([AppsflyerSdkPlugin gcdCallback]){
NSString *installDataJson = [self mapToJson:installData withError:error];
NSDictionary *fullResponse = @{
@"id": afGCDCallback,
@"data": installDataJson,
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
return;
}else if (error) {
return;
}
}

- (NSString *)mapToJson:(NSDictionary *)data withError:(NSError *)error{
NSData *JSON = [NSJSONSerialization dataWithJSONObject:data options:0 error:&error];
NSString *JSONString = [[NSString alloc] initWithData:JSON encoding:NSUTF8StringEncoding];
return JSONString;
}

- (void)onConversionDataFail:(NSError *)error {
//use callbacks
if([AppsflyerSdkPlugin gcdCallback]){
NSDictionary *fullResponse = @{
@"id": afGCDCallback,
@"data": error.localizedDescription,
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
return;
}

if (error) {
return;
}
}


- (void)onAppOpenAttribution:(NSDictionary *)attributionData {
NSError *error;
//use callbacks
if([AppsflyerSdkPlugin oaoaCallback]){
NSString* attributionDataJson = [self mapToJson:attributionData withError:error];
NSDictionary *fullResponse = @{
@"id": afOAOACallback,
@"data": attributionDataJson,
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
return;
}

if (error) {
return;
}
}

- (void)onAppOpenAttributionFailure:(NSError *)error {
if([AppsflyerSdkPlugin oaoaCallback]){
NSDictionary *fullResponse = @{
@"id": afOAOACallback,
@"data": error.localizedDescription,
@"status": afSuccess
};
NSString *JSONString = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONString];
return;
}

}

- (void)didResolveDeepLink:(AppsFlyerDeepLinkResult* _Nonnull) deepLinkResult {
NSError *error;
if([AppsflyerSdkPlugin udpCallback]){
NSMutableDictionary *fullResponse = [[NSMutableDictionary alloc] initWithCapacity:4];

fullResponse[ @"id"] = afUDPCallback;
fullResponse[ @"deepLinkStatus"] = [self getStatusAsString:deepLinkResult.status];
if(deepLinkResult.deepLink != nil){
NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithCapacity: deepLinkResult.deepLink.clickEvent.count + 1];
[dic addEntriesFromDictionary:deepLinkResult.deepLink.clickEvent];
dic[@"is_deferred"] = [NSNumber numberWithBool:deepLinkResult.deepLink.isDeferred];
fullResponse [@"deepLinkObj"] = dic;

}
if (deepLinkResult.error != nil && deepLinkResult.error.localizedDescription) {
fullResponse [@"deepLinkError"] = deepLinkResult.error.localizedDescription;

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

if (error) {
return;
}
}


- (void)sendResponseToFlutter:(NSString *)responseID status:(NSString *)status data:(NSDictionary *)data{
NSError *error;
NSString *JSONdata;

if(data != nil){
JSONdata = [self mapToJson:data withError:error];
}else{
JSONdata = @"empty data";
}
if (error) {
return;
}
NSDictionary *fullResponse = @{
@"id": responseID,
@"data": JSONdata,
@"status": status
};
JSONdata = [self mapToJson:fullResponse withError:error];
[AppsflyerSdkPlugin.callbackChannel invokeMethod:@"callListener" arguments:JSONdata];
}

- (NSString*) getStatusAsString:(AFSDKDeepLinkResultStatus)value{
switch (value) {
case AFSDKDeepLinkResultStatusFound:
return @"FOUND";
case AFSDKDeepLinkResultStatusNotFound:
return @"NOT_FOUND";
default:
return @"ERROR";

}
}



@end
Loading