3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

反射神経測定アプリ「Speed Test Reflexes」のTestFlightアップロード自動化

Posted at

I Love Claude Codeにて完全バイブコーディングにてリリースまで行ったときに得た知見を公開です!!!

はじめに

私は「Speed Test Reflexes」という反射神経測定ゲームアプリを開発・運営しています。このアプリは、光・音・振動などの刺激に対する反応速度を測定し、ユーザーの反射神経を鍛えることができるアプリです。

App Store: https://apps.apple.com/us/app/speed-test-reflexes/id6748366021

8つのゲームモード(光のみ、音のみ、振動のみ、9/16/25ボタンチャレンジ、対戦モードなど)を搭載し、測定結果は0.00001秒単位の高精度で記録されます。2025年8月にはバージョン2.0をリリースし、広告削除のアプリ内課金機能も実装しました。

このアプリの開発において、頻繁なTestFlightへのビルドアップロードが必要でした。当初はXcodeのOrganizerから手動でアップロードしていましたが、効率化のためにApp Store Connect APIを使用した自動化スクリプトを開発しました。

fastlaneがなかなかセットアップ出来なかったから苦肉の策でAIに作ってもらいました:relaxed:

本記事では、実際の開発現場で使用している、エラーハンドリングが充実したTestFlightアップロードスクリプトの実装について解説します。

Speed Test Reflexes 開発での課題

アプリ開発における頻繁なアップデートの必要性

「Speed Test Reflexes」は、以下の理由で頻繁なアップデートが必要でした:

  • iOS/Android同時開発: 両プラットフォーム間の機能パリティ維持
  • 精密なタイミング調整: ミリ秒単位の反応速度測定のための継続的な改善
  • ユーザーフィードバック対応: App Storeレビューへの迅速な対応
  • Apple審査対応: v2.0のIAP実装時は特に頻繁な修正とテストが必要

従来の方法の課題

  1. Xcodeの手動アップロード

    • 毎回GUIを操作する必要があり、時間がかかる
    • 自動化が困難
  2. fastlaneの課題

    • セットアップが複雑
    • 依存関係が多い
    • エラー時のデバッグが困難
  3. Application Loaderの廃止

    • Apple が Application Loader を廃止し、xcrun altoolへの移行を推奨

このスクリプトのメリット

  • 🚀 シンプルな実行: ./upload_testflight.sh [ipa_path] のワンコマンド
  • 🔐 セキュアな認証: App Store Connect API Key を使用(パスワード不要)
  • 🎨 視覚的なフィードバック: カラー出力で進捗が一目瞭然
  • 🛡️ 堅牢なエラーハンドリング: 問題発生時に具体的な解決策を提示
  • 📊 詳細なレポート: アップロード結果とNext Stepsを明確に表示

Speed Test Reflexes での実装

1. スクリプトの全体構造

スクリプトは以下の主要な処理フローで構成されています:

1. 設定の読み込みと検証
2. IPAファイルの存在確認
3. 依存関係のチェック
4. JWT トークンの生成
5. App Store Connect APIでアプリ情報の取得
6. xcrun altool を使用したアップロード
7. 結果の表示とエラーハンドリング

2. JWT認証の実装

App Store Connect APIでは、JWT (JSON Web Token) を使用した認証が必要です。Python スクリプト(generate_jwt.py)で実装しています:

def generate_jwt_token(key_id, issuer_id, private_key_path):
    """Generate JWT token for App Store Connect API"""
    
    # JWT header
    header = {
        "alg": "ES256",  # ECDSA with SHA-256
        "kid": key_id,    # Key ID
        "typ": "JWT"
    }
    
    # JWT payload
    payload = {
        "iss": issuer_id,                    # Issuer ID
        "iat": int(time.time()),             # Issued at
        "exp": int(time.time()) + 1200,      # Expires in 20 minutes
        "aud": "appstoreconnect-v1"          # Audience
    }
    
    # Generate JWT token
    token = jwt.encode(payload, private_key, algorithm="ES256", headers=header)
    return token

ポイント:

  • ES256(ECDSA with SHA-256)アルゴリズムを使用
  • トークンの有効期限は20分(Appleの推奨値)
  • エラーハンドリングで、鍵ファイルが見つからない場合などに対応

3. カラフルな出力によるUX向上

スクリプトの実行状況を視覚的に把握できるよう、カラー出力を実装:

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

echo -e "${BLUE}🚀 TestFlight Upload Script${NC}"
echo -e "${GREEN}✅ Found IPA file: $IPA_PATH${NC}"
echo -e "${YELLOW}📋 Checking dependencies...${NC}"

各ステップに絵文字とカラーを使用することで:

  • ✅ 成功(緑)
  • ❌ エラー(赤)
  • 📋 情報(黄)
  • 🚀 プロセス開始(青)

