MacApp用アップデート管理システムSparkleの使い方

先日MacOS用クリップボード拡張アプリ「[Clipy](http://clipy-app.com)」をリリースしました。
Clipyの開発については[先日のQiitaの記事](http://qiita.com/econa77/items/1848bf3fdfb7127ca9b8)を参照ください。

今回はClipy内でも使用しているMacOSアプリケーション用アップデート管理フレームワークの[Sparkle](http://sparkle-project.org/)の使用方法についてまとめます。
Sparkleを導入することにより、Macアプリケーションの自動アップデートなどをとても簡単に行うことができます。
Macアプリケーションをリリースされる方はぜひ導入してください。

今回の解説はMacAppStoreで配布しない場合の作成方法です。
基本的な作成方法は変わりませんが、証明書等少し変わってきますので、MacAppStoreで使用する場合は[公式ドキュメント](https://github.com/sparkle-project/Sparkle/wiki)を参照ください。

## Sparkleのインストール
SparkleはCocoaPodsで導入することができます。

```
platform :osx, '10.9'
source 'https://github.com/CocoaPods/Specs.git'
pod 'Sparkle'
```
```
pod install
```
でライブラリを導入してください。

## 証明書の作成
証明書の作成は用意されているシェルスクリプトを使用します。
[Sparkleリポジトリ](https://github.com/sparkle-project/Sparkle)
よりコードをダウンロード/クローンしてください。

```
cd cloneDirectory
./bin/generate_keys.sh
```
上記のコードより、証明書作成シェルを実行すると、同階層に`dsa_priv.pem `と`dsa_pub.pem`が作成されます。
この時、`dsa_priv.pem`の方は今後のアップデートでも使用するものなので、大切に保管してください。

また、作成した`dsa_pub.pem`はXcodeにドラッグアンドドロップでインポートしてください。
その後、Info.plistの`SUPublicDSAKeyFile `に`dsa_pub.pem`を指定してください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/1500/36617a2f-2e6c-33d4-6aeb-a9ad74be0720.png" width="400px" />

## AppCastの準備
Sparkleのアップデートの確認はAppcastといわれるフォーマットを使用します。
[公式のサンプルAppcast](http://sparkle-project.org/files/sparkletestcast.xml)を参考していきます

サンプルを見てわかるよう、基本的にはxmlでアップデート情報を記載していきます。
以下のappcastが実際にclipyで使用しているappcastファイルです。

```
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>Clipy changelog</title>
    <link>http://clipy-app.com/appcast.xml</link>
    <description>Clipy update changelog.</description>
    <item>
      <title>Clipy 1.0.1</title>
      <description>
        <![CDATA[
          <ul> <li>Marvericsでメニューバーが表示されない問題の修正</li> </ul>
        ]]>
      </description>
      <pubDate>Sun, 5 July 2015 03:04:00 +0900</pubDate>
      <sparkle:minimumSystemVersion>10.9</sparkle:minimumSystemVersion>
      <enclosure url="https://github.com/Clipy/Clipy/releases/download/1.0.1/Clipy_1.0.1.dmg" sparkle:version="1.0.1" length="20150704" type="application/octet-stream" sparkle:dsaSignature="MCwCFDIOlk3gJGTc46P1CQgWrE8S5HYlAhQ5BW9XLWApES7lmglPg0Vn0pXpnA=="/>
    </item>
  </channel>
</rss>
```

重要な点は、
`<sparkle:minimumSystemVersion>10.9</sparkle:minimumSystemVersion>`
ここで、対象のOSのバージョンを指定しています。もし、使用ライブラリ等のアップデートでサポートするOSのバージョンが上がった際はこちらを変更します。

```
<enclosure url="https://github.com/Clipy/Clipy/releases/download/1.0.1/Clipy_1.0.1.dmg" sparkle:version="1.0.1" length="20150704" type="application/octet-stream" sparkle:dsaSignature="MCwCFDIOlk3gJGTc46P1CQgWrE8S5HYlAhQ5BW9XLWApES7lmglPg0Vn0pXpnA=="/>
```
ここでは、アップデートを行うdmgファイルのパスと、アプリのバージョン等を指定しています。

`length`はどこの情報も見ても何をいれるか定かではないため、アップデートを行った日付を暫定的に入れています。
どなたか正解を教えて下さい。

`dsaSignature`については後述するアップデートについてを御覧ください。

## Info.plistに設定
作成したappcastファイルをサーバに置いてください。
Clipyの場合はすべてgithubで管理していますので、appcastファイルもgithubに置いています。

設置したファイルのURLはInfo.plistの`SUFeedURL`に設定してください。
<img src="https://qiita-image-store.s3.amazonaws.com/0/1500/36617a2f-2e6c-33d4-6aeb-a9ad74be0720.png" width="400px" />

## 設定画面にSparkleを設置
アプリケーションでは、よく見かける設定画面の今すぐアップデートボタンの設置を実装していきます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/1500/7490b8d5-0d65-6e14-f753-972e2fd2bdcb.png" width="400px" />

まずは、Storyboard，xibにSUUpdaterを設置していきます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/1500/b39519d4-3b02-5634-a2f3-851bafee6695.png" width="800px" />

SUUpdaterのActionを設置しているボタンのActionに適応させます。
これだけで、自動的にアップデートを検査しにいきます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/1500/091c57f2-393b-3727-02f6-447d86c1e6a6.png" width="800px" />

また、前回チェック時の時間のBindingも可能で、Binding設定するだけで自動で反映されます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/1500/0dcfa6a9-2229-769e-c1b7-4ecc4f7cd7f2.png" width="800px" />

## アップデート設定
コードでアップデート確認や、自動アップデートやアップデートチェックの時間設定等も行えます。
基本的にはアプリ起動時に設定を行って下さい。

```
let updater = SUUpdater.sharedUpdater()
// URL指定
let feed = "http://clipy-app.com/appcast.xml"
if let feedURL = NSURL(string: feed) {
  SUUpdater.sharedUpdater().feedURL = feedURL
}
// 自動アップデートを設定
updater.automaticallyChecksForUpdates = true
// 24時間（１日）毎にアップデート確認
updater.updateCheckInterval = 86400
```

## 実際にアップデート
実際にアップデートを行う手順を説明していきます。

### アーカイブファイルを用意
`.app`を圧縮します。
ここで`.app`ファイルのファイル名はアプリの名前のみにしてください。
`Clipy.app`という形です。
`Clipy_1.0.1.app`といった形では使用することができません。
対応しているフォーマットは`zip` `tar.gz` `tar.gz2` `dmg` `pkg` です。
作成したアーカイブファイルに署名を行っていきます。

### 署名
こちらもSparkleのリポジトリ内に入っているシェルファイルを使用します。

```
./bin/sign_update.sh path_to_your_update.zip path_to_your_dsa_priv.pem
```

アーカイブしたアプリに証明書の作成時に作成した`dsa_priv.pem`を使って署名します。

実行すると

```
MCwCFDIOlk3gJGTc46P1CQgWrE8S5HYlAhQ5BW9XLWApES7lmglPg0Vn0pXpnA==
```

こんな感じで文字列が出力されます。
この出力された文字列がappcast内の`dsaSignature`に対応します。


### appcastの書き換え
appcastにアップデートを行った内容、アップデートするファイルのパス等を書き換えて完成です。

これでアプリが起動するたびにSparkleによってアップデートチェック走り、appcastに更新がかかっていればアップデート行うようになります。

## まとめ
更新作業は割りと時間がかかってしまいますが、自動化するTips等もありますので、今後紹介できればと思います。
アプリアップデート機能はどのアプリでも使用しておいて損はないので、ぜひ導入してください。


はじめにもかきましたが、先日クリップボード拡張アプリケーションの「[Clipy](http://clipy-app.com)」をリリースしました。
Clipyにも自動アップデート機能がありますので、ぜひダウンロードしてお試し下さい！

[ソースコードもすべてオープンソース](https://github.com/Clipy/Clipy)で公開しておりますので、ご参考にしてください！