vxrn 2.0.0-beta.137.1 → 2.0.0-beta.139.1

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.
@@ -75,21 +75,14 @@ import ReactAppDependencyProvider
75
75
  class SceneDelegate: UIResponder, UIWindowSceneDelegate {
76
76
  var window: UIWindow?
77
77
 
78
- var reactNativeDelegate: ReactNativeDelegate?
79
- var reactNativeFactory: RCTReactNativeFactory?
80
-
81
78
  func scene(
82
79
  _ scene: UIScene,
83
80
  willConnectTo session: UISceneSession,
84
81
  options connectionOptions: UIScene.ConnectionOptions
85
82
  ) {
86
83
  guard let windowScene = scene as? UIWindowScene else { return }
87
- let delegate = ReactNativeDelegate()
88
- let factory = RCTReactNativeFactory(delegate: delegate)
89
- delegate.dependencyProvider = RCTAppDependencyProvider()
90
-
91
- reactNativeDelegate = delegate
92
- reactNativeFactory = factory
84
+ guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
85
+ let factory = appDelegate.reactNativeFactory else { return }
93
86
 
94
87
  let window = UIWindow(windowScene: windowScene)
95
88
  self.window = window
@@ -165,7 +158,7 @@ function patchIosInfoPlistSceneManifest(rendered) {
165
158
  </dict>
166
159
  ${anchor}`);
167
160
  }
168
- function patchIosAppDelegateSceneLifecycle(rendered, appName) {
161
+ function patchIosAppDelegateSceneLifecycle(rendered, appName, backgroundTasks) {
169
162
  const anchor = `@main
170
163
  class AppDelegate: UIResponder, UIApplicationDelegate {
171
164
  var window: UIWindow?
@@ -200,11 +193,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
200
193
  }
201
194
  return rendered.replace(anchor, `@main
202
195
  class AppDelegate: UIResponder, UIApplicationDelegate {
196
+ var reactNativeDelegate: ReactNativeDelegate?
197
+ var reactNativeFactory: RCTReactNativeFactory?
198
+
203
199
  func application(
204
200
  _ application: UIApplication,
205
201
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
206
202
  ) -> Bool {
207
- true
203
+ let delegate = ReactNativeDelegate()
204
+ let factory = RCTReactNativeFactory(delegate: delegate)
205
+ delegate.dependencyProvider = RCTAppDependencyProvider()
206
+ reactNativeDelegate = delegate
207
+ reactNativeFactory = factory
208
+ ${backgroundTasks ? ` OneBackgroundTasksRegisterHost(factory, launchOptions)
209
+ ` : ""} return true
208
210
  }
209
211
  }`);
210
212
  }
@@ -212,9 +214,9 @@ const ONE_BRIDGING_HEADER_NAME = "One-Bridging-Header.h";
212
214
  function renderOneBridgingHeader(app) {
213
215
  const imports = [nativeProjectPatches.ONE_LAUNCH_SCREEN.bridgingHeaderImport];
214
216
  if (app.updates !== void 0) imports.push(updatesHost.bridgingHeaderImport);
215
- return `// generated by one prebuild: exposes One's c entry points (the launch
216
- // screen hold, the updates launcher) to AppDelegate without importing the
217
- // One module.
217
+ if (app.ios?.backgroundTasks !== void 0) imports.push("#import \"OneBackgroundTasksBridge.h\"");
218
+ return `// generated by one prebuild: exposes One's c entry points to AppDelegate
219
+ // without importing the One module.
218
220
  ${imports.join("\n")}
219
221
  `;
220
222
  }
@@ -1026,30 +1028,43 @@ class OneWidgetsBridge: RCTEventEmitter {
1026
1028
  async function generateAppIcons(args) {
1027
1029
  const { root, dest, platform, app } = args;
1028
1030
  if (!app.icon) return;
1031
+ if (platform === "ios") {
1032
+ const assetsDir = path.join(dest, app.name, "Images.xcassets");
1033
+ const template = JSON.parse(FSExtra.readFileSync(path.join(assetsDir, "AppIcon.appiconset", "Contents.json"), "utf8"));
1034
+ const icons = [["AppIcon", app.icon], ...Object.entries(app.ios?.alternateIcons ?? {})];
1035
+ for (const [name, icon] of icons) {
1036
+ const source2 = path.resolve(root, icon.source);
1037
+ if (!FSExtra.existsSync(source2)) {
1038
+ throw new Error(`[vxrn] native.app icon source does not exist: ${source2}`);
1039
+ }
1040
+ const metadata2 = await sharp(source2).metadata();
1041
+ if (metadata2.width !== metadata2.height || metadata2.width === void 0 || metadata2.width < 1024) {
1042
+ throw new Error(`[vxrn] native.app icon source must be a square image at least 1024px wide: ${source2}`);
1043
+ }
1044
+ const iconDir = path.join(assetsDir, `${name}.appiconset`);
1045
+ FSExtra.mkdirSync(iconDir, { recursive: true });
1046
+ const contents = structuredClone(template);
1047
+ for (const image of contents.images) {
1048
+ const points = Number.parseFloat(image.size.split("x")[0]);
1049
+ const scale = Number.parseInt(image.scale, 10);
1050
+ const pixels = points * scale;
1051
+ const filename = image.idiom === "ios-marketing" ? "icon-1024.png" : `icon-${points}@${image.scale}.png`;
1052
+ image.filename = filename;
1053
+ await sharp(source2).rotate().resize(pixels, pixels, { fit: "cover" }).flatten({ background: icon.backgroundColor }).png().toFile(path.join(iconDir, filename));
1054
+ }
1055
+ FSExtra.writeFileSync(path.join(iconDir, "Contents.json"), `${JSON.stringify(contents, null, 2)}
1056
+ `);
1057
+ }
1058
+ return;
1059
+ }
1029
1060
  const source = path.resolve(root, app.icon.source);
1030
1061
  if (!FSExtra.existsSync(source)) {
1031
1062
  throw new Error(`[vxrn] native.app.icon source does not exist: ${source}`);
1032
1063
  }
1033
1064
  const metadata = await sharp(source).metadata();
1034
- if (metadata.width === void 0 || metadata.height === void 0 || metadata.width !== metadata.height || metadata.width < 1024) {
1065
+ if (metadata.width !== metadata.height || metadata.width === void 0 || metadata.width < 1024) {
1035
1066
  throw new Error("[vxrn] native.app.icon source must be a square image at least 1024px wide");
1036
1067
  }
1037
- if (platform === "ios") {
1038
- const iconDir = path.join(dest, app.name, "Images.xcassets", "AppIcon.appiconset");
1039
- const contentsPath = path.join(iconDir, "Contents.json");
1040
- const contents = JSON.parse(FSExtra.readFileSync(contentsPath, "utf8"));
1041
- for (const image of contents.images) {
1042
- const points = Number.parseFloat(image.size.split("x")[0]);
1043
- const scale = Number.parseInt(image.scale, 10);
1044
- const pixels = points * scale;
1045
- const filename = image.idiom === "ios-marketing" ? "icon-1024.png" : `icon-${points}@${image.scale}.png`;
1046
- image.filename = filename;
1047
- await sharp(source).rotate().resize(pixels, pixels, { fit: "cover" }).flatten({ background: app.icon.backgroundColor }).png().toFile(path.join(iconDir, filename));
1048
- }
1049
- FSExtra.writeFileSync(contentsPath, `${JSON.stringify(contents, null, 2)}
1050
- `);
1051
- return;
1052
- }
1053
1068
  for (const [density, multiplier] of Object.entries(ANDROID_DENSITIES)) {
1054
1069
  const pixels = 48 * multiplier;
1055
1070
  const iconDir = path.join(dest, "app", "src", "main", "res", `mipmap-${density}`);
@@ -1396,6 +1411,9 @@ ${schemes.map((scheme) => ` <string>${scheme}</string>`).join("\n")}
1396
1411
  <key>LSSupportsOpeningDocumentsInPlace</key>
1397
1412
  <true/>`);
1398
1413
  }
1414
+ if (app.photoLibrary?.readWrite !== void 0) {
1415
+ stamps.push(" <key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>\n <true/>");
1416
+ }
1399
1417
  if (app.notifications !== void 0) {
1400
1418
  stamps.push(` <key>${notificationsHost.enabledInfoPlistKey}</key>
1401
1419
  <true/>`);
@@ -1407,7 +1425,23 @@ ${schemes.map((scheme) => ` <string>${scheme}</string>`).join("\n")}
1407
1425
  if (app.ios?.widgets) {
1408
1426
  stamps.push(" <key>NSSupportsLiveActivities</key>\n <true/>");
1409
1427
  }
1410
- const backgroundModes = [...app.pictureInPicture || app.audio?.background ? ["audio"] : [], ...app.location?.background ? ["location"] : []];
1428
+ const refreshTasks = app.ios?.backgroundTasks?.refresh ?? [];
1429
+ const processingTasks = app.ios?.backgroundTasks?.processing ?? [];
1430
+ if (refreshTasks.length || processingTasks.length) {
1431
+ const plistArray = (key, identifiers) => ` <key>${key}</key>
1432
+ <array>
1433
+ ${identifiers.map((identifier) => ` <string>${escapeXml(identifier)}</string>`).join("\n")}
1434
+ </array>`;
1435
+ stamps.push(plistArray("BGTaskSchedulerPermittedIdentifiers", [...refreshTasks, ...processingTasks]));
1436
+ stamps.push(plistArray("OneBackgroundRefreshTaskIdentifiers", refreshTasks));
1437
+ stamps.push(plistArray("OneBackgroundProcessingTaskIdentifiers", processingTasks));
1438
+ }
1439
+ const backgroundModes = [
1440
+ ...app.pictureInPicture || app.audio?.background ? ["audio"] : [],
1441
+ ...app.location?.background ? ["location"] : [],
1442
+ ...refreshTasks.length ? ["fetch"] : [],
1443
+ ...processingTasks.length ? ["processing"] : []
1444
+ ];
1411
1445
  if (backgroundModes.length) {
1412
1446
  stamps.push(` <key>UIBackgroundModes</key>
1413
1447
  <array>
@@ -1619,6 +1653,15 @@ ${schemes.map((scheme) => ` <data android:scheme="${scheme}" />`).joi
1619
1653
  rendered = rendered.replace(/CURRENT_PROJECT_VERSION = [^;]+;/g, `CURRENT_PROJECT_VERSION = ${app.ios.buildNumber};`);
1620
1654
  }
1621
1655
  }
1656
+ if (platform === "ios" && relativePath.endsWith(".xcodeproj/project.pbxproj") && app.ios?.alternateIcons !== void 0) {
1657
+ const anchor = "ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;";
1658
+ if (!rendered.includes(anchor)) {
1659
+ throw new Error("[vxrn] prebuild template lost its primary app icon build setting");
1660
+ }
1661
+ const names = Object.keys(app.ios.alternateIcons).sort().join(" ");
1662
+ rendered = rendered.replaceAll(anchor, `${anchor}
1663
+ ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "${names}";`);
1664
+ }
1622
1665
  if (platform === "ios" && relativePath.endsWith("/Info.plist")) {
1623
1666
  const usage = /* @__PURE__ */ new Map();
1624
1667
  if (app.imagePicker?.camera !== void 0) {
@@ -1678,7 +1721,7 @@ ${schemes.map((scheme) => ` <data android:scheme="${scheme}" />`).joi
1678
1721
  rendered = patchIosInfoPlistSceneManifest(rendered);
1679
1722
  }
1680
1723
  if (platform === "ios" && relativePath.endsWith("/AppDelegate.swift")) {
1681
- rendered = patchIosAppDelegateSceneLifecycle(rendered, appName);
1724
+ rendered = patchIosAppDelegateSceneLifecycle(rendered, appName, !!app.ios?.backgroundTasks);
1682
1725
  rendered = nativeProjectPatches.holdLaunchScreenOverRootView(rendered);
1683
1726
  if (app.updates !== void 0) {
1684
1727
  rendered = nativeProjectPatches.pointReleaseBundleURLAtOneUpdates(rendered);