動態

詳情 返回 返回

在企業內部分發 iOS App 時如何生成並使用 manifest.plist - 動態 詳情

前言

最近在給同事講企業簽名分發流程時,發現很多人卡在一個點上:如何正確生成 manifest.plist 並把它和 .ipa 一起託管,讓同事或測試設備通過 itms-services 一鍵安裝。本文把完整思路、常見坑、生產環境注意點都聊清楚,並給出一個可運行的 Python Demo,可以直接用來生成 manifest.plist

寫得儘量口語化、接近日常工作交流;每一部分都儘量講清楚“為什麼要這麼做”以及“怎麼做”,並配上示例。

為什麼需要 manifest.plist,它做了什麼

iOS 的企業內部分發(Enterprise Distribution)通過一個特殊的 URL 協議 itms-services://?action=download-manifest&url=<manifest_url> 觸發安裝。系統讀取你提供的 manifest.plist 來知道要下載哪一個 .ipa,以及這個應用的 bundle identifier、版本、名稱等元信息。

換句話説,manifest.plist 是安裝器的“導航文件”——iOS 不是直接下載 .ipa,而是先下載並解析 plist,然後據此去拿 .ipa。另外必須滿足兩點:

  • manifest.plistipa 都必須通過 HTTPS 提供(真實有效的證書);
  • manifest.plist 的結構和字段要準確,否則 iOS 會報錯或不安裝。

生產環境最容易忽略的點就是:HTTPS 必須是真實可信的 CA 證書(自簽名/不信任證書在 iOS 上通常無法安裝應用),以及 manifest 的 URL 必須可訪問且返回正確的 Content-Type。下面細講結構與示例。

manifest.plist 示例與字段解析(逐項説明)

這是一個最常見的 manifest.plist(把你的 ipa 地址、bundle id 等替換為真實值):

<?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>https://yourserver.com/path/to/app.ipa</string>
        </dict>
      </array>
      <key>metadata</key>
      <dict>
        <key>bundle-identifier</key>
        <string>com.company.app</string>
        <key>bundle-version</key>
        <string>1.0.0</string>
        <key>kind</key>
        <string>software</string>
        <key>title</key>
        <string>CompanyApp</string>
      </dict>
    </dict>
  </array>
</dict>
</plist>

字段説明(很重要):

  • items:根數組,通常只放一條描述一個 app。
  • assetskind: software-package:表示這是一個應用包;
  • assetsurl:指向 .ipa 的 HTTPS 地址(必須能通過瀏覽器直接訪問並用有效證書);
  • metadatabundle-identifier:要跟你應用的 Bundle Identifier 一致(Xcode 中的那個);
  • metadatabundle-version:App 的版本號(CFBundleShortVersionString);
  • metadatatitle:在安裝對話框裏顯示的應用名字。

如果某個字段錯了(比如 bundle id 不一致、或者 ipa 地址不可達),iOS 會提示“無法安裝”或“無法檢索清單”。

手工生成 manifest.plist(簡單場景)

你可以直接按上面的模板把 manifest.plist 寫好,保存為 manifest.plist,上傳到 HTTPS 可訪問的服務器上,然後用如下鏈接觸發安裝:

itms-services://?action=download-manifest&url=https://yourserver.com/manifest.plist

如果想方便點,可以把上述鏈接做成頁面上的按鈕或二維碼,用户用 Safari 打開後就會提示安裝。

但手寫容易出錯,建議用腳本自動生成(下面有可運行 Demo)。

可運行 Demo:用 Python 自動生成 manifest.plist(推薦)

下面是一個小腳本,能把你傳入的參數寫成合規的 manifest.plist。用 Python 的 plistlib 來生成,少出錯。保存為 gen_manifest.py

#!/usr/bin/env python3
"""
gen_manifest.py
生成 manifest.plist 的小工具。

用法示例:
python3 gen_manifest.py \
  --ipa-url https://example.com/builds/app.ipa \
  --bundle-id com.company.app \
  --version 1.2.3 \
  --title "Company App" \
  --output manifest.plist
"""

