bentest-target-adder
v0.0.2
Published
Example Capacitor plugin: add extra iOS targets (e.g. a widget) to an OutSystems-built app, with per-target provisioning profiles delivered as App Resources
Downloads
162
Readme
bentest-target-adder
An example Capacitor plugin that adds extra iOS targets — such as a WidgetKit widget — to an OutSystems-built mobile app, with each target bound to its own provisioning profile. It runs entirely at build time via a capacitor:sync:before hook; it has no runtime API and contributes no native module (so it never touches the app's Podfile/Package.swift).
Provided as-is, as a working reference. It is not an OutSystems product and is not supported as one.
How it works
During npx cap sync ios (which the OutSystems build pipeline runs on every build):
- Profiles — every
.mobileprovisionfound in the web dir (delivered as App Resources) is decoded withsecurity cmsand installed into~/Library/MobileDevice/Provisioning Profiles/<UUID>.mobileprovision, the directory Xcode reads at signing time. Profiles are identified by the App ID inside them, never by filename — App Resource delivery renames files. - Payload — every
.zipin the web dir is extracted; a zip containing a directory that matches apayloadDirfromscripts/targets.jsonis treated as that target's payload (Sources/,Info.plist, optional*.entitlementsandResources/) and staged underios/App/<TargetName>/. Loose files are supported as a fallback, classified by extension. - Target —
scripts/add_target.rb(Ruby, using thexcodeprojgem — the only complete API for editingproject.pbxproj) creates the target, sets its bundle ID (<appId>.<bundleIdSuffix>), Manual signing against the matched profile, sources/resources phases, the host-app dependency, and the kind-appropriate embed phase. If the payload carries an App Group entitlement, the group is also merged into the host app's entitlements.
Everything is idempotent — repeated syncs never duplicate targets, phases, or profiles — and a build with no payload present is left untouched (the hook exits 0).
Declaring targets
Edit scripts/targets.json. Example:
{
"targets": [
{
"name": "TestWidget",
"kind": "app_extension",
"bundleIdSuffix": "widget",
"payloadDir": "widgetpayload",
"entitlements": true
}
]
}kind accepts any Xcodeproj product-type symbol (app_extension, messages_extension, sticker_pack, tv_extension, application_on_demand_install_capable, …). An unknown kind fails with the full list of supported symbols.
What to upload as App Resources
- One
.zipper target:<payloadDir>/Sources/**,<payloadDir>/Info.plist(use$(...)build-setting variables for identity values so the file needs no per-app edits), optional<payloadDir>/*.entitlementsand<payloadDir>/Resources/**. - Each extra target's provisioning profile as a bare
.mobileprovision(outside the zip), so renewing a profile is a one-file re-upload.
Prerequisites (Apple Developer portal)
- An explicit App ID per target:
<app's App ID>.<bundleIdSuffix>, under the same team whose certificate signs the app in the OutSystems build. - One provisioning profile per App ID, of the type matching the build (e.g. ad-hoc), backed by that same distribution certificate.
- For App Groups: assign the group to both the host and extension App IDs and regenerate both profiles.
Build-log markers
Successful runs log, in order: [target-adder] cwd=… → extracted App Resource zip … → installed profile … → matched profile … by App ID → the created target's resolved build settings. Later in the build, fastlane's Detected provisioning profile mapping: line should list every bundle ID with its profile.
