Skip to content

Commit 0a94c8f

Browse files
authored
Merge pull request #251 from Resgrid/develop
RU-T50 Bug fix
2 parents 326d418 + 9e3c560 commit 0a94c8f

2 files changed

Lines changed: 81 additions & 63 deletions

File tree

app.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
239239
'./plugins/withCheckInLiveActivity.js',
240240
{
241241
teamId: 'QKQVAJMTCN',
242-
enableLiveActivityEntitlement: Env.APP_ENV === 'production' || Env.APP_ENV === 'internal',
243242
},
244243
],
245244
'./plugins/withInCallAudioModule.js',

plugins/withCheckInLiveActivity.js

Lines changed: 81 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { withDangerousMod, withInfoPlist, withEntitlementsPlist, withXcodeProject } = require('expo/config-plugins');
1+
const { withDangerousMod, withInfoPlist, withXcodeProject } = require('expo/config-plugins');
22
const fs = require('fs');
33
const path = require('path');
44

@@ -299,21 +299,19 @@ const WIDGET_INFO_PLIST = `<?xml version="1.0" encoding="UTF-8"?>
299299
`;
300300

301301
const withCheckInLiveActivity = (config, props = {}) => {
302-
const { teamId, enableLiveActivityEntitlement = true } = props;
302+
const { teamId } = props;
303303

304304
// Step 1: Add NSSupportsLiveActivities to Info.plist
305305
config = withInfoPlist(config, (config) => {
306306
config.modResults.NSSupportsLiveActivities = true;
307307
return config;
308308
});
309309

310-
// Step 2: Add live activity entitlement (only if the provisioning profile supports it)
311-
if (enableLiveActivityEntitlement) {
312-
config = withEntitlementsPlist(config, (config) => {
313-
config.modResults['com.apple.developer.live-activity'] = true;
314-
return config;
315-
});
316-
}
310+
// Step 2: (intentionally none) Live Activities require NO code-signing entitlement —
311+
// only the NSSupportsLiveActivities Info.plist key above. Do NOT add
312+
// `com.apple.developer.live-activity`: it is not a valid Apple entitlement, so the
313+
// provisioning profile cannot include it and the archive fails with "Entitlement
314+
// com.apple.developer.live-activity not found and could not be included in profile."
317315

318316
// Step 3: Write Swift Widget Extension files and native bridge
319317
config = withDangerousMod(config, [
@@ -426,60 +424,81 @@ const withCheckInLiveActivity = (config, props = {}) => {
426424
}
427425
}
428426

429-
// Widget-target creation is idempotent: skip if it was already added in a
430-
// previous prebuild run. addTarget stores names with surrounding quotes in the
431-
// comment key, so check both forms. (Only widget CREATION is gated here — the
432-
// host-target wiring above already ran.)
433-
if (project.pbxTargetByName(WIDGET_NAME) || project.pbxTargetByName(`"${WIDGET_NAME}"`)) {
434-
return config;
435-
}
427+
// Resolve the widget target: reuse an existing one (created in a previous
428+
// prebuild) or create it now. Only the one-time CREATION steps below are gated by
429+
// this check — the build-settings / signing patches further down run for BOTH the
430+
// new and existing cases, so an updated teamId or version is applied even when the
431+
// widget target already exists (e.g. on an incremental, non `--clean` prebuild).
432+
// addTarget stores names with surrounding quotes in the comment key, so check both
433+
// forms.
434+
const widgetAlreadyExists = !!(project.pbxTargetByName(WIDGET_NAME) || project.pbxTargetByName(`"${WIDGET_NAME}"`));
435+
let widgetTargetUuid = null;
436+
437+
if (widgetAlreadyExists) {
438+
// Find the existing target's uuid in the native-target section (skip the
439+
// companion "<uuid>_comment" string entries).
440+
for (const key of Object.keys(targetSection)) {
441+
if (key.endsWith('_comment')) continue;
442+
const existing = targetSection[key];
443+
if (existing && (existing.name === WIDGET_NAME || existing.name === `"${WIDGET_NAME}"`)) {
444+
widgetTargetUuid = key;
445+
break;
446+
}
447+
}
448+
} else {
449+
// 1. Create the PBXNativeTarget.
450+
// addTarget('app_extension') also:
451+
// - adds an "Embed App Extensions" CopyFiles phase to the main target
452+
// - adds a PBXTargetDependency from main app → widget
453+
// - creates Debug/Release XCBuildConfigurations with basic defaults
454+
const widgetTarget = project.addTarget(WIDGET_NAME, 'app_extension', WIDGET_NAME, widgetBundleId);
455+
widgetTargetUuid = widgetTarget.uuid;
456+
457+
// 2. Add the three build phases the widget target needs.
458+
// These must be added before files/frameworks are wired, because the
459+
// addSourceFile / addFramework helpers find phases by scanning the
460+
// target's buildPhases array.
461+
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', widgetTarget.uuid);
462+
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', widgetTarget.uuid);
463+
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', widgetTarget.uuid);
464+
465+
// 3. Create a PBX group for the widget folder and attach it to the project's
466+
// main group so the files appear in the Xcode file navigator.
467+
const { uuid: widgetGroupUuid } = project.addPbxGroup([], WIDGET_NAME, WIDGET_NAME);
468+
const { firstProject } = project.getFirstProject();
469+
const mainGroup = project.getPBXGroupByKey(firstProject.mainGroup);
470+
if (mainGroup && !mainGroup.children.find((c) => c.comment === WIDGET_NAME)) {
471+
mainGroup.children.push({ value: widgetGroupUuid, comment: WIDGET_NAME });
472+
}
436473

437-
// 1. Create the PBXNativeTarget.
438-
// addTarget('app_extension') also:
439-
// - adds an "Embed App Extensions" CopyFiles phase to the main target
440-
// - adds a PBXTargetDependency from main app → widget
441-
// - creates Debug/Release XCBuildConfigurations with basic defaults
442-
const widgetTarget = project.addTarget(WIDGET_NAME, 'app_extension', WIDGET_NAME, widgetBundleId);
443-
444-
// 2. Add the three build phases the widget target needs.
445-
// These must be added before files/frameworks are wired, because the
446-
// addSourceFile / addFramework helpers find phases by scanning the
447-
// target's buildPhases array.
448-
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', widgetTarget.uuid);
449-
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', widgetTarget.uuid);
450-
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', widgetTarget.uuid);
451-
452-
// 3. Create a PBX group for the widget folder and attach it to the project's
453-
// main group so the files appear in the Xcode file navigator.
454-
const { uuid: widgetGroupUuid } = project.addPbxGroup([], WIDGET_NAME, WIDGET_NAME);
455-
const { firstProject } = project.getFirstProject();
456-
const mainGroup = project.getPBXGroupByKey(firstProject.mainGroup);
457-
if (mainGroup && !mainGroup.children.find((c) => c.comment === WIDGET_NAME)) {
458-
mainGroup.children.push({ value: widgetGroupUuid, comment: WIDGET_NAME });
459-
}
474+
// 4. Add Swift source files to the widget group and to the widget's Sources phase.
475+
// Passing the group key as the third argument to addSourceFile ensures the
476+
// file reference lands in the right PBX group; opt.target routes the build
477+
// file to the widget's PBXSourcesBuildPhase rather than the main app's.
478+
const SWIFT_SOURCES = [
479+
'CheckInTimerAttributes.swift',
480+
'CheckInTimerLiveActivity.swift',
481+
'CheckInTimerWidgetBundle.swift',
482+
];
483+
for (const filename of SWIFT_SOURCES) {
484+
project.addSourceFile(
485+
filename,
486+
{ target: widgetTarget.uuid },
487+
widgetGroupUuid
488+
);
489+
}
460490

461-
// 4. Add Swift source files to the widget group and to the widget's Sources phase.
462-
// Passing the group key as the third argument to addSourceFile ensures the
463-
// file reference lands in the right PBX group; opt.target routes the build
464-
// file to the widget's PBXSourcesBuildPhase rather than the main app's.
465-
const SWIFT_SOURCES = [
466-
'CheckInTimerAttributes.swift',
467-
'CheckInTimerLiveActivity.swift',
468-
'CheckInTimerWidgetBundle.swift',
469-
];
470-
for (const filename of SWIFT_SOURCES) {
471-
project.addSourceFile(
472-
filename,
473-
{ target: widgetTarget.uuid },
474-
widgetGroupUuid
475-
);
491+
// 5. Link WidgetKit and ActivityKit into the widget's Frameworks phase.
492+
// opt.target directs addToPbxFrameworksBuildPhase to use the widget's
493+
// PBXFrameworksBuildPhase (added above) instead of the main app's.
494+
project.addFramework('WidgetKit.framework', { target: widgetTarget.uuid });
495+
project.addFramework('ActivityKit.framework', { target: widgetTarget.uuid });
476496
}
477497

478-
// 5. Link WidgetKit and ActivityKit into the widget's Frameworks phase.
479-
// opt.target directs addToPbxFrameworksBuildPhase to use the widget's
480-
// PBXFrameworksBuildPhase (added above) instead of the main app's.
481-
project.addFramework('WidgetKit.framework', { target: widgetTarget.uuid });
482-
project.addFramework('ActivityKit.framework', { target: widgetTarget.uuid });
498+
// If the widget target can't be resolved, skip the build-settings/signing patches.
499+
if (!widgetTargetUuid) {
500+
return config;
501+
}
483502

484503
// 6. Patch build settings on both Debug and Release configurations so the
485504
// widget compiles as a Swift 5 app-extension targeting iOS 16.1+.
@@ -533,7 +552,7 @@ const withCheckInLiveActivity = (config, props = {}) => {
533552
hostDevelopmentTeam = process.env.EXPO_APPLE_TEAM_ID || null;
534553
}
535554

536-
const buildConfigListId = targetSection[widgetTarget.uuid].buildConfigurationList;
555+
const buildConfigListId = targetSection[widgetTargetUuid].buildConfigurationList;
537556
const buildConfigList = project.pbxXCConfigurationList()[buildConfigListId];
538557
if (buildConfigList) {
539558
for (const { value: configUuid } of buildConfigList.buildConfigurations) {
@@ -569,8 +588,8 @@ const withCheckInLiveActivity = (config, props = {}) => {
569588
// the parent app (matches the host target's signing identity). Mirrors the
570589
// Responder app's working Live Activity signing setup.
571590
if (hostDevelopmentTeam) {
572-
project.addTargetAttribute('DevelopmentTeam', hostDevelopmentTeam, widgetTarget);
573-
project.addTargetAttribute('ProvisioningStyle', 'Automatic', widgetTarget);
591+
project.addTargetAttribute('DevelopmentTeam', hostDevelopmentTeam, { uuid: widgetTargetUuid });
592+
project.addTargetAttribute('ProvisioningStyle', 'Automatic', { uuid: widgetTargetUuid });
574593
}
575594

576595
return config;

0 commit comments

Comments
 (0)