Password AutoFill 기능으로 저장된 암호의 도메인이 airbridge.io 또는 abr.ge로 보이는 현상이 앱을 사용하는 유저에게 발생할 수 있습니다.
에어브릿지 SDK의 딥링크를 설정한 후에 Password AutoFill 기능을 활용하면 도메인이 에어브릿지 딥링크의 앱 링크(applinks) 도메인 airbridge.io 또는 abr.ge로 저장됩니다.
Password AutoFill에 사용하는 webcredentials 도메인을 설정하면 문제를 해결할 수 있습니다.
1. 암호를 저장하는 도메인을 준비해주세요.
2. 아래 JSON을 https://YOUR_DOMAIN/.well-known/apple-app-site-association에 Content-Type: application/json으로 호스팅합니다. 준비한 도메인이 YOUR_DOMAIN입니다.
애플 개발자 대시보드의 [Identifiers]>[YOUR_APP]에서 App ID Prefix, Bundle ID를 확인할 수 있습니다.
{ "webcredentials": { "apps": ["YOUR_APP_ID_PREFIX.YOUR_BUNDLE_ID"] }}3. Xcode의 [YOUR_PROJECT]>[Signing & Capabilities]로 이동합니다.
4. '+ Capability'를 클릭해 Associated Domains를 추가합니다. Associated Domains에 webcredentials:YOUR_DOMAIN을 입력합니다.
Cordova-Ionic SDK 2.0.1 이상의 버전으로 iOS 빌드 시 Bitcode 를 지원하지 않아서 아래와 같이 에러가 발생 할 수 있습니다.
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)Cordova-Ionic SDK 2.0.1 버전 부터 Airbridge iOS SDK 1.28.0 버전 으로 업데이트 되었습니다. Airbridge iOS SDK 는 1.28.0 버전 부터 Bitcode 를 지원하지 않습니다.
해당 에러 발생 시 아래 가이드를 참고 바랍니다.
알립니다
이 문서는 기존 AppDelegate 기반 앱을 UIScene(SceneDelegate)로 전환해야 하는 고객을 위한 트러블슈팅 가이드입니다.
Xcode 27에서 빌드한 앱을 실행하면 앱이 정상적으로 실행되지 않을 수 있습니다.
스킴 딥링크(Scheme Deeplink) 또는 유니버설 링크(Universal Links)로 앱에 진입해도 Airbridge 딥링크 이벤트가 수집되지 않습니다.
setOnDeeplinkReceived 콜백이 호출되지 않습니다.
개발자 가이드의 SDK 초기화하기와 앱에서 딥링크 이벤트 수집하기 예제대로 AppDelegate에 코드를 작성했는데도 동작하지 않습니다.
Apple은 앱 생명주기를 UIApplicationDelegate(AppDelegate) 단독 방식에서 UIScene 기반(SceneDelegate) 방식으로 전환하고 있으며, 최신 Xcode/iOS에서는 Scene 생명주기 채택이 사실상 필수가 되었습니다.
앱이 UIApplicationSceneManifest를 통해 Scene 생명주기를 채택하면, 다음 AppDelegate 콜백은 더 이상 호출되지 않습니다.
cordova-ios 7.x 이하에서는 CDVAppDelegate가 application:didFinishLaunchingWithOptions:에서 window와 MainViewController를 생성합니다. Scene 생명주기에서는 window를 SceneDelegate가 소유하므로 이 window가 화면에 표시되지 않아 흰 화면이 발생합니다.
작업 내용은 cordova-ios 플랫폼 버전에 따라 다릅니다. cordova platform ls 로 버전을 확인한 뒤, 아래 각 단계에서 해당하는 탭을 선택해 주세요.
알립니다
cordova-ios 8.0.0 / 8.0.1의 CDVSceneDelegate에는 scene:continueUserActivity: 구현이 없어, 아래 코드를 적용하면 유니버설 링크로 앱에 진입할 때 앱이 종료됩니다. cordova-ios 8.1.0 이상으로 업데이트한 뒤 적용해 주세요.
platforms/ios/App/App-Info.plist에 기본 포함되어 있습니다. 값이 누락되지 않았는지만 확인해 주세요. UISceneStoryboardFile에 Main이 반드시 지정되어 있어야 하며, 이 값이 없으면 흰 화면이 발생합니다.
<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>$(PRODUCT_MODULE_NAME).SceneDelegate</string> <key>UISceneStoryboardFile</key> <string>Main</string> </dict> </array> </dict></dict>platforms/ios/YOUR_PROJECT_NAME/YOUR_PROJECT_NAME-Info.plist에 아래 UIApplicationSceneManifest를 추가합니다. cordova-ios 7.x 이하에는 Main.storyboard가 없으므로 UISceneStoryboardFile은 지정하지 않습니다.
<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> </dict> </array> </dict></dict>platforms/ios/App/App-Info.plist에 기본 포함되어 있습니다. 값이 누락되지 않았는지만 확인해 주세요. UISceneStoryboardFile에 Main이 반드시 지정되어 있어야 하며, 이 값이 없으면 흰 화면이 발생합니다.
<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>$(PRODUCT_MODULE_NAME).SceneDelegate</string> <key>UISceneStoryboardFile</key> <string>Main</string> </dict> </array> </dict></dict>platforms/ios/YOUR_PROJECT_NAME/YOUR_PROJECT_NAME-Info.plist에 아래 UIApplicationSceneManifest를 추가합니다. cordova-ios 7.x 이하에는 Main.storyboard가 없으므로 UISceneStoryboardFile은 지정하지 않습니다.
<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> </dict> </array> </dict></dict>아래 코드는 각 cordova-ios 버전의 기본 템플릿 언어(8.1.0 이상은 Swift, 7.x 이하는 Objective-C)를 기준으로 합니다. 다른 언어로 구현하는 경우 아래를 함께 적용해 주세요.
cordova-ios 8.1.0 이상을 Objective-C로 구현하는 경우: 기본 제공되는 App/SceneDelegate.swift를 삭제하고, App-Info.plist의 UISceneDelegateClassName을 SceneDelegate로 변경합니다. AirbridgeCordova 호출을 위해 #import "App-Swift.h"가 필요하며, AppDelegate.h는 import하지 마세요. 빌드 경고가 발생합니다.
platforms/ios/App/SceneDelegate.swift를 아래와 같이 수정합니다.
import Cordova class SceneDelegate: CDVSceneDelegate { // when terminated app is opened with scheme deeplink or universal links override func scene( _ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions ) { // CDVSceneDelegate forwards connectionOptions.urlContexts to scene(_:openURLContexts:), // so scheme deeplinks are handled in scene(_:openURLContexts:) below. super.scene(scene, willConnectTo: session, options: connectionOptions) // CDVSceneDelegate does not forward connectionOptions.userActivities, // so delegate to scene(_:continue:) below. if let userActivity = connectionOptions.userActivities.first { self.scene(scene, continue: userActivity) } } // when backgrounded app is opened with scheme deeplink override func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { // Must be called so that CDVPluginHandleOpenURLNotification reaches Cordova plugins. super.scene(scene, openURLContexts: URLContexts) // track deeplink if let context = URLContexts.first { AirbridgeCordova.trackDeeplink(url: context.url) } } // when backgrounded app is opened with universal links override func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { super.scene(scene, continue: userActivity) // track deeplink AirbridgeCordova.trackDeeplink(userActivity: userActivity) }}cordova-ios 7.x 이하를 Swift로 구현하는 경우: UISceneDelegateClassName을 $(PRODUCT_MODULE_NAME).SceneDelegate로 변경하고, 플랫폼이 기본 제공하는 platforms/ios/YOUR_PROJECT_NAME/Bridging-Header.h에 #import "AppDelegate.h", #import "MainViewController.h", #import <Cordova/CDVPlugin.h>를 추가합니다. AirbridgeCordova는 앱 타겟에 직접 컴파일되므로 별도 import가 필요하지 않습니다.
platforms/ios/YOUR_PROJECT_NAME/Classes 하위에 아래 두 파일을 추가하고, Xcode에서 앱 타겟에 포함시킵니다.
// SceneDelegate.h#import <UIKit/UIKit.h> API_AVAILABLE(ios(13.0))@interface SceneDelegate : UIResponder <UIWindowSceneDelegate> @property (strong, nonatomic) UIWindow *window; @end // SceneDelegate.m#import "SceneDelegate.h"#import "AppDelegate.h"#import "MainViewController.h"#import <Cordova/CDVPlugin.h>// The Airbridge Cordova-Ionic SDK is written in Swift.#import "YOUR_PROJECT_NAME-Swift.h" @implementation SceneDelegate // when terminated app is opened with scheme deeplink or universal links- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions{ if (![scene isKindOfClass:[UIWindowScene class]]) { return; } UIWindowScene *windowScene = (UIWindowScene *)scene; // In the Scene lifecycle, the window is owned by the SceneDelegate. // The window and MainViewController that CDVAppDelegate used to create must be // created here, otherwise the screen stays blank. MainViewController *viewController = [[MainViewController alloc] init]; self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; self.window.autoresizesSubviews = YES; self.window.rootViewController = viewController; [self.window makeKeyAndVisible]; // Populate AppDelegate's window/viewController properties for existing code that reads them. AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; appDelegate.window = self.window; appDelegate.viewController = viewController; // track deeplink UIOpenURLContext *context = connectionOptions.URLContexts.allObjects.firstObject; if (context != nil) { [self postCordovaOpenURLNotificationWithContext:context]; [AirbridgeCordova trackDeeplinkWithUrl:context.URL]; } NSUserActivity *userActivity = connectionOptions.userActivities.allObjects.firstObject; if (userActivity != nil) { [AirbridgeCordova trackDeeplinkWithUserActivity:userActivity]; }} // when backgrounded app is opened with scheme deeplink- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts{ UIOpenURLContext *context = URLContexts.allObjects.firstObject; if (context == nil) { return; } [self postCordovaOpenURLNotificationWithContext:context]; // track deeplink [AirbridgeCordova trackDeeplinkWithUrl:context.URL];} // when backgrounded app is opened with universal links- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity{ // track deeplink [AirbridgeCordova trackDeeplinkWithUserActivity:userActivity];} // Replaces the notification dispatch that CDVAppDelegate's application:openURL:options: performed.// Required for deep link handling in other Cordova plugins, such as window.handleOpenURL.- (void)postCordovaOpenURLNotificationWithContext:(UIOpenURLContext *)context API_AVAILABLE(ios(13.0)){ if (context.URL == nil) { return; } [[NSNotificationCenter defaultCenter] postNotificationName:CDVPluginHandleOpenURLNotification object:context.URL]; NSMutableDictionary *openURLData = [[NSMutableDictionary alloc] init]; [openURLData setValue:context.URL forKey:@"url"]; [openURLData setValue:context.options.sourceApplication forKey:@"sourceApplication"]; [openURLData setValue:context.options.annotation forKey:@"annotation"]; [[NSNotificationCenter defaultCenter] postNotificationName:CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification object:openURLData];} @end알립니다
cordova-ios 8.1.0 이상에서 scene(_:willConnectTo:options:)에 connectionOptions.urlContexts로 trackDeeplink(url:)을 추가로 호출하면 안 됩니다. CDVSceneDelegate가 scene(_:openURLContexts:)로 다시 전달하므로 콜드 스타트 시 딥링크가 두 번 수집됩니다.
cordova-ios 8.1.0 이상을 Objective-C로 구현하는 경우: 기본 제공되는 App/SceneDelegate.swift를 삭제하고, App-Info.plist의 UISceneDelegateClassName을 SceneDelegate로 변경합니다. AirbridgeCordova 호출을 위해 #import "App-Swift.h"가 필요하며, AppDelegate.h는 import하지 마세요. 빌드 경고가 발생합니다.
platforms/ios/App/SceneDelegate.swift를 아래와 같이 수정합니다.
import Cordova class SceneDelegate: CDVSceneDelegate { // when terminated app is opened with scheme deeplink or universal links override func scene( _ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions ) { // CDVSceneDelegate forwards connectionOptions.urlContexts to scene(_:openURLContexts:), // so scheme deeplinks are handled in scene(_:openURLContexts:) below. super.scene(scene, willConnectTo: session, options: connectionOptions) // CDVSceneDelegate does not forward connectionOptions.userActivities, // so delegate to scene(_:continue:) below. if let userActivity = connectionOptions.userActivities.first { self.scene(scene, continue: userActivity) } } // when backgrounded app is opened with scheme deeplink override func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { // Must be called so that CDVPluginHandleOpenURLNotification reaches Cordova plugins. super.scene(scene, openURLContexts: URLContexts) // track deeplink if let context = URLContexts.first { AirbridgeCordova.trackDeeplink(url: context.url) } } // when backgrounded app is opened with universal links override func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { super.scene(scene, continue: userActivity) // track deeplink AirbridgeCordova.trackDeeplink(userActivity: userActivity) }}cordova-ios 7.x 이하를 Swift로 구현하는 경우: UISceneDelegateClassName을 $(PRODUCT_MODULE_NAME).SceneDelegate로 변경하고, 플랫폼이 기본 제공하는 platforms/ios/YOUR_PROJECT_NAME/Bridging-Header.h에 #import "AppDelegate.h", #import "MainViewController.h", #import <Cordova/CDVPlugin.h>를 추가합니다. AirbridgeCordova는 앱 타겟에 직접 컴파일되므로 별도 import가 필요하지 않습니다.
platforms/ios/YOUR_PROJECT_NAME/Classes 하위에 아래 두 파일을 추가하고, Xcode에서 앱 타겟에 포함시킵니다.
// SceneDelegate.h#import <UIKit/UIKit.h> API_AVAILABLE(ios(13.0))@interface SceneDelegate : UIResponder <UIWindowSceneDelegate> @property (strong, nonatomic) UIWindow *window; @end // SceneDelegate.m#import "SceneDelegate.h"#import "AppDelegate.h"#import "MainViewController.h"#import <Cordova/CDVPlugin.h>// The Airbridge Cordova-Ionic SDK is written in Swift.#import "YOUR_PROJECT_NAME-Swift.h" @implementation SceneDelegate // when terminated app is opened with scheme deeplink or universal links- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions{ if (![scene isKindOfClass:[UIWindowScene class]]) { return; } UIWindowScene *windowScene = (UIWindowScene *)scene; // In the Scene lifecycle, the window is owned by the SceneDelegate. // The window and MainViewController that CDVAppDelegate used to create must be // created here, otherwise the screen stays blank. MainViewController *viewController = [[MainViewController alloc] init]; self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; self.window.autoresizesSubviews = YES; self.window.rootViewController = viewController; [self.window makeKeyAndVisible]; // Populate AppDelegate's window/viewController properties for existing code that reads them. AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; appDelegate.window = self.window; appDelegate.viewController = viewController; // track deeplink UIOpenURLContext *context = connectionOptions.URLContexts.allObjects.firstObject; if (context != nil) { [self postCordovaOpenURLNotificationWithContext:context]; [AirbridgeCordova trackDeeplinkWithUrl:context.URL]; } NSUserActivity *userActivity = connectionOptions.userActivities.allObjects.firstObject; if (userActivity != nil) { [AirbridgeCordova trackDeeplinkWithUserActivity:userActivity]; }} // when backgrounded app is opened with scheme deeplink- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts{ UIOpenURLContext *context = URLContexts.allObjects.firstObject; if (context == nil) { return; } [self postCordovaOpenURLNotificationWithContext:context]; // track deeplink [AirbridgeCordova trackDeeplinkWithUrl:context.URL];} // when backgrounded app is opened with universal links- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity{ // track deeplink [AirbridgeCordova trackDeeplinkWithUserActivity:userActivity];} // Replaces the notification dispatch that CDVAppDelegate's application:openURL:options: performed.// Required for deep link handling in other Cordova plugins, such as window.handleOpenURL.- (void)postCordovaOpenURLNotificationWithContext:(UIOpenURLContext *)context API_AVAILABLE(ios(13.0)){ if (context.URL == nil) { return; } [[NSNotificationCenter defaultCenter] postNotificationName:CDVPluginHandleOpenURLNotification object:context.URL]; NSMutableDictionary *openURLData = [[NSMutableDictionary alloc] init]; [openURLData setValue:context.URL forKey:@"url"]; [openURLData setValue:context.options.sourceApplication forKey:@"sourceApplication"]; [openURLData setValue:context.options.annotation forKey:@"annotation"]; [[NSNotificationCenter defaultCenter] postNotificationName:CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification object:openURLData];} @end알립니다
cordova-ios 8.1.0 이상에서 scene(_:willConnectTo:options:)에 connectionOptions.urlContexts로 trackDeeplink(url:)을 추가로 호출하면 안 됩니다. CDVSceneDelegate가 scene(_:openURLContexts:)로 다시 전달하므로 콜드 스타트 시 딥링크가 두 번 수집됩니다.
SDK 초기화 코드(initializeSDK)는 application:didFinishLaunchingWithOptions:에 그대로 둡니다.
딥링크 수집을 위해 작성했던 application:openURL:options:와 application:continueUserActivity:restorationHandler:
는 Scene 생명주기에서 호출되지 않으므로, SceneDelegate로 이전한 뒤 AppDelegate에서는 삭제합니다.
cordova-ios 7.x 이하에서는 self.viewController = [[MainViewController alloc] init];와 return [super application:application didFinishLaunchingWithOptions:launchOptions];를 삭제하고 return YES;로 변경합니다. CDVAppDelegate의 기본 구현이 window와 CDVViewController를 새로 생성하므로, 그대로 두면 화면에 표시되지 않는 두 번째 Cordova 웹뷰가 생성됩니다.
빌드하면 아래 메시지와 함께 Dependencies coroutines 오류가 발생합니다.
java.lang.NoClassDefFoundError: kotlin/coroutines/AbstractCoroutineContextKey at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016) ...kotlinx-coroutines-core 라이브러리 버전이 v1.3.5 이상이면 kotlin-stdlib 라이브러리 버전이 일정 레벨 이상이어야 합니다.
gradlew dependencies 커멘드로 kotlin-stdlib 라이브러리 버전이 v1.3.70 이상인지 확인해 주세요. 해당 버전 미만이라면 업데이트를 진행해야 합니다.
브레이즈 SDK를 사용한 푸시 알림으로 발생한 딥링크 실행(Deeplink Open) 이벤트가 수집되지 않습니다. 대신 실행(Open) 이벤트가 수집됩니다.
에어브릿지 안드로이드 SDK는 activity의 action에 있는 dataString과 intent의 dataString으로 딥링크 실행 이벤트와 실행 이벤트를 판별합니다.
브레이즈 SDK를 사용한 푸시 알림으로 앱을 실행하면 NotificationTrampolineActivity을 사용합니다. 브레이즈 SDK를 사용한 푸시 알림으로 앱을 실행하면 NotificationTrampolineActivity의 activity의 action과 intent로부터 dataString을 확인할 수 없습니다. 이로 인해 딥링크 실행 이벤트와 실행 이벤트를 판별할 수 없습니다.
android/app/src/main/java/.../MainApplication.kt 파일에 아래와 같이 코드를 추가해주세요.
import co.ab180.airbridge.cordova.AirbridgeCordova import co.ab180.airbridge.cordova.common.AirbridgeLifecycleIntegration...AirbridgeCordova .setLifecycleIntegration = { activity -> return@setLifecycleIntegration activity .takeIf { it.javaClass.name == "com.braze.push.NotificationTrampolineActivity" } ?.run { intent?.extras?.getString("uri") }}import co.ab180.airbridge.cordova.AirbridgeCordova ;import co.ab180.airbridge.cordova.common.AirbridgeLifecycleIntegration;...AirbridgeCordova .setLifecycleIntegration(new AirbridgeLifecycleIntegration() { @Nullable @Override public String getDataString(@NonNull Activity activity) { if ( activity.getClass().getName().equals("com.braze.push.NotificationTrampolineActivity") && activity.getIntent() != null && activity.getIntent().getExtras() != null ) { return activity.getIntent().getExtras().getString("uri"); } return null; }});import co.ab180.airbridge.cordova.AirbridgeCordova import co.ab180.airbridge.cordova.common.AirbridgeLifecycleIntegration...AirbridgeCordova .setLifecycleIntegration = { activity -> return@setLifecycleIntegration activity .takeIf { it.javaClass.name == "com.braze.push.NotificationTrampolineActivity" } ?.run { intent?.extras?.getString("uri") }}import co.ab180.airbridge.cordova.AirbridgeCordova ;import co.ab180.airbridge.cordova.common.AirbridgeLifecycleIntegration;...AirbridgeCordova .setLifecycleIntegration(new AirbridgeLifecycleIntegration() { @Nullable @Override public String getDataString(@NonNull Activity activity) { if ( activity.getClass().getName().equals("com.braze.push.NotificationTrampolineActivity") && activity.getIntent() != null && activity.getIntent().getExtras() != null ) { return activity.getIntent().getExtras().getString("uri"); } return null; }});빌드하면 Manifest merger failed 오류가 발생합니다.
에어브릿지 SDK의 AndroidManifest.xml에는 공유 환경 설정 데이터 백업을 옵트아웃하는 규칙이 포함되어 있습니다. 이는 재설치 중에 동일한 에어브릿지 설정 값들을 유지하지 않도록 하여 새로운 설치 또는 재설치를 정확하게 감지하도록 하기 위해 수행됩니다.
에어브릿지 SDK 백업 규칙과 앱 백업 규칙의 병합 과정에서 충돌이 발생할 수 있습니다.
에어브릿지 SDK에서 정의하는 옵트아웃하는 규칙은 다음과 같습니다.
<?xml version="1.0" encoding="utf-8"?><data-extraction-rules> <cloud-backup> <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" /> </cloud-backup> <device-transfer> <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" /> </device-transfer></data-extraction-rules><?xml version="1.0" encoding="utf-8"?><full-backup-content> <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" /></full-backup-content><?xml version="1.0" encoding="utf-8"?><data-extraction-rules> <cloud-backup> <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" /> </cloud-backup> <device-transfer> <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" /> </device-transfer></data-extraction-rules><?xml version="1.0" encoding="utf-8"?><full-backup-content> <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" /></full-backup-content>android:fullBackupContent="string"를 AndroidManifest.xml에 추가하면 다음과 같은 오류가 발생할 수 있습니다.
Manifest merger failed : Attribute application@fullBackupContent value=(string) from AndroidManifest.xml위 오류를 해결하려면 AndroidManifest.xml 파일의
<manifest> 태그에 xmlns:tools="http://schemas.android.com/tools"를 추가 해주세요.
<application> 태그에 tools:replace="android:fullBackupContent"를 추가 해주세요.
android:dataExtractionRules="string resource"를 AndroidManifest.xml에 추가하면 다음과 같은 오류가 발생할 수 있습니다.
Manifest merger failed : Attribute application@dataExtractionRules value=(string resource) from AndroidManifest.xml 위 오류를 해결하려면 AndroidManifest.xml 파일의
<manifest> 태그에 xmlns:tools="http://schemas.android.com/tools"를 추가 해주세요.
<application> 태그에 tools:replace="android:dataExtractionRules"를 추가 해주세요.
android:allowBackup="false"를 AndroidManifest.xml에 추가하면 다음과 같은 오류가 발생할 수 있습니다.
Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:32:9-36 is also present at [:airbridge] AndroidManifest.xml:27:9-35 value=(true). Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:30:5-250:19 to override.위 오류를 해결하려면 AndroidManifest.xml 파일의
<manifest> 태그에 xmlns:tools="http://schemas.android.com/tools"를 추가 해주세요.
<application> 태그에 tools:replace="android:allowBackup"를 추가 해주세요.
android:dataExtractionRules 기능이 API Level 31 부터 추가되었기 때문에 compileSdkVersion이 31 미만일 경우, 다음과 같은 오류가 발생할 수 있습니다.
AndroidManifest.xml: AAPT: error: attribute android:dataExtractionRules not found.위 오류를 해결하려면 AndroidManifest.xml 파일의
<manifest> 태그에 xmlns:tools="http://schemas.android.com/tools"를 추가 해주세요.
<application> 태그에 tools:remove="android:dataExtractionRules"를 추가 해주세요.
아래 가이드를 함께 참고해 주세요.
에어브릿지 SDK 백업 규칙과 다른 서드파티 SDK (예. Appsflyer SDK) 백업 규칙이 중복 적용되는 경우 아래와 같은 빌드 에러가 표시됩니다.
Attribute application@fullBackupContent value=(@xml/appsflyer_backup_rules) from [com.appsflyer:af-android-sdk:6.6.1] AndroidManifest.xml:14:18-73is 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.에어브릿지 SDK 백업 규칙과 서드파티 SDK 백업 규칙 중복 적용으로 인해 빌드 에러가 발생할 수 있습니다.
android/app/src/main/res/xml 폴더를 생성해주세요.
생성된 xml 폴더 내부에 (e.g. custom_backup_rules.xml) 파일을 생성해주세요.
에어브릿지 SDK에서 정의하는 데이터 백업 규칙을 다음과 같이 추가해 주세요.
<?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>생성된 xml 폴더 내부에 (e.g. custom_data_extraction_rule.xml) 파일을 생성해주세요.
에어브릿지 SDK에서 정의하는 데이터 백업 규칙을 다음과 같이 추가해 주세요.
<?xml version="1.0" encoding="utf-8"?><data-extraction-rules> <cloud-backup> <!-- 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 --> </cloud-backup> <device-transfer> <!-- 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 --> </device-transfer></data-extraction-rules><manifest ... xmlns:tools="http://schemas.android.com/tools"> <application ... android:allowBackup="true" android:fullBackupContent="@xml/custom_backup_rules" android:dataExtractionRules="@xml/custom_data_extraction_rules" tools:replace="android:fullBackupContent,android:dataExtractionRules">Google Play Console에 앱을 업로드할 때 다음과 같은 경고 또는 거부 메시지를 받을 수 있습니다:
"Your app doesn't support 16 KB page size"
"16 KB page size compatibility required"
앱 업로드가 거부되거나 경고 상태로 표시됩니다
이는 Android 15(API 수준 35) 이상을 타겟팅하는 앱이 16KB 페이지 크기를 지원해야 한다는 Google Play 정책 요구사항 때문입니다.
16KB 페이지 크기 지원을 위해서는 앱 전체(APK/AAB)가 16KB zipalign으로 패키징되어야 Google Play 정책을 통과합니다.
APK/AAB가 16KB zipalign으로 빌드되지 않으면:
Google Play Console 업로드 시 경고 또는 거부 메시지를 받습니다
2025년 11월 1일 이후에는 업로드가 완전히 차단됩니다
16KB 페이지 크기 기기에서 향후 정상 작동하지 않을 수 있습니다
Airbridge SDK 알려진 이슈: PT_GNU_RELRO 정렬
Airbridge SDK는 v4.6부터 16KB 페이지 크기를 지원하고 있습니다. 다만 SDK 빌드에 NDK r21을 사용한 버전에서는 네이티브 라이브러리(.so)의 LOAD 세그먼트는 16KB 경계로 정렬되지만 PT_GNU_RELRO 세그먼트는 정렬되지 않습니다.
앱 동작에는 영향이 없습니다. 16KB 페이지 크기 기기에서도 정상적으로 동작합니다.
다만 Google Play Console 업로드 시 16KB 페이지 크기 관련 경고 메시지가 표시될 수 있습니다.
Android Studio의 lint, APK Analyzer, check_elf_alignment.sh 등 정렬 검사 도구에서도 경고로 표시될 수 있습니다.
해당 이슈는 SDK 빌드 툴체인을 NDK r27로 업그레이드하여 수정했으며, 수정 이후 16KB 페이지 크기 지원에 이상이 없음을 확인했습니다. 플랫폼별 16KB 페이지 크기 지원 버전과 권장 버전은 아래 표를 참고해 주세요.
Step 1: Airbridge SDK 버전 확인
사용 중인 Airbridge SDK 버전을 위 표와 비교해 확인해 주세요.
16KB 지원 시작 버전보다 낮은 버전을 사용 중인 경우:
16KB 페이지 크기를 지원하지 않습니다. 반드시 업데이트해 주세요.
16KB 지원 시작 버전 이상, 권장 버전 미만을 사용 중인 경우:
위에 안내한 PT_GNU_RELRO 정렬 이슈로 인해 Google Play Console 경고가 계속 표시될 수 있습니다. 앱 동작에는 영향이 없지만, 경고를 해소하려면 권장 버전 이상으로 업데이트해 주세요.
Step 2: Android Gradle Plugin(AGP) 버전 확인 및 설정
현재 프로젝트의 AGP 버전을 확인하세요.
AGP 8.5.1 이상을 사용하는 경우:
별도의 추가 설정 없이 자동으로 16KB zipalign이 적용됩니다.
Step 3으로 바로 이동하세요.
AGP 8.5.0 이하를 사용하는 경우:
옵션 A: AGP를 8.5.1 이상으로 업그레이드
옵션 B: 현재 AGP 버전 유지 + 설정 추가
android { ... packagingOptions { jniLibs { useLegacyPackaging true } }}Step 3: 앱 다시 빌드 및 확인
설정을 변경한 후 앱을 클린 빌드하세요.
Step 4: 16KB zipalign 적용 확인
빌드된 APK/AAB가 올바르게 16KB zipalign되었는지 확인하세요.
기존 | Scene 생명주기에서의 대체 콜백( |
|---|---|
|
|
앱 콜드 스타트 시점의 딥링크 |
|
|
|
|
|
cordova-ios 8.1.0 이상 | cordova-ios 7.x 이하 | |
|---|---|---|
앱이 꺼진 상태에서 딥링크 | 스킴 딥링크는 |
|
스킴 딥링크(백그라운드) |
| 동일 |
유니버설 링크(백그라운드) |
| 동일 |
Platform | 16KB 지원 시작 버전 | 권장 버전 (PT_GNU_RELRO 이슈 수정) |
|---|---|---|
Android | v4.6 | v4.9.4 |
React Native | v4.6 | v4.9.0 |
Cordova Ionic | v4.4.1 | x(지원 예정) |
Flutter | v4.6 | v4.9.0 |
Expo | v4.6 | v4.9.0 |
Unity | v4.6 | v4.9.2 |
Unreal | v4.6 | v4.9.0 |
도움이 되었나요?