@bacons/apple-targets 0.1.8 → 0.1.10
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 +40 -53
- package/app.plugin.d.ts +1 -0
- package/build/config-plugin.js +8 -4
- package/build/target.d.ts +1 -0
- package/build/target.js +30 -5
- package/build/withWidget.js +80 -20
- package/build/withXcodeChanges.js +2 -11
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -11,48 +11,47 @@ An experimental Expo Config Plugin that generates native Apple Targets like Widg
|
|
|
11
11
|
|
|
12
12
|
> This plugin requires at least CocoaPods 1.16.2, Xcode 16, and Expo SDK +52.
|
|
13
13
|
|
|
14
|
-
Run
|
|
14
|
+
1. Run `npx create-target` in your Expo project to generate an Apple target.
|
|
15
|
+
2. Select a target to generate, I recommend starting with a `widget` (e.g. `npx create-target widget`). This will generate the required widget files in the root `/targets` directory, install `@bacons/apple-targets`, and add the Expo Config Plugin to your project.
|
|
16
|
+
3. If you already know your Apple Team ID, then set it under the `ios.appleTeamId` property in your `app.json`. You can find this in Xcode under the Signing & Capabilities tab and set it later if needed.
|
|
17
|
+
4. Run `npx expo prebuild -p ios --clean` to generate the Xcode project.
|
|
18
|
+
5. You can now open your project in Xcode (`xed ios`) and develop the widget inside the `expo:targets/<target>` folder.
|
|
19
|
+
6. When you're ready to build, select the target in Xcode and build it.
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
npx create-target
|
|
18
|
-
```
|
|
21
|
+
### How it works
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
The root `/targets` folder is magic, each sub-directory should have a `expo-target.config.js` file that defines the target settings. When you run `npx expo prebuild --clean`, the plugin will generate the Xcode project and link the target files to the project. The plugin will also generate an `Info.plist` file if one doesn't exist.
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
The Config Plugin will link the subfolder (e.g. `targets/widget`) to Xcode, and all files inside of it will be part of the target. This means you can develop the target inside the `expo:targets` folder and the changes will be saved outside of the generated `ios` directory.
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
The root `Info.plist` file in each target directory is not managed and can be freely modified.
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
Any files in a top-level `target/{name}/assets` directory will be linked as resources of the target. This rule was added to support Safari Extensions.
|
|
27
30
|
|
|
28
|
-
###
|
|
31
|
+
### Entitlements
|
|
29
32
|
|
|
30
|
-
-
|
|
31
|
-
- If you don't have an `Info.plist`, it'll be generated on `npx expo prebuild`. This may be changed in the future so if you have an `Info.plist` it'll be used, otherwise, it'll be generated.
|
|
32
|
-
- Any files in a top-level `target/*/assets` directory will be linked as resources of the target. This was added to support Safari Extensions.
|
|
33
|
-
- A single top-level `*.entitlements` file will be linked as the entitlements of the target. This is not currently used in EAS Capability signing, but may be in the future.
|
|
34
|
-
- All Swift files will be linked as build sources of the target. There is currently no support for storyboard or `.xib` files because I can't be bothered.
|
|
35
|
-
- All `*.xcassets` files will be linked as resources, and accessible in the targets. If you add files outside of Xcode, you'll need to re-run `npx expo prebuild` to link them.
|
|
36
|
-
- In Expo SDK +52, set the `ios.appleTeamId`, for SDK 51 and below, set the `appleTeamId` prop in the Config Plugin in `app.config.js`:
|
|
33
|
+
If the `expo-target.config` file defines an `entitlements: {}` object, then a `generated.entitlements` will be added. Avoid using this file directly, and instead update the `expo-target.config.js` file. If the `entitlements` object is not defined, you can manually add any top-level `*.entitlements` file to the target directory—re-running `npx expo prebuild` will link this file to the target as the entitlements file. Only one top-level `*.entitlements` file is supported per target.
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
35
|
+
Some targets have special entitlements behavior:
|
|
36
|
+
|
|
37
|
+
- App Clips (`clip`) automatically set the required `com.apple.developer.parent-application-identifiers` to `$(AppIdentifierPrefix)${config.ios.bundleIdentifier}`
|
|
38
|
+
- Targets that can utilize App Groups will automatically mirror the `ios.entitlements['com.apple.security.application-groups']` array from the `app.json` if it's defined. This can be overwritten by specifying an `entitlements['com.apple.security.application-groups']` array in the `expo-target.config.js` file.
|
|
39
|
+
|
|
40
|
+
### Development
|
|
41
|
+
|
|
42
|
+
Any changes you make outside of the `expo:targets` directory in Xcode are subject to being overwritten by the next `npx expo prebuild --clean`. Check to see if the settings you want to toggle are available in the Info.plist or the `expo-target.config.js` file.
|
|
43
|
+
If you modify the `expo-target.config.js` or your root `app.json`, you will need to re-run `npx expo prebuild --clean` to sync the changes.
|
|
44
|
+
|
|
45
|
+
You can use the custom Prebuild template `--template node_modules/@bacons/apple-targets/prebuild-blank.tgz` to create a build without React Native, this can make development a bit faster since there's less to compile.
|
|
46
|
+
|
|
47
|
+
## Target config
|
|
50
48
|
|
|
51
|
-
|
|
49
|
+
The target config can be a `expo-target.config.js`, or `expo-target.config.json` file.
|
|
52
50
|
|
|
53
51
|
This file can have the following properties:
|
|
54
52
|
|
|
55
53
|
```js
|
|
54
|
+
/** @type {import('@bacons/apple-targets/app.plugin').Config} */
|
|
56
55
|
module.exports = {
|
|
57
56
|
type: "widget",
|
|
58
57
|
|
|
@@ -67,7 +66,7 @@ module.exports = {
|
|
|
67
66
|
icon: "../assets/icon.png",
|
|
68
67
|
// Can also be a URL
|
|
69
68
|
frameworks: [
|
|
70
|
-
// Frameworks without the extension, these will be added to the target.
|
|
69
|
+
// Frameworks with or without the `.frameworks` extension, these will be added to the target.
|
|
71
70
|
"SwiftUI",
|
|
72
71
|
],
|
|
73
72
|
entitlements: {
|
|
@@ -84,26 +83,16 @@ module.exports = {
|
|
|
84
83
|
// Optional bundle identifier for the target. Will default to a sanitized version of the root project bundle id + target name.
|
|
85
84
|
// If the specified bundle identifier is prefixed with a dot (.), the bundle identifier will be appended to the main app's bundle identifier.
|
|
86
85
|
bundleIdentifier: ".mywidget",
|
|
87
|
-
};
|
|
88
|
-
```
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
```js
|
|
93
|
-
/** @type {import('@bacons/apple-targets').Config} */
|
|
94
|
-
module.exports = {
|
|
95
|
-
type: "watch",
|
|
96
|
-
colors: {
|
|
97
|
-
$accent: "steelblue",
|
|
98
|
-
},
|
|
99
|
-
deploymentTarget: "9.4",
|
|
87
|
+
// Should the release build export the JS bundle and embed. Intended for App Clips and Share Extensions where you may want to use React Native.
|
|
88
|
+
exportJs: false,
|
|
100
89
|
};
|
|
101
90
|
```
|
|
102
91
|
|
|
103
|
-
|
|
92
|
+
You can also return a function that accepts the Expo Config and returns a target function for syncing entitlements and other values:
|
|
104
93
|
|
|
105
94
|
```js
|
|
106
|
-
/** @type {import('@bacons/apple-targets').ConfigFunction} */
|
|
95
|
+
/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
|
|
107
96
|
module.exports = (config) => ({
|
|
108
97
|
type: "widget",
|
|
109
98
|
colors: {
|
|
@@ -192,7 +181,7 @@ The name of the target must match the name of the target directory.
|
|
|
192
181
|
> I wrote a blog about this one and used it in production. Learn more: [Expo x Apple Widgets](https://evanbacon.dev/blog/apple-home-screen-widgets).
|
|
193
182
|
|
|
194
183
|
```js
|
|
195
|
-
/** @type {import('@bacons/apple-targets').Config} */
|
|
184
|
+
/** @type {import('@bacons/apple-targets/app.plugin').Config} */
|
|
196
185
|
module.exports = {
|
|
197
186
|
type: "widget",
|
|
198
187
|
icon: "../../icons/widget.png",
|
|
@@ -224,7 +213,7 @@ module.exports = {
|
|
|
224
213
|
These show up in the share sheet. The icon should be transparent as it will be masked by the system.
|
|
225
214
|
|
|
226
215
|
```js
|
|
227
|
-
/** @type {import('@bacons/apple-targets').Config} */
|
|
216
|
+
/** @type {import('@bacons/apple-targets/app.plugin').Config} */
|
|
228
217
|
module.exports = {
|
|
229
218
|
type: "action",
|
|
230
219
|
name: "Inspect Element",
|
|
@@ -269,7 +258,7 @@ Ensure `NSExtensionJavaScriptPreprocessingFile: "index"` in the Info.plist.
|
|
|
269
258
|
Populate the Spotlight search results with your app's content.
|
|
270
259
|
|
|
271
260
|
```js
|
|
272
|
-
/** @type {import('@bacons/apple-targets').Config} */
|
|
261
|
+
/** @type {import('@bacons/apple-targets/app.plugin').Config} */
|
|
273
262
|
module.exports = {
|
|
274
263
|
type: "spotlight",
|
|
275
264
|
};
|
|
@@ -308,9 +297,7 @@ The codesigning is theoretically handled entirely by [EAS Build](https://docs.ex
|
|
|
308
297
|
|
|
309
298
|
You can also manually sign all sub-targets if you want, I'll light a candle for you.
|
|
310
299
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
This plugin makes use of my proprietary Xcode parsing library, [`@bacons/xcode`](https://github.com/evanbacon/xcode). It's mostly typed, very untested, and possibly full of bugs––however, it's still 10x nicer than the alternative.
|
|
300
|
+
> I haven't gotten App Clip codesigning to be fully automated yet. PRs welcome.
|
|
314
301
|
|
|
315
302
|
## Building Widgets
|
|
316
303
|
|
|
@@ -349,7 +336,7 @@ First, define your main App Group entitlement in your `app.json`:
|
|
|
349
336
|
Second, define the same App Group in your target's `expo-target.config.js`:
|
|
350
337
|
|
|
351
338
|
```js
|
|
352
|
-
/** @type {import('@bacons/apple-targets').ConfigFunction} */
|
|
339
|
+
/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
|
|
353
340
|
module.exports = (config) => ({
|
|
354
341
|
type: "widget",
|
|
355
342
|
entitlements: {
|
|
@@ -402,6 +389,6 @@ let defaults = UserDefaults(suiteName:
|
|
|
402
389
|
let index = defaults?.string(forKey: "myKey")
|
|
403
390
|
```
|
|
404
391
|
|
|
405
|
-
##
|
|
392
|
+
## Xcode parsing
|
|
406
393
|
|
|
407
|
-
|
|
394
|
+
This plugin makes use of my proprietary Xcode parsing library, [`@bacons/xcode`](https://github.com/evanbacon/xcode). It's mostly typed, very untested, and possibly full of bugs––however, it's still 10x nicer than the alternative.
|
package/app.plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./build/config-plugin";
|
package/build/config-plugin.js
CHANGED
|
@@ -6,16 +6,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.withTargetsDir = void 0;
|
|
7
7
|
const glob_1 = require("glob");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
10
|
const withPodTargetExtension_1 = require("./withPodTargetExtension");
|
|
10
11
|
const withWidget_1 = __importDefault(require("./withWidget"));
|
|
11
12
|
const withXcparse_1 = require("./withXcparse");
|
|
13
|
+
let hasWarned = false;
|
|
12
14
|
const withTargetsDir = (config, _props) => {
|
|
13
15
|
var _a;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new Error(`You must specify an \`appleTeamId\` in your app config to use the \`withTargetsDir\` plugin.`);
|
|
17
|
-
}
|
|
16
|
+
let { appleTeamId = (_a = config === null || config === void 0 ? void 0 : config.ios) === null || _a === void 0 ? void 0 : _a.appleTeamId } = _props || {};
|
|
17
|
+
const { root = "./targets", match = "*" } = _props || {};
|
|
18
18
|
const projectRoot = config._internal.projectRoot;
|
|
19
|
+
if (!appleTeamId && !hasWarned) {
|
|
20
|
+
hasWarned = true;
|
|
21
|
+
console.warn((0, chalk_1.default) `{yellow [bacons/apple-targets]} Expo config is missing required {cyan ios.appleTeamId} property. Find this in Xcode and add to the Expo Config to correct. iOS builds may fail until this is corrected.`);
|
|
22
|
+
}
|
|
19
23
|
const targets = (0, glob_1.sync)(`${root}/${match}/expo-target.config.@(json|js)`, {
|
|
20
24
|
// const targets = globSync(`./targets/action/expo-target.config.@(json|js)`, {
|
|
21
25
|
cwd: projectRoot,
|
package/build/target.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PBXNativeTarget, XcodeProject } from "@bacons/xcode";
|
|
2
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
|
+
export declare const SHOULD_USE_APP_GROUPS_BY_DEFAULT: Record<ExtensionType, boolean>;
|
|
4
5
|
export declare function getTargetInfoPlistForType(type: ExtensionType): string;
|
|
5
6
|
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
7
|
export declare function needsEmbeddedSwift(type: ExtensionType): boolean;
|
package/build/target.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getAuxiliaryTargets = exports.getMainAppTarget = exports.isNativeTargetOfType = exports.getFrameworksForType = exports.needsEmbeddedSwift = exports.productTypeForType = exports.getTargetInfoPlistForType = exports.KNOWN_EXTENSION_POINT_IDENTIFIERS = void 0;
|
|
6
|
+
exports.getAuxiliaryTargets = exports.getMainAppTarget = exports.isNativeTargetOfType = exports.getFrameworksForType = exports.needsEmbeddedSwift = exports.productTypeForType = exports.getTargetInfoPlistForType = exports.SHOULD_USE_APP_GROUPS_BY_DEFAULT = exports.KNOWN_EXTENSION_POINT_IDENTIFIERS = void 0;
|
|
7
7
|
const plist_1 = __importDefault(require("@expo/plist"));
|
|
8
8
|
exports.KNOWN_EXTENSION_POINT_IDENTIFIERS = {
|
|
9
9
|
"com.apple.message-payload-provider": "imessage",
|
|
@@ -26,12 +26,37 @@ exports.KNOWN_EXTENSION_POINT_IDENTIFIERS = {
|
|
|
26
26
|
"com.apple.deviceactivity.monitor-extension": "device-activity-monitor",
|
|
27
27
|
// "com.apple.intents-service": "intents",
|
|
28
28
|
};
|
|
29
|
+
// An exhaustive list of extension types that should sync app groups from the main target by default when
|
|
30
|
+
// no app groups are specified.
|
|
31
|
+
exports.SHOULD_USE_APP_GROUPS_BY_DEFAULT = {
|
|
32
|
+
share: true,
|
|
33
|
+
"bg-download": true,
|
|
34
|
+
clip: true,
|
|
35
|
+
widget: true,
|
|
36
|
+
"account-auth": false,
|
|
37
|
+
"credentials-provider": false,
|
|
38
|
+
"device-activity-monitor": false,
|
|
39
|
+
"app-intent": false,
|
|
40
|
+
"intent-ui": false,
|
|
41
|
+
"location-push": false,
|
|
42
|
+
"notification-content": false,
|
|
43
|
+
"notification-service": false,
|
|
44
|
+
"quicklook-thumbnail": false,
|
|
45
|
+
action: false,
|
|
46
|
+
imessage: false,
|
|
47
|
+
intent: false,
|
|
48
|
+
matter: false,
|
|
49
|
+
safari: false,
|
|
50
|
+
spotlight: false,
|
|
51
|
+
watch: false,
|
|
52
|
+
};
|
|
29
53
|
// TODO: Maybe we can replace `NSExtensionPrincipalClass` with the `@main` annotation that newer extensions use?
|
|
30
54
|
function getTargetInfoPlistForType(type) {
|
|
55
|
+
// TODO: Use exhaustive switch to ensure external contributors don't forget to add this.
|
|
31
56
|
if (type === "watch") {
|
|
32
57
|
return plist_1.default.build({});
|
|
33
58
|
}
|
|
34
|
-
if (type === "action") {
|
|
59
|
+
else if (type === "action") {
|
|
35
60
|
return plist_1.default.build({
|
|
36
61
|
NSExtension: {
|
|
37
62
|
NSExtensionAttributes: {
|
|
@@ -93,7 +118,7 @@ function getTargetInfoPlistForType(type) {
|
|
|
93
118
|
},
|
|
94
119
|
});
|
|
95
120
|
}
|
|
96
|
-
if (type === "account-auth") {
|
|
121
|
+
else if (type === "account-auth") {
|
|
97
122
|
return plist_1.default.build({
|
|
98
123
|
NSExtension: {
|
|
99
124
|
NSExtensionPointIdentifier,
|
|
@@ -105,7 +130,7 @@ function getTargetInfoPlistForType(type) {
|
|
|
105
130
|
},
|
|
106
131
|
});
|
|
107
132
|
}
|
|
108
|
-
if (type === "credentials-provider") {
|
|
133
|
+
else if (type === "credentials-provider") {
|
|
109
134
|
return plist_1.default.build({
|
|
110
135
|
NSExtension: {
|
|
111
136
|
NSExtensionPointIdentifier,
|
|
@@ -113,7 +138,7 @@ function getTargetInfoPlistForType(type) {
|
|
|
113
138
|
},
|
|
114
139
|
});
|
|
115
140
|
}
|
|
116
|
-
if (type === "notification-service") {
|
|
141
|
+
else if (type === "notification-service") {
|
|
117
142
|
return plist_1.default.build({
|
|
118
143
|
NSExtension: {
|
|
119
144
|
NSExtensionAttributes: {
|
package/build/withWidget.js
CHANGED
|
@@ -8,21 +8,51 @@ const plist_1 = __importDefault(require("@expo/plist"));
|
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const glob_1 = require("glob");
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
12
|
const withIosColorset_1 = require("./colorset/withIosColorset");
|
|
12
13
|
const withImageAsset_1 = require("./icon/withImageAsset");
|
|
13
14
|
const withIosIcon_1 = require("./icon/withIosIcon");
|
|
14
15
|
const target_1 = require("./target");
|
|
15
16
|
const withEasCredentials_1 = require("./withEasCredentials");
|
|
16
17
|
const withXcodeChanges_1 = require("./withXcodeChanges");
|
|
17
|
-
|
|
18
|
+
function memoize(fn) {
|
|
19
|
+
const cache = new Map();
|
|
20
|
+
return ((...args) => {
|
|
21
|
+
const key = JSON.stringify(args);
|
|
22
|
+
if (cache.has(key)) {
|
|
23
|
+
return cache.get(key);
|
|
24
|
+
}
|
|
25
|
+
const result = fn(...args);
|
|
26
|
+
cache.set(key, result);
|
|
27
|
+
return result;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const warnOnce = memoize(console.warn);
|
|
31
|
+
const logOnce = memoize(console.log);
|
|
32
|
+
function createLogQueue() {
|
|
33
|
+
const queue = [];
|
|
34
|
+
const flush = () => {
|
|
35
|
+
queue.forEach((fn) => fn());
|
|
36
|
+
queue.length = 0;
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
flush,
|
|
40
|
+
add: (fn) => {
|
|
41
|
+
queue.push(fn);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Queue up logs so they only run when prebuild is actually running and not during standard config reads.
|
|
46
|
+
const prebuildLogQueue = createLogQueue();
|
|
18
47
|
function kebabToCamelCase(str) {
|
|
19
48
|
return str.replace(/-([a-z])/g, function (g) {
|
|
20
49
|
return g[1].toUpperCase();
|
|
21
50
|
});
|
|
22
51
|
}
|
|
23
52
|
const withWidget = (config, props) => {
|
|
24
|
-
// TODO: Magically based on the top-level folders in the `ios-widgets/` folder
|
|
25
53
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
54
|
+
prebuildLogQueue.add(() => warnOnce((0, chalk_1.default) `\nUsing experimental Config Plugin {bold @bacons/apple-targets} that is subject to breaking changes.`));
|
|
55
|
+
// TODO: Magically based on the top-level folders in the `ios-widgets/` folder
|
|
26
56
|
if (props.icon && !/https?:\/\//.test(props.icon)) {
|
|
27
57
|
props.icon = path_1.default.join(props.directory, props.icon);
|
|
28
58
|
}
|
|
@@ -37,21 +67,49 @@ const withWidget = (config, props) => {
|
|
|
37
67
|
cwd: widgetFolderAbsolutePath,
|
|
38
68
|
});
|
|
39
69
|
if (entitlementsFiles.length > 1) {
|
|
40
|
-
throw new Error(`Found
|
|
70
|
+
throw new Error(`[bacons/apple-targets][${props.type}] Found more than one '*.entitlements' file in ${widgetFolderAbsolutePath}`);
|
|
41
71
|
}
|
|
42
72
|
let entitlementsJson = props.entitlements;
|
|
43
|
-
// Apply default entitlements that must be present for a target to work.
|
|
44
|
-
function applyDefaultEntitlements(entitlements) {
|
|
45
|
-
if (props.type === "clip") {
|
|
46
|
-
entitlements["com.apple.developer.parent-application-identifiers"] = [
|
|
47
|
-
`$(AppIdentifierPrefix)${config.ios.bundleIdentifier}`,
|
|
48
|
-
];
|
|
49
|
-
// NOTE: This doesn't seem to be required anymore (Oct 12 2024):
|
|
50
|
-
// entitlements["com.apple.developer.on-demand-install-capable"] = true;
|
|
51
|
-
}
|
|
52
|
-
return entitlements;
|
|
53
|
-
}
|
|
54
73
|
if (entitlementsJson) {
|
|
74
|
+
// Apply default entitlements that must be present for a target to work.
|
|
75
|
+
const applyDefaultEntitlements = (entitlements) => {
|
|
76
|
+
var _a, _b, _c, _d;
|
|
77
|
+
if (props.type === "clip") {
|
|
78
|
+
entitlements["com.apple.developer.parent-application-identifiers"] = [
|
|
79
|
+
`$(AppIdentifierPrefix)${config.ios.bundleIdentifier}`,
|
|
80
|
+
];
|
|
81
|
+
// NOTE: This doesn't seem to be required anymore (Oct 12 2024):
|
|
82
|
+
// entitlements["com.apple.developer.on-demand-install-capable"] = true;
|
|
83
|
+
}
|
|
84
|
+
const APP_GROUP_KEY = "com.apple.security.application-groups";
|
|
85
|
+
const hasDefinedAppGroupsManually = APP_GROUP_KEY in entitlements;
|
|
86
|
+
if (
|
|
87
|
+
// If the user hasn't manually defined the app groups array.
|
|
88
|
+
!hasDefinedAppGroupsManually &&
|
|
89
|
+
// And the target is part of a predefined list of types that benefit from app groups that match the main app...
|
|
90
|
+
target_1.SHOULD_USE_APP_GROUPS_BY_DEFAULT[props.type]) {
|
|
91
|
+
const mainAppGroups = (_b = (_a = config.ios) === null || _a === void 0 ? void 0 : _a.entitlements) === null || _b === void 0 ? void 0 : _b[APP_GROUP_KEY];
|
|
92
|
+
if (Array.isArray(mainAppGroups) && mainAppGroups.length > 0) {
|
|
93
|
+
// Then set the target app groups to match the main app.
|
|
94
|
+
entitlements[APP_GROUP_KEY] = mainAppGroups;
|
|
95
|
+
prebuildLogQueue.add(() => {
|
|
96
|
+
logOnce((0, chalk_1.default) `[${widget}] Syncing app groups with main app. {dim Define entitlements[${JSON.stringify(APP_GROUP_KEY)}] in the {bold expo-target.config} file to override.}`);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
config_plugins_1.WarningAggregator.addWarningIOS(`ios.entitlements["${APP_GROUP_KEY}"]`, `[${props.type}] Apple target may require the App Groups entitlement but none were found in the Expo config.\nExample:\n${JSON.stringify({
|
|
101
|
+
ios: {
|
|
102
|
+
entitlements: {
|
|
103
|
+
[APP_GROUP_KEY]: [
|
|
104
|
+
`group.${(_d = (_c = config.ios) === null || _c === void 0 ? void 0 : _c.bundleIdentifier) !== null && _d !== void 0 ? _d : `com.example.${config.slug}`}`,
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
}, null, 2)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return entitlements;
|
|
112
|
+
};
|
|
55
113
|
entitlementsJson = applyDefaultEntitlements(entitlementsJson);
|
|
56
114
|
}
|
|
57
115
|
// If the user defined entitlements, then overwrite any existing entitlements file
|
|
@@ -60,11 +118,15 @@ const withWidget = (config, props) => {
|
|
|
60
118
|
"ios",
|
|
61
119
|
async (config) => {
|
|
62
120
|
var _a;
|
|
121
|
+
const GENERATED_ENTITLEMENTS_FILE_NAME = "generated.entitlements";
|
|
63
122
|
const entitlementsFilePath = (_a = entitlementsFiles[0]) !== null && _a !== void 0 ? _a :
|
|
64
123
|
// Use the name `generated` to help indicate that this file should be in sync with the config
|
|
65
|
-
path_1.default.join(widgetFolderAbsolutePath,
|
|
124
|
+
path_1.default.join(widgetFolderAbsolutePath, GENERATED_ENTITLEMENTS_FILE_NAME);
|
|
66
125
|
if (entitlementsFiles[0]) {
|
|
67
|
-
|
|
126
|
+
const relativeName = path_1.default.relative(widgetFolderAbsolutePath, entitlementsFiles[0]);
|
|
127
|
+
if (relativeName !== GENERATED_ENTITLEMENTS_FILE_NAME) {
|
|
128
|
+
console.log(`[${widget}] Replacing ${path_1.default.relative(widgetFolderAbsolutePath, entitlementsFiles[0])} with entitlements JSON from config`);
|
|
129
|
+
}
|
|
68
130
|
}
|
|
69
131
|
fs_1.default.writeFileSync(entitlementsFilePath, plist_1.default.build(entitlementsJson));
|
|
70
132
|
return config;
|
|
@@ -80,10 +142,7 @@ const withWidget = (config, props) => {
|
|
|
80
142
|
(0, config_plugins_1.withDangerousMod)(config, [
|
|
81
143
|
"ios",
|
|
82
144
|
async (config) => {
|
|
83
|
-
|
|
84
|
-
hasWarned = true;
|
|
85
|
-
console.warn("You're using an experimental Config Plugin that is subject to breaking changes and has no E2E tests.");
|
|
86
|
-
}
|
|
145
|
+
prebuildLogQueue.flush();
|
|
87
146
|
fs_1.default.mkdirSync(widgetFolderAbsolutePath, { recursive: true });
|
|
88
147
|
const files = [
|
|
89
148
|
["Info.plist", (0, target_1.getTargetInfoPlistForType)(props.type)],
|
|
@@ -179,6 +238,7 @@ const withConfigColors = (config, props) => {
|
|
|
179
238
|
});
|
|
180
239
|
});
|
|
181
240
|
}
|
|
241
|
+
// TODO: Add clean-up maybe? This would possibly restrict the ability to create native colors outside of the Expo target config.
|
|
182
242
|
return config;
|
|
183
243
|
};
|
|
184
244
|
exports.default = withWidget;
|
|
@@ -634,7 +634,7 @@ function createConfigurationListForType(project, props) {
|
|
|
634
634
|
}
|
|
635
635
|
}
|
|
636
636
|
async function applyXcodeChanges(config, project, props) {
|
|
637
|
-
var _a
|
|
637
|
+
var _a;
|
|
638
638
|
const mainAppTarget = (0, target_1.getMainAppTarget)(project);
|
|
639
639
|
// Special setting for share extensions.
|
|
640
640
|
if ((0, target_1.needsEmbeddedSwift)(props.type)) {
|
|
@@ -653,20 +653,11 @@ async function applyXcodeChanges(config, project, props) {
|
|
|
653
653
|
console.log(`Target "${targetToUpdate.props.productName}" already exists, updating instead of creating a new one`);
|
|
654
654
|
}
|
|
655
655
|
const magicCwd = path_1.default.join(config._internal.projectRoot, "ios", props.cwd);
|
|
656
|
-
let developmentTeamId = (_b = props.teamId) !== null && _b !== void 0 ? _b : mainAppTarget.getDefaultBuildSetting("DEVELOPMENT_TEAM");
|
|
657
|
-
if (!developmentTeamId) {
|
|
658
|
-
console.error("Couldn't find DEVELOPMENT_TEAM in Xcode project and none were provided in the Expo config. Using placeholder value.");
|
|
659
|
-
// Using a placeholder gives users a chance to see the value in Xcode.
|
|
660
|
-
developmentTeamId = "XXXXXXXXXX";
|
|
661
|
-
// throw new Error(
|
|
662
|
-
// "Couldn't find DEVELOPMENT_TEAM in Xcode project and none were provided in the Expo config."
|
|
663
|
-
// );
|
|
664
|
-
}
|
|
665
656
|
function applyDevelopmentTeamIdToTargets() {
|
|
666
657
|
var _a, _b;
|
|
667
658
|
var _c, _d, _e;
|
|
668
659
|
// Set to the provided value or any value.
|
|
669
|
-
const devTeamId =
|
|
660
|
+
const devTeamId = props.teamId ||
|
|
670
661
|
project.rootObject.props.targets
|
|
671
662
|
.map((target) => target.getDefaultBuildSetting("DEVELOPMENT_TEAM"))
|
|
672
663
|
.find(Boolean);
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacons/apple-targets",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Generate Apple Targets with Expo Prebuild",
|
|
5
5
|
"main": "build/ExtensionStorage.js",
|
|
6
6
|
"types": "build/ExtensionStorage.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"app.plugin.js",
|
|
9
|
+
"app.plugin.d.ts",
|
|
9
10
|
"build",
|
|
10
11
|
"ios",
|
|
11
12
|
"expo-module.config.json",
|