macOSアプリケーションの更新フレームワークSparkleの使い方

# 概要
- SparkleはオープンソースのmacOSアプリケーション用アップデート・フレームワークです。
- 今回更新用のファイルをGitHubに置くという想定で導入してみようと思います。

# 参考
- 公式のチュートリアル通りに進めます。
    - https://sparkle-project.org/documentation/
- [今更ながら、アップデートの処理に Sparkleを導入](https://blog.macfeeling.com/archives/30822)
    - 参考になります。ただし当時とクラス名などが変わっているので注意。
- [MacApp用アップデート管理システムSparkleの使い方](https://qiita.com/econa77/items/20105ab32740f669f43e)
- バージョン表記諸々の話。
    - [XcodeProjectのバージョン情報管理](https://qiita.com/satoru_pripara/items/e49d90945e977514b68d)
    - [iOSアプリのバージョン番号とビルド番号を設定](https://swift-study.com/ios-app-version-and-build/)
- 最近Swift Package Managerで管理できるようになりました。

>[オープンソースのmacOSアプリケーション用アップデート・フレームワーク「Sparkle 2\.0\.0」がリリース。](https://applech2.com/archives/20211228-sparkle-2-now-available.html)
>v1.25.0-rc1から進められていたSwift Package Managerでの管理も可能になっている

# GitHub
- 今回記事で作成したものをGitHubに配置しています。
    - https://github.com/pommdau/SparkleDemoWithSPM

# 実装
## SandBoxの解除

- 今回Sandboxは解除する前提で進めます。

![](https://i.imgur.com/h4r7148.jpg)

## Swift Packager Managerでの導入
- 下記の通り、Swift Package ManagerでSparkleをインポート
    - https://sparkle-project.org/documentation/

![](https://i.imgur.com/RyB7xlL.jpg)

## StoryBoardで設定画面の作成
### 更新確認ボタンの作成

- `Object`を追加。

![](https://i.imgur.com/zgdJyL9.jpg)

- Objectのクラスを`SPUStandardUpdaterController`に設定。

![](https://i.imgur.com/ntNLghe.jpg)

- ボタンのアクションに`checkForUpdates:`を設定。

![](https://i.imgur.com/Az1sX7L.jpg)

- ボタンを押してアクションの紐付けを確認します。
  - まだ更新の設定をしていないので当然エラーが表示されますがOKです。

![](https://i.imgur.com/GAqILbu.jpg)

### 自動更新の有無のチェックボックスの作成
- CotEditorのような`自動的にアップデートを確認`のチェックボックスを実装します。

<img width="400" alt="image" src="https://i.imgur.com/opIL9Gm.jpg">

- 下記の`Enable automatic checking`を参照し、下図の通り設定。
    - https://sparkle-project.org/documentation/preferences-ui/
    - 他にも例えば更新確認の間隔も設定できたりするので、その際は上記URLを参照。
- チェック状態はライブラリ側で管理してくれているらしく、アプリケーション側で気にする必要はないようです。

![](https://i.imgur.com/PtmyRrz.jpg)

## Spackle用の秘密鍵・公開鍵の作成
- [sparkle\-project/Sparkle: Releases](https://github.com/sparkle-project/Sparkle/releases)から下記をダウンロード。
- 秘密鍵・公開鍵作成をするためのスクリプトなどが含まれています。

![](https://i.imgur.com/rNk3MVK.jpg)

- 解答したフォルダ内の`./bin/generate_keys`を実行。
- 公開鍵がコンソール上に表示されるので控えておきます。

![](https://i.imgur.com/5GtAVcZ.jpg)

- また同時に秘密鍵がKeyChainに登録されています。
- PCを移行する際は`./bin/generate_keys -x private-key-file`と`./bin/generate_keys -f private-key-file`でそれぞれエクスポート・インポートする。

![](https://i.imgur.com/yAvQf4n.jpg)

- 公開鍵を`Info.plist`の`SUPublicEDKey`のキーとして記載します。

![](https://i.imgur.com/yvcZGA8.jpg)

## appcast.xmlのURLの紐付け

- 今回、更新情報を記載するファイル`appcast.xml`は下記に配置します。

<img width="512" alt="image" src="https://i.imgur.com/IixUrXM.jpg">

- URLとしては以下の通り。

```
https://raw.githubusercontent.com/pommdau/SparkleDemoWithSPM/main/appcast.xml
```

- このURLを`Info.plist`に`SUFeedURL`をキーとして記載します。

![](https://i.imgur.com/ULF0QrG.jpg)

## 更新用アプリケーションのビルドとdmgの作成
- 更新用のアプリケーションとして、適当にバージョンを上げてビルド。

![](https://i.imgur.com/AsHxYdS.jpg)

- dmgの作成。

![](https://i.imgur.com/0WcWpyt.jpg)

```console
hdiutil create -srcfolder "${source_folder_name}" -format UDZO -fs HFS+ \
  -volname "${dmg_folder_name}" "${dmg_folder_name}.dmg"

(e.g.)
ikeh@HIROKInoMacBook-Pro SparkleDemoWithSPM_GitHub % cd /Users/ikeh/Programming/Swift/SparkleDemoWithSPM_GitHub/TempForUpdateProgram
ikeh@HIROKInoMacBook-Pro TempForUpdateProgram % hdiutil create -srcfolder "SparkleDemoWithSPM.app" -format UDZO -fs HFS+ \
  -volname "SparkleDemoWithSPM" "SparkleDemoWithSPM_2.0.0.0.dmg"
...............................
created: /Users/ikeh/Programming/Swift/SparkleDemoWithSPM_GitHub/TempForUpdateProgram/SparkleDemoWithSPM_2.0.0.0.dmg
```

## appcast.xmlのコンテンツ作成

- dmgの配置したフォルダを対象に`./bin/generate_appcast`を実行すると、そのフォルダに`appcast.xml`が作成される。

```console
./bin/generate_appcast /path/to/your/updates_folder/

(e.g.)
/Users/ikeh/Programming/Swift/SparkleDemoWithSPM/From_GitHub/Sparkle-2.0.0/bin/generate_appcast /Users/ikeh/Programming/Swift/SparkleDemoWithSPM_GitHub/TempForUpdateProgram
Wrote 1 new update and updated 0 existing updates
```

- `appcast.xml`の中身は例えば以下の通り。

```xml
<?xml version="1.0" standalone="yes"?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
    <channel>
        <title>SparkleDemoWithSPM</title>
        <item>
            <title>2.0.0</title>
            <pubDate>Fri, 31 Dec 2021 17:02:05 +0900</pubDate>
            <sparkle:version>2.0.0.0</sparkle:version>
            <sparkle:shortVersionString>2.0.0</sparkle:shortVersionString>
            <sparkle:minimumSystemVersion>12.1</sparkle:minimumSystemVersion>
            <enclosure url="https://raw.githubusercontent.com/pommdau/SparkleDemoWithSPM/main/SparkleDemoWithSPM_2.0.0.0.dmg" length="1280118" type="application/octet-stream" sparkle:edSignature="WTsROa2mMSWXpnTXaFredBaYgPXPVaYYDXBx98jNnvpVBN9WyAD0feCPH+AXrsUfpnM9Keevy18LPyTQ6Xe6Bg=="/>
        </item>
    </channel>
</rss>
```

- 下記に更新対象のdmgが置かれているURLが記載されており、デフォルトでは`appcast.xml`と同じ階層が指定されるみたいです。

```xml
<enclosure url="https://raw.githubusercontent.com/pommdau/SparkleDemoWithSPM/main/SparkleDemoWithSPM_2.0.0.0.dmg" length="1280118" type="application/octet-stream" sparkle:edSignature="WTsROa2mMSWXpnTXaFredBaYgPXPVaYYDXBx98jNnvpVBN9WyAD0feCPH+AXrsUfpnM9Keevy18LPyTQ6Xe6Bg=="/>
```

- 今回は[coteditor\.github\.io/appcast\.xml](https://github.com/coteditor/coteditor.github.io/blob/master/appcast.xml)を参考に、Releaseにdmgを置いてこれを更新対象としたい。

![](https://i.imgur.com/GjKUtHm.jpg)

- `appcast.xml`のURLを上記のものに置き換えます。

```diff
-<enclosure url="https://raw.githubusercontent.com/pommdau/SparkleDemoWithSPM/main/SparkleDemoWithSPM_2.0.0.0.dmg" length="1280118" type="application/octet-stream" sparkle:edSignature="WTsROa2mMSWXpnTXaFredBaYgPXPVaYYDXBx98jNnvpVBN9WyAD0feCPH+AXrsUfpnM9Keevy18LPyTQ6Xe6Bg=="/>
+<enclosure url="https://github.com/pommdau/SparkleDemoWithSPM/releases/download/2.0.0/SparkleDemoWithSPM_2.0.0.dmg" length="1280118" type="application/octet-stream" sparkle:edSignature="WTsROa2mMSWXpnTXaFredBaYgPXPVaYYDXBx98jNnvpVBN9WyAD0feCPH+AXrsUfpnM9Keevy18LPyTQ6Xe6Bg=="/>
```

- 更新した`appcast.xml`をGitHubにPushして準備完了。

# 動作確認
- `Check for UPdates...`を選択。
<img width="400" alt="image" src="https://i.imgur.com/dUQR5Z6.jpg">

- アップデートがある旨の通知。

<img width="400" alt="image" src="https://i.imgur.com/Ya2oG2N.jpg">
<img width="400" alt="image" src="https://i.imgur.com/l1F5MOX.jpg">
<img width="400" alt="image" src="https://i.imgur.com/9oqPX5h.jpg">

- ちゃんと更新されていることがわかります！

<img width="400" alt="image" src="https://i.imgur.com/Tardtfp.jpg">

# 改善点
- 公開用リポジトリ名は[CotEditor](https://github.com/coteditor/coteditor.github.io)に倣って`sparkle-demo-with-spm.github.io`のようにしたらいいのかなと思います。
    - [github\.ioを使ってMySiteを作ろう](https://qiita.com/MokeeeMokeee/items/4b33691b829aaf119bbf)
    - 後々こんな感じで管理もできるのがいいと思います。
- また`appcast.xml`のURLをいちいちGitHubのReleasesに書き換えるのが面倒なので、いい方法を考えたい所です。