diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e41b0c2b..113590a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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() @@ -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 diff --git a/.github/workflows/production-release.yml b/.github/workflows/production-release.yml index 62234f8f..e1ae0ee4 100644 --- a/.github/workflows/production-release.yml +++ b/.github/workflows/production-release.yml @@ -220,7 +220,24 @@ jobs: - name: 🔍 Validate package run: | echo "Running pub publish dry-run to validate package..." + # Run dry-run and capture exit code + # Exit code 65 = warnings only (acceptable, e.g., gitignored files) + # Exit code 0 = success + # Other exit codes = real errors + set +e flutter pub publish --dry-run + EXIT_CODE=$? + set -e + + if [ $EXIT_CODE -eq 0 ]; then + echo "✅ Package validation passed with no warnings" + elif [ $EXIT_CODE -eq 65 ]; then + echo "âš ī¸ Package validation passed with warnings (acceptable)" + echo "Warnings don't prevent publishing" + else + echo "❌ Package validation failed with exit code $EXIT_CODE" + exit $EXIT_CODE + fi - name: 📝 Check pub.dev credentials run: | diff --git a/.github/workflows/promote-release.yml b/.github/workflows/promote-release.yml index d7096497..d348dd03 100644 --- a/.github/workflows/promote-release.yml +++ b/.github/workflows/promote-release.yml @@ -1,97 +1,214 @@ -name: Promote Release - Merge on QA Pass and Publish +# ============================================================================= +# Promote Release - Prepare Release Branch for Production +# ============================================================================= +# +# Purpose: When QA approves an RC, this workflow prepares the release branch +# for production by removing the -rc suffix from version numbers. +# +# IMPORTANT: This workflow does NOT merge the PR (org rules prevent bot merges). +# Instead, it updates the release branch so when a human merges, the version +# is clean (e.g., 6.17.8 instead of 6.17.8-rc1). +# +# Flow: +# 1. QA tests the RC version +# 2. QA adds label "pass QA ready for deploy" to the PR +# 3. This workflow triggers and: +# - Updates the release branch to remove -rcN suffix +# - Updates all version files +# - Commits changes to the release branch +# 4. Human reviews and manually merges the PR +# 5. production-release.yml triggers on merge +# +# ============================================================================= + +name: Promote Release - Prepare for Production on: pull_request: - types: [labeled, synchronize, reopened, ready_for_review] - branches: - - master - pull_request_review: - types: [submitted] + types: [labeled] branches: - master concurrency: - group: promote-release-${{ github.event.pull_request.number || github.run_id }} + group: promote-release-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: - gate-and-merge: - name: 🔐 Gate, Verify Checks, and Merge - if: >- - ${ { github.event.pull_request.head.ref } } == '' || startsWith(github.event.pull_request.head.ref, 'releases/') + # =========================================================================== + # Job 1: Prepare Release Branch for Production + # =========================================================================== + prepare-for-production: + name: 🚀 Prepare Release for Production + # Only run when the specific label is added AND it's from a releases/ branch + if: | + github.event.label.name == 'pass QA ready for deploy' && + startsWith(github.event.pull_request.head.ref, 'releases/') runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - checks: read - statuses: read + outputs: - merged: ${{ steps.merge.outputs.merged }} - version: ${{ steps.version.outputs.version }} + version: ${{ steps.compute-version.outputs.version }} + release_branch: ${{ steps.compute-version.outputs.release_branch }} + steps: - - name: 🧠 Evaluate conditions - id: eval - uses: actions/github-script@v7 + - name: đŸ“Ĩ Checkout release branch + uses: actions/checkout@v4 with: - script: | - const core = require('@actions/core'); - const pr = context.payload.pull_request || (await github.rest.pulls.get({owner: context.repo.owner, repo: context.repo.repo, pull_number: context.payload.pull_request?.number || context.issue.number})).data; - if (!pr) core.setFailed('No PR context'); - const hasLabel = pr.labels.some(l => l.name === 'pass QA ready for deploy'); - if (!hasLabel) core.setFailed('Required label not present: pass QA ready for deploy'); - // Check approvals - const reviews = await github.rest.pulls.listReviews({owner: context.repo.owner, repo: context.repo.repo, pull_number: pr.number}); - const approved = reviews.data.some(r => r.state === 'APPROVED'); - if (!approved) core.setFailed('No approval found on the PR'); - core.setOutput('pr_number', pr.number.toString()); - - name: âŗ Wait for required status checks to pass + ref: ${{ github.event.pull_request.head.ref }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: 🔍 Compute production version + id: compute-version + run: | + RELEASE_BRANCH="${{ github.event.pull_request.head.ref }}" + echo "Release branch: $RELEASE_BRANCH" + + # Get current version from pubspec.yaml + CURRENT_VERSION=$(grep "^version:" pubspec.yaml | sed 's/version: //' | tr -d ' ') + echo "Current version: $CURRENT_VERSION" + + # Remove -rcN suffix to get production version + PROD_VERSION=$(echo "$CURRENT_VERSION" | sed 's/-rc[0-9]*$//') + echo "Production version: $PROD_VERSION" + + # Validate it's different (was an RC version) + if [[ "$CURRENT_VERSION" == "$PROD_VERSION" ]]; then + echo "âš ī¸ Version doesn't have -rc suffix. Already production ready?" + echo "Current: $CURRENT_VERSION" + fi + + echo "version=$PROD_VERSION" >> $GITHUB_OUTPUT + echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT + echo "release_branch=$RELEASE_BRANCH" >> $GITHUB_OUTPUT + + - name: 📝 Update pubspec.yaml to production version + run: | + VERSION='${{ steps.compute-version.outputs.version }}' + echo "Updating pubspec.yaml to production version: $VERSION" + sed -i "s/^version: .*/version: $VERSION/" pubspec.yaml + grep "^version:" pubspec.yaml + + - name: 📝 Update plugin version constants (Android/iOS/Dart) + run: | + VERSION='${{ steps.compute-version.outputs.version }}' + echo "Updating PLUGIN_VERSION constants to: $VERSION" + + # Android - AppsFlyerConstants.java + ANDROID_FILE="android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java" + if [ -f "$ANDROID_FILE" ]; then + sed -i "s/PLUGIN_VERSION = \".*\"/PLUGIN_VERSION = \"$VERSION\"/" "$ANDROID_FILE" + echo "✅ Android:" && grep "PLUGIN_VERSION" "$ANDROID_FILE" + fi + + # Dart - appsflyer_constants.dart + DART_FILE="lib/src/appsflyer_constants.dart" + if [ -f "$DART_FILE" ]; then + sed -i "s/PLUGIN_VERSION = \".*\"/PLUGIN_VERSION = \"$VERSION\"/" "$DART_FILE" + echo "✅ Dart:" && grep "PLUGIN_VERSION" "$DART_FILE" + fi + + # iOS - AppsflyerSdkPlugin.h (#define) + IOS_FILE="ios/Classes/AppsflyerSdkPlugin.h" + if [ -f "$IOS_FILE" ]; then + sed -i 's/kAppsFlyerPluginVersion[[:space:]]*@"[^"]*"/kAppsFlyerPluginVersion @"'"$VERSION"'"/' "$IOS_FILE" + echo "✅ iOS:" && grep "kAppsFlyerPluginVersion" "$IOS_FILE" + fi + + - name: 💾 Commit and push version changes + run: | + VERSION='${{ steps.compute-version.outputs.version }}' + CURRENT='${{ steps.compute-version.outputs.current_version }}' + + git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + + if [[ -n $(git status -s) ]]; then + git add pubspec.yaml android/ ios/ + git commit -m "chore: prepare production release $VERSION (from $CURRENT)" + git push + echo "✅ Pushed version update to release branch" + else + echo "â„šī¸ No version changes needed" + fi + + - name: 📝 Update PR description uses: actions/github-script@v7 with: script: | - const prNumber = Number(core.getInput('pr_number', { required: false })) || ${{ steps.eval.outputs.pr_number || '0' }}; - const { data: pr } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: prNumber }); - const ref = pr.head.sha; - const start = Date.now(); - const timeoutMs = 60*60*1000; // 60 minutes - const sleep = ms => new Promise(r => setTimeout(r, ms)); - while (true) { - const { data: combined } = await github.rest.repos.getCombinedStatusForRef({ owner: context.repo.owner, repo: context.repo.repo, ref }); - const checksOk = combined.state === 'success'; - if (checksOk) break; - if (Date.now() - start > timeoutMs) throw new Error('Timeout waiting for status checks to pass'); - core.info(`Waiting for checks. Current state: ${combined.state}`); - await sleep(15000); - } - - name: đŸ“Ĩ Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: 🔀 Merge PR immediately - id: merge + const version = '${{ steps.compute-version.outputs.version }}'; + const currentVersion = '${{ steps.compute-version.outputs.current_version }}'; + const pr = context.payload.pull_request; + + const newBody = `### Production Release ${version} + + **Status:** ✅ Ready for manual merge + + **Version updated:** ${currentVersion} → ${version} + + --- + + ${pr.body || ''} + + --- + + **Next steps:** + 1. ✅ QA approved (label added) + 2. ✅ Version updated to production (${version}) + 3. âŗ **Awaiting manual merge** by a maintainer + 4. âŗ Production release will trigger automatically after merge + `; + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + body: newBody + }); + + - name: đŸ“ĸ Add comment to PR uses: actions/github-script@v7 with: script: | - const prNumber = Number(${ { steps.eval.outputs.pr_number } }); - const { data: pr } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: prNumber }); - if (pr.merged) { core.setOutput('merged', 'true'); return; } - const method = 'merge'; // use repo default merge method - await github.rest.pulls.merge({ owner: context.repo.owner, repo: context.repo.repo, pull_number: pr.number, merge_method: method }); - core.setOutput('merged', 'true'); - - name: 📝 Read version from pubspec on master - id: version - run: | - git fetch origin master:master - git checkout master - VER=$(grep '^version:' pubspec.yaml | sed 's/version: //' | tr -d ' ') - echo "version=$VER" >> $GITHUB_OUTPUT - - call-production: - name: 🚀 Production Release - needs: gate-and-merge - if: needs.gate-and-merge.outputs.merged == 'true' - uses: ./.github/workflows/production-release.yml - with: - version: ${{ needs.gate-and-merge.outputs.version }} - skip_tests: false - dry_run: false - secrets: inherit + const version = '${{ steps.compute-version.outputs.version }}'; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: `## 🚀 Ready for Production Release + + The release branch has been updated: + - **Version:** \`${version}\` (removed -rc suffix) + - **All version files updated** + + ### Next Steps + 1. **Review the changes** in this PR + 2. **Merge this PR** when ready + 3. The production release workflow will automatically: + - Publish \`${version}\` to pub.dev + - Create GitHub release + - Send notifications + + > âš ī¸ **Note:** This PR requires manual merge due to branch protection rules.` + }); + + # =========================================================================== + # Job 2: Notify Team + # =========================================================================== + notify-ready: + name: đŸ“ĸ Notify Ready for Merge + needs: prepare-for-production + runs-on: ubuntu-latest + if: always() && needs.prepare-for-production.result == 'success' + + steps: + - name: 📨 Send Slack notification + uses: slackapi/slack-github-action@v1 + with: + payload: | + { + "text": "\n:white_check_mark: *Flutter Plugin Ready for Production*\n\nVersion: ${{ needs.prepare-for-production.outputs.version }}\nPR: ${{ github.event.pull_request.html_url }}\n\n*Action Required:* A maintainer needs to manually merge the PR to trigger the production release." + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.CI_SLACK_WEBHOOK_URL }} + continue-on-error: true # Will gracefully fail if webhook not configured diff --git a/.github/workflows/rc-release.yml b/.github/workflows/rc-release.yml index a08acf6a..eec41353 100644 --- a/.github/workflows/rc-release.yml +++ b/.github/workflows/rc-release.yml @@ -230,27 +230,58 @@ jobs: echo "âš ī¸ $FILE not found" fi - - name: 📝 Update plugin version constants (Android/iOS) + - name: 📝 Update plugin version constants (Android/iOS/Dart) run: | VERSION='${{ needs.validate-release.outputs.version }}' - AND_FILE="android/src/main/java/com/appsflyer/appsflyersdk/AppsflyerConstants.java" - IOS_FILE="ios/Classes/AppsflyerSdkPlugin.m" - if [ -f "$AND_FILE" ]; then - sed -i.bak "s/kPluginVersion = \".*\"/kPluginVersion = \"$VERSION\"/" "$AND_FILE" && rm "$AND_FILE.bak" || true + echo "Updating PLUGIN_VERSION constants to: $VERSION" + + # Android - AppsFlyerConstants.java + ANDROID_FILE="android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java" + if [ -f "$ANDROID_FILE" ]; then + sed -i.bak "s/PLUGIN_VERSION = \".*\"/PLUGIN_VERSION = \"$VERSION\"/" "$ANDROID_FILE" + rm "$ANDROID_FILE.bak" + echo "✅ Android:" && grep "PLUGIN_VERSION" "$ANDROID_FILE" + fi + + # Dart - appsflyer_constants.dart + DART_FILE="lib/src/appsflyer_constants.dart" + if [ -f "$DART_FILE" ]; then + sed -i.bak "s/PLUGIN_VERSION = \".*\"/PLUGIN_VERSION = \"$VERSION\"/" "$DART_FILE" + rm "$DART_FILE.bak" + echo "✅ Dart:" && grep "PLUGIN_VERSION" "$DART_FILE" fi + + # iOS - AppsflyerSdkPlugin.h (#define) + IOS_FILE="ios/Classes/AppsflyerSdkPlugin.h" if [ -f "$IOS_FILE" ]; then - sed -i.bak "s/kPluginVersion = @\".*\"/kPluginVersion = @\"$VERSION\"/" "$IOS_FILE" && rm "$IOS_FILE.bak" || true + sed -i.bak 's/kAppsFlyerPluginVersion[[:space:]]*@"[^"]*"/kAppsFlyerPluginVersion @"'"$VERSION"'"/' "$IOS_FILE" + rm "$IOS_FILE.bak" + echo "✅ iOS:" && grep "kAppsFlyerPluginVersion" "$IOS_FILE" fi - - name: 📝 Update README SDK versions + - name: 📝 Update README SDK and Purchase Connector versions run: | IOS_VER='${{ needs.validate-release.outputs.ios_sdk_version }}' AND_VER='${{ needs.validate-release.outputs.android_sdk_version }}' + + # Main SDK versions sed -i.bak -E "s/- Android AppsFlyer SDK \*\*v[0-9.]+\*\*/- Android AppsFlyer SDK **v${AND_VER}**/" README.md sed -i.bak -E "s/- iOS AppsFlyer SDK \*\*v[0-9.]+\*\*/- iOS AppsFlyer SDK **v${IOS_VER}**/" README.md - rm README.md.bak - echo "README updated SDK versions:" - sed -n '12,20p' README.md | cat + + # Purchase Connector versions (extract from build files) + AND_PC=$(grep "purchase-connector:" android/build.gradle | sed -n "s/.*purchase-connector:\([^']*\).*/\1/p" | head -1) + IOS_PC=$(grep "PurchaseConnector" ios/appsflyer_sdk.podspec | sed -n "s/.*PurchaseConnector',.*'\([^']*\)'.*/\1/p" | head -1) + + if [ -n "$AND_PC" ]; then + sed -i.bak -E "/^### Purchase Connector/,/^##|^$/{s/^- Android [0-9.]+$/- Android ${AND_PC}/;}" README.md + fi + if [ -n "$IOS_PC" ]; then + sed -i.bak -E "/^### Purchase Connector/,/^##|^$/{s/^- iOS [0-9.]+$/- iOS ${IOS_PC}/;}" README.md + fi + + rm -f README.md.bak + echo "README updated versions:" + sed -n '/## SDK Versions/,/## ❗/p' README.md | head -12 - name: 💾 Commit & push changes id: push @@ -444,12 +475,15 @@ jobs: notify-team: name: đŸ“ĸ Notify Team runs-on: ubuntu-latest - needs: [validate-release, create-prerelease] + needs: [validate-release, create-prerelease, prepare-branch] if: always() steps: - name: đŸ“Ĩ Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ needs.prepare-branch.outputs.release_branch }} - name: 📝 Extract SDK versions and changelog id: extract-info diff --git a/CHANGELOG.md b/CHANGELOG.md index 77abd659..189e5b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Versions +## 6.17.9 + +- Added UIScene lifecycle support for iOS deep linking (Flutter 3.41+ compatibility) + - Adopted `FlutterSceneLifeCycleDelegate` with compile-time guard for backward compatibility + - Added `scene:openURLContexts:` for warm-start URI-scheme deep links + - Added `scene:willConnectToSession:options:` for cold-start URI-scheme and Universal Links + - Added `scene:continueUserActivity:` for Universal Links via UIScene +- Fixed `getViewController` to use `UIWindowScene` lookup on iOS 13+ (replaces deprecated `UIApplication.delegate.window`) +- Updated iOS SDK from 6.17.8 to 6.17.9 +- Updated iOS Purchase Connector from 6.17.8 to 6.17.9 + ## 6.17.8 - Updated Android SDK from 6.17.4 to 6.17.5 diff --git a/README.md b/README.md index 78385505..3c310168 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,13 @@ To do so, please follow [this article](https://support.appsflyer.com/hc/en-us/ar ## SDK Versions -- Android AppsFlyer SDK **v6.17.5** -- iOS AppsFlyer SDK **v6.17.8** +- Android AppsFlyer SDK **v6.17.6** +- iOS AppsFlyer SDK **v6.17.9** ### Purchase Connector versions - Android 2.2.0 -- iOS 6.17.8 +- iOS 6.17.9 ## ❗❗ Breaking changes when updating to v6.x.x❗❗ diff --git a/android/build.gradle b/android/build.gradle index db95fa52..fff9a125 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -53,7 +53,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'com.appsflyer:af-android-sdk:6.17.5' + implementation 'com.appsflyer:af-android-sdk:6.17.6' implementation 'com.android.installreferrer:installreferrer:2.2' // implementation 'androidx.core:core-ktx:1.13.1' if (includeConnector) { diff --git a/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java b/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java index 0b05df7c..e782f1c1 100644 --- a/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java +++ b/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java @@ -1,7 +1,7 @@ package com.appsflyer.appsflyersdk; public final class AppsFlyerConstants { - final static String PLUGIN_VERSION = "6.17.8"; + final static String PLUGIN_VERSION = "6.17.9"; final static String AF_APP_INVITE_ONE_LINK = "appInviteOneLink"; final static String AF_HOST_PREFIX = "hostPrefix"; final static String AF_HOST_NAME = "hostName"; diff --git a/covBadgeGen.js b/covBadgeGen.js deleted file mode 100755 index 16a32cdb..00000000 --- a/covBadgeGen.js +++ /dev/null @@ -1,20 +0,0 @@ -const lcov2badge = require("lcov2badge"); -const fs = require("fs"); - -lcov2badge.badge("./coverage/lcov.info", function (err, svgBadge) { - if (err) throw err; - - try { - if (fs.existsSync("./coverage_badge.svg")) { - fs.unlinkSync("./coverage_badge.svg"); - console.log("[INFO] remove old file"); - } - } catch (err) { - console.error(err); - } - - console.log("[INFO] generate coverage image"); - fs.writeFile("./coverage_badge.svg", svgBadge, (_) => - console.log("[INFO] complete") - ); -}); \ No newline at end of file diff --git a/example/.env b/example/.env deleted file mode 100644 index 644f4b18..00000000 --- a/example/.env +++ /dev/null @@ -1,2 +0,0 @@ -DEV_KEY="7jKdYxdnYcbSQ5iWrGytWc" -APP_ID="112233554" \ No newline at end of file diff --git a/example/.metadata b/example/.metadata deleted file mode 100644 index 417ad2fc..00000000 --- a/example/.metadata +++ /dev/null @@ -1,45 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "ef1af02aead6fe2414f3aafa5a61087b610e1332" - channel: "stable" - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - - platform: android - create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - - platform: ios - create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - - platform: linux - create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - - platform: macos - create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - - platform: web - create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - - platform: windows - create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/ios/Classes/AppsflyerSdkPlugin.h b/ios/Classes/AppsflyerSdkPlugin.h index 5b38dd3c..ec746454 100644 --- a/ios/Classes/AppsflyerSdkPlugin.h +++ b/ios/Classes/AppsflyerSdkPlugin.h @@ -6,7 +6,11 @@ #import "AppsFlyerLib.h" #endif +#if __has_include() +@interface AppsflyerSdkPlugin: NSObject +#else @interface AppsflyerSdkPlugin: NSObject +#endif @property (readwrite, nonatomic) BOOL isManualStart; @@ -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" diff --git a/ios/Classes/AppsflyerSdkPlugin.m b/ios/Classes/AppsflyerSdkPlugin.m index ca06d5df..b76ec662 100644 --- a/ios/Classes/AppsflyerSdkPlugin.m +++ b/ios/Classes/AppsflyerSdkPlugin.m @@ -68,7 +68,11 @@ + (void)registerWithRegistrar:(NSObject*)registrar { [registrar addMethodCallDelegate:instance channel:channel]; [registrar addMethodCallDelegate:instance channel:callbackChannel]; [registrar addApplicationDelegate:instance]; - +#if __has_include() + if (@available(iOS 13.0, *)) { + [registrar addSceneDelegate:instance]; + } +#endif } @@ -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; } @@ -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() +#pragma mark - FlutterSceneLifeCycleDelegate + +// UIScene-based URI-scheme deep links (iOS 13+, Flutter 3.41+ UIScene migration) +- (BOOL)scene:(UIScene*)scene openURLContexts:(NSSet*)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() + @end diff --git a/ios/appsflyer_sdk.podspec b/ios/appsflyer_sdk.podspec index c7656f9a..03850085 100644 --- a/ios/appsflyer_sdk.podspec +++ b/ios/appsflyer_sdk.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'appsflyer_sdk' - s.version = '6.17.8' + s.version = '6.17.9' s.summary = 'AppsFlyer Integration for Flutter' s.description = 'AppsFlyer is the market leader in mobile advertising attribution & analytics, helping marketers to pinpoint their targeting, optimize their ad spend and boost their ROI.' s.homepage = 'https://github.com/AppsFlyerSDK/flutter_appsflyer_sdk' @@ -21,12 +21,12 @@ Pod::Spec.new do |s| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' ss.dependency 'Flutter' - ss.ios.dependency 'AppsFlyerFramework','6.17.8' + ss.ios.dependency 'AppsFlyerFramework','6.17.9' end s.subspec 'PurchaseConnector' do |ss| ss.dependency 'Flutter' - ss.ios.dependency 'PurchaseConnector', '6.17.8' + ss.ios.dependency 'PurchaseConnector', '6.17.9' ss.source_files = 'PurchaseConnector/**/*' ss.public_header_files = 'PurchaseConnector/**/*.h' diff --git a/lib/src/appsflyer_constants.dart b/lib/src/appsflyer_constants.dart index ed032c3e..91f3fdf5 100644 --- a/lib/src/appsflyer_constants.dart +++ b/lib/src/appsflyer_constants.dart @@ -3,7 +3,7 @@ part of appsflyer_sdk; enum EmailCryptType { EmailCryptTypeNone, EmailCryptTypeSHA256 } class AppsflyerConstants { - static const String PLUGIN_VERSION = "6.17.8"; + static const String PLUGIN_VERSION = "6.17.9"; static const String AF_DEV_KEY = "afDevKey"; static const String AF_APP_Id = "afAppId"; static const String AF_IS_DEBUG = "isDebug"; diff --git a/pubspec.yaml b/pubspec.yaml index f00c0561..9193c3ad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: appsflyer_sdk description: A Flutter plugin for AppsFlyer SDK. Supports iOS and Android. -version: 6.17.8 +version: 6.17.9 homepage: https://github.com/AppsFlyerSDK/flutter_appsflyer_sdk