[msbuild] Embed on-demand resource asset packs in the app bundle for the simulator. Fixes #26218 - #26481
[msbuild] Embed on-demand resource asset packs in the app bundle for the simulator. Fixes #26218#26481rolfbjarne wants to merge 5 commits into
Conversation
…the simulator On-demand resources (ODR) don't work on the simulator: the build creates the asset packs next to the app bundle (in the OnDemandResources directory in the intermediate output directory) and writes an AssetPackManifestTemplate.plist that points at http://127.0.0.1 dev-hosting URLs. This works on device (where the IDE hosts the packs over http), but there's no such hosting server for the simulator, so NSBundleResourceRequest fails at runtime with "No manifest found for bundleID". Xcode handles this with its "Embed Asset Packs In Product Bundle" setting, which copies the asset packs into the app bundle and rewrites the manifest to point at the embedded (non-streamable) packs. Do the same here: add a new _EmbedOnDemandResources target that, for simulator builds, copies the asset packs into <app>/OnDemandResources, rewrites AssetPackManifestTemplate.plist into AssetPackManifest.plist with relative (non-streamable) URLs, and removes the streaming template. This mirrors the existing embed logic used for AdHoc (IPA) builds. Also add a new test project (AppWithOnDemandResources) and a test (OnDemandResourcesTest) that builds a simulator app with a tagged BundleResource and verifies the app bundle contains the embedded asset pack, a valid OnDemandResources.plist and AssetPackManifest.plist, and no leftover streaming AssetPackManifestTemplate.plist. Fixes #26218 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: be0c22c8-907d-407c-ba83-a262132246f6
…runtime Turn the test app into a real UIKit app that tries to access the on-demand resource tagged "MusicTag" (the SoundBank.bin file). The screen is yellow while the request is in progress, turns green if the resource could be accessed and read, and red if anything went wrong. This makes it easy to verify at runtime (in particular on the simulator) that on-demand resources actually work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: be0c22c8-907d-407c-ba83-a262132246f6
…he app bundle The WriteAssetPackManifest and Delete steps in _EmbedOnDemandResources gated on Exists(...) of a file inside the app bundle ($(AppBundleDir)). On a Windows (Pair to Mac) build the app bundle is produced on the Mac and isn't surfaced back to the build host, so these conditions (evaluated on the build host) would always be false and silently skip rewriting/removing the streaming manifest template, leaving the http://127.0.0.1 URLs that don't work on the simulator. Gate all three steps on the intermediate '$(DeviceSpecificOutputPath)OnDemandResources' directory instead (where the asset packs are compiled), which is what the existing _PackageOnDemandResources target does. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: be0c22c8-907d-407c-ba83-a262132246f6
There was a problem hiding this comment.
Pull request overview
This PR fixes on-demand resources (ODR) on simulator builds by embedding compiled asset packs into the app bundle and rewriting the asset pack manifest to use relative (non-streamable) URLs, matching Xcode’s “Embed Asset Packs In Product Bundle” behavior. It also adds a new runtime-focused test app and a unit test to validate the resulting bundle structure and manifests.
Changes:
- Add an MSBuild target (
_EmbedOnDemandResources) to copyOnDemandResources/*.assetpackinto<app>.app/OnDemandResourcesfor simulator builds and generateAssetPackManifest.plist(removing the streaming template). - Wire embedding into the .NET SDK build flow via
_CreateAssetPackManifestMobile. - Add
AppWithOnDemandResourcesplusOnDemandResourcesTestto validate embedded packs and manifests for iOS/tvOS simulators.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/dotnet/UnitTests/OnDemandResourcesTest.cs | New unit test that builds a test app and asserts embedded asset packs + rewritten manifest for simulator builds. |
| tests/dotnet/AppWithOnDemandResources/AppDelegate.cs | Runtime smoke app that requests an ODR tag and visually reports success/failure. |
| tests/dotnet/AppWithOnDemandResources/Main.cs | App entry point for the ODR test app. |
| tests/dotnet/AppWithOnDemandResources/shared.csproj | Shared test app project settings and a tagged BundleResource to force asset pack creation. |
| tests/dotnet/AppWithOnDemandResources/SoundBank.bin | Test payload file to be packaged as an on-demand resource. |
| tests/dotnet/AppWithOnDemandResources/Makefile | Test project build integration. |
| tests/dotnet/AppWithOnDemandResources/shared.mk | Shared make include for platform-specific builds. |
| tests/dotnet/AppWithOnDemandResources/iOS/Makefile | iOS make include for the test app. |
| tests/dotnet/AppWithOnDemandResources/iOS/AppWithOnDemandResources.csproj | iOS tfm wrapper project importing shared settings. |
| tests/dotnet/AppWithOnDemandResources/tvOS/Makefile | tvOS make include for the test app. |
| tests/dotnet/AppWithOnDemandResources/tvOS/AppWithOnDemandResources.csproj | tvOS tfm wrapper project importing shared settings. |
| msbuild/Xamarin.Shared/Xamarin.iOS.Common.targets | Adds _EmbedOnDemandResources target to copy packs + rewrite manifest for simulator builds. |
| dotnet/targets/Xamarin.Shared.Sdk.targets | Ensures the embedding runs as part of _CreateAssetPackManifestMobile for non-macOS platforms. |
Suppressed comments (1)
tests/dotnet/UnitTests/OnDemandResourcesTest.cs:66
- 🤖 ❌ Nullable — Avoid the null-forgiving operator (
!) in the manifest parsing as well; use explicit null checks so the test both fails clearly and stays compliant with the repo’s nullable rules.
var manifest = PDictionary.OpenFile (manifestPath);
var resources = manifest!.GetArray ("resources");
Assert.That (resources, Is.Not.Null.And.Not.Empty, "AssetPackManifest.plist must contain resources.");
foreach (var resource in resources!.OfType<PDictionary> ()) {
var url = resource.Get<PString> ("URL")?.Value;
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
This comment has been minimized.
This comment has been minimized.
Address PR review feedback:
- Split the embedding into a cheap always-run part (delete the streaming
'AssetPackManifestTemplate.plist' that '_CreateAssetPackManifest'
regenerates every build) and an incremental part
('_CopyOnDemandResourcesToAppBundle') keyed on the compiled asset packs.
This avoids rewriting 'AssetPackManifest.plist' on every incremental
build, which was forcing a re-codesign.
- Remove/recreate the embedded 'OnDemandResources' directory before copying
so stale asset packs (removed or renamed since the last build) don't
linger, mirroring '_PackageOnDemandResources'.
- Collect the incremental inputs in a dependency target rather than in the
'Inputs' attribute: a target's 'Inputs' doesn't expand recursive '**'
wildcards (it would treat 'OnDemandResources/**/*' as a literal missing
file and always run), and a static ItemGroup wouldn't see the packs
because they're generated during the build.
- Fix the test's nullable handling (drop the '!' null-forgiving operator in
favour of an 'AssertNotNull' helper) and dispose the 'NSData' returned by
'NSData.FromFile' in the test app.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: be0c22c8-907d-407c-ba83-a262132246f6
|
Addressed the review comments in fc56021:
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
All test apps get tests/common/shared-dotnet.plist as a partial app
manifest, and it declares a UISceneDelegateClassName of "SceneDelegate".
AppWithOnDemandResources didn't have such a class, so at launch iOS
printed:
[SceneConfiguration] Info.plist configuration "Default Configuration" for
UIWindowSceneSessionRoleApplication contained UISceneDelegateClassName key,
but could not load class with name "SceneDelegate".
and no scene (and thus no window) was ever created, so the app showed
nothing at all.
Move the UI and the on-demand resource logic into a SceneDelegate, and
leave the AppDelegate with just the scene configuration, the same way
the other test apps do it. Also print the result of the resource request
to the console, so the outcome is visible without looking at the screen.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
✅ API diff for current PR / commitNET (empty diffs)✅ API diff vs stableNET (empty diffs)ℹ️ Generator diffGenerator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes) Pipeline on Agent |
🔥 [CI Build #97da63c] Test results 🔥Test results❌ Tests failed on VSTS: test results 1 tests crashed, 0 tests failed, 200 tests passed. Failures❌ Tests on macOS Ventura (13) tests🔥 Failed catastrophically on VSTS: test results - mac_ventura (no summary found). Html Report (VSDrops) Download Successes✅ assembly-processing: All 1 tests passed. Html Report (VSDrops) Download macOS tests✅ Tests on macOS Monterey (12): All 5 tests passed. Html Report (VSDrops) Download Linux Build VerificationPipeline on Agent |
On-demand resources (ODR) didn't work on the simulator. The build creates the asset packs next to the app bundle (
$(DeviceSpecificOutputPath)OnDemandResources/*.assetpack) and writes anAssetPackManifestTemplate.plistthat points athttp://127.0.0.1dev-hosting URLs. This works on device (where the IDE hosts the packs over http), but there's no such hosting server for the simulator, soNSBundleResourceRequestfails at runtime with "No manifest found for bundleID".Xcode handles this with its "Embed Asset Packs In Product Bundle" setting, which copies the asset packs into the app bundle and rewrites the manifest to point at the embedded (non-streamable) packs. This PR does the same: a new
_EmbedOnDemandResourcestarget (wired into_CreateAssetPackManifestMobile) that, for simulator builds, copies the asset packs into<app>/OnDemandResources, rewritesAssetPackManifestTemplate.plistintoAssetPackManifest.plistwith relative (non-streamable) URLs, and removes the streaming template. This mirrors the existing embed logic used for AdHoc (IPA) builds (_PackageOnDemandResources), including gating the steps on the intermediateOnDemandResourcesdirectory so it also works on Windows (Pair to Mac) builds.Testing: a new test project (
AppWithOnDemandResources) — a small UIKit app that tries to access its tagged on-demand resource at runtime and turns the screen green on success, red on failure (yellow while loading) — and a new build test (OnDemandResourcesTest) that verifies the simulator app bundle contains the embedded asset pack, a validOnDemandResources.plistandAssetPackManifest.plist, and no leftover streamingAssetPackManifestTemplate.plist.Fixes #26218
🤖 Pull request created by Copilot