4. 堅牢なエラーハンドリング

スクリプトの各段階でエラーチェックを実装し、問題が発生した場合は具体的な解決策を提示:

# IPAファイルの存在確認
if [ ! -f "$IPA_PATH" ]; then
    echo -e "${RED}❌ IPA file not found: $IPA_PATH${NC}"
    echo "Please provide a valid IPA file path or run fastlane build first."
    exit 1
fi

# アップロード失敗時の詳細なエラー分析
if echo "$UPLOAD_OUTPUT" | grep -q "authentication"; then
    echo -e "${YELLOW}💡 Tip: Authentication issue detected. Please verify:${NC}"
    echo "  • API Key ID: $KEY_ID"
    echo "  • Issuer ID: $ISSUER_ID"
    echo "  • Private key file exists and is valid"
fi

if echo "$UPLOAD_OUTPUT" | grep -q "bundle"; then
    echo -e "${YELLOW}💡 Tip: Bundle issue detected. Please verify:${NC}"
    echo "  • Bundle ID matches App Store Connect: $APP_BUNDLE_ID"
    echo "  • Code signing is valid"
    echo "  • Build version is incremented"
fi

5. App Store Connect API の活用

アプリ情報の取得にApp Store Connect APIを使用:

APP_RESPONSE=$(curl -s -H "Authorization: Bearer $JWT_TOKEN" \
    -H "Content-Type: application/json" \
    "https://api.appstoreconnect.apple.com/v1/apps?filter%5BbundleId%5D=$APP_BUNDLE_ID")

APIレスポンスの解析にはPythonを使用し、JSONを安全にパース:

import sys, json
try:
    data = json.load(sys.stdin)
    if 'data' in data and len(data['data']) > 0:
        print(data['data'][0]['id'])
    else:
        print('ERROR: App not found')
        sys.exit(1)
except Exception as e:
    print(f'ERROR: {e}')
    sys.exit(1)

6. xcrun altool によるアップロード

Appleが推奨するxcrun altoolを使用してIPAファイルをアップロード:

xcrun altool --upload-app \
    --type ios \
    --file "$IPA_PATH" \
    --apiKey "$KEY_ID" \
    --apiIssuer "$ISSUER_ID" \
    --verbose 2>&1

メリット:

  • Xcodeコマンドラインツールに含まれている
  • App Store Connect API Key認証をネイティブサポート
  • 詳細なエラーメッセージを提供

セットアップ手順

1. App Store Connect API Keyの作成

  1. App Store Connect にログイン
  2. ユーザーとアクセス → キー → App Store Connect API
  3. 新しいキーを作成(Admin権限推奨)
  4. .p8ファイルをダウンロード(一度だけダウンロード可能)
  5. Key IDとIssuer IDをメモ

2. 必要な依存関係のインストール

# Python3とpipが必要
pip3 install pyjwt cryptography

# Xcode Command Line Toolsが必要
xcode-select --install

3. スクリプトの設定

upload_testflight.shの設定部分を編集:

# Configuration
KEY_ID="YOUR_KEY_ID"
ISSUER_ID="YOUR_ISSUER_ID"
PRIVATE_KEY_PATH="/path/to/AuthKey_XXXXX.p8"
APP_BUNDLE_ID="com.yourcompany.app"

4. 実行権限の付与

chmod +x upload_testflight.sh
chmod +x generate_jwt.py

使用方法

基本的な使用法

Speed Test Reflexes プロジェクトでの実際の使用例:

# ビルド番号を自動インクリメント & ビルド
bundle exec fastlane build

# TestFlightへアップロード(デフォルトパス: builds/ReflexMaster-SpeedTest.ipa)
./scripts/upload_testflight.sh

# カスタムIPAパスを指定する場合
./scripts/upload_testflight.sh builds/ReflexMaster-SpeedTest.ipa

成功時の出力例

🚀 TestFlight Upload Script
=======================================
✅ Found IPA file: builds/ReflexMaster-SpeedTest.ipa
📋 Checking dependencies...
✅ All dependencies satisfied
🔑 Generating JWT token...
✅ JWT token generated successfully
📱 Getting app information...
✅ Found app ID: 1234567890
📤 Uploading to TestFlight using xcrun altool...
🔄 Starting upload process...
This may take several minutes depending on file size and connection speed.
✅ Upload completed successfully!
📋 Upload Summary:
  • IPA File: builds/ReflexMaster-SpeedTest.ipa
  • App ID: 1234567890
  • Bundle ID: com.lft-farm.reflexes

🎉 Your app has been uploaded to TestFlight!
⏳ Processing time: Apple will process your build, which usually takes 5-15 minutes.
📱 Next steps:
  1. Check App Store Connect for processing status
  2. Once processed, you can add it to TestFlight groups
  3. Distribute to internal/external testers

