macos-auto-update について
ネイティブ macOS アプリに Sparkle の自動アップデートを追加するスキルです。依存関係、アップデーター管理、アプリ設定、更新 UI の組み込みを説明します。
主な機能
- 01SPM による Sparkle の追加
- 02UpdaterManager の実装案内
- 03Info.plist の更新設定
- 04設定画面とメニューの更新操作
活用シーン
- 01macOS アプリへの更新確認の追加
- 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 つの部分で構成されます。
- SPM 依存関係 -- Xcode プロジェクトに Sparkle パッケージを追加します
- UpdaterManager.swift --
SPUStandardUpdaterControllerをラップするシングルトンです - Info.plist キー --
SUFeedURLとSUPublicEDKeyです - 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= バイト単位のファイルサイズ