@bacons/apple-targets 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -36,7 +36,7 @@ You can change the root directory from `./targets` to something else with `root:
36
36
 
37
37
  ## Using React Native in Targets
38
38
 
39
- I'm not sure, that's not the purpose of this plugin. I built this so I could easily build iOS widgets and other minor targets with SwiftUI. I imagine it would be straightforward to use React Native in share, notification, iMessage, Safari, and photo editing extensions, you can build that on top of this plugin if you want.
39
+ I'm not sure, that's not the purpose of this plugin. I built this so I could easily build iOS widgets and other minor targets with SwiftUI. I imagine it would be straightforward to use React Native in share, notification, iMessage, Safari, and photo editing extensions, you can build that on top of this plugin if you want. Look at the App Clip example for a starting point.
40
40
 
41
41
  ## `expo-target.config.json`
42
42
 
@@ -69,7 +69,11 @@ This file can have the following properties:
69
69
  },
70
70
 
71
71
  // The iOS version fot the target.
72
- "deploymentTarget": "13.4"
72
+ "deploymentTarget": "13.4",
73
+
74
+ // Optional bundle identifier for the target. Will default to a sanitized version of the root project bundle id + target name.
75
+ // If the specified bundle identifier is prefixed with a dot (.), the bundle identifier will be appended to the main app's bundle identifier.
76
+ "bundleIdentifier": ".mywidget"
73
77
  }
74
78
  ```
75
79
 
