🍏

AltStore PALでアプリを配信してみる

に公開

EUにあるiOS/iPadOSではApp Store以外のアプリストア「代替アプリマーケットプレイス」が利用できるようになっています。日本でもスマホ競争促進法によって2025年内には使えるようになる予定です。

面白そうなので触りたい。他に触ってる人を日本で見たこともないし、せっかくなのでAltStore PALに配信するまでの過程を記事にしようと思います。

最初に知るべきこと

本題に入る前にいくつか知識を入れておきましょう。中には金銭に関わるものも含まれるため、試そうと思っている場合は必ず把握しておいた方が良いです。

  1. AltStore PALが何なのか知らない人は調べてください。
  2. AltStore PALで配信するには、アプリのパッケージとソース(リポジトリのようなもの)を自分でホストする必要があります。ソースの書き方はあとで説明します。
  3. 代替アプリマーケットプレイスが利用可能な地域からでないと、直接インストール周りのテストをすることはできません(配信は可能です)。
  4. App Storeに配信しない場合でも、Apple Developer Programに登録する必要があります。もちろん有料です。
  5. 代替アプリマーケットプレイスにのみ配信する場合はApp Storeのような厳格な審査はありませんが、Appleによる公証(Notarization)は受ける必要があります。
  6. 代替アプリマーケットプレイスで配信をするためには「EUにおけるアプリに関する新しい規約の付属文書」に同意する必要があります。この規約にはAppleに支払う手数料に関して重大な変更が含まれており、簡単に説明はしますがご自身の責任のもとで本文も確認してください。

コア技術料(CTF)

これは対象地域におけるアプリのインストール数に応じて課される追加の手数料です。詳しく知りたい場合は付属文書を読むか、サポートページを確認しましょう。

https://developer.apple.com/jp/support/core-technology-fee/

対象地域に住むユーザーが同じApple Accountで過去1年間でアプリを1回以上インストールした回数を、「年間初回インストール」といいます。これが100万回を超えた場合、それ以降の1インストールごとに€0.50をAppleに支払う必要があります。年間初回インストールの対象になるインストールの種類は以下のページで確認できます。

https://developer.apple.com/jp/help/app-store-connect/understanding-the-core-technology-fee/measure-your-first-annual-installs

もしも何かの拍子(SNS等で話題になるなど)で対象地域での総インストール数が100万を突破してしまい、かつ収益が不十分だった場合は損失になります。趣味やOSSのアプリを開発しているデベロッパーにとっては、不必要にリスクを負うことになると思います。

一方で、CTFには以下のように免除される条件もあります。

  • 年間初回インストールが100万に満たない場合
  • 非営利団体、認定教育機関、政府機関などで、Apple Developer Programの免除を受けている場合
  • 全世界で、アプリを通して商業的な収益(有料アプリ、App内課金、サブスク、広告収入、物販などすべて)を得ていない非商業のデベロッパーの場合
  • さらに条件を満たす小規模事業者は最大3年間CTFを免除される(ややこしいので詳しくはサポートを読んでください)

一般のデベロッパーであれば、CTFを支払わなくて済む方法で配信をしたいのであれば、アプリからの収益は完全に0にする必要があります。注意すべきなのは、収益を得ているという判定は全世界でされること、すでに収益の一部を徴収されているApp Storeからの収益も含むこと、App内課金などに限らず広告収入やアプリ内の物販なども含むことです。

追記: このCTF、来年2026年にCore Technology Commission (CTC)というものに移行されるようです。CTCへの移行の詳細についてはまだ公表されていませんが、CTFと同様に1インストールごとにかかる料金は存続するようです。

「EUにおけるアプリに関する新しい規約の付属文書」に同意する

以下のページで規約に同意できます。

https://developer.apple.com/contact/request/alternative-eu-terms-addendum/

ちなみにこれ以降のことは大体AltStore PALのFAQにも書かれています。

AltStore PALを代替アプリマーケットプレイスに追加する

REST APIを利用してデベロッパーIDとメールアドレスを送信します。

https://faq.altstore.io/developers/rest-api#register-developer-id

App Store ConnectでデベロッパーIDを確認してから、以下のコマンドを実行します。

curl --header "Content-Type: application/json" \
  -X POST \
  --data '{ 
    "developerID": "[Your Apple Developer ID]", 
    "email": "[Your Email Address]"
  }' \
  https://api.altstore.io/register

受け取ったセキュリティトークンをApp Store Connectで入力します。この時、代替アプリマーケットプレイスで配信するアプリも選択します(後から変更可能)。

