Model Size Budgets for App Stores: Delivery Without a Rejection
9 min read · updated August 4, 2026
A quantised model is hundreds of megabytes to a few gigabytes. Both app stores will let you ship that, and neither will let you ship it the naive way. The mechanisms for large assets are well-defined; the numbers attached to them are not, because both stores have moved their thresholds repeatedly.
Three sizes, not one
Discussion of app size goes wrong because people use one word for three different quantities, and the stores enforce limits on different ones.
| Quantity | Description |
|---|---|
| download size | What the user's device transfers from the store. Compressed, and thinned to the one architecture and asset set that device needs. This is the number a store warning threshold applies to, and the number a user sees before tapping install. |
| install size | What the app occupies on disk after installation. Larger than download size, because much of it is decompressed. This is what makes users uninstall you. |
| uncompressed artefact size | Limits applied to individual pieces — the executable, a single generated package. Rarely relevant to a model file, but the one that produces the most confusing rejection when it does apply. |
A model file is usually already incompressible: quantised weights are near-random bytes and gain almost nothing from the store’s compression. Assume a model contributes its full size to both download and install, and do not plan around a compression ratio you have not measured on your actual file.
Delivery on iOS
- App thinning. The store builds and delivers a variant per device class, so architecture slices and unused asset resolutions are not downloaded. This is automatic and it is why the size Xcode reports and the size a user downloads differ. Use the App Store Connect size report for the real per-device figures rather than the size of your archive.
- On-demand resources. Assets tagged and hosted by the store, downloaded when your app requests the tag and evicted by the system when space is needed. The key property, and the one that bites: eviction is not under your control. Your code must handle “the model I downloaded last week is gone” as a normal state, not an error.
- Background asset download. Apple provides a mechanism for fetching large assets around install and update time, outside the app’s own lifecycle, so a first launch is not blocked on a multi-hundred-megabyte transfer. The names and capabilities here have changed across OS versions; check the current framework documentation for the deployment target you support.
- Your own CDN. Always permitted for data assets, and often simplest. You keep full control of versioning and rollout — see updating models on devices you do not control — and you pay for the bandwidth, which is the section below.
Delivery on Android
- App bundles are mandatory for Play. You upload one bundle; Play generates and signs the per-device artefacts. Size limits apply to those generated artefacts, not to your upload, which is a frequent source of confusion when a build that seems small is rejected.
- Play Asset Delivery, with three modes. Install-time asset packs arrive with the app and behave like part of it. Fast-follow packs download automatically immediately after install, so the app is usable at once and complete shortly after. On-demand packs download when your code asks. For a model that gates a secondary feature, fast-follow is usually the right default: the user is not blocked, and by the time they find the feature it is ready.
- Compression is configurable per pack, and for a weight file you generally want it uncompressed so it can be memory-mapped directly from storage rather than expanded into a second copy. Mapping matters more than a few per cent of download size — it is the difference between the weights being evictable clean pages and being dirty allocations counted fully against you.
- Your own CDN is permitted for data, but downloading and then executing code is not. A model file is data; a downloaded interpreter or native library is not, and the distinction is enforced. Keep the runtime in the app and only the weights on the network.
What review needs from you
A model that downloads after install creates a specific review problem: the reviewer opens your app on a network you do not control and finds a progress bar, or a disabled feature, and has no way to know that is intended.
- Make the app useful before the model arrives. An app whose first screen is a mandatory 900 MB download is a rejection risk on both stores and an uninstall risk regardless. Ship something functional, and treat the model as an enhancement that lands.
- Write the review note. State plainly that the feature requires a one-time download of approximately N megabytes, how long it typically takes, and exactly which screen shows its progress. Reviewers do read these, and the absence of one is why reviews come back with “we were unable to locate the feature”.
- Handle the offline reviewer. If the download fails, show a clear state with a retry, not a spinner. A permanent spinner is indistinguishable from a broken app.
- Declare what the model does, honestly. Both stores now ask about AI-driven functionality and about user-generated content risk, and an on-device generative model is squarely in scope. Under-declaring is a slow rejection; the disclosure obligations themselves are covered in AI transparency obligations.
- Check the storage story. Users can and do clear app data. Your code must detect a missing or corrupted model file and re-fetch it rather than crashing on an incomplete read.
The bill nobody budgets: egress
If you host the model yourself, its size multiplies by every install and every update. That is a real line item and it is easy to compute before it appears:
egress_gb = model_gb × downloads Worked: a 1.5 GB model, 100,000 first-time downloads in a month 1.5 × 100,000 = 150,000 GB = 150 TB At an assumed CDN rate of $0.05 per GB: 150,000 × 0.05 = $7,500 for that month Now ship a new model version to the same 100,000 users: another $7,500 — unless the update is a delta.
The assumed rate is the one number here you must replace with your own; egress pricing varies by more than an order of magnitude between providers and tiers. The structure of the calculation does not change.
Two mitigations follow directly from the arithmetic. Use the store’s own asset hosting where it fits, because that bandwidth is not billed to you. And make updates differential rather than wholesale — a fine-tuned adapter measured in tens of megabytes, shipped against a base model that stays put, turns the second $7,500 into something closer to $250.
Deciding what goes in the bundle
A short decision rule that survives both stores:
- In the bundle if the model is small, if the feature is the reason people install the app, and if the app is useless without it. Paying the size cost buys a first launch that simply works.
- Fast-follow or background download if the feature is important but not the entry point. The user is never blocked and the model is nearly always there when wanted.
- On-demand if the feature is used by a minority, or if you ship several models and most users need one of them. Only fetch what that user’s configuration requires.
- Not on the device at all if the model is large, the feature needs a network anyway, and privacy is not the reason for running locally. Half of on-device projects fail this test and are better served by a hosted call — the honest case for local models is worth reading before committing a gigabyte of somebody’s phone to one.