@adstage/react-native-sdk 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app.plugin.js ADDED
@@ -0,0 +1,3 @@
1
+ // Expo Config Plugin entry point
2
+ // 빌드된 플러그인을 내보냅니다.
3
+ module.exports = require('./plugin/build/index');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adstage/react-native-sdk",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "AdStage SDK for React Native - 광고 어트리뷰션, 딥링크, 인앱 이벤트 트래킹",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -12,6 +12,8 @@
12
12
  "lib",
13
13
  "android",
14
14
  "ios",
15
+ "plugin",
16
+ "app.plugin.js",
15
17
  "*.podspec",
16
18
  "!ios/build",
17
19
  "!android/build",
@@ -28,8 +30,9 @@
28
30
  "test": "jest",
29
31
  "typecheck": "tsc --noEmit",
30
32
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
31
- "clean": "del-cli lib",
32
- "prepare": "bob build"
33
+ "clean": "del-cli lib plugin/build",
34
+ "build:plugin": "tsc -p plugin/tsconfig.json",
35
+ "prepare": "bob build && npm run build:plugin"
33
36
  },
34
37
  "keywords": [
35
38
  "react-native",
@@ -55,6 +58,7 @@
55
58
  "registry": "https://registry.npmjs.org/"
56
59
  },
57
60
  "devDependencies": {
61
+ "@expo/config-plugins": "^8.0.0",
58
62
  "@react-native/eslint-config": "^0.73.0",
59
63
  "@types/jest": "^29.5.0",
60
64
  "@types/react": "^18.2.0",
@@ -0,0 +1,16 @@
1
+ /**
2
+ * AdStage React Native SDK - Expo Config Plugin
3
+ *
4
+ * iOS AppDelegate에 딥링크 핸들링 코드를 자동으로 추가합니다.
5
+ *
6
+ * 사용법:
7
+ * app.json 또는 app.config.js에 추가:
8
+ * {
9
+ * "expo": {
10
+ * "plugins": ["@adstage/react-native-sdk"]
11
+ * }
12
+ * }
13
+ */
14
+ import { ConfigPlugin } from '@expo/config-plugins';
15
+ declare const _default: ConfigPlugin<void>;
16
+ export default _default;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * AdStage React Native SDK - Expo Config Plugin
4
+ *
5
+ * iOS AppDelegate에 딥링크 핸들링 코드를 자동으로 추가합니다.
6
+ *
7
+ * 사용법:
8
+ * app.json 또는 app.config.js에 추가:
9
+ * {
10
+ * "expo": {
11
+ * "plugins": ["@adstage/react-native-sdk"]
12
+ * }
13
+ * }
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ const config_plugins_1 = require("@expo/config-plugins");
17
+ const withAdStageIOS_1 = require("./withAdStageIOS");
18
+ const pkg = require('../../package.json');
19
+ /**
20
+ * AdStage SDK Expo Config Plugin
21
+ *
22
+ * iOS에서 딥링크(URL Scheme, Universal Link)를 자동으로 처리하도록
23
+ * AppDelegate를 수정합니다.
24
+ */
25
+ const withAdStage = (config) => {
26
+ // iOS 설정 적용
27
+ config = (0, withAdStageIOS_1.withAdStageIOS)(config);
28
+ return config;
29
+ };
30
+ exports.default = (0, config_plugins_1.createRunOncePlugin)(withAdStage, pkg.name, pkg.version);
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7R0FZRzs7QUFFSCx5REFBeUU7QUFDekUscURBQWtEO0FBRWxELE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0FBRTFDOzs7OztHQUtHO0FBQ0gsTUFBTSxXQUFXLEdBQWlCLENBQUMsTUFBTSxFQUFFLEVBQUU7SUFDM0MsWUFBWTtJQUNaLE1BQU0sR0FBRyxJQUFBLCtCQUFjLEVBQUMsTUFBTSxDQUFDLENBQUM7SUFFaEMsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyxDQUFDO0FBRUYsa0JBQWUsSUFBQSxvQ0FBbUIsRUFBQyxXQUFXLEVBQUUsR0FBRyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEFkU3RhZ2UgUmVhY3QgTmF0aXZlIFNESyAtIEV4cG8gQ29uZmlnIFBsdWdpblxuICpcbiAqIGlPUyBBcHBEZWxlZ2F0ZeyXkCDrlKXrp4Htgawg7ZW465Ok66eBIOy9lOuTnOulvCDsnpDrj5nsnLzroZwg7LaU6rCA7ZWp64uI64ukLlxuICpcbiAqIOyCrOyaqeuylTpcbiAqIGFwcC5qc29uIOuYkOuKlCBhcHAuY29uZmlnLmpz7JeQIOy2lOqwgDpcbiAqIHtcbiAqICAgXCJleHBvXCI6IHtcbiAqICAgICBcInBsdWdpbnNcIjogW1wiQGFkc3RhZ2UvcmVhY3QtbmF0aXZlLXNka1wiXVxuICogICB9XG4gKiB9XG4gKi9cblxuaW1wb3J0IHsgQ29uZmlnUGx1Z2luLCBjcmVhdGVSdW5PbmNlUGx1Z2luIH0gZnJvbSAnQGV4cG8vY29uZmlnLXBsdWdpbnMnO1xuaW1wb3J0IHsgd2l0aEFkU3RhZ2VJT1MgfSBmcm9tICcuL3dpdGhBZFN0YWdlSU9TJztcblxuY29uc3QgcGtnID0gcmVxdWlyZSgnLi4vLi4vcGFja2FnZS5qc29uJyk7XG5cbi8qKlxuICogQWRTdGFnZSBTREsgRXhwbyBDb25maWcgUGx1Z2luXG4gKlxuICogaU9T7JeQ7IScIOuUpeunge2BrChVUkwgU2NoZW1lLCBVbml2ZXJzYWwgTGluaynrpbwg7J6Q64+Z7Jy866GcIOyymOumrO2VmOuPhOuhnVxuICogQXBwRGVsZWdhdGXrpbwg7IiY7KCV7ZWp64uI64ukLlxuICovXG5jb25zdCB3aXRoQWRTdGFnZTogQ29uZmlnUGx1Z2luID0gKGNvbmZpZykgPT4ge1xuICAvLyBpT1Mg7ISk7KCVIOyggeyaqVxuICBjb25maWcgPSB3aXRoQWRTdGFnZUlPUyhjb25maWcpO1xuXG4gIHJldHVybiBjb25maWc7XG59O1xuXG5leHBvcnQgZGVmYXVsdCBjcmVhdGVSdW5PbmNlUGx1Z2luKHdpdGhBZFN0YWdlLCBwa2cubmFtZSwgcGtnLnZlcnNpb24pO1xuIl19
@@ -0,0 +1,11 @@
1
+ /**
2
+ * iOS AppDelegate 수정을 위한 Config Plugin
3
+ *
4
+ * AppDelegate.swift에 AdStage 딥링크 핸들링 코드를 추가합니다.
5
+ */
6
+ import { ConfigPlugin } from '@expo/config-plugins';
7
+ /**
8
+ * iOS AppDelegate에 AdStage 딥링크 핸들링 코드를 추가하는 Config Plugin
9
+ */
10
+ export declare const withAdStageIOS: ConfigPlugin;
11
+ export default withAdStageIOS;
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ /**
3
+ * iOS AppDelegate 수정을 위한 Config Plugin
4
+ *
5
+ * AppDelegate.swift에 AdStage 딥링크 핸들링 코드를 추가합니다.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.withAdStageIOS = void 0;
9
+ const config_plugins_1 = require("@expo/config-plugins");
10
+ /**
11
+ * AppDelegate.swift에 추가할 import 문
12
+ */
13
+ const ADSTAGE_IMPORT = 'import AdapterAdStage';
14
+ /**
15
+ * application:openURL:options: 메서드에 추가할 딥링크 핸들링 코드
16
+ */
17
+ const DEEPLINK_HANDLER_CODE = `
18
+ // AdStage 딥링크 처리
19
+ AdStageManager.shared.handleDeepLink(url)`;
20
+ /**
21
+ * application:continueUserActivity: 메서드에 추가할 Universal Link 핸들링 코드
22
+ */
23
+ const UNIVERSAL_LINK_HANDLER_CODE = `
24
+ // AdStage Universal Link 처리
25
+ if let url = userActivity.webpageURL {
26
+ AdStageManager.shared.handleUniversalLink(userActivity)
27
+ }`;
28
+ /**
29
+ * iOS AppDelegate에 AdStage 딥링크 핸들링 코드를 추가하는 Config Plugin
30
+ */
31
+ const withAdStageIOS = (config) => {
32
+ return (0, config_plugins_1.withAppDelegate)(config, (cfg) => {
33
+ const appDelegate = cfg.modResults;
34
+ if (appDelegate.language === 'swift') {
35
+ appDelegate.contents = modifySwiftAppDelegate(appDelegate.contents);
36
+ }
37
+ else if (appDelegate.language === 'objc' || appDelegate.language === 'objcpp') {
38
+ appDelegate.contents = modifyObjCAppDelegate(appDelegate.contents);
39
+ }
40
+ return cfg;
41
+ });
42
+ };
43
+ exports.withAdStageIOS = withAdStageIOS;
44
+ /**
45
+ * Swift AppDelegate 수정
46
+ */
47
+ function modifySwiftAppDelegate(contents) {
48
+ // 이미 AdStage import가 있는지 확인
49
+ if (contents.includes(ADSTAGE_IMPORT)) {
50
+ console.log('[AdStage] AppDelegate already configured, skipping...');
51
+ return contents;
52
+ }
53
+ // 1. import 문 추가
54
+ contents = addSwiftImport(contents, ADSTAGE_IMPORT);
55
+ // 2. application:openURL:options: 메서드에 딥링크 핸들러 추가
56
+ contents = addDeepLinkHandler(contents);
57
+ // 3. application:continueUserActivity: 메서드에 Universal Link 핸들러 추가
58
+ contents = addUniversalLinkHandler(contents);
59
+ return contents;
60
+ }
61
+ /**
62
+ * Swift import 문 추가
63
+ */
64
+ function addSwiftImport(contents, importStatement) {
65
+ // 마지막 import 문 뒤에 추가
66
+ const importRegex = /^import\s+\w+.*$/gm;
67
+ const matches = [...contents.matchAll(importRegex)];
68
+ if (matches.length > 0) {
69
+ const lastMatch = matches[matches.length - 1];
70
+ const insertPosition = lastMatch.index + lastMatch[0].length;
71
+ contents =
72
+ contents.slice(0, insertPosition) +
73
+ '\n' +
74
+ importStatement +
75
+ contents.slice(insertPosition);
76
+ }
77
+ return contents;
78
+ }
79
+ /**
80
+ * application:openURL:options: 메서드에 딥링크 핸들러 추가
81
+ */
82
+ function addDeepLinkHandler(contents) {
83
+ // application(_:open:options:) 메서드 찾기
84
+ const openURLMethodRegex = /func\s+application\s*\(\s*_\s+app:\s*UIApplication\s*,\s*open\s+url:\s*URL\s*,\s*options[^)]*\)\s*->\s*Bool\s*\{/;
85
+ const match = contents.match(openURLMethodRegex);
86
+ if (match) {
87
+ // 기존 메서드에 코드 추가
88
+ const insertPosition = match.index + match[0].length;
89
+ contents =
90
+ contents.slice(0, insertPosition) +
91
+ DEEPLINK_HANDLER_CODE +
92
+ contents.slice(insertPosition);
93
+ }
94
+ else {
95
+ // 메서드가 없으면 AppDelegate 클래스에 추가
96
+ contents = addOpenURLMethod(contents);
97
+ }
98
+ return contents;
99
+ }
100
+ /**
101
+ * application:openURL:options: 메서드 전체 추가
102
+ */
103
+ function addOpenURLMethod(contents) {
104
+ const newMethod = `
105
+
106
+ // MARK: - AdStage Deep Link Handling
107
+
108
+ func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
109
+ // AdStage 딥링크 처리
110
+ AdStageManager.shared.handleDeepLink(url)
111
+
112
+ // RCTLinkingManager로 전달 (React Native Linking 지원)
113
+ return RCTLinkingManager.application(app, open: url, options: options)
114
+ }`;
115
+ // 클래스 끝 (마지막 }) 앞에 삽입
116
+ const classEndRegex = /\n\s*\}\s*$/;
117
+ const match = contents.match(classEndRegex);
118
+ if (match) {
119
+ contents = contents.replace(classEndRegex, newMethod + match[0]);
120
+ }
121
+ return contents;
122
+ }
123
+ /**
124
+ * application:continueUserActivity: 메서드에 Universal Link 핸들러 추가
125
+ */
126
+ function addUniversalLinkHandler(contents) {
127
+ // application(_:continue:restorationHandler:) 메서드 찾기
128
+ const continueUserActivityRegex = /func\s+application\s*\([^)]*continue\s+userActivity:\s*NSUserActivity[^)]*\)\s*->\s*Bool\s*\{/;
129
+ const match = contents.match(continueUserActivityRegex);
130
+ if (match) {
131
+ // 기존 메서드에 코드 추가
132
+ const insertPosition = match.index + match[0].length;
133
+ contents =
134
+ contents.slice(0, insertPosition) +
135
+ UNIVERSAL_LINK_HANDLER_CODE +
136
+ contents.slice(insertPosition);
137
+ }
138
+ else {
139
+ // 메서드가 없으면 AppDelegate 클래스에 추가
140
+ contents = addContinueUserActivityMethod(contents);
141
+ }
142
+ return contents;
143
+ }
144
+ /**
145
+ * application:continueUserActivity: 메서드 전체 추가
146
+ */
147
+ function addContinueUserActivityMethod(contents) {
148
+ const newMethod = `
149
+
150
+ // MARK: - AdStage Universal Link Handling
151
+
152
+ func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
153
+ // AdStage Universal Link 처리
154
+ if let url = userActivity.webpageURL {
155
+ AdStageManager.shared.handleUniversalLink(userActivity)
156
+ }
157
+
158
+ // RCTLinkingManager로 전달 (React Native Linking 지원)
159
+ return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
160
+ }`;
161
+ // 클래스 끝 (마지막 }) 앞에 삽입
162
+ const classEndRegex = /\n\s*\}\s*$/;
163
+ const match = contents.match(classEndRegex);
164
+ if (match) {
165
+ contents = contents.replace(classEndRegex, newMethod + match[0]);
166
+ }
167
+ return contents;
168
+ }
169
+ /**
170
+ * Objective-C AppDelegate 수정 (레거시 지원)
171
+ */
172
+ function modifyObjCAppDelegate(contents) {
173
+ // 이미 AdStage import가 있는지 확인
174
+ if (contents.includes('#import <AdapterAdStage/AdapterAdStage.h>')) {
175
+ console.log('[AdStage] AppDelegate already configured, skipping...');
176
+ return contents;
177
+ }
178
+ // 1. import 문 추가
179
+ const importRegex = /#import\s+[<"][^>"]+[>"]/g;
180
+ const matches = [...contents.matchAll(importRegex)];
181
+ if (matches.length > 0) {
182
+ const lastMatch = matches[matches.length - 1];
183
+ const insertPosition = lastMatch.index + lastMatch[0].length;
184
+ contents =
185
+ contents.slice(0, insertPosition) +
186
+ '\n#import <AdapterAdStage/AdapterAdStage.h>' +
187
+ contents.slice(insertPosition);
188
+ }
189
+ // 2. openURL 메서드에 딥링크 핸들러 추가
190
+ const openURLMethodRegex = /-\s*\(BOOL\)\s*application:\s*\(UIApplication\s*\*\)\s*\w+\s+openURL:\s*\(NSURL\s*\*\)\s*(\w+)/;
191
+ const openURLMatch = contents.match(openURLMethodRegex);
192
+ if (openURLMatch) {
193
+ const urlVarName = openURLMatch[1];
194
+ const methodBodyStart = contents.indexOf('{', openURLMatch.index) + 1;
195
+ const handlerCode = `\n // AdStage 딥링크 처리\n [[AdStageManager shared] handleDeepLink:${urlVarName}];\n`;
196
+ contents =
197
+ contents.slice(0, methodBodyStart) +
198
+ handlerCode +
199
+ contents.slice(methodBodyStart);
200
+ }
201
+ return contents;
202
+ }
203
+ exports.default = exports.withAdStageIOS;
204
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aEFkU3RhZ2VJT1MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd2l0aEFkU3RhZ2VJT1MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7OztBQUVILHlEQUFxRTtBQUVyRTs7R0FFRztBQUNILE1BQU0sY0FBYyxHQUFHLHVCQUF1QixDQUFDO0FBRS9DOztHQUVHO0FBQ0gsTUFBTSxxQkFBcUIsR0FBRzs7OENBRWdCLENBQUM7QUFFL0M7O0dBRUc7QUFDSCxNQUFNLDJCQUEyQixHQUFHOzs7O01BSTlCLENBQUM7QUFFUDs7R0FFRztBQUNJLE1BQU0sY0FBYyxHQUFpQixDQUFDLE1BQU0sRUFBRSxFQUFFO0lBQ3JELE9BQU8sSUFBQSxnQ0FBZSxFQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO1FBQ3JDLE1BQU0sV0FBVyxHQUFHLEdBQUcsQ0FBQyxVQUFVLENBQUM7UUFFbkMsSUFBSSxXQUFXLENBQUMsUUFBUSxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQ3JDLFdBQVcsQ0FBQyxRQUFRLEdBQUcsc0JBQXNCLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3RFLENBQUM7YUFBTSxJQUFJLFdBQVcsQ0FBQyxRQUFRLEtBQUssTUFBTSxJQUFJLFdBQVcsQ0FBQyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDaEYsV0FBVyxDQUFDLFFBQVEsR0FBRyxxQkFBcUIsQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDckUsQ0FBQztRQUVELE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUM7QUFaVyxRQUFBLGNBQWMsa0JBWXpCO0FBRUY7O0dBRUc7QUFDSCxTQUFTLHNCQUFzQixDQUFDLFFBQWdCO0lBQzlDLDRCQUE0QjtJQUM1QixJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztRQUN0QyxPQUFPLENBQUMs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QUMsV0FBVyxDQUFDLENBQUMsQ0FBQztJQUVwRCxJQUFJLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDdkIsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDOUMsTUFBTSxjQUFjLEdBQUcsU0FBUyxDQUFDLEtBQU0sR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO1FBQzlELFFBQVE7WUFDTixRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxjQUFjLENBQUM7Z0JBQ2pDLDZDQUE2QztnQkFDN0MsUUFBUSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQsNkJBQTZCO0lBQzdCLE1BQU0sa0JBQWtCLEdBQ3RCLGdHQUFnRyxDQUFDO0lBRW5HLE1BQU0sWUFBWSxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsa0JBQWtCLENBQUMsQ0FBQztJQUN4RCxJQUFJLFlBQVksRUFBRSxDQUFDO1FBQ2pCLE1BQU0sVUFBVSxHQUFHLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuQyxNQUFNLGVBQWUsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxZQUFZLENBQUMsS0FBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZFLE1BQU0sV0FBVyxHQUFHLG9FQUFvRSxVQUFVLE1BQU0sQ0FBQztRQUN6RyxRQUFRO1lBQ04sUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsZUFBZSxDQUFDO2dCQUNsQyxXQUFXO2dCQUNYLFFBQVEsQ0FBQyxLQUFLLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUVELE9BQU8sUUFBUSxDQUFDO0FBQ2xCLENBQUM7QUFFRCxrQkFBZSxzQkFBYyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBpT1MgQXBwRGVsZWdhdGUg7IiY7KCV7J2EIOychO2VnCBDb25maWcgUGx1Z2luXG4gKlxuICogQXBwRGVsZWdhdGUuc3dpZnTsl5AgQWRTdGFnZSDrlKXrp4Htgawg7ZW465Ok66eBIOy9lOuTnOulvCDstpTqsIDtlanri4jri6QuXG4gKi9cblxuaW1wb3J0IHsgQ29uZmlnUGx1Z2luLCB3aXRoQXBwRGVsZWdhdGUgfSBmcm9tICdAZXhwby9jb25maWctcGx1Z2lucyc7XG5cbi8qKlxuICogQXBwRGVsZWdhdGUuc3dpZnTsl5Ag7LaU6rCA7ZWgIGltcG9ydCDrrLhcbiAqL1xuY29uc3QgQURTVEFHRV9JTVBPUlQgPSAnaW1wb3J0IEFkYXB0ZXJBZFN0YWdlJztcblxuLyoqXG4gKiBhcHBsaWNhdGlvbjpvcGVuVVJMOm9wdGlvbnM6IOuplOyEnOuTnOyXkCDstpTqsIDtlaAg65Sl66eB7YGsIO2VuOuTpOungSDsvZTrk5xcbiAqL1xuY29uc3QgREVFUExJTktfSEFORExFUl9DT0RFID0gYFxuICAgIC8vIEFkU3RhZ2Ug65Sl66eB7YGsIOyymOumrFxuICAgIEFkU3RhZ2VNYW5hZ2VyLnNoYXJlZC5oYW5kbGVEZWVwTGluayh1cmwpYDtcblxuLyoqXG4gKiBhcHBsaWNhdGlvbjpjb250aW51ZVVzZXJBY3Rpdml0eTog66mU7ISc65Oc7JeQIOy2lOqwgO2VoCBVbml2ZXJzYWwgTGluayDtlbjrk6Trp4Eg7L2U65OcXG4gKi9cbmNvbnN0IFVOSVZFUlNBTF9MSU5LX0hBTkRMRVJfQ09ERSA9IGBcbiAgICAvLyBBZFN0YWdlIFVuaXZlcnNhbCBMaW5rIOyymOumrFxuICAgIGlmIGxldCB1cmwgPSB1c2VyQWN0aXZpdHkud2VicGFnZVVSTCB7XG4gICAgICBBZFN0YWdlTWFuYWdlci5zaGFyZWQuaGFuZGxlVW5pdmVyc2FsTGluayh1c2VyQWN0aXZpdHkpXG4gICAgfWA7XG5cbi8qKlxuICogaU9TIEFwcERlbGVnYXRl7JeQIEFkU3RhZ2Ug65Sl66eB7YGsIO2VuOuTpOungSDsvZTrk5zrpbwg7LaU6rCA7ZWY64qUIENvbmZpZyBQbHVnaW5cbiAqL1xuZXhwb3J0IGNvbnN0IHdpdGhBZFN0YWdlSU9TOiBDb25maWdQbHVnaW4gPSAoY29uZmlnKSA9PiB7XG4gIHJldHVybiB3aXRoQXBwRGVsZWdhdGUoY29uZmlnLCAoY2ZnKSA9PiB7XG4gICAgY29uc3QgYXBwRGVsZWdhdGUgPSBjZmcubW9kUmVzdWx0cztcblxuICAgIGlmIChhcHBEZWxlZ2F0ZS5sYW5ndWFnZSA9PT0gJ3N3aWZ0Jykge1xuICAgICAgYXBwRGVsZWdhdGUuY29udGVudHMgPSBtb2RpZnlTd2lmdEFwcERlbGVnYXRlKGFwcERlbGVnYXRlLmNvbnRlbnRzKTtcbiAgICB9IGVsc2UgaWYgKGFwcERlbGVnYXRlLmxhbmd1YWdlID09PSAnb2JqYycgfHwgYXBwRGVsZWdhdGUubGFuZ3VhZ2UgPT09ICdvYmpjcHAnKSB7XG4gICAgICBhcHBEZWxlZ2F0ZS5jb250ZW50cyA9IG1vZGlmeU9iakNBcHBEZWxlZ2F0ZShhcHBEZWxlZ2F0ZS5jb250ZW50cyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGNmZztcbiAgfSk7XG59O1xuXG4vKipcbiAqIFN3aWZ0IEFwcERlbGVnYXRlIOyImOyglVxuICovXG5mdW5jdGlvbiBtb2RpZnlTd2lmdEFwcERlbGVnYXRlKGNvbnRlbnRzOiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyDsnbTrr7ggQWRTdGFnZSBpbXBvcnTqsIAg7J6I64qU7KeAIO2ZleyduFxuICBpZiAoY29udGVudHMuaW5jbHVkZXMoQURTVEFHRV9JTVBPUlQpKSB7XG4gICAgY29uc29sZS5sb2coJ1tBZFN0YWdlXSBBcHBEZWxlZ2F0ZSBhbHJlYWR5IGNvbmZpZ3VyZWQsIHNraXBwaW5nLi4uJyk7XG4gICAgcmV0dXJuIGNvbnRlbnRzO1xuICB9XG5cbiAgLy8gMS4gaW1wb3J0IOusuCDstpTqsIBcbiAgY29udGVudHMgPSBhZGRTd2lmdEltcG9ydChjb250ZW50cywgQURTVEFHRV9JTVBPUlQpO1xuXG4gIC8vIDIuIGFwcGxpY2F0aW9uOm9wZW5VUkw6b3B0aW9uczog66mU7ISc65Oc7JeQIOuUpeunge2BrCDtlbjrk6Trn6wg7LaU6rCAXG4gIGNvbnRlbnRzID0gYWRkRGVlcExpbmtIYW5kbGVyKGNvbnRlbnRzKTtcblxuICAvLyAzLiBhcHBsaWNhdGlvbjpjb250aW51ZVVzZXJBY3Rpdml0eTog66mU7ISc65Oc7JeQIFVuaXZlcnNhbCBMaW5rIO2VuOuTpOufrCDstpTqsIBcbiAgY29udGVudHMgPSBhZGRVbml2ZXJzYWxMaW5rSGFuZGxlcihjb250ZW50cyk7XG5cbiAgcmV0dXJuIGNvbnRlbnRzO1xufVxuXG4vKipcbiAqIFN3aWZ0IGltcG9ydCDrrLgg7LaU6rCAXG4gKi9cbmZ1bmN0aW9uIGFkZFN3aWZ0SW1wb3J0KGNvbnRlbnRzOiBzdHJpbmcsIGltcG9ydFN0YXRlbWVudDogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8g66eI7KeA66eJIGltcG9ydCDrrLgg65Kk7JeQIOy2lOqwgFxuICBjb25zdCBpbXBvcnRSZWdleCA9IC9eaW1wb3J0XFxzK1xcdysuKiQvZ207XG4gIGNvbnN0IG1hdGNoZXMgPSBbLi4uY29udGVudHMubWF0Y2hBbGwoaW1wb3J0UmVnZXgpXTtcblxuICBpZiAobWF0Y2hlcy5sZW5ndGggPiAwKSB7XG4gICAgY29uc3QgbGFzdE1hdGNoID0gbWF0Y2hlc1ttYXRjaGVzLmxlbmd0aCAtIDFdO1xuICAgIGNvbnN0IGluc2VydFBvc2l0aW9uID0gbGFzdE1hdGNoLmluZGV4ISArIGxhc3RNYXRjaFswXS5sZW5ndGg7XG4gICAgY29udGVudHMgPVxuICAgICAgY29udGVudHMuc2xpY2UoMCwgaW5zZXJ0UG9zaXRpb24pICtcbiAgICAgICdcXG4nICtcbiAgICAgIGltcG9ydFN0YXRlbWVudCArXG4gICAgICBjb250ZW50cy5zbGljZShpbnNlcnRQb3NpdGlvbik7XG4gIH1cblxuICByZXR1cm4gY29udGVudHM7XG59XG5cbi8qKlxuICogYXBwbGljYXRpb246b3BlblVSTDpvcHRpb25zOiDrqZTshJzrk5zsl5Ag65Sl66eB7YGsIO2VuOuTpOufrCDstpTqsIBcbiAqL1xuZnVuY3Rpb24gYWRkRGVlcExpbmtIYW5kbGVyKGNvbnRlbnRzOiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBhcHBsaWNhdGlvbihfOm9wZW46b3B0aW9uczopIOuplOyEnOuTnCDssL7quLBcbiAgY29uc3Qgb3BlblVSTE1ldGhvZFJlZ2V4ID1cbiAgICAvZnVuY1xccythcHBsaWNhdGlvblxccypcXChcXHMqX1xccythcHA6XFxzKlVJQXBwbGljYXRpb25cXHMqLFxccypvcGVuXFxzK3VybDpcXHMqVVJMXFxzKixcXHMqb3B0aW9uc1teKV0qXFwpXFxzKi0+XFxzKkJvb2xcXHMqXFx7LztcblxuICBjb25zdCBtYXRjaCA9IGNvbnRlbnRzLm1hdGNoKG9wZW5VUkxNZXRob2RSZWdleCk7XG5cbiAgaWYgKG1hdGNoKSB7XG4gICAgLy8g6riw7KG0IOuplOyEnOuTnOyXkCDsvZTrk5wg7LaU6rCAXG4gICAgY29uc3QgaW5zZXJ0UG9zaXRpb24gPSBtYXRjaC5pbmRleCEgKyBtYXRjaFswXS5sZW5ndGg7XG4gICAgY29udGVudHMgPVxuICAgICAgY29udGVudHMuc2xpY2UoMCwgaW5zZXJ0UG9zaXRpb24pICtcbiAgICAgIERFRVBMSU5LX0hBTkRMRVJfQ09ERSArXG4gICAgICBjb250ZW50cy5zbGljZShpbnNlcnRQb3NpdGlvbik7XG4gIH0gZWxzZSB7XG4gICAgLy8g66mU7ISc65Oc6rCAIOyXhuycvOuptCBBcHBEZWxlZ2F0ZSDtgbTrnpjsiqTsl5Ag7LaU6rCAXG4gICAgY29udGVudHMgPSBhZGRPcGVuVVJMTWV0aG9kKGNvbnRlbnRzKTtcbiAgfVxuXG4gIHJldHVybiBjb250ZW50cztcbn1cblxuLyoqXG4gKiBhcHBsaWNhdGlvbjpvcGVuVVJMOm9wdGlvbnM6IOuplOyEnOuTnCDsoITssrQg7LaU6rCAXG4gKi9cbmZ1bmN0aW9uIGFkZE9wZW5VUkxNZXRob2QoY29udGVudHM6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IG5ld01ldGhvZCA9IGBcblxuICAvLyBNQVJLOiAtIEFkU3RhZ2UgRGVlcCBMaW5rIEhhbmRsaW5nXG4gIFxuICBmdW5jIGFwcGxpY2F0aW9uKF8gYXBwOiBVSUFwcGxpY2F0aW9uLCBvcGVuIHVybDogVVJMLCBvcHRpb25zOiBbVUlBcHBsaWNhdGlvbi5PcGVuVVJMT3B0aW9uc0tleTogQW55XSA9IFs6XSkgLT4gQm9vbCB7XG4gICAgLy8gQWRTdGFnZSDrlKXrp4Htgawg7LKY66asXG4gICAgQWRTdGFnZU1hbmFnZXIuc2hhcmVkLmhhbmRsZURlZXBMaW5rKHVybClcbiAgICBcbiAgICAvLyBSQ1RMaW5raW5nTWFuYWdlcuuhnCDsoITri6wgKFJlYWN0IE5hdGl2ZSBMaW5raW5nIOyngOybkClcbiAgICByZXR1cm4gUkNUTGlua2luZ01hbmFnZXIuYXBwbGljYXRpb24oYXBwLCBvcGVuOiB1cmwsIG9wdGlvbnM6IG9wdGlvbnMpXG4gIH1gO1xuXG4gIC8vIO2BtOuemOyKpCDrgZ0gKOuniOyngOuniSB9KSDslZ7sl5Ag7IK97J6FXG4gIGNvbnN0IGNsYXNzRW5kUmVnZXggPSAvXFxuXFxzKlxcfVxccyokLztcbiAgY29uc3QgbWF0Y2ggPSBjb250ZW50cy5tYXRjaChjbGFzc0VuZFJlZ2V4KTtcblxuICBpZiAobWF0Y2gpIHtcbiAgICBjb250ZW50cyA9IGNvbnRlbnRzLnJlcGxhY2UoY2xhc3NFbmRSZWdleCwgbmV3TWV0aG9kICsgbWF0Y2hbMF0pO1xuICB9XG5cbiAgcmV0dXJuIGNvbnRlbnRzO1xufVxuXG4vKipcbiAqIGFwcGxpY2F0aW9uOmNvbnRpbnVlVXNlckFjdGl2aXR5OiDrqZTshJzrk5zsl5AgVW5pdmVyc2FsIExpbmsg7ZW465Ok65+sIOy2lOqwgFxuICovXG5mdW5jdGlvbiBhZGRVbml2ZXJzYWxMaW5rSGFuZGxlcihjb250ZW50czogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gYXBwbGljYXRpb24oXzpjb250aW51ZTpyZXN0b3JhdGlvbkhhbmRsZXI6KSDrqZTshJzrk5wg7LC+6riwXG4gIGNvbnN0IGNvbnRpbnVlVXNlckFjdGl2aXR5UmVnZXggPVxuICAgIC9mdW5jXFxzK2FwcGxpY2F0aW9uXFxzKlxcKFteKV0qY29udGludWVcXHMrdXNlckFjdGl2aXR5OlxccypOU1VzZXJBY3Rpdml0eVteKV0qXFwpXFxzKi0+XFxzKkJvb2xcXHMqXFx7LztcblxuICBjb25zdCBtYXRjaCA9IGNvbnRlbnRzLm1hdGNoKGNvbnRpbnVlVXNlckFjdGl2aXR5UmVnZXgpO1xuXG4gIGlmIChtYXRjaCkge1xuICAgIC8vIOq4sOyhtCDrqZTshJzrk5zsl5Ag7L2U65OcIOy2lOqwgFxuICAgIGNvbnN0IGluc2VydFBvc2l0aW9uID0gbWF0Y2guaW5kZXghICsgbWF0Y2hbMF0ubGVuZ3RoO1xuICAgIGNvbnRlbnRzID1cbiAgICAgIGNvbnRlbnRzLnNsaWNlKDAsIGluc2VydFBvc2l0aW9uKSArXG4gICAgICBVTklWRVJTQUxfTElOS19IQU5ETEVSX0NPREUgK1xuICAgICAgY29udGVudHMuc2xpY2UoaW5zZXJ0UG9zaXRpb24pO1xuICB9IGVsc2Uge1xuICAgIC8vIOuplOyEnOuTnOqwgCDsl4bsnLzrqbQgQXBwRGVsZWdhdGUg7YG0656Y7Iqk7JeQIOy2lOqwgFxuICAgIGNvbnRlbnRzID0gYWRkQ29udGludWVVc2VyQWN0aXZpdHlNZXRob2QoY29udGVudHMpO1xuICB9XG5cbiAgcmV0dXJuIGNvbnRlbnRzO1xufVxuXG4vKipcbiAqIGFwcGxpY2F0aW9uOmNvbnRpbnVlVXNlckFjdGl2aXR5OiDrqZTshJzrk5wg7KCE7LK0IOy2lOqwgFxuICovXG5mdW5jdGlvbiBhZGRDb250aW51ZVVzZXJBY3Rpdml0eU1ldGhvZChjb250ZW50czogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgbmV3TWV0aG9kID0gYFxuXG4gIC8vIE1BUks6IC0gQWRTdGFnZSBVbml2ZXJzYWwgTGluayBIYW5kbGluZ1xuICBcbiAgZnVuYyBhcHBsaWNhdGlvbihfIGFwcGxpY2F0aW9uOiBVSUFwcGxpY2F0aW9uLCBjb250aW51ZSB1c2VyQWN0aXZpdHk6IE5TVXNlckFjdGl2aXR5LCByZXN0b3JhdGlvbkhhbmRsZXI6IEBlc2NhcGluZyAoW1VJVXNlckFjdGl2aXR5UmVzdG9yaW5nXT8pIC0+IFZvaWQpIC0+IEJvb2wge1xuICAgIC8vIEFkU3RhZ2UgVW5pdmVyc2FsIExpbmsg7LKY66asXG4gICAgaWYgbGV0IHVybCA9IHVzZXJBY3Rpdml0eS53ZWJwYWdlVVJMIHtcbiAgICAgIEFkU3RhZ2VNYW5hZ2VyLnNoYXJlZC5oYW5kbGVVbml2ZXJzYWxMaW5rKHVzZXJBY3Rpdml0eSlcbiAgICB9XG4gICAgXG4gICAgLy8gUkNUTGlua2luZ01hbmFnZXLroZwg7KCE64usIChSZWFjdCBOYXRpdmUgTGlua2luZyDsp4Dsm5ApXG4gICAgcmV0dXJuIFJDVExpbmtpbmdNYW5hZ2VyLmFwcGxpY2F0aW9uKGFwcGxpY2F0aW9uLCBjb250aW51ZTogdXNlckFjdGl2aXR5LCByZXN0b3JhdGlvbkhhbmRsZXI6IHJlc3RvcmF0aW9uSGFuZGxlcilcbiAgfWA7XG5cbiAgLy8g7YG0656Y7IqkIOuBnSAo66eI7KeA66eJIH0pIOyVnuyXkCDsgr3snoVcbiAgY29uc3QgY2xhc3NFbmRSZWdleCA9IC9cXG5cXHMqXFx9XFxzKiQvO1xuICBjb25zdCBtYXRjaCA9IGNvbnRlbnRzLm1hdGNoKGNsYXNzRW5kUmVnZXgpO1xuXG4gIGlmIChtYXRjaCkge1xuICAgIGNvbnRlbnRzID0gY29udGVudHMucmVwbGFjZShjbGFzc0VuZFJlZ2V4LCBuZXdNZXRob2QgKyBtYXRjaFswXSk7XG4gIH1cblxuICByZXR1cm4gY29udGVudHM7XG59XG5cbi8qKlxuICogT2JqZWN0aXZlLUMgQXBwRGVsZWdhdGUg7IiY7KCVICjroIjqsbDsi5wg7KeA7JuQKVxuICovXG5mdW5jdGlvbiBtb2RpZnlPYmpDQXBwRGVsZWdhdGUoY29udGVudHM6IHN0cmluZyk6IHN0cmluZyB7XG4gIC8vIOydtOuvuCBBZFN0YWdlIGltcG9ydOqwgCDsnojripTsp4Ag7ZmV7J24XG4gIGlmIChjb250ZW50cy5pbmNsdWRlcygnI2ltcG9ydCA8QWRhcHRlckFkU3RhZ2UvQWRhcHRlckFkU3RhZ2UuaD4nKSkge1xuICAgIGNvbnNvbGUubG9nKCdbQWRTdGFnZV0gQXBwRGVsZWdhdGUgYWxyZWFkeSBjb25maWd1cmVkLCBza2lwcGluZy4uLicpO1xuICAgIHJldHVybiBjb250ZW50cztcbiAgfVxuXG4gIC8vIDEuIGltcG9ydCDrrLgg7LaU6rCAXG4gIGNvbnN0IGltcG9ydFJlZ2V4ID0gLyNpbXBvcnRcXHMrWzxcIl1bXj5cIl0rWz5cIl0vZztcbiAgY29uc3QgbWF0Y2hlcyA9IFsuLi5jb250ZW50cy5tYXRjaEFsbChpbXBvcnRSZWdleCldO1xuXG4gIGlmIChtYXRjaGVzLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBsYXN0TWF0Y2ggPSBtYXRjaGVzW21hdGNoZXMubGVuZ3RoIC0gMV07XG4gICAgY29uc3QgaW5zZXJ0UG9zaXRpb24gPSBsYXN0TWF0Y2guaW5kZXghICsgbGFzdE1hdGNoWzBdLmxlbmd0aDtcbiAgICBjb250ZW50cyA9XG4gICAgICBjb250ZW50cy5zbGljZSgwLCBpbnNlcnRQb3NpdGlvbikgK1xuICAgICAgJ1xcbiNpbXBvcnQgPEFkYXB0ZXJBZFN0YWdlL0FkYXB0ZXJBZFN0YWdlLmg+JyArXG4gICAgICBjb250ZW50cy5zbGljZShpbnNlcnRQb3NpdGlvbik7XG4gIH1cblxuICAvLyAyLiBvcGVuVVJMIOuplOyEnOuTnOyXkCDrlKXrp4Htgawg7ZW465Ok65+sIOy2lOqwgFxuICBjb25zdCBvcGVuVVJMTWV0aG9kUmVnZXggPVxuICAgIC8tXFxzKlxcKEJPT0xcXClcXHMqYXBwbGljYXRpb246XFxzKlxcKFVJQXBwbGljYXRpb25cXHMqXFwqXFwpXFxzKlxcdytcXHMrb3BlblVSTDpcXHMqXFwoTlNVUkxcXHMqXFwqXFwpXFxzKihcXHcrKS87XG5cbiAgY29uc3Qgb3BlblVSTE1hdGNoID0gY29udGVudHMubWF0Y2gob3BlblVSTE1ldGhvZFJlZ2V4KTtcbiAgaWYgKG9wZW5VUkxNYXRjaCkge1xuICAgIGNvbnN0IHVybFZhck5hbWUgPSBvcGVuVVJMTWF0Y2hbMV07XG4gICAgY29uc3QgbWV0aG9kQm9keVN0YXJ0ID0gY29udGVudHMuaW5kZXhPZigneycsIG9wZW5VUkxNYXRjaC5pbmRleCEpICsgMTtcbiAgICBjb25zdCBoYW5kbGVyQ29kZSA9IGBcXG4gIC8vIEFkU3RhZ2Ug65Sl66eB7YGsIOyymOumrFxcbiAgW1tBZFN0YWdlTWFuYWdlciBzaGFyZWRdIGhhbmRsZURlZXBMaW5rOiR7dXJsVmFyTmFtZX1dO1xcbmA7XG4gICAgY29udGVudHMgPVxuICAgICAgY29udGVudHMuc2xpY2UoMCwgbWV0aG9kQm9keVN0YXJ0KSArXG4gICAgICBoYW5kbGVyQ29kZSArXG4gICAgICBjb250ZW50cy5zbGljZShtZXRob2RCb2R5U3RhcnQpO1xuICB9XG5cbiAgcmV0dXJuIGNvbnRlbnRzO1xufVxuXG5leHBvcnQgZGVmYXVsdCB3aXRoQWRTdGFnZUlPUztcbiJdfQ==
@@ -0,0 +1,33 @@
1
+ /**
2
+ * AdStage React Native SDK - Expo Config Plugin
3
+ *
4
+ * iOS AppDelegate에 딥링크 핸들링 코드를 자동으로 추가합니다.
5
+ *
6
+ * 사용법:
7
+ * app.json 또는 app.config.js에 추가:
8
+ * {
9
+ * "expo": {
10
+ * "plugins": ["@adstage/react-native-sdk"]
11
+ * }
12
+ * }
13
+ */
14
+
15
+ import { ConfigPlugin, createRunOncePlugin } from '@expo/config-plugins';
16
+ import { withAdStageIOS } from './withAdStageIOS';
17
+
18
+ const pkg = require('../../package.json');
19
+
20
+ /**
21
+ * AdStage SDK Expo Config Plugin
22
+ *
23
+ * iOS에서 딥링크(URL Scheme, Universal Link)를 자동으로 처리하도록
24
+ * AppDelegate를 수정합니다.
25
+ */
26
+ const withAdStage: ConfigPlugin = (config) => {
27
+ // iOS 설정 적용
28
+ config = withAdStageIOS(config);
29
+
30
+ return config;
31
+ };
32
+
33
+ export default createRunOncePlugin(withAdStage, pkg.name, pkg.version);
@@ -0,0 +1,237 @@
1
+ /**
2
+ * iOS AppDelegate 수정을 위한 Config Plugin
3
+ *
4
+ * AppDelegate.swift에 AdStage 딥링크 핸들링 코드를 추가합니다.
5
+ */
6
+
7
+ import { ConfigPlugin, withAppDelegate } from '@expo/config-plugins';
8
+
9
+ /**
10
+ * AppDelegate.swift에 추가할 import 문
11
+ */
12
+ const ADSTAGE_IMPORT = 'import AdapterAdStage';
13
+
14
+ /**
15
+ * application:openURL:options: 메서드에 추가할 딥링크 핸들링 코드
16
+ */
17
+ const DEEPLINK_HANDLER_CODE = `
18
+ // AdStage 딥링크 처리
19
+ AdStageManager.shared.handleDeepLink(url)`;
20
+
21
+ /**
22
+ * application:continueUserActivity: 메서드에 추가할 Universal Link 핸들링 코드
23
+ */
24
+ const UNIVERSAL_LINK_HANDLER_CODE = `
25
+ // AdStage Universal Link 처리
26
+ if let url = userActivity.webpageURL {
27
+ AdStageManager.shared.handleUniversalLink(userActivity)
28
+ }`;
29
+
30
+ /**
31
+ * iOS AppDelegate에 AdStage 딥링크 핸들링 코드를 추가하는 Config Plugin
32
+ */
33
+ export const withAdStageIOS: ConfigPlugin = (config) => {
34
+ return withAppDelegate(config, (cfg) => {
35
+ const appDelegate = cfg.modResults;
36
+
37
+ if (appDelegate.language === 'swift') {
38
+ appDelegate.contents = modifySwiftAppDelegate(appDelegate.contents);
39
+ } else if (appDelegate.language === 'objc' || appDelegate.language === 'objcpp') {
40
+ appDelegate.contents = modifyObjCAppDelegate(appDelegate.contents);
41
+ }
42
+
43
+ return cfg;
44
+ });
45
+ };
46
+
47
+ /**
48
+ * Swift AppDelegate 수정
49
+ */
50
+ function modifySwiftAppDelegate(contents: string): string {
51
+ // 이미 AdStage import가 있는지 확인
52
+ if (contents.includes(ADSTAGE_IMPORT)) {
53
+ console.log('[AdStage] AppDelegate already configured, skipping...');
54
+ return contents;
55
+ }
56
+
57
+ // 1. import 문 추가
58
+ contents = addSwiftImport(contents, ADSTAGE_IMPORT);
59
+
60
+ // 2. application:openURL:options: 메서드에 딥링크 핸들러 추가
61
+ contents = addDeepLinkHandler(contents);
62
+
63
+ // 3. application:continueUserActivity: 메서드에 Universal Link 핸들러 추가
64
+ contents = addUniversalLinkHandler(contents);
65
+
66
+ return contents;
67
+ }
68
+
69
+ /**
70
+ * Swift import 문 추가
71
+ */
72
+ function addSwiftImport(contents: string, importStatement: string): string {
73
+ // 마지막 import 문 뒤에 추가
74
+ const importRegex = /^import\s+\w+.*$/gm;
75
+ const matches = [...contents.matchAll(importRegex)];
76
+
77
+ if (matches.length > 0) {
78
+ const lastMatch = matches[matches.length - 1];
79
+ const insertPosition = lastMatch.index! + lastMatch[0].length;
80
+ contents =
81
+ contents.slice(0, insertPosition) +
82
+ '\n' +
83
+ importStatement +
84
+ contents.slice(insertPosition);
85
+ }
86
+
87
+ return contents;
88
+ }
89
+
90
+ /**
91
+ * application:openURL:options: 메서드에 딥링크 핸들러 추가
92
+ */
93
+ function addDeepLinkHandler(contents: string): string {
94
+ // application(_:open:options:) 메서드 찾기
95
+ const openURLMethodRegex =
96
+ /func\s+application\s*\(\s*_\s+app:\s*UIApplication\s*,\s*open\s+url:\s*URL\s*,\s*options[^)]*\)\s*->\s*Bool\s*\{/;
97
+
98
+ const match = contents.match(openURLMethodRegex);
99
+
100
+ if (match) {
101
+ // 기존 메서드에 코드 추가
102
+ const insertPosition = match.index! + match[0].length;
103
+ contents =
104
+ contents.slice(0, insertPosition) +
105
+ DEEPLINK_HANDLER_CODE +
106
+ contents.slice(insertPosition);
107
+ } else {
108
+ // 메서드가 없으면 AppDelegate 클래스에 추가
109
+ contents = addOpenURLMethod(contents);
110
+ }
111
+
112
+ return contents;
113
+ }
114
+
115
+ /**
116
+ * application:openURL:options: 메서드 전체 추가
117
+ */
118
+ function addOpenURLMethod(contents: string): string {
119
+ const newMethod = `
120
+
121
+ // MARK: - AdStage Deep Link Handling
122
+
123
+ func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
124
+ // AdStage 딥링크 처리
125
+ AdStageManager.shared.handleDeepLink(url)
126
+
127
+ // RCTLinkingManager로 전달 (React Native Linking 지원)
128
+ return RCTLinkingManager.application(app, open: url, options: options)
129
+ }`;
130
+
131
+ // 클래스 끝 (마지막 }) 앞에 삽입
132
+ const classEndRegex = /\n\s*\}\s*$/;
133
+ const match = contents.match(classEndRegex);
134
+
135
+ if (match) {
136
+ contents = contents.replace(classEndRegex, newMethod + match[0]);
137
+ }
138
+
139
+ return contents;
140
+ }
141
+
142
+ /**
143
+ * application:continueUserActivity: 메서드에 Universal Link 핸들러 추가
144
+ */
145
+ function addUniversalLinkHandler(contents: string): string {
146
+ // application(_:continue:restorationHandler:) 메서드 찾기
147
+ const continueUserActivityRegex =
148
+ /func\s+application\s*\([^)]*continue\s+userActivity:\s*NSUserActivity[^)]*\)\s*->\s*Bool\s*\{/;
149
+
150
+ const match = contents.match(continueUserActivityRegex);
151
+
152
+ if (match) {
153
+ // 기존 메서드에 코드 추가
154
+ const insertPosition = match.index! + match[0].length;
155
+ contents =
156
+ contents.slice(0, insertPosition) +
157
+ UNIVERSAL_LINK_HANDLER_CODE +
158
+ contents.slice(insertPosition);
159
+ } else {
160
+ // 메서드가 없으면 AppDelegate 클래스에 추가
161
+ contents = addContinueUserActivityMethod(contents);
162
+ }
163
+
164
+ return contents;
165
+ }
166
+
167
+ /**
168
+ * application:continueUserActivity: 메서드 전체 추가
169
+ */
170
+ function addContinueUserActivityMethod(contents: string): string {
171
+ const newMethod = `
172
+
173
+ // MARK: - AdStage Universal Link Handling
174
+
175
+ func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
176
+ // AdStage Universal Link 처리
177
+ if let url = userActivity.webpageURL {
178
+ AdStageManager.shared.handleUniversalLink(userActivity)
179
+ }
180
+
181
+ // RCTLinkingManager로 전달 (React Native Linking 지원)
182
+ return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
183
+ }`;
184
+
185
+ // 클래스 끝 (마지막 }) 앞에 삽입
186
+ const classEndRegex = /\n\s*\}\s*$/;
187
+ const match = contents.match(classEndRegex);
188
+
189
+ if (match) {
190
+ contents = contents.replace(classEndRegex, newMethod + match[0]);
191
+ }
192
+
193
+ return contents;
194
+ }
195
+
196
+ /**
197
+ * Objective-C AppDelegate 수정 (레거시 지원)
198
+ */
199
+ function modifyObjCAppDelegate(contents: string): string {
200
+ // 이미 AdStage import가 있는지 확인
201
+ if (contents.includes('#import <AdapterAdStage/AdapterAdStage.h>')) {
202
+ console.log('[AdStage] AppDelegate already configured, skipping...');
203
+ return contents;
204
+ }
205
+
206
+ // 1. import 문 추가
207
+ const importRegex = /#import\s+[<"][^>"]+[>"]/g;
208
+ const matches = [...contents.matchAll(importRegex)];
209
+
210
+ if (matches.length > 0) {
211
+ const lastMatch = matches[matches.length - 1];
212
+ const insertPosition = lastMatch.index! + lastMatch[0].length;
213
+ contents =
214
+ contents.slice(0, insertPosition) +
215
+ '\n#import <AdapterAdStage/AdapterAdStage.h>' +
216
+ contents.slice(insertPosition);
217
+ }
218
+
219
+ // 2. openURL 메서드에 딥링크 핸들러 추가
220
+ const openURLMethodRegex =
221
+ /-\s*\(BOOL\)\s*application:\s*\(UIApplication\s*\*\)\s*\w+\s+openURL:\s*\(NSURL\s*\*\)\s*(\w+)/;
222
+
223
+ const openURLMatch = contents.match(openURLMethodRegex);
224
+ if (openURLMatch) {
225
+ const urlVarName = openURLMatch[1];
226
+ const methodBodyStart = contents.indexOf('{', openURLMatch.index!) + 1;
227
+ const handlerCode = `\n // AdStage 딥링크 처리\n [[AdStageManager shared] handleDeepLink:${urlVarName}];\n`;
228
+ contents =
229
+ contents.slice(0, methodBodyStart) +
230
+ handlerCode +
231
+ contents.slice(methodBodyStart);
232
+ }
233
+
234
+ return contents;
235
+ }
236
+
237
+ export default withAdStageIOS;
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "declaration": true,
7
+ "strict": true,
8
+ "noImplicitAny": true,
9
+ "strictNullChecks": true,
10
+ "noImplicitThis": true,
11
+ "alwaysStrict": true,
12
+ "noUnusedLocals": false,
13
+ "noUnusedParameters": false,
14
+ "noImplicitReturns": true,
15
+ "noFallthroughCasesInSwitch": false,
16
+ "inlineSourceMap": true,
17
+ "inlineSources": true,
18
+ "experimentalDecorators": true,
19
+ "strictPropertyInitialization": false,
20
+ "outDir": "./build",
21
+ "rootDir": "./src",
22
+ "skipLibCheck": true,
23
+ "esModuleInterop": true,
24
+ "resolveJsonModule": true
25
+ },
26
+ "include": ["src/**/*"],
27
+ "exclude": ["node_modules", "build"]
28
+ }