Skip to content

Commit 6667f2e

Browse files
committed
Adds moengage targets to the eas build appExtensions. This notifies EAS to generate provisioning profiles for these targets
1 parent 45a8a97 commit 6667f2e

File tree

4 files changed

+248
-4
lines changed

4 files changed

+248
-4
lines changed

sdk/expo/src/apple/index.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { withMoEngageInfoPlist } from "./withInfoPlist";
44
import { withMoEngageEntitlements } from "./withEntitlements";
55
import { withMoEngageXcodeProject } from "./withXcodeProject";
66
import { withMoEngageDangerousMod } from "./withDangerousMod";
7-
import * as constants from './constants';
7+
import * as constants from "./constants";
8+
import { withEASManagedProject } from "./withEasManagedProject";
89

910
/**
1011
* The main iOS plugin that applies all modifiers to set up MoEngage iOS integration
@@ -17,22 +18,32 @@ import * as constants from './constants';
1718
* 2. withMoEngageEntitlements - Sets up app groups and other entitlements
1819
* 3. withMoEngageXcodeProject - Configures the Xcode project with extension targets
1920
* 4. withMoEngageDangerousMod - Performs file operations and updates Podfile
21+
* 5. withEASManagedProject - Adds any targets to config.extra.eas.build.experimental.ios.appExtensions, to assist with automatic provisioning profile generation via EAS build.
2022
*
2123
* @param config - The Expo config object
2224
* @param props - The MoEngage plugin properties
2325
* @returns The fully configured Expo config
2426
*/
25-
export const withMoEngageIos: ConfigPlugin<MoEngagePluginProps> = (config, props) => {
27+
export const withMoEngageIos: ConfigPlugin<MoEngagePluginProps> = (
28+
config,
29+
props
30+
) => {
2631
config = withMoEngageInfoPlist(config, props);
2732
config = withMoEngageEntitlements(config, props);
2833
config = withMoEngageXcodeProject(config, props);
2934
config = withMoEngageDangerousMod(config, props);
35+
config = withEASManagedProject(config, props);
3036
return config;
3137
};
3238

3339
// Export all constants and individual modifiers for advanced usage
3440
export { constants };
35-
export { withMoEngageInfoPlist, withMoEngageEntitlements, withMoEngageXcodeProject, withMoEngageDangerousMod };
41+
export {
42+
withMoEngageInfoPlist,
43+
withMoEngageEntitlements,
44+
withMoEngageXcodeProject,
45+
withMoEngageDangerousMod,
46+
};
3647

3748
// Default export
3849
export default withMoEngageIos;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { ExpoConfig } from "@expo/config-types";
2+
import { EasManagedExtra } from "../types";
3+
4+
/**
5+
* Taps into the EAS build process, exposing what targets are being built
6+
* so EAS build an automatically generate/maintain the appropriate provisioning profiles for the app
7+
* More info here: https://docs.expo.dev/build-reference/app-extensions/
8+
*/
9+
export default function injectAppExtensionIntoEasManagedExtra(
10+
config: ExpoConfig,
11+
injectedValue: {
12+
targetName: string;
13+
bundleIdentifier: string;
14+
entitlements: Record<string, string[]>;
15+
}
16+
): ExpoConfig["extra"] {
17+
const easExtra = (config.extra ?? {}) as EasManagedExtra;
18+
easExtra.eas ??= {};
19+
easExtra.eas.build ??= {};
20+
easExtra.eas.build.experimental ??= {};
21+
easExtra.eas.build.experimental.ios ??= {};
22+
easExtra.eas.build.experimental.ios.appExtensions = [];
23+
24+
config.extra = easExtra;
25+
26+
return {
27+
...easExtra,
28+
eas: {
29+
...easExtra?.eas,
30+
build: {
31+
...easExtra?.eas?.build,
32+
experimental: {
33+
...easExtra.eas?.build?.experimental,
34+
ios: {
35+
...easExtra.eas?.build?.experimental?.ios,
36+
appExtensions: [
37+
...(easExtra.eas?.build?.experimental?.ios?.appExtensions ?? []),
38+
injectedValue,
39+
],
40+
},
41+
},
42+
},
43+
},
44+
};
45+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import {
2+
ConfigPlugin,
3+
ExportedConfigWithProps,
4+
withDangerousMod,
5+
} from "@expo/config-plugins";
6+
import { AppExtension, MoEngagePluginProps } from "../types";
7+
import * as fs from "fs";
8+
import * as path from "path";
9+
import { ExpoConfig } from "@expo/config-types";
10+
import { EasManagedExtra } from "../types";
11+
import {
12+
MOENGAGE_IOS_LIVE_ACTIVITY_TARGET,
13+
MOENGAGE_IOS_PUSH_TEMPLATE_TARGET,
14+
MOENGAGE_IOS_RICH_PUSH_TARGET,
15+
} from "./constants";
16+
const plist = require("plist");
17+
18+
const isTargetAlreadyPresent = (extra: EasManagedExtra, target: string) => {
19+
const extension = extra.eas?.build?.experimental?.ios?.appExtensions?.find(
20+
(extension: { targetName?: string }) => extension.targetName === target
21+
);
22+
if (extension) {
23+
console.log(
24+
`Target '${target}' already present in eas appExtensions. Skipping appExtension autogeneration....`
25+
);
26+
}
27+
return Boolean(extension);
28+
};
29+
30+
/**
31+
* Taps into the EAS build process, exposing what targets are being built
32+
* so EAS build an automatically generate/maintain the appropriate provisioning profiles for the app
33+
* More info here: https://docs.expo.dev/build-reference/app-extensions/
34+
*/
35+
export default function injectAppExtensionIntoEasManagedExtra(
36+
config: ExpoConfig,
37+
injectedValue: AppExtension
38+
): EasManagedExtra {
39+
assertTargetNotPresent(
40+
config.extra as EasManagedExtra,
41+
injectedValue.bundleIdentifier
42+
);
43+
44+
const easExtra = (config.extra ?? {}) as EasManagedExtra;
45+
easExtra.eas ??= {};
46+
easExtra.eas.build ??= {};
47+
easExtra.eas.build.experimental ??= {};
48+
easExtra.eas.build.experimental.ios ??= {};
49+
easExtra.eas.build.experimental.ios.appExtensions = [];
50+
51+
config.extra = easExtra;
52+
53+
return {
54+
...easExtra,
55+
eas: {
56+
...easExtra?.eas,
57+
build: {
58+
...easExtra?.eas?.build,
59+
experimental: {
60+
...easExtra.eas?.build?.experimental,
61+
ios: {
62+
...easExtra.eas?.build?.experimental?.ios,
63+
appExtensions: [
64+
...(easExtra.eas?.build?.experimental?.ios?.appExtensions ?? []),
65+
injectedValue,
66+
],
67+
},
68+
},
69+
},
70+
},
71+
};
72+
}
73+
74+
const getAppGroupName = (
75+
exportedConfig: ExportedConfigWithProps<unknown>,
76+
filePath: string
77+
) => {
78+
try {
79+
const configFilePath = path.join(
80+
exportedConfig.modRequest.projectRoot,
81+
filePath
82+
);
83+
if (fs.existsSync(configFilePath)) {
84+
const configPlist = plist.parse(
85+
fs.readFileSync(configFilePath, "utf8")
86+
) as { [key: string]: any };
87+
return configPlist["AppGroupName"] as string;
88+
} else {
89+
const message = `MoEngage configuration does not exist`;
90+
console.error(message);
91+
throw new Error(message);
92+
}
93+
} catch (e) {
94+
const message = `Could not import MoEngage configuration: ${e}`;
95+
console.error(message);
96+
throw new Error(message);
97+
}
98+
};
99+
100+
export const withEASManagedProject: ConfigPlugin<MoEngagePluginProps> = (
101+
config,
102+
props
103+
) => {
104+
return withDangerousMod(config, [
105+
"ios",
106+
(exportedConfig) => {
107+
const { apple } = props;
108+
let easExtra = (exportedConfig.extra ?? {}) as EasManagedExtra;
109+
easExtra.eas ??= {};
110+
easExtra.eas.build ??= {};
111+
easExtra.eas.build.experimental ??= {};
112+
easExtra.eas.build.experimental.ios ??= {};
113+
easExtra.eas.build.experimental.ios.appExtensions ??= [];
114+
115+
const shouldAddRichPushExtension =
116+
apple.richPushNotificationEnabled ||
117+
apple.pushNotificationImpressionTrackingEnabled ||
118+
apple.pushTemplatesEnabled;
119+
120+
const appGroupName = getAppGroupName(
121+
exportedConfig,
122+
apple.configFilePath
123+
);
124+
if (!exportedConfig.ios?.bundleIdentifier) {
125+
const message = `Could not find bundle identifier under 'ios' key`;
126+
console.error(message);
127+
throw new Error(message);
128+
}
129+
if (
130+
shouldAddRichPushExtension &&
131+
!isTargetAlreadyPresent(easExtra, MOENGAGE_IOS_RICH_PUSH_TARGET)
132+
) {
133+
const richPushExtensionBundleID = `${exportedConfig.ios.bundleIdentifier}.${MOENGAGE_IOS_RICH_PUSH_TARGET}`;
134+
easExtra = injectAppExtensionIntoEasManagedExtra(config, {
135+
targetName: MOENGAGE_IOS_RICH_PUSH_TARGET,
136+
bundleIdentifier: richPushExtensionBundleID,
137+
entitlements: {
138+
"com.apple.security.application-groups": [appGroupName],
139+
},
140+
});
141+
}
142+
if (
143+
apple.pushTemplatesEnabled &&
144+
!isTargetAlreadyPresent(easExtra, MOENGAGE_IOS_PUSH_TEMPLATE_TARGET)
145+
) {
146+
const templatePushBundleId = `${exportedConfig.ios.bundleIdentifier}.${MOENGAGE_IOS_PUSH_TEMPLATE_TARGET}`;
147+
easExtra = injectAppExtensionIntoEasManagedExtra(config, {
148+
targetName: MOENGAGE_IOS_PUSH_TEMPLATE_TARGET,
149+
bundleIdentifier: templatePushBundleId,
150+
entitlements: {
151+
"com.apple.security.application-groups": [appGroupName],
152+
},
153+
});
154+
}
155+
if (
156+
apple.liveActivityTargetPath &&
157+
apple.liveActivityTargetPath.length &&
158+
!isTargetAlreadyPresent(easExtra, MOENGAGE_IOS_LIVE_ACTIVITY_TARGET)
159+
) {
160+
easExtra = injectAppExtensionIntoEasManagedExtra(config, {
161+
targetName: MOENGAGE_IOS_LIVE_ACTIVITY_TARGET,
162+
bundleIdentifier: exportedConfig.ios.bundleIdentifier,
163+
entitlements: {},
164+
});
165+
}
166+
exportedConfig.extra = easExtra;
167+
return exportedConfig;
168+
},
169+
]);
170+
};

sdk/expo/src/types.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,22 @@ export interface MoEngagePluginProps {
9797
* iOS-specific configuration options
9898
*/
9999
apple: MoEngageIosConfig;
100-
}
100+
}
101+
102+
export type AppExtension = {
103+
targetName: string;
104+
bundleIdentifier: string;
105+
entitlements: Record<string, string[]>;
106+
};
107+
108+
export type EasManagedExtra = {
109+
eas?: {
110+
build?: {
111+
experimental?: {
112+
ios?: {
113+
appExtensions?: AppExtension[];
114+
};
115+
};
116+
};
117+
};
118+
};

0 commit comments

Comments
 (0)