代替アプリマーケットプレイスへの通知を有効にすると、アプリが審査を通過した時に後で紹介する各ストア側の処理を自動で開始させることができます。この「ストア側の処理」についてもあとで説明しますが、とりあえず有効にしておいて良いと思います。

公証(Notarization)を受ける

前述の通り、App Storeに配信しない場合でもAppleによる審査を受ける必要がありますが、ガイドラインはかなり緩和されています。App Storeにも配信している場合はこの手順をスキップできます。

https://developer.apple.com/jp/help/app-store-connect/managing-alternative-distribution/submit-for-notarization

App Storeにアプリを配信しない場合でも、いままでと同じようにApp Store Connectにアプリを追加して詳細情報を入力します。このとき「レビュータイプ」を「認証」にすることで、代替アプリストア専用の審査に切り替えることができます。

審査を通過すると、そのバージョンには「代替配信パッケージID」が付与されます。これはあとで使用します。

代替配信パッケージをAltStore PALに処理してもらう

アプリはAppleに加えて各ストア側でも処理してもらう必要があります。AltStoreでのやり方は以下のページにも書いてありますが、一応説明しようと思います。

https://faq.altstore.io/developers/adp-rest-api#download-adp

以下の手順でパッケージの処理を手動で開始します。(App Store Connectで代替アプリマーケットプレイスを追加する際に、審査通過後に自動で処理を開始するように設定できます。)

curlが使えるシェルで以下のコマンドを実行します。

curl --header "Content-Type: application/json" \
  -X POST \
  --data '{ "adpID": "[Your ADP ID]" }' \
  https://api.altstore.io/adps

[Your ADP ID]のところに前述の代替配信パッケージIDを入力します。

アプリのサイズにもよるかもしれませんが概ね3-5分くらいで処理が完了するので、以下のコマンドでパッケージのダウンロードリンクを取得します。

curl -X GET https://api.altstore.io/adps/[Your ADP ID] 

statussuccessになっていれば処理が完了しているため、downloadURLをブラウザ等に入力すればパッケージをダウンロードできます。(Macのターミナルからブラウザの検索窓に直接コピペすると、エスケープのせいで正しくアクセスできないので注意)

ダウンロードしたパッケージは別の場所で自分でホストする必要があります。Zipファイルを展開して、ディレクトリの構造を変更せずにそのままWeb上に公開します。HTTPSでダウンロードできる状態にしておいてください。

ソースを作る

AltStore PALの前身、AltStore Classic時代からあるもので、パッケージマネージャーのソース(リポジトリ)と同じような感じです。公式のドキュメントも紹介しておきます。

https://faq.altstore.io/developers/make-a-source

あと私のソースも参考適度に覗いてみてください: https://i.cizzuk.net/altstore/source.pal.json

ソースのメタデータ

ソースはJSON形式です。ベースを作ってみましょう。

{
  "name": "My Example Source",
  "subtitle": "A source for all of my apps",
  "description": "Welcome to my source! Here you'll find all of my apps.",
  "iconURL": "https://example.com/source_icon.png",
  "headerURL": "https://example.com/source_header.png",
  "website": "https://example.com",
  "tintColor": "#F54F32",
  "featuredApps": [
    "com.example.myapp",
    "com.example.anotherapp"
  ],
  "apps": [],
  "news": []
}

nameapps以外はすべてオプションです。

  • nameにはソースの名前を入れます。
  • subtitleはサブタイトルで、名前の下に表示されます。
  • descriptionはソースの説明文で、ソースの最下部に表示されます。URLを入れることもできます。
  • iconURLはオプションですが、視認性のために設定することをおすすめします。設定しない場合は最初のアプリがアイコンになります。
  • headerURLはヘッダーです。ユーザーの操作がない限りぼかされていてほとんど見ることはありません。設定しない場合はアイコンになります。
  • websiteはサブタイトルの下に表示されるリンクです。
  • tintColorはソースのテーマカラーで16進数のカラーコードを入力します。見やすい色にしましょう。
  • featuredAppsはおすすめのアプリで最大5つ設定できます。ソースの目立つところに表示されます。
  • appsはこのあと説明します。newsは割愛します。

アプリを追加

apps内は以下のような感じです。

