Skip to content

release: bundle the activated plugins' Info.plist keys in the iOS release - #157

Open
Malian wants to merge 1 commit into
GenericJam:masterfrom
Malian:release-plugin-plist-keys
Open

Malian wants to merge 1 commit into
GenericJam:masterfrom
Malian:release-plugin-plist-keys

Conversation

@Malian

@Malian Malian commented Oct 25, 2026 •

Copy link
Copy Markdown

Most of the content, including the description, and the fix has been writen with Claude. I reviewed the fix, and I have the feeling that claude tooks a lot of my local memory to built it. Feel free to tell me if's too verbose.

Thank you for Mob ! ❤️

Problem

mix mob.release --ios copies ios/Info.plist into the bundle as written (release_device.sh: cp ios/Info.plist "$APP/"). The ios.plist_keys that activated plugins declare are merged by the sim and device builds (NativeBuild.write_bundle_info_plist!/3 → apply_plugin_plist_keys!), but never by the release. An App Store build therefore loses every plugin's privacy strings and Bonjour service types, and iOS refuses what the plugin needs.

Found with a plugin declaring NSBonjourServices: ["_http._tcp"] and NSBluetoothAlwaysUsageDescription: the dev build found devices on the network, the release build (installed on an iPhone 16 Pro, iOS 26.6.1) found nothing, and neither key was in the bundle's Info.plist. Present in 0.7.29 and 0.7.31.

Fix

  • NativeBuild.apply_plugin_plist_keys!/2 takes the activated plugins and is public and documented. The dev path passes MobDev.Plugin.activated(); the release passes the list build_ipa/1 already gated.
  • Release.write_release_info_plist!/2 writes ios/Info.plist plus the plugins' keys to the device build-inputs dir (release_Info.plist). The project's file is left alone.
  • release_env/5 takes that path as a required argument, next to plugin_env and project_env:
    • the script bundles it (MOB_IOS_INFO_PLIST, ${…:?}: an unset variable stops the script instead of shipping the bare plist);
    • the URL-types and UIAppFonts commands are computed from it, as the dev build computes them from the bundle's merged copy. Computed from ios/Info.plist, a plugin that creates CFBundleURLTypes made the script stop on Add :CFBundleURLTypes array ("Entry Already Exists").
  • A preflight refuses a project without ios/Info.plist before the OTP download.

Precedence is unchanged: the project's keys win, plugins fill gaps (decisions/2026-05-28-plugin-plist-keys-merge.md).

Decision records

  • 2026-05-28-plugin-plist-keys-merge.md: addendum (the release path; why a merged copy rather than PlistBuddy commands passed to the script; the file in the inputs dir), plus an in-place correction of "what the author sees in ios/Info.plist is what App Store Connect will see".
  • 2026-10-01-ios-layout-plist-keys.md and 2026-10-03-url-schemes.md: in-place corrections, since both stated the release copied plugin keys / read ios/Info.plist for URL types.
  • Three records that named release_env/4 now say release_env/5.

Tests

test/mob_dev/release_plugin_plist_test.exs, each checked to fail with its part of the fix reverted:

  • the release plist carries the plugins' keys, and ios/Info.plist is untouched (fails without the merge);
  • a key the project sets wins, a plugin's other key is still added (fails without the merge);
  • release_env/5 hands the script the plist it is given (fails if MOB_IOS_INFO_PLIST leaves the env);
  • the URL types are computed from the merged plist, so a plugin's CFBundleURLTypes doesn't stop the script's loop (fails if release_env reads ios/Info.plist);
  • the script bundles $MOB_IOS_INFO_PLIST, not ios/Info.plist.

release_test.exs asserts the variable in the full release env.

Verification

  • Rungs 1–2: mix format --check-formatted, mix credo --strict (no issues), mix compile --warnings-as-errors, mix test. 8 failures, all pre-existing on master on this machine:
    • release_script_test dead-strip test and zig_build_test: fail on master too;
    • 6 mob.adopt tests: need the mob_new archive, which isn't installed here.
  • Rung 3: mix mob.release --ios of a real app with one plugin, against this branch. The bundle's Info.plist has the plugin's NSBonjourServices and NSBluetoothAlwaysUsageDescription; UIAppFonts is intact.
  • Rung 5: the first version of this change (same merge, URL types still computed from ios/Info.plist) was installed on an iPhone 16 Pro and the plugin's Bonjour browse worked. The final version was built (rung 3) but not reinstalled.

🤖 Generated with Claude Code

…ease

`mix mob.release --ios` copied ios/Info.plist into the bundle as written, so
the `ios.plist_keys` a plugin declares, which the sim and device builds merge
through `NativeBuild.apply_plugin_plist_keys!`, were missing from the App
Store build: a plugin's privacy strings and Bonjour service types were gone,
and iOS refused what the plugin needed.

`apply_plugin_plist_keys!/2` now takes the activated plugins and is public.
`Release.write_release_info_plist!/2` writes the project's plist with the
plugins' keys to the device build-inputs dir, and `release_env/5` takes its
path as a required argument: the script bundles that file and the URL-types
and UIAppFonts commands are computed from it, as the dev build computes them
from the bundle's merged copy. A preflight refuses a project without
ios/Info.plist before the OTP download.

Decision records corrected where they claimed the release merged the keys or
read ios/Info.plist for the URL types; the plist-keys record gets an addendum.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant