Sending a build to a tester
Most teams send the file: a Google Drive link, a Dropbox share, a WeTransfer, an S3 signed URL, or the .apk attached to an email. On Android that mostly works. On iOS it cannot work at all — not because of a setting, but because iOS has no way to install an .ipa from a downloaded file. Your tester ends up holding a file their phone will not open.
This page explains the mechanism precisely, because the mechanism is the argument. Then it does the honest half: what you give up by not sending a file, and the cases where a Drive link is genuinely the right answer and reaching for a tool would be overhead.
Why a Drive link cannot install an iOS build
An iPhone will not install an app from a file. There is no handler for .ipa in Files, nothing to long-press in Safari's downloads, no "open with" that leads anywhere. The file lands, and that is the end of the road. The routes onto an iPhone are few, and none of them is a file: the App Store, TestFlight, an over-the-air install, a Mac with Xcode or Apple Configurator and a cable, or an MDM server pushing the app if the device is managed. A file-sharing link is none of them.
An over-the-air install does not start with the build. It starts with a URL in Apple's itms-services scheme, whose entire payload is a pointer to a manifest:
itms-services://?action=download-manifest&url=https%3A%2F%2Fexample.com%2Fmanifest.plistSafari does not follow that itself. It hands it to the system install daemon, which fetches the manifest — a property list describing the build — and only then downloads and installs the .ipa the manifest names:
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>https://…/YourApp.ipa</string> <!-- direct, HTTPS, no redirect -->
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>com.example.yourapp</string>
<key>bundle-version</key>
<string>1.4.2</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>Your App</string>
</dict>
</dict>
</array>
</dict>Now look at what that requires, and at what a share link provides. A share link is a URL to a file. The install needs a URL to a document about the file, and there is nowhere in Drive, Dropbox or WeTransfer to put one. That alone ends it — but three further constraints are worth knowing, because they are what defeats the clever workarounds people try next:
- The daemon does not follow redirects. Our own manifest handler is written around this: the asset URL has to be the object URL itself, never a pointer back at our API that would redirect. A Drive "direct download" URL is a redirect to a different host, so pointing a hand-rolled manifest at one fails on the first hop.
- The daemon has its own empty cookie jar. It is a separate process from the browser and carries none of its session. A Drive file restricted to your organisation is authorized by the viewer's Google session cookie, which the daemon does not have and cannot get. We hit the same wall from the inside: a password-protected build has to mint proof of unlock into the URL, because a cookie cannot reach the process doing the install.
- Everything must be HTTPS, and it must be Safari. Both the manifest and the
.ipaare fetched over HTTPS with a trusted certificate. And an install link tapped inside an in-app browser — Slack, Gmail, Telegram, LinkedIn — does nothing at all, silently, because those WebViews swallow the scheme. That one is not hypothetical; our install pages warn about it on sight, because it is the most common way a working link looks broken.
None of this is a limitation of Drive. Drive is doing its job correctly: it stores a file and gives you a link to it. The mismatch is that an iOS install is not a file transfer. If you want the mechanics in full, we wrote them up separately in installing IPA files over the air.
Android: the file arrives, and then the tester is on their own
Android is the case where sending the file genuinely works, and it would be dishonest to claim otherwise. An .apk downloaded from Drive will install. The objection here is not "impossible", it is "friction you cannot see and cannot help with".
What the tester typically walks through: the browser warns that this kind of file can harm their device. They tap the download anyway. Android tells them the app they are installing from is not allowed to install unknown apps — since Android 8.0 that permission is granted per source rather than by one global switch, so it is Chrome or Drive or Files specifically, and the tester has to find the toggle in Settings and come back. Play Protect usually adds a prompt about an app from an unknown developer. Three scary-sounding dialogs, no context, and nothing anywhere on screen that says this is expected. The exact wording moves with the Android version and the OEM skin, which is its own problem: you cannot write one set of instructions that matches every tester's screen.
A tester who has sideloaded before shrugs and gets through it. A client, a designer, a stakeholder or an external QA contractor frequently does not — and the way that failure reaches you is silence, or a message two days later. It is a conversion problem more than a technical one: the steps are simple once written down, which is precisely why they should be on the page next to the button rather than in a message you retype for every tester.
There is a second, quieter Android problem: nothing about the file tells the tester whether it is even the right build for their phone. Wrong ABI, a minSdkVersion above their Android version, a debug build sent by accident, or last week's binary because the filename never changed — app-release.apk looks identical to app-release.apk.
Sending a file vs sending an install link
| What you need | Drive / Dropbox / email | BetaDrop |
|---|---|---|
| Installs an iOS build over the air | No — iOS cannot install a downloaded .ipa at all | Yes — itms-services manifest generated per build |
| Installs an Android build | Yes, after the tester grants install-unknown-apps | Yes, from an install page that says what the prompt will ask |
| What the tester receives | A file, and whatever instructions you typed by hand | A page with an install button, a QR code and the build's details |
| Did they install it? | Unknowable — installation happens after the download | Install attempts with outcome, device model, OS version, country |
| Did it fail, and why? | Only if the tester tells you | Failure count and failure reasons per build |
| Per-tester attribution | One link, no way to tell who used it | A labelled share link per tester, each with its own device count |
| Link lifetime | Until someone remembers to revoke it | 24 hours for a guest upload, 3 days by default, up to 7 days free |
| Build metadata visible to the tester | The filename | Version, build number, bundle id, min OS, architectures, permissions |
| Access control | Strong — your existing directory groups, if the file is restricted | Unlisted link, optionally password-protected |
| Cost of setting it up | Zero — it is already in your workspace | An upload |
The middle column describes any file-sharing link, because every row above is caused by the one thing they have in common — they deliver a file. It is not a claim about any provider's feature list, and the access-control row is a row they win.
Three things a file link cannot tell you
Whether anyone actually installed it
A storage provider can, at best, tell you a file was downloaded. Installation happens afterwards, on the device, with the provider no longer involved — so "downloaded 4 times" and "installed 4 times" are different numbers and you only ever see the first one. The gap matters most in the case that fails: an ad-hoc iOS build only installs on devices whose UDIDs were in the provisioning profile when it was signed, and when the tester is not covered, the install dies on their phone. To you it looks like nothing happened. To them it looks like your link is broken.
An install link is served by something that is present at install time, so it can record the outcome: each attempt with its status, device model, OS version and country, rolled up per build into unique devices, failed attempts and the reasons those attempts failed. Give each tester their own labelled share link and the device counts split by person — which is the only honest way to answer "did Priya get it?". Before that ever comes up, a tester can check their UDID so you can sign a build that covers them.
When the build should stop being available
A Drive link stops working when someone remembers to revoke it. In practice nobody does — the link is in a Slack thread, the thread is in a channel a contractor is still a member of, and it was forwarded twice on the way. For an unreleased app that is a leak with no end date: the binary is your product before launch, and it is readable by anyone holding a URL you have forgotten about.
A build link has a deadline you choose. A guest upload lasts 24 hours, a signed-in build defaults to 3 days, and a free account can push it to 7 days; paid plans go considerably further. Note the trade honestly, because it runs both ways: a Drive link outlives a free BetaDrop link easily, since it never expires at all. If what you want is a link that is still live next year, that is a paid plan here and free there. The argument is not that shorter is better — it is that a deadline you set beats one you forgot.
What is actually inside the binary
A file link shows a filename and a size. An install page reads the binary and shows the tester what they are about to install: app name and icon, version and build number, bundle or package id, minimum OS version, supported architectures, device families, the permissions an Android build requests, and — for an ad-hoc iOS build — the devices its provisioning profile covers. That is the difference between "is this the build with the fix?" being a question you answer in chat and one the page answers before they tap. If you only want the inspection and not the hosting, the IPA inspector and APK inspector run in the browser and upload nothing.
The S3 signed-URL route, honestly
An S3 bucket is the one file-sharing answer that can be made to install an iOS build, because unlike Drive you control the URLs. The recipe is real and it works: write a manifest.plist per build with the app's bundle id, version and title, upload it next to the .ipa, serve both over HTTPS, and hand the tester an itms-services:// link pointing at the manifest.
Two constraints decide whether your version works, and they are the same two from the top of this page. The asset URL in the manifest must be the object URL itself, because the install daemon will not follow a redirect through your own API. And any authorization has to travel in the URL rather than in a cookie — which is exactly why a pre-signed S3 URL works where a session-gated Drive file cannot. We run this arrangement on our own storage backend, which is how we know both halves are true.
What you are signing up for is the rest of it: regenerating the manifest on every build, keeping signature expiry longer than the tester's attention span, an install page and a QR code if you want one, per-tester links, and a cleanup job so old builds do not sit in a bucket forever. Plus the thing no amount of plumbing gets you — a record of who installed. If your team enjoys owning that, it is a legitimate choice and this page is not going to pretend otherwise. Most teams would rather it were a step in the workflow that already builds the app.
When a Drive link is the right answer
Often, and it costs nothing to say so. If any of these describes your situation, stop reading and send the link:
- One colleague, on Android, who has done this before. They know what the unknown-sources prompt is, they are two desks or one call away, and the whole exchange is over in a minute. Introducing a tool here adds a step and removes nothing.
- The build is not going onto a phone. An emulator, a QA machine, a security reviewer, a partner's CI, an archive of last quarter's releases. Those want a file, and a file is what Drive is for.
- Access has to follow your directory, not a link. This is a genuine advantage and worth stating clearly: a Drive file restricted to your domain is governed by your existing groups and offboarding, which is stronger than any unlisted URL. Our answer is a password on the link — real protection, but a different model, and for a build under NDA the directory-backed one may well be the one your security review prefers.
- The file is enormous. Our free upload stops at 500 MB. If you are moving a multi-gigabyte archive of assets rather than a build, that is a storage job and not a distribution one.
- You already have a working pipeline. If your testers are on TestFlight and happy, they are in the right place; we wrote about where TestFlight wins and where it does not rather than pretending it never does.
The short version: send the file when the recipient is technical, internal, and not on an iPhone. Send an install link when any one of those three stops being true.
What sending a build looks like instead
Upload the .ipa or .apk and you get a URL. Opening it on a phone gives the tester an install button that does the right thing for their platform — an itms-services handoff on iOS, a download with the unknown-sources step explained on Android — under the app's real name, icon, version and build number. On a laptop it shows a QR code instead, because the build is not going to install on the laptop.
Nothing about that requires an account to try: an anonymous upload works and the link lasts 24 hours. Signing in is what adds expiry control, per-tester share links and the install records. If it turns out to be something you do on every commit, it belongs in CI — the CLI and the GitHub Action publish the build as the last step of the pipeline, which is also the point at which people stop sending files by hand.
Frequently Asked Questions
Can you install an IPA from a Google Drive link?
No. iOS has no way to install an .ipa from a downloaded file — not from Files, not from Safari's downloads, not from the Drive app. An over-the-air install starts with an itms-services:// URL that points at an HTTPS manifest describing the build, and a file-sharing link cannot supply one. The tester ends up with a file on their phone that nothing on the phone can open. The other routes onto an iPhone are TestFlight, the App Store, a Mac with Xcode or Apple Configurator and a cable, or an MDM server if the device is managed.
Why does nothing happen when a tester taps the .ipa on their iPhone?
Because iOS treats a downloaded .ipa as an inert archive: there is no handler registered for it, so there is nothing to tap that installs. Installing over the air is a different flow entirely — Safari hands an itms-services:// URL to the system install daemon, which fetches a manifest plist over HTTPS and only then downloads and installs the app. A separate case worth knowing about: even a correct install link silently does nothing inside an in-app browser such as Slack, Gmail, Telegram or LinkedIn, because those WebViews swallow the itms-services scheme. Open the link in Safari.
Can you install an APK from Google Drive?
Yes — this is the case where a file link genuinely works, and it is worth saying so. Android will install an APK the tester downloaded, after they grant "install unknown apps" to whichever app is opening it (since Android 8.0 that permission is per-source rather than one global switch) and clear the Play Protect prompt that usually follows for an app from an unknown developer. The cost is friction and silence: the tester makes a trip into Settings with no guidance, and you never learn whether they finished.
Is Dropbox, WeTransfer or an email attachment any different?
Not in the way that matters. All of them deliver a file, and on iOS a file is not installable, so the iOS objection applies to every one of them identically. There are smaller differences: WeTransfer expires its links by design, which is closer to right than a Drive link that lives until someone remembers to revoke it, and email adds an attachment ceiling — Gmail's is 25 MB, well under most builds — so mailing an .ipa or .apk usually turns into a file-sharing link anyway.
Can I host the IPA on S3 myself with a signed URL?
Yes, and this is the one DIY route that actually works, so it deserves a straight answer. You generate a manifest plist per build, host it over HTTPS, and point its software-package asset at the .ipa. Two constraints bite: the install daemon does not follow redirects, so the asset URL has to be the object URL itself rather than a pointer at your own API, and it carries none of the browser's cookies, so any authorization has to travel in the URL — which is exactly why a signed URL works and a session-gated Drive file does not. What you still do not get is an install page, a QR code, expiry, or any record of who installed.
How do I know whether a tester actually installed the build?
With a file-sharing link, you do not. Storage providers can at best tell you a file was downloaded; installation happens afterwards, on the device, with the provider no longer involved — so the failure that matters most, an ad-hoc build whose provisioning profile does not cover the tester's device, is invisible to you and looks to the tester like the link is broken. BetaDrop records each install attempt with its outcome, device model, OS version and country, and reports unique devices, failure count and failure reasons per build. Give each tester their own labelled share link and the device counts split by person.
When is sending a Drive link actually fine?
When the recipient is one colleague on Android who has sideloaded before, the build is not confidential, and you are sitting close enough to unblock them. Also when the file is going somewhere that is not a phone at all — an emulator, a QA machine, a security reviewer, an archive. And a domain-restricted Drive file has one real advantage over an unlisted URL: access is governed by your existing directory groups rather than by whoever holds the link. Reach for an install link when the build is for iOS, when the recipient is outside your team, or when you need to know it landed.
Does a build link expire?
A BetaDrop link expires on a schedule you set. A guest upload lasts 24 hours, a signed-in build defaults to 3 days, and a free account can push a link out to 7 days; paid plans extend it much further. Be clear about the trade in both directions: a Drive link outlives a free BetaDrop link comfortably, because it never stops at all. That is the point — an unreleased build behind a forwarded link that nobody revokes is a leak with no end date, and the fix is a deadline you chose rather than one you forgot.