Xamarin.MacからアップデートライブラリSparkleのC♯バインディングのSparkleSharpを使用する

# 概要
* Xamarin.Macでアプリを作るにあたって、アプリアップデート用のライブラリ（Sparkle）を使いたい！
* Xamarin.Macからネイティブのライブラリを使用するためにはObjective Sharpieを使ってXamarin.Macに向けてライブラリのAPIを公開する必要がある
* その手順を踏んで公開されてるライブラリ、SparkleSharpがあったのでそれを使ってアップデータを実装してみる
* .pkg で配布する前提で準備する際にファイルの準備等でハマったのでその辺も共有したい

# Sparkle is なに？
これ（[sparkle-project/Sparkle](https://github.com/sparkle-project/Sparkle/tree/bb299d075b7be7eb7dd1cbf8a5c5a82a77b90ad6)）です。

アプリをApp Store外で配布するにあたっては効率的にアップデートを確認し、アップデートがあればインストール！みたいな仕組みが欲しいですよね？

それで調べてみると大体これが出てきます。

ざっくりとした仕組みは、

* アップデート内容を書いたxmlファイル（appcast.xml）を自分でホスト
* Sparkleのメソッドを呼び出してそれを見に行って、更新があるか確認
* 更新があればSparkleがそのxmlの内容を拾って表示
* appcast.xmlで指定したURLにアプリ本体を探しに行ってくれる

というものです。

Sparkleが出してくるUIはこんな感じで、

<img width="665" alt="スクリーンショット 2017-07-25 11.27.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/37c3f0c1-2521-e1d9-2089-e53110fff35d.png">


準備するxmlはこんな感じです。

```xml: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>Sparkle Sharp Sample App Changelog</title>
    <description>Most recent changes with links to updates.</description>
    <language>en</language>
      <item>
        <title>Version 2.1</title>
        <description>
          <![CDATA[
            <ul>
              <li>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</li>
              <li>Suspendisse sed felis ac ante ultrices rhoncus. Etiam quis elit vel nibh placerat facilisis in id leo.</li>
              <li>Vestibulum nec tortor odio, nec malesuada libero. Cras vel convallis nunc.</li>
              <li>Suspendisse tristique massa eget velit consequat tincidunt. Praesent sodales hendrerit pretium.</li>
            </ul>
          ]]>
        </description>
        <pubDate>2017-07-24T17:32:53+0900</pubDate>
        <enclosure url="https://github.com/toshi0607/SparkleSharpSample/releases/download/1.2/SparkleSharpSample.zip" sparkle:version="2.0" length="5075016" type="application/octet-stream" sparkle:dsaSignature="MCwCFEVkEFdF43INMskJjylMGxO9odWnAhRPtY0QEgIaOqjXnlHnWZXKYJWwcg==" />
      </item>
  </channel>
</rss>
```


* appcast.xmlにはhttpsで接続しないといけない
* 配布する.pkg（.zipとか.dmgとかでもよい）に秘密鍵で署名し、アプリの中には公開鍵を含めておく

のような感じで、セキュリティ面の配慮もありいい感じですね。

# Xamarin.MacプロジェクトでSparkleを使う
基本こんな手順です。

[MacApp用アップデート管理システムSparkleの使い方（Xamarin.Mac編）](http://qiita.com/ailen0ada/items/eff032808920180acfe1#objective-sharpie)

Objective Sharpieを使ってバインディング情報を生成し、使用したいXamarin.Macに突っ込んで参照するみたいな感じです。

記事に書いてある通り、 `めげない気持ち` が必要です。

ただ、探してみるとおそらくこの手順を踏んで公開してくれてるであろう [SparkleSharp](https://github.com/rainycape/SparkleSharp)というのがあったので今回はそれで実装してみます。

# Xmarin.MacプロジェクトにSparkleSharpを取り込む

対象プロジェクトで下記実行します。

```
git submodule add https://github.com/rainycape/SparkleSharp.git
```

`Sparkle.csproj` をプロジェクトに追加します。

<img width="713" alt="スクリーンショット 2017-07-25 11.47.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/5c74bf7b-246b-4d9e-aebe-c456338a0907.png">

<img width="857" alt="スクリーンショット 2017-07-25 11.48.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/8ec7fa58-c82a-9e0c-ac0f-6b1d0f12d938.png">


参照を追加します。
<img width="1518" alt="スクリーンショット 2017-07-25 11.50.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/8ef5e329-229d-7e93-7101-803bd4fdd9cb.png">



この状態で**ビルドしたら**Sparkleがメインのプロジェクトから使用できます。

# Sparkleで必要な設定の準備を行う
## 秘密鍵・公開鍵
インストールするアプリの妥当性をチェックするための秘密鍵・公開鍵を準備します。

Sparkle（本家）には秘密鍵・公開鍵を生成するやつが入ってるのでクリックして生成します。

`SparkleSharpSample/SparkleSharp/Sparkle/3rdparty/Sparkle/bin/generate_keys`

↓

* dsa_priv.pem
* dsa_pub.pem

## appcast.xml
更新情報を確認するためのファイルです。

* enclosure: アプリ（インストーラ）本体を配布するURL
* dsaSignature: アプリ（インストーラ）を秘密鍵で署名するときに生成される文字列
* length: アプリ（インストーラ）のファイルサイズ

```xml: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>Sparkle Sharp Sample App Changelog</title>
    <description>Most recent changes with links to updates.</description>
    <language>en</language>
      <item>
        <title>Version 2.1</title>
        <description>
          <![CDATA[
            <ul>
              <li>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</li>
              <li>Suspendisse sed felis ac ante ultrices rhoncus. Etiam quis elit vel nibh placerat facilisis in id leo.</li>
              <li>Vestibulum nec tortor odio, nec malesuada libero. Cras vel convallis nunc.</li>
              <li>Suspendisse tristique massa eget velit consequat tincidunt. Praesent sodales hendrerit pretium.</li>
            </ul>
          ]]>
        </description>
        <pubDate>2017-07-24T17:32:53+0900</pubDate>
        <enclosure url="https://github.com/toshi0607/SparkleSharpSample/releases/download/1.2/SparkleSharpSample.zip" sparkle:version="2.0" length="5075016" type="application/octet-stream" sparkle:dsaSignature="MCwCFEVkEFdF43INMskJjylMGxO9odWnAhRPtY0QEgIaOqjXnlHnWZXKYJWwcg==" />
      </item>
  </channel>
</rss>
```

とりあえずgithubに置いたものを参照するようにしてます。


# Sparkleで必要な設定
`info.plist`のソーなんとか（いじっても切れててわからない…）に `SUFeedURL` と `SUPublicDSAKeyFile` を追加します。
<img width="1072" alt="スクリーンショット 2017-08-02 23.37.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/4f178a9d-643d-1f8e-8d64-c2e8f5a0ade0.png">


おもむろに `Main.Storyboard` をダブルクリックして Storyboard を立ち上げて先ほど生成した `dsa_pub.pem` を D&D します。

<img width="527" alt="スクリーンショット 2017-07-25 15.01.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/32480316-f8b1-0c47-4429-7ed892310023.png">


本家は Xcode に D&D してねって書いてあったのですが、VS for Mac だとできないので…

VS for Macから見ると追加されていて、ビルド時にBundleに含むことになってるのでこれでよさそうです。



# UIの追加
何でもいいのですが、とりあえずアプリを立ち上げたときにアップデートチェックするボタンを追加します。

Storyboard からボタンを追加し、C#コードにボタンをクリックしたときに呼ぶメソッド（ `Action` ）を公開するために `ViewController.h` にリンクします。

<img width="1472" alt="スクリーンショット 2017-07-25 12.31.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/12ae76f1-9bda-3a42-c459-31b05636645b.png">


これで Storyboard を閉じると VS for Mac 上の `ViewController.designer.cs` にActionが同期され、 `ViewController.cs` でC#でメソッドが書けます。

とても手前味噌なのですが、この辺は僕が土曜日に翻訳した記事で丁寧に説明されてますw

[Xamarin.Macを初めて触る人に贈る、Xamarin公式 Hello, Mac の日本語訳記事
](http://qiita.com/toshi0607/items/34b3e04f9ad24a5d0153)

`Sparkle`　を `using` して、ボタンをクリックしたときに呼び出すメソッドを追加します。

```C#:ViewController.cs
using System;
using Sparkle;

using AppKit;
using Foundation;

namespace SparkleSharpSample
{
    public partial class ViewController : NSViewController
    {
        SUUpdater updater;

        public ViewController(IntPtr handle) : base(handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Do any additional setup after loading the view.
        }

        public override NSObject RepresentedObject
        {
            get
            {
                return base.RepresentedObject;
            }
            set
            {
                base.RepresentedObject = value;
                // Update the view, if already loaded.
            }
        }

        public override void AwakeFromNib()
        {
            base.AwakeFromNib();
            updater = new SUUpdater();
            updater.AutomaticallyChecksForUpdates = false;
        }

        partial void CheckUpdates(NSObject sender)
        {
            updater.CheckForUpdates(new NSObject());
        }

    }
}
```

# インストーラの準備と配置
## インストーラ作成
ビルドのオプション（インストーラーパッケージを作成する）にチェックを入れれば.pkgを作ってくれます。

<img width="1512" alt="スクリーンショット 2017-07-25 12.46.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/97e96183-85e1-078f-4d1e-77a5f2dec440.png">

## インストーラを署名する
プロジェクトを右クリック、オプションをクリックして「インストーラーパッケージを署名する」にチェックを入れます。

<img width="954" alt="スクリーンショット 2017-08-02 23.01.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/7512b040-2250-464c-045b-04230908fb44.png">

これについてはXmairn.Macの公式ページでも詳細に説明されています。Developer IDの登録、Developer ID証明書（インストーラ）の発行が必要です。

* 証明書発行: [Certificates and Identifiers](https://developer.xamarin.com/guides/mac/deployment,_testing,_and_metrics/publishing_to_the_app_store/certificates-identifiers/)
* 署名: [Sign with Developer ID](https://developer.xamarin.com/guides/mac/deployment,_testing,_and_metrics/publishing_to_the_app_store/signing/)

## インストーラをzip化する
* ビルドで生成した `SparkleSharpSample-x.0.pkg` を使用します。

.pkg名は **必ず最終的にできるアプリと同じに** してください。

デフォルトでは`SparkleSharpSample-1.2.pkg` のような形で生成されますが、最終的にアプリ名は `SparkleSharpSample.app` となるのでバージョンのサフィックスは削除します。

```
ditto -c -k --sequesterRsrc --keepParent ~/Desktop/SparkleSharpSample.pkg ~/Desktop/SparkleSharpSample.zip
```

## zip化したインストーラを署名する

* `SparkleSharpSample/SparkleSharp/Sparkle/3rdparty/Sparkle/bin/sign_update` 
* 先ほど生成した `dsa_priv.pem` 
* Developer ID証明書での署名が済んだ `SparkleSharpSample.zip` 

を使用します。

```
/Users/sugita-toshinori/codes/xamarin/SparkleSharpSample/SparkleSharp/Sparkle/3rdparty/Sparkle/bin/sign_update ~/Desktop/SparkleSharpSample.zip ~/Desktop/dsa_priv.pem

// => MCwCFEVkEFdF43INMskJjylMGxO9odWnAhRPtY0QEgIaOqjXnlHnWZXKYJWwcg==
```
※パス通しましょう

appcast.xmlに使用する `length` もここでようやくわかります。

```
wc -c < ~/Desktop/SparkleSharpSample.zip                                                                                                                                  
// => 5075016
```

#　動作確認
アプリを起動して、「CheckUpdates」をクリックするとアップデートが確認できます。
<img width="614" alt="スクリーンショット 2017-07-25 13.01.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/38cb0c01-06fb-d1fb-16a7-b6b283a449c0.png">

そして「Install Update」をクリックすると、5.1Mのファイルをダウンロードできました。

<img width="399" alt="スクリーンショット 2017-08-02 22.33.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/7707d4f2-379c-9dc1-9b7e-ff65a2d9d52c.png">

<img width="395" alt="スクリーンショット 2017-08-02 22.33.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/ca160cc8-1984-3b33-f4f5-56c329c25d4f.png">

引き続きpkgでのアップデートを走らせることができます。

<img width="616" alt="スクリーンショット 2017-08-02 22.33.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/46547/0eb45e91-6adc-33d2-e463-826c1eab1195.png">

# 最後に
* [MacApp用アップデート管理システムSparkleの使い方](http://qiita.com/econa77/items/20105ab32740f669f43e)の @econa77 さん、[MacApp用アップデート管理システムSparkleの使い方（Xamarin.Mac編](http://qiita.com/ailen0ada/items/eff032808920180acfe1)の @ailen0ada さんに助けていただきましたありがとうございました…！
* ライブラリの準備/設定に必要なことは元のネイティヴライブラリのドキュメントがやはり最強なので、困ったら熟読するのが良さげです。
  * [Basic Setup](https://sparkle-project.org/documentation/)
  * [Publishing an update](https://sparkle-project.org/documentation/publishing/#publishing-an-update)
  * [Customizing Sparkle](https://sparkle-project.org/documentation/customization/)