mcpField
M
macos-auto-update

macos-auto-update

github.com/fayazara開発685574 回インストール22 週間前に更新
リポジトリ

macos-auto-update について

ネイティブ macOS アプリに Sparkle の自動アップデートを追加するスキルです。依存関係、アップデーター管理、アプリ設定、更新 UI の組み込みを説明します。

主な機能

  1. 01SPM による Sparkle の追加
  2. 02UpdaterManager の実装案内
  3. 03Info.plist の更新設定
  4. 04設定画面とメニューの更新操作

活用シーン

  1. 01macOS アプリへの更新確認の追加
  2. 02Sparkle フィードと署名キーの設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/fayazara/macos-app-skills --skill macos-auto-update

インストール後、Claude Code で/macos-auto-updateを入力して読み込まれたか確認します。

同梱ファイルreferences/UpdaterManager.swift

詳細

macOS アプリ向け Sparkle 自動アップデート

このスキルは、ネイティブ macOS アプリに Sparkle による自動アップデート機能を追加します。Sparkle は、Mac App Store 以外で配布する macOS アプリのアップデートに使われる標準的なオープンソースフレームワークです。

概要

実装は次の 4 つの部分で構成されます。

  1. SPM 依存関係 -- Xcode プロジェクトに Sparkle パッケージを追加します
  2. UpdaterManager.swift -- SPUStandardUpdaterController をラップするシングルトンです
  3. Info.plist キー -- SUFeedURL と SUPublicEDKey です
  4. UI との統合 -- 設定画面/メニューバーに「アップデートを確認」ボタンを追加します

ステップ 1:SPM 経由で Sparkle を追加する

Xcode で「File」>「Add Package Dependencies」を選択し、次を入力します。

https://github.com/sparkle-project/Sparkle

「Up to Next Major Version」ルールを使用し、2.0.0 を指定します。Sparkle フレームワークをアプリのターゲットに追加します。

または、プロジェクトで Package.swift を使用している場合は、そこに追加します。

.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.0.0")

ステップ 2:UpdaterManager.swift を作成する

references/UpdaterManager.swift をプロジェクトにコピーします。これは、次の機能を持つシングルトンです。

  • SPUStandardUpdaterController を早い段階で作成します(applicationDidFinishLaunching が戻る前)。
  • UI バインディング用に canCheckForUpdates を公開します。
  • automaticallyChecksForUpdates の切り替え機能を提供します。
  • DEBUG ビルドではアップデート関連のロジックをすべてスキップします(開発中にアップデートのプロンプトが表示されないようにします)。
  • メニューバーのみのアプリでは、アップデートウィンドウを表示する前にアクティベーションポリシーを一時的に .regular に切り替えます。

このファイルの主な設計上の判断は次のとおりです。

  • イニシャライザーでは startingUpdater: false を指定し、applicationDidFinishLaunching で明示的に start() を呼び出します。これにより、タイミングを制御できます。
  • start() と checkForUpdates() に DEBUG ガードを設けています。Sparkle はデバッグビルドでは実行しないでください。デバッグ用アプリがリリースビルドでアップデートされてしまいます。
  • Sparkle の KVO から Combine の publisher(for:) ブリッジを使用する必要があるため、@Observable ではなく @Published を使った ObservableObject を使用します。

ステップ 3:Info.plist を設定する

アプリの Info.plist に次のキーを追加します。

<key>SUFeedURL</key>
<string>https://raw.githubusercontent.com/OWNER/REPO/main/appcast.xml</string>

<key>SUPublicEDKey</key>
<string>YOUR_PUBLIC_EDDSA_KEY_HERE</string>

<key>SUEnableInstallerLauncherService</key>
<true/>
EdDSA キーの生成

Sparkle は EdDSA(Ed25519)署名を使用します。キーペアを生成します。

# Find generate_keys in your DerivedData after building the project with Sparkle
find ~/Library/Developer/Xcode/DerivedData -name "generate_keys" -type f 2>/dev/null | head -1

