> ## Documentation Index
> Fetch the complete documentation index at: https://help.airbridge.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 트러블 슈팅 - 플러터 SDK (Deprecated)

<AccordionGroup>
  <Accordion title="[Xcode 27] SceneDelegate 전환 필수화 및 Airbridge 딥링크 추가 설정 안내">
    <Note>
      **알립니다**

      이 문서는 기존 AppDelegate 기반 앱을 `SceneDelegate`(UIScene 생명주기)로 전환해야 하는 고객을 위한 트러블슈팅 가이드입니다.
    </Note>

    ### 문제 현상

    * Xcode 27(iOS 27 SDK)로 빌드한 앱이 실행되지 않고 `UIScene life cycle is required for apps built with this SDK` 런타임 이슈와 함께 즉시 종료됩니다.
    * Scene 생명주기를 채택한 뒤에는 `GeneratedPluginRegistrant.register(with:)` 부근에서 `EXC_BAD_ACCESS (code=1, address=0x0)` 크래시가 발생하며 앱이 즉시 종료됩니다.
    * 스킴 딥링크(Scheme Deeplink) 또는 유니버설 링크(Universal Links)로 앱에 진입해도 Airbridge 딥링크 이벤트가 수집되지 않습니다.
    * 개발자 가이드의 [프로젝트 설정 > iOS 설정](/ko/developers/deprecated-flutter-sdk-v3#ios-%EC%84%A4%EC%A0%95)과 [딥링크 설정 > iOS 딥링크 설정](/ko/developers/deprecated-flutter-sdk-v3#ios-%EB%94%A5%EB%A7%81%ED%81%AC-%EC%84%A4%EC%A0%95) 예제대로 `AppDelegate`에 코드를 작성했는데도 동작하지 않습니다.

    ### 발생 원인

    Apple은 앱 생명주기를 `UIApplicationDelegate`(AppDelegate) 단독 방식에서 `UIScene` 기반(SceneDelegate) 방식으로 전환하고 있으며, 최신 Xcode/iOS에서는 Scene 생명주기 채택이 사실상 필수가 되었습니다.

    앱이 `UIApplicationSceneManifest`를 통해 Scene 생명주기를 채택하면, 다음 `AppDelegate` 콜백은 **더 이상 호출되지 않습니다.**

    | 기존 `AppDelegate` 콜백 | Scene 생명주기에서의 대체 콜백 (`SceneDelegate`) |
    | - | - |
    | `application:didFinishLaunchingWithOptions:`에서의 `window` 생성 | `scene:willConnectToSession:options:` |
    | `application:openURL:options:` | `scene:openURLContexts:` |
    | `application:continueUserActivity:restorationHandler:` | `scene:continueUserActivity:` |
    | 앱이 꺼진 상태에서 딥링크 | `scene:willConnectToSession:options:`의 `connectionOptions` |

    ### 해결 방법

    | 앱이 꺼진 상태에서 딥링크 | `scene:willConnectToSession:options:`에서 `connectionOptions`<br />  • `URLContexts`에서 `URL`을 꺼내 `handleURLSchemeDeeplink:` 호출<br />  • `userActivities`에서 `NSUserActivity`를 꺼내 `handleUserActivity:` 호출 |
    | - | - |
    | 스킴 딥링크(백그라운드) | `URLContexts`에서 `URL`을 꺼내 `handleURLSchemeDeeplink:` 호출 |
    | 유니버설 링크(백그라운드) | `userActivities`에서 `NSUserActivity`를 꺼내 `handleUserActivity:` 호출 |

    #### 1. Info.plist에 Scene Manifest 추가

    `Info.plist`에 아래 `UIApplicationSceneManifest`를 추가합니다.

    `UISceneDelegateClassName`은 사용하는 모듈에 맞게 지정하고, **`UISceneStoryboardFile`에 `Main`을 반드시 지정**합니다.

    (이 값이 없으면 `FlutterViewController`가 scene의 `rootViewController`로 생성되지 않아 플러그인 등록이 실패합니다.)

    * `UISceneDelegateClassName`
      * Objective-C: `SceneDelegate`
      * Swift: `$(PRODUCT_MODULE_NAME).SceneDelegate`

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <false/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>SceneDelegate</string>
                    <key>UISceneStoryboardFile</key>
                    <string>Main</string>
                </dict>
            </array>
        </dict>
    </dict>
    ```

    #### 2. SceneDelegate 생성 및 딥링크 수집 코드를 SceneDelegate로 이전

    <Tabs>
      <Tab title="Swift">
        ```swift lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        import UIKit
        import Flutter
        import airbridge_flutter_sdk

        class SceneDelegate: UIResponder, UIWindowSceneDelegate {
            var window: UIWindow?

            func scene(_ scene: UIScene,
                willConnectTo session: UISceneSession,
                options connectionOptions: UIScene.ConnectionOptions) {
                guard let windowScene = scene as? UIWindowScene else { return }

                if let controller = window?.rootViewController as? FlutterViewController {
                    GeneratedPluginRegistrant.register(with: controller)
                }

                if let context = connectionOptions.urlContexts.first {
                    AirbridgeFlutter.deeplink.handleURLSchemeDeeplink(context.url)
                }
                if let userActivity = connectionOptions.userActivities.first {
                    AirbridgeFlutter.deeplink.handle(userActivity)
                }
            }

            func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
                if let context = URLContexts.first {
                    AirbridgeFlutter.deeplink.handleURLSchemeDeeplink(context.url)
                }
            }

            func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
                AirbridgeFlutter.deeplink.handle(userActivity)
            }
        }
        ```
      </Tab>

      <Tab title="Objective-C">
        `SceneDelegate.h`와 `SceneDelegate.m`을 추가합니다.

        ```objective-c lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        #import <UIKit/UIKit.h>

        API_AVAILABLE(ios(13.0))
        @interface SceneDelegate : UIResponder <UIWindowSceneDelegate>

        @property (strong, nonatomic) UIWindow *window;

        @end
        ```

        ```objective-c lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        #import "SceneDelegate.h"
        #import "AppDelegate.h"
        #import <Flutter/Flutter.h>
        #import "GeneratedPluginRegistrant.h"
        #import <airbridge_flutter_sdk/AirbridgeFlutter.h>

        @implementation SceneDelegate

        - (void)scene:(UIScene *)scene
            willConnectToSession:(UISceneSession *)session
            options:(UISceneConnectionOptions *)connectionOptions
        {
            if (![scene isKindOfClass:[UIWindowScene class]]) {
                return;
            }

            if ([self.window.rootViewController isKindOfClass:[FlutterViewController class]]) {
                FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
                [GeneratedPluginRegistrant registerWithRegistry:controller];
            }

            UIOpenURLContext *context = connectionOptions.URLContexts.allObjects.firstObject;
            if (context != nil) {
                [AirbridgeFlutter.deeplink handleURLSchemeDeeplink:context.URL];
            }
            NSUserActivity *userActivity = connectionOptions.userActivities.allObjects.firstObject;
            if (userActivity != nil) {
                [AirbridgeFlutter.deeplink handleUserActivity:userActivity];
            }
        }

        - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
        {
            UIOpenURLContext *context = URLContexts.allObjects.firstObject;
            if (context != nil) {
                [AirbridgeFlutter.deeplink handleURLSchemeDeeplink:context.URL];
            }
        }

        - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity
        {
            [AirbridgeFlutter.deeplink handleUserActivity:userActivity];
        }

        @end
        ```
      </Tab>
    </Tabs>

    #### 3. AppDelegate 정리

    * SDK 초기화 코드(Swift: `AirbridgeFlutter.initSDK(appName:appToken:withLaunchOptions:)`, Objective-C: `[AirbridgeFlutter initSDKWithAppName:appToken:withLaunchOptions:]`)는 `application:didFinishLaunchingWithOptions:`에 **그대로 둡니다.**
    * 딥링크 수집을 위해 작성했던 `application:openURL:options:`와 `application:continueUserActivity:restorationHandler:`는 Scene 생명주기에서 호출되지 않으므로, `SceneDelegate`로 이전한 뒤 `AppDelegate`에서는 삭제합니다.
    * `GeneratedPluginRegistrant.register(with: self)`는 `FlutterAppDelegate`가 `self.window.rootViewController`(FlutterViewController)를 통해 플러그인 레지스트라를 만듭니다. Scene 생명주기에서는 `window`가 `SceneDelegate` 소유라 `AppDelegate`의 `self.window`가 `nil`이 됩니다. 그 결과 레지스트라가 `nil`로 반환되고, 이를 받은 플러그인이 `nil`을 역참조하면서 `EXC_BAD_ACCESS (address=0x0)` 크래시가 발생합니다. 따라서 플러그인 등록(`GeneratedPluginRegistrant.register`)을 **반드시 `FlutterViewController`가 존재하는 `SceneDelegate`로 이전**해야 합니다.

    ```swift lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    import UIKit
    import Flutter
    import airbridge_flutter_sdk

    @main
    @objc class AppDelegate: FlutterAppDelegate {
        override func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {
            AirbridgeFlutter.initSDK(appName: "YOUR_APP_NAME", appToken: "YOUR_APP_TOKEN", withLaunchOptions: launchOptions)

            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
    }
    ```
  </Accordion>

  <Accordion title="Bitcode 컴파일 에러">
    `Flutter SDK 3.0.1` 이상의 버전으로 iOS 빌드 시 Bitcode 를 지원하지 않아서 아래와 같이 에러가 발생 할 수 있습니다.

    ```Text theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    ld: XCFrameworkIntermediates/AirBridge/AirBridge.framework/AirBridge(AirBridge-arm64-master.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE)
    ```

    `Flutter SDK 3.0.1` 버전 부터 `Airbridge iOS SDK 1.28.0` 버전 으로 업데이트 되었습니다. `Airbridge iOS SDK 는 1.28.0` 버전 부터 Bitcode 를 지원하지 않습니다.

    해당 에러 발생 시 아래 가이드를 참고 바랍니다.

    * [Bitcode 컴파일 에러 가이드](/ko/developers/troubleshooting-deprecated-ios-sdk-v1#bitcode-컴파일-에러)
  </Accordion>

  <Accordion title="settings.gradle">
    Android build 시에 `Plugin project ... not found. Please update settings.gradle` 에러가 발생하면 `android/settings.gradle` 파일을 아래와 같이 수정해서 문제를 해결 할 수 있습니다.

    ```groovy lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    include ':app'

    def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()

    def plugins = new Properties()
    def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
    if (pluginsFile.exists()) {
        pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
    }

    plugins.each { name, path ->
        def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
        include ":$name"
        project(":$name").projectDir = pluginDirectory
    }
    ```
  </Accordion>

  <Accordion title="Could not find or use auto-linked library...">
    Flutter issue: [https://github.com/flutter/flutter/issues/16049](https://github.com/flutter/flutter/issues/16049)

    Airbridge Flutter SDK 는 swift plugin 이고, Flutter 에는 100% Objective C Project 에서 Swift Plugin 을 사용하면 해당 오류가 발생하는 문제가 있습니다.

    `File > New > File... > Swift File` 을 선택해서 `File.swift` 파일을 생성하고, Bridge Header 를 생성하는 것으로 문제를 해결할 수 있습니다.

    Objective C & Swift Project 및 100% Swift Project 에서는 발생하지 않는 문제입니다.
  </Accordion>

  <Accordion title="에어브릿지 SDK 백업 규칙 병합 충돌 문제">
    * 참조 : [Android SDK Auto Backup](/ko/developers/troubleshooting-deprecated-android-sdk-v2#auto-backup)

    에어브릿지 SDK 백업 규칙과 서드파티 SDK 백업 규칙 중복 적용으로 인해 빌드 에러가 발생하는 경우 해결 방법은 아래를 참고해주세요.

    예) 에어브릿지 SDK 백업 규칙과 Appsflyer SDK 백업 규칙이 중복 적용되는 경우 아래와 같은 빌드 에러가 표시됩니다.

    ```html lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    Attribute application@fullBackupContent value=(@xml/appsflyer_backup_rules) from [com.appsflyer:af-android-sdk:6.6.1] AndroidManifest.xml:14:18-73
    is also present at [io.airbridge:sdk-android:2.14.0] AndroidManifest.xml:27:18-78 value=(@xml/airbridge_auto_backup_rules).
    Suggestion: add 'tools:replace="android:fullBackupContent"' to <application> element at AndroidManifest.xml:7:5-13:19 to override.
    ```

    이 문제를 해결하기 위해서는 아래와 같이 설정해주세요.

    <AccordionGroup>
      <Accordion title="backup_rules.xml 설정">
        1. `android/app/src/main/res/xml` 폴더를 생성해주세요.
        2. 생성된 xml 폴더 내부에 (e.g. `custom_backup_rules.xml`) 파일을 생성해주세요.
        3. 에어브릿지 SDK에서 정의하는 데이터 백업 규칙을 다음과 같이 추가해 주세요.

        ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        <?xml version="1.0" encoding="utf-8"?>
        <full-backup-content>
            <!-- Airbridge Backup Rules -->
            <exclude domain="sharedpref" path="airbridge-internal" />
            <exclude domain="sharedpref" path="airbridge-install" />
            <exclude domain="sharedpref" path="airbridge-user-info" />
            <exclude domain="sharedpref" path="airbridge-user-alias" />
            <exclude domain="sharedpref" path="airbridge-user-attributes" />
            <exclude domain="sharedpref" path="airbridge-device-alias" />
            <exclude domain="database" path="airbridge.db" />

            <!-- Appsflyer Backup Rules -->
            <exclude domain="sharedpref" path="appsflyer-data"/>

            <!-- Your Custom Backup Rules -->
        </full-backup-content>
        ```
      </Accordion>

      <Accordion title="AndroidManifest.xml 설정">
        ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        <manifest
            ...
            xmlns:tools="http://schemas.android.com/tools">

            <application
                ...
                android:allowBackup="true"
                android:fullBackupContent="@xml/custom_backup_rules"
                tools:replace="android:fullBackupContent">
        ```
      </Accordion>
    </AccordionGroup>
  </Accordion>
</AccordionGroup>
