Packaging packs¶
Every pack is a folder packs/<packId>/ in your app repo. Every file path starts with the pack
ID, because Apple merges all packs into one namespace: packs/postdelivery/town/sedan.glb is
read as readBytes("postdelivery", "town/sedan.glb") on every platform.
1. The integrity file¶
Each pack carries <packId>/assetdropkit.json: a content version and the SHA-256 of every file.
AssetDropKit checks a pack against it once per version; a mismatch removes the pack, fetches it
once more, then reports Failed(Corrupt).
python3 scripts/write-pack-manifest.py packs postdelivery --version 1
Run it after every change to the pack and raise --version each time. Hidden files are skipped.
2. Android: an asset-pack module¶
One module per pack. Its assets folder is the pack's single home: point Xcode and the scripts at the same folder rather than keeping a second copy.
packs/postdelivery-play/
build.gradle.kts
src/main/assets/postdelivery/ <- the pack: assetdropkit.json, town/sedan.glb, ...
// packs/postdelivery-play/build.gradle.kts
plugins { id("com.android.asset-pack") }
assetPack {
packName.set("postdelivery")
dynamicDelivery { deliveryType.set("on-demand") }
}
List the module in the app:
android { assetPacks += listOf(":packs:postdelivery-play") }
With that layout the scripts take the assets folder as <packsDir>:
write-pack-manifest.py packs/postdelivery-play/src/main/assets postdelivery --version 1.
Debug APKs carry the same folder as plain assets (android.sourceSets["debug"].assets.srcDir(...))
and read it with BundledAssetPacks, picked by PackSources.default(context, debug = true).
Release builds always go through Play.
3. iOS 17 and 18: On-Demand Resources¶
- Drag the
postdeliverypack folder into the Xcode project as a folder reference (blue folder), added to the app target. - In the File inspector, set its On Demand Resource Tags to
postdelivery(the pack ID). - Build Settings:
ENABLE_ON_DEMAND_RESOURCES = YES.
Xcode hosts the tags itself during Xcode runs; the App Store hosts them in release.
4. iOS 26 and later: Apple-hosted Background Assets¶
-
Downloader extension. File > New > Target > Background Download, type Apple-Hosted, Managed, deployment target iOS 26. The generated extension is enough:
import BackgroundAssets import ExtensionFoundation import StoreKit @main struct DownloaderExtension: StoreDownloaderExtension { func shouldDownload(_ assetPack: AssetPack) -> Bool { true } } -
App Group. Add the same App Group to the app and the extension.
-
Info.plist of the app and the extension:
Key Value BAAppGroupIDthe App Group ID BAHasManagedAssetPacksYESBAUsesAppleHostingYES -
Archive the pack (Xcode 26):
scripts/apple-hosted-pack.sh packs postdelivery # writes packs/postdelivery.aar -
Upload
postdelivery.aarthrough the App Store Connect API (background asset uploads) in your release lane. Each pack version goes through review; once approved it goes live for every installed version of the app.
Versioning rules for Apple-hosted packs¶
A pack version is not tied to an app build, and the system can update it in the background. Keep a pack's contents backward-compatible; ship a breaking change under a new pack ID (Apple allows 100 packs per app and never reuses an archived ID). Read a pack once per session and keep using what you loaded.