実行します。

/path/to/generate_keys

公開鍵が出力され、秘密鍵が Keychain に保存されます。公開鍵を Info.plist の SUPublicEDKey に設定します。秘密鍵は Keychain に保存されたままになり、リリース時に sign_update で使用されます。

ステップ 4:アプリに組み込む

App Delegate
final class AppDelegate: NSObject, NSApplicationDelegate {
    private let updaterManager = UpdaterManager.shared

    func applicationDidFinishLaunching(_ notification: Notification) {
        updaterManager.start()
    }
}

アプリの起動が完了する前に SPUStandardUpdaterController が作成されるよう、UpdaterManager.shared プロパティには早い段階でアクセスする必要があります。AppDelegate のプロパティから参照すれば、これを確実に行えます。

設定 UI(About ペイン)
struct AboutSettingsPane: View {
    @ObservedObject private var updaterManager = UpdaterManager.shared

    var body: some View {
        Form {
            Section("Updates") {
                Toggle(isOn: Binding(
                    get: { updaterManager.automaticallyChecksForUpdates },
                    set: { updaterManager.automaticallyChecksForUpdates = $0 }
                )) {
                    Text("Automatically check for updates")
                }

                Button("Check for Updates...") {
                    updaterManager.checkForUpdates()
                }
                .disabled(!updaterManager.canCheckForUpdates)
            }
        }
        .formStyle(.grouped)
        .scrollContentBackground(.hidden)
    }
}
メニューバー(任意)
Button {
    updaterManager.checkForUpdates()
} label: {
    Label("Check for Updates...", systemImage: "arrow.down.circle")
}
.disabled(!updaterManager.canCheckForUpdates)

ステップ 5:初期 appcast を作成する

リポジトリのルートに appcast.xml を作成します。最初は空で、リリースプロセスによって内容が追加されます。

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>YourApp</title>
    <description>Most recent changes for YourApp.</description>
    <language>en</language>
  </channel>
</rss>

このファイルを GitHub(raw URL)または任意の静的ファイルホストで公開します。URL は Info.plist の SUFeedURL と一致する必要があります。

メニューバーのみのアプリ

アプリが .accessory(Dock アイコンなし)として動作する場合、一時的に .regular に切り替えないと Sparkle のアップデートウィンドウは表示されません。リファレンスの UpdaterManager は、checkForUpdates() でこの処理を行います。

func checkForUpdates() {
    NSApp.setActivationPolicy(.regular)
    NSApp.activate(ignoringOtherApps: true)
    controller.checkForUpdates(nil)
}

アップデートウィンドウが閉じると、アプリは .accessory に戻ります(既存のアクティベーションポリシーマネージャーが処理します)。

Hardened Runtime のエンタイトルメント

アプリで Hardened Runtime(公証に必須)を使用する場合、Sparkle 固有のエンタイトルメントは必要ありません。Sparkle 2.x は、標準の Hardened Runtime 設定で動作します。

Appcast アイテム形式

appcast の各リリースは、次のような形式です(リリースツールを作成する際の参考)。

<item>
  <title>Version 1.2 (Build 5)</title>
  <pubDate>Mon, 26 May 2026 12:00:00 +0000</pubDate>
  <sparkle:version>5</sparkle:version>
  <sparkle:shortVersionString>1.2</sparkle:shortVersionString>
  <sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>
  <description><![CDATA[<ul><li>New feature</li><li>Bug fix</li></ul>]]></description>
  <enclosure url="https://github.com/OWNER/REPO/releases/download/v1.2/YourApp.dmg"
             type="application/octet-stream"
             sparkle:edSignature="BASE64_EDDSA_SIGNATURE"
             length="FILE_SIZE_BYTES" />
</item>
  • sparkle:version = CFBundleVersion(ビルド番号)
  • sparkle:shortVersionString = CFBundleShortVersionString(マーケティングバージョン)
  • sparkle:edSignature = sign_update YourApp.dmg の出力
  • length = バイト単位のファイルサイズ

コメント