@@ -242,24 +246,26 @@ module.exports = {
242
246
 
243
247
  Ideally, this would be generated automatically based on a fully qualified Xcode project, but for now it's a manual process. The currently supported types are based on static analysis of the most commonly used targets in the iOS App Store. I haven't tested all of these and they may not work.
244
248
 
245
- | Type | Description |
246
- | -------------------- | ---------------------------------- |
247
- | action | Share Action |
248
- | widget | Widget / Live Activity |
249
- | watch | Watch App (with companion iOS App) |
250
- | clip | App Clip |
251
- | safari | Safari Extension |
252
- | share | Share Extension |
253
- | notification-content | Notification Content Extension |
254
- | notification-service | Notification Service Extension |
255
- | intent | Siri Intent Extension |
256
- | intent-ui | Siri Intent UI Extension |
257
- | spotlight | Spotlight Index Extension |
258
- | bg-download | Background Download Extension |
259
- | quicklook-thumbnail | Quick Look Thumbnail Extension |
260
- | location-push | Location Push Service Extension |
261
- | credentials-provider | Credentials Provider Extension |
262
- | account-auth | Account Authentication Extension |
249
+ | Type | Description |
250
+ | ----------------------- | ---------------------------------- |
251
+ | action | Share Action |
252
+ | app-intent | App Intent Extension |
253
+ | widget | Widget / Live Activity |
254
+ | watch | Watch App (with companion iOS App) |
255
+ | clip | App Clip |
256
+ | safari | Safari Extension |
257
+ | share | Share Extension |
258
+ | notification-content | Notification Content Extension |
259
+ | notification-service | Notification Service Extension |
260
+ | intent | Siri Intent Extension |
261
+ | intent-ui | Siri Intent UI Extension |
262
+ | spotlight | Spotlight Index Extension |
263
+ | bg-download | Background Download Extension |
264
+ | quicklook-thumbnail | Quick Look Thumbnail Extension |
265
+ | location-push | Location Push Service Extension |
266
+ | credentials-provider | Credentials Provider Extension |
267
+ | account-auth | Account Authentication Extension |
268
+ | device-activity-monitor | Device Activity Monitor Extension |
263
269
 
264
270
  <!-- | imessage | iMessage Extension | -->
265
271
 
package/build/config.d.ts CHANGED
@@ -68,6 +68,11 @@ export type Config = {
68
68
  type: ExtensionType;
69
69
  /** Name of the target. Will default to a sanitized version of the directory name. */
70
70
  name?: string;
71
+ /**
72
+ * Bundle identifier for the target. Will default to a sanitized version of the root project + name.
73
+ * If the specified bundle identifier is prefixed with a dot (.), the bundle identifier will be appended to the main app's bundle identifier.
74
+ **/
75
+ bundleIdentifier?: string;
71
76
  /**
72
77
  * A local file path or URL to an image asset.
73
78
  * @example "./assets/icon.png"
package/build/target.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { PBXNativeTarget, XcodeProject } from "@bacons/xcode";
2
- export type ExtensionType = "widget" | "notification-content" | "notification-service" | "share" | "intent" | "bg-download" | "intent-ui" | "spotlight" | "matter" | "quicklook-thumbnail" | "imessage" | "clip" | "watch" | "location-push" | "credentials-provider" | "account-auth" | "action" | "safari";
2
+ export type ExtensionType = "widget" | "notification-content" | "notification-service" | "share" | "intent" | "bg-download" | "intent-ui" | "spotlight" | "matter" | "quicklook-thumbnail" | "imessage" | "clip" | "watch" | "location-push" | "credentials-provider" | "account-auth" | "action" | "safari" | "app-intent" | "device-activity-monitor";
3
3
  export declare const KNOWN_EXTENSION_POINT_IDENTIFIERS: Record<string, ExtensionType>;
4
4
  export declare function getTargetInfoPlistForType(type: ExtensionType): string;
5
- export declare function productTypeForType(type: ExtensionType): "com.apple.product-type.application.on-demand-install-capable" | "com.apple.product-type.application" | "com.apple.product-type.app-extension";
5
+ export declare function productTypeForType(type: ExtensionType): "com.apple.product-type.application.on-demand-install-capable" | "com.apple.product-type.application" | "com.apple.product-type.extensionkit-extension" | "com.apple.product-type.app-extension";
6
6
  export declare function needsEmbeddedSwift(type: ExtensionType): boolean;
7
7
  export declare function getFrameworksForType(type: ExtensionType): string[];
8
8
  export declare function isNativeTargetOfType(target: PBXNativeTarget, type: ExtensionType): boolean;
package/build/target.js CHANGED
@@ -22,6 +22,7 @@ exports.KNOWN_EXTENSION_POINT_IDENTIFIERS = {
22
22
  "com.apple.authentication-services-credential-provider-ui": "credentials-provider",
23
23
  "com.apple.authentication-services-account-authentication-modification-ui": "account-auth",
24
24
  "com.apple.services": "action",
25
+ "com.apple.appintents-extension": "app-intent",
25
26
  // "com.apple.intents-service": "intents",
26
27
  };
27
28
  // TODO: Maybe we can replace `NSExtensionPrincipalClass` with the `@main` annotation that newer extensions use?
@@ -232,6 +233,8 @@ function productTypeForType(type) {
232
233
  return "com.apple.product-type.application.on-demand-install-capable";
233
234
  case "watch":
234
235
  return "com.apple.product-type.application";
236
+ case "app-intent":
237
+ return "com.apple.product-type.extensionkit-extension";
235
238
  default:
236
239
  return "com.apple.product-type.app-extension";
237
240
  }
@@ -272,6 +275,12 @@ function getFrameworksForType(type) {
272
275
  else if (type === "notification-content") {
273
276
  return ["UserNotifications", "UserNotificationsUI"];
274
277
  }
278
+ else if (type === "app-intent") {
279
+ return ["AppIntents"];
280
+ }
281
+ else if (type === "device-activity-monitor") {
282
+ return ["DeviceActivity"];
283
+ }
275
284
  else if (type === "action") {
276
285
  return [
277
286
  // "UniformTypeIdentifiers"
@@ -22,7 +22,7 @@ function kebabToCamelCase(str) {
22
22
  }
23
23
  const withWidget = (config, props) => {
24
24
  // TODO: Magically based on the top-level folders in the `ios-widgets/` folder
25
- var _a, _b, _c, _d, _e, _f, _g;
25
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
26
26
  if (props.icon && !/https?:\/\//.test(props.icon)) {
27
27
  props.icon = path_1.default.join(props.directory, props.icon);
28
28
  }
@@ -111,22 +111,25 @@ const withWidget = (config, props) => {
111
111
  },
112
112
  ]);
113
113
  const targetName = (_c = props.name) !== null && _c !== void 0 ? _c : widget;
114
- const bundleId = config.ios.bundleIdentifier + "." + widget;
114
+ const mainAppBundleId = config.ios.bundleIdentifier;
115
+ const bundleId = ((_d = props.bundleIdentifier) === null || _d === void 0 ? void 0 : _d.startsWith("."))
116
+ ? mainAppBundleId + props.bundleIdentifier
117
+ : (_e = props.bundleIdentifier) !== null && _e !== void 0 ? _e : `${mainAppBundleId}.${targetName}`;
115
118
  (0, withXcodeChanges_1.withXcodeChanges)(config, {
116
119
  configPath: props.configPath,
117
120
  name: targetName,
118
121
  cwd: "../" +
119
122
  path_1.default.relative(config._internal.projectRoot, path_1.default.resolve(props.directory)),
120
- deploymentTarget: (_d = props.deploymentTarget) !== null && _d !== void 0 ? _d : "16.4",
123
+ deploymentTarget: (_f = props.deploymentTarget) !== null && _f !== void 0 ? _f : "16.4",
121
124
  bundleId,
122
125
  icon: props.icon,
123
- hasAccentColor: !!((_e = props.colors) === null || _e === void 0 ? void 0 : _e.$accent),
126
+ hasAccentColor: !!((_g = props.colors) === null || _g === void 0 ? void 0 : _g.$accent),
124
127
  // @ts-expect-error: who cares
125
- currentProjectVersion: ((_f = config.ios) === null || _f === void 0 ? void 0 : _f.buildNumber) || 1,
128
+ currentProjectVersion: ((_h = config.ios) === null || _h === void 0 ? void 0 : _h.buildNumber) || 1,
126
129
  frameworks: (0, target_1.getFrameworksForType)(props.type).concat(props.frameworks || []),
127
130
  type: props.type,
128
131
  teamId: props.appleTeamId,
129
- exportJs: (_g = props.exportJs) !== null && _g !== void 0 ? _g :
132
+ exportJs: (_j = props.exportJs) !== null && _j !== void 0 ? _j :
130
133
  // Assume App Clips are used for React Native.
131
134
  props.type === "clip",
132
135
  });
@@ -121,6 +121,68 @@ extensionType, { name, cwd, bundleId, deploymentTarget, currentProjectVersion, i
121
121
  });
122
122
  return configurationList;
123
123
  }
124
+ function createAppIntentConfigurationList(project, { name, cwd, bundleId }) {
125
+ const commonBuildSettings = {
126
+ // @ts-expect-error
127
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS: "YES",
128
+ CLANG_ANALYZER_NONNULL: "YES",
129
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION: "YES_AGGRESSIVE",
130
+ CLANG_CXX_LANGUAGE_STANDARD: "gnu++20",
131
+ CLANG_ENABLE_OBJC_WEAK: "YES",
132
+ CLANG_WARN_DOCUMENTATION_COMMENTS: "YES",
133
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER: "YES",
134
+ CLANG_WARN_UNGUARDED_AVAILABILITY: "YES_AGGRESSIVE",
135
+ CODE_SIGN_STYLE: "Automatic",
136
+ CURRENT_PROJECT_VERSION: "1",
137
+ DEBUG_INFORMATION_FORMAT: "dwarf",
138
+ ENABLE_USER_SCRIPT_SANDBOXING: "YES",
139
+ GCC_C_LANGUAGE_STANDARD: "gnu17",
140
+ GENERATE_INFOPLIST_FILE: "YES",
141
+ INFOPLIST_FILE: cwd + "/Info.plist",
142
+ INFOPLIST_KEY_CFBundleDisplayName: name,
143
+ INFOPLIST_KEY_NSHumanReadableCopyright: "",
144
+ IPHONEOS_DEPLOYMENT_TARGET: "17.0",
145
+ LD_RUNPATH_SEARCH_PATHS: [
146
+ "$(inherited)",
147
+ "@executable_path/Frameworks",
148
+ "@executable_path/../../Frameworks",
149
+ ],
150
+ LOCALIZATION_PREFERS_STRING_CATALOGS: "YES",
151
+ MARKETING_VERSION: "1.0",
152
+ MTL_FAST_MATH: "YES",
153
+ PRODUCT_BUNDLE_IDENTIFIER: bundleId,
154
+ PRODUCT_NAME: "$(TARGET_NAME)",
155
+ SKIP_INSTALL: "YES",
156
+ SWIFT_EMIT_LOC_STRINGS: "YES",
157
+ SWIFT_VERSION: "5.0",
158
+ TARGETED_DEVICE_FAMILY: "1,2",
159
+ };
160
+ const debugBuildConfig = xcode_1.XCBuildConfiguration.create(project, {
161
+ name: "Debug",
162
+ buildSettings: {
163
+ ...commonBuildSettings,
164
+ GCC_PREPROCESSOR_DEFINITIONS: ["DEBUG=1", "$(inherited)"],
165
+ MTL_ENABLE_DEBUG_INFO: "INCLUDE_SOURCE",
166
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS: "DEBUG $(inherited)",
167
+ SWIFT_OPTIMIZATION_LEVEL: "-Onone",
168
+ },
169
+ });
170
+ const releaseBuildConfig = xcode_1.XCBuildConfiguration.create(project, {
171
+ name: "Release",
172
+ buildSettings: {
173
+ ...commonBuildSettings,
174
+ COPY_PHASE_STRIP: "NO",
175
+ DEBUG_INFORMATION_FORMAT: "dwarf-with-dsym",
176
+ ...{ SWIFT_COMPILATION_MODE: "wholemodule" },
177
+ },
178
+ });
179
+ const configurationList = xcode_1.XCConfigurationList.create(project, {
180
+ buildConfigurations: [debugBuildConfig, releaseBuildConfig],
181
+ defaultConfigurationIsVisible: 0,
182
+ defaultConfigurationName: "Release",
183
+ });
184
+ return configurationList;
185
+ }
124
186
  function createShareConfigurationList(project, { name, cwd, bundleId, deploymentTarget, currentProjectVersion, }) {
125
187
  const common = {
126
188
  CLANG_ANALYZER_NONNULL: "YES",
@@ -563,6 +625,9 @@ function createConfigurationListForType(project, props) {
563
625
  else if (props.type === "watch") {
564
626
  return createWatchAppConfigurationList(project, props);
565
627
  }
628
+ else if (props.type === "app-intent") {
629
+ return createAppIntentConfigurationList(project, props);
630
+ }
566
631
  else {
567
632
  // TODO: More
568
633
  return createNotificationContentConfigurationList(project, props);
@@ -722,11 +787,16 @@ async function applyXcodeChanges(config, project, props) {
722
787
  syncMarketingVersions();
723
788
  return project;
724
789
  }
790
+ const productType = (0, target_1.productTypeForType)(props.type);
791
+ const isExtension = productType === "com.apple.product-type.app-extension";
792
+ const isExtensionKit = productType === "com.apple.product-type.extensionkit-extension";
725
793
  const appExtensionBuildFile = xcode_1.PBXBuildFile.create(project, {
726
794
  fileRef: xcode_1.PBXFileReference.create(project, {
727
- explicitFileType: "wrapper.app-extension",
795
+ explicitFileType: isExtensionKit
796
+ ? "wrapper.extensionkit-extension"
797
+ : "wrapper.app-extension",
728
798
  includeInIndex: 0,
729
- path: productName + ".appex",
799
+ path: productName + (isExtension ? ".appex" : ".app"),
730
800
  sourceTree: "BUILT_PRODUCTS_DIR",
731
801
  }),
732
802
  settings: {
@@ -742,7 +812,7 @@ async function applyXcodeChanges(config, project, props) {
742
812
  productName,
743
813
  // @ts-expect-error
744
814
  productReference: appExtensionBuildFile.props.fileRef /* alphaExtension.appex */,
745
- productType: (0, target_1.productTypeForType)(props.type),
815
+ productType: productType,
746
816
  });
