Skip to content
Merged
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
30 changes: 22 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,26 @@ jobs:
working-directory: example
run: flutter pub get

# Step 7: Build Android APK (debug mode)
# Step 7: Create dummy .env file for CI (not committed to repo)
- name: 🔑 Create dummy .env for CI build
working-directory: example
run: |
echo "DEV_KEY=dummy_dev_key" > .env
echo "APP_ID=dummy_app_id" >> .env

# Step 8: Build Android APK (debug mode)
# This validates that the plugin integrates correctly with Android
- name: 🔨 Build Android APK (debug)
working-directory: example
run: flutter build apk --debug

# Step 8: Build Android App Bundle (release mode, no signing)
# Step 9: Build Android App Bundle (release mode, no signing)
# App Bundle is the preferred format for Play Store
- name: 🔨 Build Android App Bundle (release)
working-directory: example
run: flutter build appbundle --release
# Step 9: Upload build artifacts (optional)

# Step 10: Upload build artifacts (optional)
# Useful for manual testing or archiving
- name: 📤 Upload APK artifact
if: success()
Expand Down Expand Up @@ -239,19 +246,26 @@ jobs:
working-directory: example/ios
run: pod install

# Step 8: Build for iOS Simulator (fastest iOS build)
# Step 8: Create dummy .env file for CI (not committed to repo)
- name: 🔑 Create dummy .env for CI build
working-directory: example
run: |
echo "DEV_KEY=dummy_dev_key" > .env
echo "APP_ID=dummy_app_id" >> .env

# Step 9: Build for iOS Simulator (fastest iOS build)
# Validates that the plugin compiles for iOS
- name: 🔨 Build iOS for Simulator
working-directory: example
run: flutter build ios --simulator --debug

# Step 9: Build iOS IPA without code signing (release mode)
# Step 10: Build iOS IPA without code signing (release mode)
# This validates a full release build without requiring certificates
- name: 🔨 Build iOS IPA (no codesign)
working-directory: example
run: flutter build ipa --release --no-codesign
# Step 10: Upload build artifacts (optional)

# Step 11: Upload build artifacts (optional)
- name: 📤 Upload iOS build artifact
if: success()
uses: actions/upload-artifact@v4
Expand Down
6 changes: 5 additions & 1 deletion ios/Classes/AppsflyerSdkPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
#import "AppsFlyerLib.h"
#endif

#if __has_include(<Flutter/FlutterSceneLifeCycleDelegate.h>)
@interface AppsflyerSdkPlugin: NSObject<FlutterPlugin, FlutterSceneLifeCycleDelegate>
Comment thread
Dani-Koza-AF marked this conversation as resolved.
#else
@interface AppsflyerSdkPlugin: NSObject<FlutterPlugin>
#endif

@property (readwrite, nonatomic) BOOL isManualStart;

Expand All @@ -18,7 +22,7 @@
@end

// Appsflyer JS objects
#define kAppsFlyerPluginVersion @"6.17.8"
#define kAppsFlyerPluginVersion @"6.17.9"
#define afDevKey @"afDevKey"
#define afAppId @"afAppId"
#define afIsDebug @"isDebug"
Expand Down
65 changes: 61 additions & 4 deletions ios/Classes/AppsflyerSdkPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[registrar addMethodCallDelegate:instance channel:channel];
[registrar addMethodCallDelegate:instance channel:callbackChannel];
[registrar addApplicationDelegate:instance];

#if __has_include(<Flutter/FlutterSceneLifeCycleDelegate.h>)
if (@available(iOS 13.0, *)) {
[registrar addSceneDelegate:instance];
}
#endif

}

Expand Down Expand Up @@ -904,9 +908,20 @@ - (void)appDidBecomeActive {


+ (FlutterViewController*) getViewController{
UIViewController *topMostViewControllerObj = [[[UIApplication sharedApplication] delegate] window].rootViewController;
UIWindow *window = nil;
if (@available(iOS 13.0, *)) {
for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
window = scene.windows.firstObject;
break;
}
}
}
if (window == nil) {
window = [[[UIApplication sharedApplication] delegate] window];
}
UIViewController *topMostViewControllerObj = window.rootViewController;
FlutterViewController *flutterViewController = (FlutterViewController *)topMostViewControllerObj;

return flutterViewController;
}

Expand Down Expand Up @@ -947,10 +962,52 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceAppl
// Open Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
[[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];

// Results of this are ORed and NO doesn't affect other delegate interceptors' result.
return NO;
}

#if __has_include(<Flutter/FlutterSceneLifeCycleDelegate.h>)
#pragma mark - FlutterSceneLifeCycleDelegate

// UIScene-based URI-scheme deep links (iOS 13+, Flutter 3.41+ UIScene migration)
- (BOOL)scene:(UIScene*)scene openURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts API_AVAILABLE(ios(13.0)) {
for (UIOpenURLContext *context in URLContexts) {
NSDictionary *opts = @{};
if (context.options.sourceApplication) {
opts = @{UIApplicationOpenURLOptionsSourceApplicationKey: context.options.sourceApplication};
}
[[AppsFlyerAttribution shared] handleOpenUrl:context.URL options:opts];
}
return NO;
}

// Cold-start deep links delivered via UISceneConnectionOptions (iOS 13+)
// Handles both URI-scheme links (URLContexts) and Universal Links (userActivities)
- (BOOL)scene:(UIScene*)scene
willConnectToSession:(UISceneSession*)session
options:(UISceneConnectionOptions*)connectionOptions API_AVAILABLE(ios(13.0)) {
for (UIOpenURLContext *context in connectionOptions.URLContexts) {
NSDictionary *opts = @{};
if (context.options.sourceApplication) {
opts = @{UIApplicationOpenURLOptionsSourceApplicationKey: context.options.sourceApplication};
}
[[AppsFlyerAttribution shared] handleOpenUrl:context.URL options:opts];
}
for (NSUserActivity *activity in connectionOptions.userActivities) {
if ([activity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
[[AppsFlyerAttribution shared] continueUserActivity:activity restorationHandler:nil];
}
}
return NO;
}

// UIScene-based Universal Links (iOS 13+)
- (BOOL)scene:(UIScene*)scene continueUserActivity:(NSUserActivity*)userActivity API_AVAILABLE(ios(13.0)) {
[[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:nil];
return NO;
}
#endif // __has_include(<Flutter/FlutterSceneLifeCycleDelegate.h>)


@end
Loading