🔗 Useful links:
  • App Store Connect: https://appstoreconnect.apple.com/apps/1234567890
  • TestFlight: https://appstoreconnect.apple.com/apps/1234567890/testflight

✨ Upload script completed!

Speed Test Reflexes でのCI/CD活用

本アプリの開発では、以下のようにCI/CDパイプラインに統合して使用しています:

GitHub Actions の例

name: Upload to TestFlight

on:
  push:
    tags:
      - 'v*'

jobs:
  upload:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build IPA
        run: fastlane build
        
      - name: Upload to TestFlight
        env:
          APP_STORE_CONNECT_API_KEY: ${{ secrets.API_KEY }}
          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.ISSUER_ID }}
        run: |
          echo "${{ secrets.API_KEY_P8 }}" > AuthKey.p8
          ./scripts/upload_testflight.sh builds/app.ipa

Speed Test Reflexes の Fastlane 設定

実際のプロジェクトで使用している Fastfile の一部:

lane :beta do
  increment_build_number
  
  gym(
    scheme: "Reflexes",
    export_method: "app-store",
    output_directory: "./builds",
    output_name: "ReflexMaster-SpeedTest"
  )
  
  # カスタムスクリプトでアップロード
  sh("../scripts/upload_testflight.sh ../builds/ReflexMaster-SpeedTest.ipa")
  
  # Slackへ通知(オプション)
  slack(
    message: "Speed Test Reflexes v2.0 がTestFlightにアップロードされました!",
    success: true
  )
end

トラブルシューティング

よくあるエラーと対処法

1. JWT トークン生成エラー

❌ Failed to generate JWT token

対処法:

  • pyjwtcryptographyがインストールされているか確認
  • Private Key ファイルのパスが正しいか確認
  • Private Key ファイルの読み取り権限を確認

2. 認証エラー

❌ Upload failed!
💡 Tip: Authentication issue detected

対処法:

  • API Key IDとIssuer IDが正しいか確認
  • API Keyの権限(Admin推奨)を確認
  • Private Keyファイルが正しい形式か確認

3. Bundle IDエラー

❌ Upload failed!
💡 Tip: Bundle issue detected

対処法:

  • IPAファイルのBundle IDがApp Store Connectと一致するか確認
  • ビルドバージョンがインクリメントされているか確認
  • Code Signingが正しく設定されているか確認

セキュリティ上の注意点

  1. Private Keyの管理

    • .p8ファイルをGitリポジトリにコミットしない
    • 環境変数や秘密管理ツールを使用する
    • ファイルのアクセス権限を制限する(chmod 600
  2. API認証情報の保護

    • Key IDとIssuer IDも機密情報として扱う
    • CI/CDではSecretsを使用する
  3. トークンの有効期限

    • JWTトークンは20分で期限切れになる設計
    • 長時間実行されるプロセスでは再生成が必要

Speed Test Reflexes 開発での成果

このスクリプトの導入により、Speed Test Reflexes の開発効率が大幅に向上しました:

導入前後の比較

導入前(手動アップロード)

  • アップロード時間: 約10-15分/回
  • エラー発生時の対処: 30分以上
  • 1日のアップロード可能回数: 3-4回

導入後(自動化スクリプト)

  • アップロード時間: 約3-5分/回
  • エラー発生時の対処: 即座に原因特定
  • 1日のアップロード可能回数: 10回以上

実際の効果

  1. v2.0開発時の効率化

    • IAP実装で頻繁なApple審査対応が必要でしたが、迅速な修正とテストが可能に
    • 審査却下から再提出まで2時間以内で対応
  2. 品質向上

    • テスターへの配信頻度が上がり、フィードバックサイクルが短縮
    • バグ修正版を即座に配信可能
  3. 開発者体験の向上

    • アップロード作業のストレスから解放
    • 開発に集中できる時間が増加

まとめ

Speed Test Reflexes の開発を通じて作成したこのTestFlightアップロードスクリプトは、以下の特徴により、効率的で信頼性の高いデプロイメントプロセスを実現しています:

  1. App Store Connect APIを使用した最新の認証方式
  2. 視覚的にわかりやすいカラー出力とプログレス表示
  3. 詳細なエラーハンドリングと解決策の提示
  4. CI/CDへの統合が容易
  5. セキュアな認証(パスワード不要)

特に頻繁なアップデートが必要なアプリ開発において、このスクリプトは開発効率を大幅に向上させます。TestFlightへのアップロードを自動化・効率化したい方は、ぜひこのスクリプトを活用してみてください。

参考リンク


Author: Futoshi Okazaki
App: Speed Test Reflexes
Company: LFT Inc. (LFT株式会社)

Speed Test Reflexes をぜひダウンロードして、あなたの反射神経を測定してみてください!

本プロジェクトで使っているシェル完全版が欲しい方がいたらコメント下さい。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?