747
817
  configureTargetWithKnownSettings(extensionTarget);
748
818
  configureTargetWithEntitlements(extensionTarget);
@@ -763,11 +833,18 @@ async function applyXcodeChanges(config, project, props) {
763
833
  });
764
834
  // Add the target dependency to the main app, should be only one.
765
835
  mainAppTarget.props.dependencies.push(targetDependency);
766
- const WELL_KNOWN_COPY_EXTENSIONS_NAME = props.type === "clip"
767
- ? "Embed App Clips"
768
- : props.type === "watch"
769
- ? "Embed Watch Content"
770
- : "Embed Foundation Extensions";
836
+ const WELL_KNOWN_COPY_EXTENSIONS_NAME = (() => {
837
+ switch (props.type) {
838
+ case "clip":
839
+ return "Embed App Clips";
840
+ case "watch":
841
+ return "Embed Watch Content";
842
+ case "app-intent":
843
+ return "Embed ExtensionKit Extensions";
844
+ default:
845
+ return "Embed Foundation Extensions";
846
+ }
847
+ })();
771
848
  // Could exist from a Share Extension
772
849
  const copyFilesBuildPhase = mainAppTarget.props.buildPhases.find((phase) => {
773
850
  if (xcode_1.PBXCopyFilesBuildPhase.is(phase)) {
@@ -791,7 +868,7 @@ async function applyXcodeChanges(config, project, props) {
791
868
  "Info.plist",
792
869
  // Exclude the config path
793
870
  path_1.default.relative(magicCwd, props.configPath),
794
- ],
871
+ ].sort(),
795
872
  });
796
873
  const assetsDir = path_1.default.join(magicCwd, "assets");
797
874
  // TODO: Maybe just limit this to Safari extensions?
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@bacons/apple-targets",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Generate Apple Targets with Expo Prebuild",
5
5
  "main": "build/index.js",
6
6
  "files": [
7
7
  "app.plugin.js",
8
- "build"
8
+ "build",
9
+ "prebuild-blank.tgz"
9
10
  ],
10
11
  "scripts": {
11
12
  "build": "expo-module build",
@@ -30,7 +31,7 @@
30
31
  "dependencies": {
31
32
  "@react-native/normalize-colors": "^0.76.1",
32
33
  "glob": "^10.2.6",
33
- "@bacons/xcode": "^1.0.0-alpha.21",
34
+ "@bacons/xcode": "^1.0.0-alpha.22",
34
35
  "fs-extra": "^11.2.0"
35
36
  },
36
37
  "devDependencies": {
Binary file