"apps": [
    {
        "name": "My Example App",
        "bundleIdentifier": "com.example.myapp",
        "marketplaceID": "12345678",
        "developerName": "Example Developer",
        "subtitle": "An awesome app.",
        "localizedDescription": "This is an awesome app only available on AltStore.",
        "iconURL": "https://example.com/myapp_icon.png",
        "tintColor": "#F54F32",
        "category": "utilities",
        "screenshots": [
            "https://example.com/myapp_screenshot1.png",
            "https://example.com/myapp_screenshot2.png",
            "https://example.com/myapp_screenshot3.png"
        ],
        "versions": [],
        "appPermissions": {
            "entitlements": [
                "com.apple.security.application-groups",
                "com.apple.developer.siri"
            ],
            "privacy": {
                "NSMicrophoneUsageDescription": "App uses the microphone to record audio.",
                "NSCameraUsageDescription": "App uses the camera to take photos."
            }
        }
    }
]

先に紹介したものは省略します。

  • bundleIdentifierはアプリのBundleIDです。Info.plistCFBundleIdentifierとかXcodeのプロジェクトの設定で確認できます。
  • marketplaceIDはApp Store ConnectのApp情報から確認できるApple IDです。
  • developerNameは開発者の名前です。OSSでたくさんの人が関わっている時などは「[開発者名] & Various Contributors」と書くことも多いです。
  • localizedDescriptionはアプリの説明です。URLを入れることもできます。
  • categoryはオプションで、以下の中のどれかひとつのカテゴリを設定します。
    • developer
    • entertainment
    • games
    • lifestyle
    • other
    • photo-video
    • social
    • utilities
  • screenshotsはオプションで、例のように簡単に設定することもできますが、iPad向けに別の画像を用意したり横向きの画像を設定することもできます。詳しくは公式のドキュメントを参考にしてください。
  • versionsはこのあと説明します。
  • appPermissionsにはアプリのEntitlementsとプライバシーに関する情報を入力します。以下2つのEntitlementsはすべてのアプリに必ず含まれるものであるため省略できます。そのほかは必須です。EntitlementsはApp Store Connectでビルドのメタデータから確認することができます。
    • com.app.developer.team-identifier
    • application-identifier

バージョンを追加

最後にバージョンを追加します。初回リリースとアップデートの管理に利用します。

"versions": [
  {
    "version": "1.0",
    "buildVersion": "60",
    "date": "2023-03-30",
    "localizedDescription": "First AltStore release!",
    "downloadURL": "https://example.com/adp",
    "size": 79821,
    "minOSVersion": "17.4"
  }
]

アップデートを配信するたびに、配列に要素を追加します。新しいバージョンは配列の先頭に追加してください。

  • versionbuildVersionにはInfo.plistに書かれているものと同じバージョンとビルド番号をそれぞれ記載します。基本的に両方がユーザーに表示されますが、marketingVersionを設定すると好きな文字列に表示上は変更できます。
  • dateはバージョンを公開した日付をISO 8601フォーマットで記述します。
  • downloadURLは代替配信パッケージへのURLを入力します。
  • sizeはアプリのサイズです。ユーザーの環境によってサイズが異なってしまうため正確にはなりません。Xcodeから出力したipaファイルのサイズとかApp Storeに記載されているサイズとかで良いと思います。単位は1バイトです。
  • minOSVersionはオプションで、iOS/iPadOSの最小要件です。AltStoreは非対応バージョンを非表示にするので設定すべきです。
  • maxOSVersionもオプションで追加できます。iOS/iPadOSの上限です。Classic時代の名残で、ほとんどのアプリには必要ないはずです。

これでソースは完成したので、Web上にホストしてHTTPSでダウンロードできるようにします。

完成したソースのURLをユーザーに公開すれば、アプリをダウンロードできるようになります。

おまけ: AltStore PALのURLスキーム

ソースのURLをそのまま公開すると、ユーザー視点ではいきなりJSONファイルを見せられて意味不明な状態になります。以下は私のソースです。

https://i.cizzuk.net/altstore/source.pal.json

セキュリティ的に若干微妙ではありますが、AltStore PALを自動で開くURLスキームがあるので紹介しておきます。

スキームはaltstore-pal://で、パスはsource、クエリのurlにソースのURLを入れてあげれば完成です。以下のような感じになります。

altstore-pal://source?url=https://i.cizzuk.net/altstore/source.pal.json

  • 2025/04/20 修正: AltStoreからセキュリティトークンを受け取る方法が変更されたため編集しました。
  • 2025/08/17 修正
  • 2025/09/07 更新
  • 2025/10/10 更新: 一部の文言や表現を変更しました。AltStore Classicでのテストに関する部分を削除しました。

Discussion