> ## 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="AndroidX 적용">
    ##### 언리얼 엔진 4.23 버전 \~ 5.1.0 버전

    Airbridge Unreal SDK v1.3.4 부터 [com.google.android.gms:play-services-appset:16.0.2](https://mvnrepository.com/artifact/com.google.android.gms/play-services-appset/16.0.2) dependency가 추가됨으로 인하여 AndroidX가 도입되어 [`Unreal Plugin Language (UPL)`](https://docs.unrealengine.com/4.26/en-US/SharingAndReleasing/Mobile/UnrealPluginLanguage/) 파일에 아래 설정을 추가해야 합니다.

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <gradleProperties>
        <insert>
        android.useAndroidX=true
        android.enableJetifier=true
        </insert>
    </gradleProperties>

    <baseBuildGradleAdditions>
        <insert>
        allprojects {
            def classMap = [
                'android.support.annotation': 'androidx.annotation',
                'android.support.v4.app.ActivityCompat': 'androidx.core.app.ActivityCompat',
                'android.support.v4.app.NotificationCompat': 'androidx.core.app.NotificationCompat',
                'android.support.v4.app.NotificationManagerCompat': 'androidx.core.app.NotificationManagerCompat',
                'android.support.v4.content.ContextCompat': 'androidx.core.content.ContextCompat',
                'android.support.v4.content.FileProvider': 'androidx.core.content.FileProvider',
                'android.support.v13.app.FragmentCompat': 'androidx.legacy.app.FragmentCompat',
                'android.arch.lifecycle': 'androidx.lifecycle',
                'android.arch.lifecycle.Lifecycle': 'androidx.lifecycle.Lifecycle',
                'android.arch.lifecycle.LifecycleObserver': 'androidx.lifecycle.LifecycleObserver',
                'android.arch.lifecycle.OnLifecycleEvent': 'androidx.lifecycle.OnLifecycleEvent',
                'android.arch.lifecycle.ProcessLifecycleOwner': 'androidx.lifecycle.ProcessLifecycleOwner',
            ]
            afterEvaluate { project ->
                project.rootProject.projectDir.traverse(type: groovy.io.FileType.FILES, nameFilter: ~/.*\.java$/) { f ->
                    classMap.each { entry ->
                        if (f.getText('UTF-8').contains(entry.key)) {
                            println "Change ${entry.key} to ${entry.value} in file ${f}"
                            ant.replace(file: f, token: entry.key, value: entry.value)
                        }
                    }
                }
            }
        }
        </insert>
    </baseBuildGradleAdditions>
    ```

    ##### 언리얼 엔진 5.1.1 또는 이후 버전

    AndroidX 적용을 위한 추가 작업이 필요하지 않습니다.
  </Accordion>

  <Accordion title="Google Play services">
    ##### 언리얼 엔진 4.23 버전 \~ 5.1.0 버전

    Airbridge Unreal SDK v1.3.4 부터 [com.google.android.gms:play-services-appset:16.0.2](https://mvnrepository.com/artifact/com.google.android.gms/play-services-appset/16.0.2) dependency가 추가됨으로 인하여 dependency resolution 해결을 위하여 [`Unreal Plugin Language (UPL)`](https://docs.unrealengine.com/4.26/en-US/SharingAndReleasing/Mobile/UnrealPluginLanguage/) 파일에 아래 설정을 추가해야 합니다.

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <baseBuildGradleAdditions>
        <insert>
        allprojects {
            subprojects {
                project.configurations.all {
                    resolutionStrategy {
                        force 'com.google.android.gms:play-services-base:11.8.0'
                        force 'com.google.android.gms:play-services-basement:11.8.0'
                        force 'com.google.android.gms:play-services-tasks:11.8.0'
                    }
                }
            }
        }
        </insert>
    </baseBuildGradleAdditions>
    ```

    <Danger>
      **알립니다**

      언리얼 엔진 4.23 버전 \~ 5.1.0 버전에서는 Airbridge Unreal SDK v1.3.4 부터 추가된 App set ID support 기능을 사용할 수 없습니다.
    </Danger>

    ##### 언리얼 엔진 5.1.1 또는 이후 버전

    dependency resolution 해결을 위한 추가 작업이 필요하지 않습니다.
  </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. 모듈 디렉터리 내부에 (e.g. `custom_backup_rules.xml`) 파일을 생성해주세요.
        2. 에어브릿지 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>
        ```

        3. 언리얼 플러그인 언어(Unreal Plugin Language,UPL)를 이용하여 `res/xml` 폴더에 해당 파일을 다음과 같이 복사해 주세요.

        ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        <?xml version="1.0" encoding="utf-8"?>
        <root xmlns:android="http://schemas.android.com/apk/res/android">
            ...
            <resourceCopies>
                <copyFile src="{YOUR_XML_BACKUP_RULES_PATH}" dst="$S(BuildDir)/res/xml/custom_backup_rules.xml" />
                ...
            </resourceCopies>
            ...
        </root>
        ```
      </Accordion>

      <Accordion title="AndroidManifest.xml 설정">
        언리얼 에디터 Project Settings 창의 `Platforms - Android` > `Advanced APK Packaging` > `Extra Tags for <manifest> node` 에 아래에 해당하는 요소들을 추가해 주세요.

        * `xmlns:tools="http://schemas.android.com/tools"`

        언리얼 에디터 Project Settings 창의 `Platforms - Android` > `Advanced APK Packaging` > `Extra Tags for <application> node` 에 아래에 해당하는 요소들을 추가해 주세요.

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

    아래 가이드를 함께 참고해 주세요.

    * [언리얼 플러그인 언어](https://dev.epicgames.com/documentation/ko-kr/unreal-engine/using-the-unreal-plugin-language-for-mobile-projects-in-unreal-engine?application_version=5.0)
    * [Android Manifest 컨트롤](https://dev.epicgames.com/documentation/ko-kr/unreal-engine/how-to-use-android-manifest-control-in-unreal-engine-projects)
  </Accordion>
</AccordionGroup>

## iOS

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

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

    ##### 문제 현상

    * Xcode 27에서 빌드한 앱을 실행하면 앱이 정상적으로 실행되지 않을 수 있습니다.
    * 스킴 딥링크(Scheme Deeplink) 또는 유니버설 링크(Universal Links)로 앱에 진입해도 Airbridge 딥링크 이벤트가 수집되지 않습니다.
    * 개발자 가이드의 SDK 초기화하기와 앱에서 딥링크 이벤트 수집하기 예제대로 `AppDelegate`에 코드를 작성했는데도 동작하지 않습니다.

    ##### 발생 원인

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

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

    ##### 해결 방법

    Airbridge Unreal SDK 1.4.1 미만 버전에서는 Airbridge Deeplink을 호출하는 Public Interface가 존재하지 않습니다. 그러므로 Airbridge Unreal SDK 를 1.4.1 이상으로 업데이트 한 뒤, SceneDelegate에 Airbridge Deeplink를 호출 해 주세요.

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

    UPL을 통해서 Xcode Project에 다음의 Info.plist에 Scene을 추가합니다.

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <?xml version="1.0" encoding="utf-8"?>
    <root xmlns:android="http://schemas.android.com/apk/res/ios">
      <!-- init section is always evaluated once per architecture -->
      <init>
      </init>
        <iosPListUpdates>
            <addElements tag="dict" once="true">
                <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>MyScene</string>
                            </dict>
                        </array>
                    </dict>
                </dict>
            </addElements>
        </iosPListUpdates>
    </root>
    ```

    ###### 2. SceneDelegate 생성

    Scene Manifest에 추가된 `UISceneDelegateClassName` 의 이름에 맞추어 SceneDelegate 파일을 생성합니다.

    ```objective-c lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    @interface MyScene : UIResponder <UIWindowSceneDelegate>

    @property (strong, nonatomic) UIWindow *window;

    @end

    @implementation MyScene

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

            UIWindowScene *windowScene = (UIWindowScene *)scene;

            if (self.window.windowScene == windowScene) {
                [self.window makeKeyAndVisible];
                return;
            }

            UIWindow *sceneWindow = [[UIWindow alloc] initWithWindowScene:windowScene];

            if (self.window.rootViewController) {
                sceneWindow.rootViewController = self.window.rootViewController;
            }

            self.window = sceneWindow;
            [self.window makeKeyAndVisible];

            return;
        }

        dispatch_sync(dispatch_get_main_queue(), ^{
            if (![scene isKindOfClass:[UIWindowScene class]]) {
                return;
            }

            UIWindowScene *windowScene = (UIWindowScene *)scene;

            if (self.window.windowScene == windowScene) {
                [self.window makeKeyAndVisible];
                return;
            }

            UIWindow *sceneWindow = [[UIWindow alloc] initWithWindowScene:windowScene];

            if (self.window.rootViewController) {
                sceneWindow.rootViewController = self.window.rootViewController;
            }

            self.window = sceneWindow;
            [self.window makeKeyAndVisible];
        });
    }
    ```

    ###### 3. 딥링크 수집 코드 추가

    만들어진 SceneDelegate에 Airbridge Deeplink 수집을 위해 다음을 코드를 구현 해 주세요.

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

    #import "IOSAppDelegate.h"
    #import AirbridgeUnrealDelegate.h"

    @interface MyScene : UIResponder <UIWindowSceneDelegate>

    @property (strong, nonatomic) UIWindow *window;

    @end

    @implementation MyScene

    - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions
    {
        [self airbridgeScene:scene willConnectToSession:session options:connectionOptions];
    }

    - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
    {
        if (@available(iOS 13.0, *)) {
            [IOSAppDelegate trackDeeplinkWithOpenURLContexts:URLContexts];
        }
    }

    - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity
    {
        [IOSAppDelegate trackDeeplinkWithUserActivity:userActivity];
    }

    - (void)airbridgeScene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions
    {
        if (@available(iOS 13.0, *)) {
            if (![scene isKindOfClass:[UIWindowScene class]]) {
                [IOSAppDelegate trackDeeplinkWithConnectionOptions:connectionOptions];
                return;
            }

            [self airbridgeAttachUnrealWindowToScene:(UIWindowScene *)scene];
            [IOSAppDelegate trackDeeplinkWithConnectionOptions:connectionOptions];
        }
    }

    - (void)airbridgeAttachUnrealWindowToScene:(UIWindowScene *)windowScene
    {
        IOSAppDelegate *appDelegate = (IOSAppDelegate *)[UIApplication sharedApplication].delegate;
        UIWindow *currentWindow = appDelegate.Window;
        UIViewController *rootViewController = currentWindow.rootViewController;

        if (currentWindow.windowScene == windowScene) {
            self.window = currentWindow;
            [self.window makeKeyAndVisible];
            return;
        }

        UIWindow *sceneWindow = [[UIWindow alloc] initWithWindowScene:windowScene];
        if (rootViewController) {
            sceneWindow.rootViewController = rootViewController;
        }
        appDelegate.Window = sceneWindow;

        self.window = sceneWindow;
        [self.window makeKeyAndVisible];
    }

    @end
    ```
  </Accordion>
</AccordionGroup>