import argparse
import plistlib
import sys
from pathlib import Path

def build_manifest(ipa_url: str, bundle_id: str, version: str, title: str):
    manifest = {
        "items": [
            {
                "assets": [
                    {
                        "kind": "software-package",
                        "url": ipa_url
                    }
                ],
                "metadata": {
                    "bundle-identifier": bundle_id,
                    "bundle-version": version,
                    "kind": "software",
                    "title": title
                }
            }
        ]
    }
    return manifest

def main():
    parser = argparse.ArgumentParser(description="Generate manifest.plist for iOS enterprise distribution")
    parser.add_argument("--ipa-url", required=True, help="Public HTTPS URL to the .ipa")
    parser.add_argument("--bundle-id", required=True, help="Bundle identifier, e.g. com.company.app")
    parser.add_argument("--version", required=True, help="App version, e.g. 1.0.0")
    parser.add_argument("--title", required=True, help="App title shown during install")
    parser.add_argument("--output", default="manifest.plist", help="Output plist filename")
    args = parser.parse_args()

    manifest = build_manifest(args.ipa_url, args.bundle_id, args.version, args.title)
    out_path = Path(args.output)
    with out_path.open("wb") as f:
        plistlib.dump(manifest, f)
    print(f"Generated {out_path.resolve()}")

if __name__ == "__main__":
    main()

説明與使用示例:

  1. gen_manifest.py 放到你的機器上,確保 Python 3 可用;
  2. 執行(替換 URL、bundle id、版本、title):

    python3 gen_manifest.py \
      --ipa-url https://cdn.example.com/apps/app-1.2.3.ipa \
      --bundle-id com.example.myapp \
      --version 1.2.3 \
      --title "Example App" \
      --output manifest.plist
  3. 腳本會生成 manifest.plist,上傳該文件與對應 .ipa 到你的 HTTPS 可訪問服務器後,用 itms-services://... 鏈接測試安裝。

這個腳本足夠輕量,也方便你把它嵌入 CI:在 CI 打包出 .ipa 並上傳到 S3 或公司內網後,CI 調用此腳本生成 manifest 並上傳,最後把安裝鏈接發給 QA。

在 CI 中自動打包導出 .ipa(xcodebuild 示例)

如果你想在 CI 中自動從 Xcode archive 導出 enterprise .ipa,可以用 xcodebuild -exportArchive 配合一個 exportOptions.plist,示例如下。

exportOptions.plist(保存為 XML plist)主要關鍵字段是 method = enterprise,並指定 provisioning profile:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>method</key>
  <string>enterprise</string>
  <key>teamID</key>
  <string>YOUR_TEAM_ID</string>
  <key>provisioningProfiles</key>
  <dict>
    <key>com.example.myapp</key>
    <string>My Enterprise Provisioning Profile Name</string>
  </dict>
</dict>
</plist>

然後在機器上運行(假設已用 xcodebuild archive 得到 MyApp.xcarchive):

xcodebuild -exportArchive \
  -archivePath /path/to/MyApp.xcarchive \
  -exportOptionsPlist /path/to/exportOptions.plist \
  -exportPath /path/to/output_dir

導出後 output_dir 下會有 .ipa,把它上傳到 HTTPS 託管後,再生成 manifest。

如果你使用 Fastlane,在 Fastfile 裏用 gym/sigh 幫助自動化也很常見。

託管 manifest.plist 與 ipa:生產環境建議(S3 / nginx 示例)

無論你把文件放哪裏,兩個硬性條件必須滿足:

  1. 訪問地址是 HTTPS(有效證書)
  2. manifest.plist.ipa 的 Content-Type 設置合理,方便調試時確認。

示例:用 AWS S3 + CloudFront(常見、穩定)

# 上傳 ipa,指定 Content-Type
aws s3 cp app.ipa s3://my-bucket/path/app-1.2.3.ipa --acl public-read --content-type application/octet-stream

# 上傳 manifest
aws s3 cp manifest.plist s3://my-bucket/path/manifest.plist --acl public-read --content-type application/xml

