Installing iOS Apps Internally: All You Need Is A Manifest

This guide briefly shows what is necessary to allow your internal users to install your iOS app on their devices without using the App Store or TestFlight.

Published on Thu, August 15, 2024

There are scenarios where you don't want Apple to have a say in whether a user is allowed to install your app or not. This is especially true for internal testing prior to shipping it to the App Store or even a broader audience via TestFlight. For internal testing purposes, you can sign your app with an enterprise certificate or use a development certificate for a limited number of devices.

However, even after you've done everything necessary, you still need to provide a way for your users to download and install the app. For Android APKs, it is as easy as providing a download link. For iOS apps, a bit more work is needed.

Provide A Manifest

Unlike for Android APKs, you can't target the IPA file directly for download. Instead, you need to provide a manifest file that points to the IPA file. The manifest file is an XML file that contains information about the app and the location of the IPA file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>items</key>
  <array>
    <dict>
      <key>assets</key>
      <array>
        <dict>
          <key>kind</key>
          <string>software-package</string>
          <key>url</key>
          <string>${downloadPath}</string>
        </dict>
      </array>
      <key>metadata</key>
      <dict>
        <key>bundle-identifier</key>
        <string>${bundleIdentifier}</string>
        <key>bundle-version</key>
        <string>${bundleVersion}</string>
        <key>kind</key>
        <string>software</string>
        <key>title</key>
        <string>${appName}</string>
      </dict>
    </dict>
  </array>
</dict>
</plist>

You need to provide ${downloadPath} which is the URL to the IPA file, ${bundleIdentifier} which is the bundle identifier of your app, ${bundleVersion} which is the version of your app, and ${appName} which is the name of your app. Then you link to that manifest instead of the IPA file in your download link.

That's it. You can create that manifest either statically, e.g., during the build process, or dynamically.

What do you think?
Drop me a line and let me know!