然後通過 CloudFront 或自定義域名配 TLS(ACM)暴露 https://dl.company.com/path/manifest.plist,拼裝 itms-services 鏈接。

如果你用 nginx 自建分發,注意 server 配置示例(關鍵點:設置 MIME,HTTPS):

server {
    listen 443 ssl;
    server_name dl.company.com;

    ssl_certificate /etc/ssl/certs/your-cert.pem;
    ssl_certificate_key /etc/ssl/private/your-key.pem;

    location /apps/ {
        root /var/www;
        # 強制使用正確 content-type(可選)
        types {
            application/octet-stream ipa;
            application/xml plist;
        }
        add_header Cache-Control "max-age=3600";
    }
}

注意事項:

  • manifest.plist 的 Content-Type 用 application/xmltext/xml 都沒問題;
  • .ipa 推薦 application/octet-stream
  • 一定要用真實 CA 證書,iOS 設備對不受信任證書會拒絕安裝。

安裝流程(最終用户體驗)

  1. 用户在 iOS 設備的 Safari 打開如下鏈接(通常是放在網頁裏的按鈕或二維碼):

    itms-services://?action=download-manifest&url=https://dl.company.com/path/manifest.plist
  2. Safari 會跳轉到系統安裝對話框,用户確認後系統開始下載並安裝。
  3. 第一次在該設備上用該企業證書安裝後,用户需要在 設置 → 通用 → 描述文件與設備管理(或“設備管理”)裏手動“信任”企業證書(部分 iOS 版本會在安裝時同時提示),這是 iOS 的安全策略。

在企業內測場景中,最好有一份安裝説明告訴用户如何信任證書。

常見問題與排查思路

  • iOS 顯示“無法安裝/未能檢索清單”

    • 檢查 manifest.plist 是否可通過 HTTPS 訪問(手機能直接打開);
    • 檢查 manifest 的根節點、字段是否完整(bundle id、bundle version、url)且無語法錯誤;
    • 檢查 ipa URL 是否返回 200,並且不是返回 HTML 頁面或 302 跳轉(iOS 可能不跟重定向);
    • 檢查 HTTPS 證書是否受信任(生產必須使用受信任 CA 證書);
    • 檢查 bundle id 是否與 ipa 內的匹配(比如你簽名時用的描述文件/證書是否對應該 bundle id)。
  • 安裝完成但 App 打不開/閃退

    • 檢查企業簽名是否包含私鑰並且沒有被撤銷;
    • 檢查 Provisioning Profile 是否包含所用證書/正確的 entitlements;
    • 查看設備 log(通過 Xcode 的 Devices 窗口)查看崩潰或簽名錯誤信息。
  • 企業證書被吊銷導致無法安裝

    • 定期審計證書使用,避免濫用;若被吊銷,需儘快重新申請企業證書並重新打包分發。

安全與合規提醒(重要)

  • Apple 對企業簽名分發管控嚴格,企業賬號僅允許企業內部分發給公司員工和受管控的設備,不得對外公開分發給任意用户;濫用可能導致證書被吊銷。
  • 在公司內部分發時,記錄好每次分發的版本、時間和接收方,便於追蹤。建議結合 MDM(移動設備管理)來統一管理應用/設備。
  • 證書與私鑰需要被妥善保管,CI 系統中的私鑰導入需做好權限隔離與密鑰週期更換策略。

總結

  • manifest.plist 是 iOS 企業內部分發的關鍵導航文件,必須通過 HTTPS 提供且格式正確;
  • 推薦用腳本自動化生成 manifest(示例提供了 Python 腳本),把生成過程放入 CI 流水線;
  • 生產環境要保證 .ipamanifest.plist 都託管在 HTTPS(可信)地址上,推送時注意正確的 Content-Type;
  • 測試安裝前,先用 iOS 設備在 Safari 裏打開 manifest.plist 的 URL,確認能訪問;
  • 最後,企業簽名合規問題不能忽視:證書管理、使用範圍、風險應對都要有流程。
user avatar tingzhu_guo 頭像
點贊 1 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.