Sparkleは、アプリが更新対象かどうかを、ビルド番号(CFBundleVersion)で判定する。表示用のバージョン番号(CFBundleShortVersionString)は、判定に使われない。Xcodeのビルド設定では、ビルド番号がCURRENT_PROJECT_VERSION、バージョン番号がMARKETING_VERSIONである。
MARKETING_VERSION = 1.0.1; # CFBundleShortVersionString(表示用)
CURRENT_PROJECT_VERSION = 2; # CFBundleVersion(Sparkleの判定用)
ここでは、2つの番号の役割と、Sparkleのバージョン比較の挙動、CIでの番号の付け方を紹介する。実行例はSparkle 2.10.0、Xcode 27.0、macOS 27.0.1のもの。
- 前の記事: 【Sparkle】generate_appcastでappcast.xmlを作る
- 親記事: GitHubを使い、macOSアプリを署名・公証・自動アップデート付きで公開する
- 次の記事: 【GitHub Releases】appcast.xmlを固定URLで配信する
2つの番号の役割
アプリの2つの番号は、次のように対応する。
| Xcodeのビルド設定 | Info.plistのキー | appcast.xmlの要素 | 用途 |
|---|---|---|---|
CURRENT_PROJECT_VERSION | CFBundleVersion | sparkle:version | Sparkleの更新の判定 |
MARKETING_VERSION | CFBundleShortVersionString | sparkle:shortVersionString | ユーザーに表示するバージョン |
generate_appcastは、zipの中のアプリのInfo.plistから、2つの番号を読み取ってappcast.xmlに書く。
参考: 【Sparkle】generate_appcastでappcast.xmlを作る
<item>
<title>1.0.1</title>
<sparkle:version>2</sparkle:version>
<sparkle:shortVersionString>1.0.1</sparkle:shortVersionString>
...
</item>
アプリが更新の通知で表示する「1.0.1」は、shortVersionStringである。更新を判断する元の値は、sparkle:versionの「2」である。
ビルドしたアプリの番号は、plutilで確認できる。
$ plutil -p [アプリ名].app/Contents/Info.plist | grep -E 'ShortVersion|BundleVersion'
"CFBundleShortVersionString" => "1.0.1"
"CFBundleVersion" => "2"
判定はsparkle:versionで行われる
appcast.xmlのsparkle:versionが、インストール済みのアプリのビルド番号より大きいときだけ、Sparkleは更新を通知する。表示用のバージョンが新しくても、ビルド番号が同じであれば、更新は通知されない。
appcast.xmlのうち、最新のエントリのsparkle:versionを、インストール済みのアプリと同じ1に変えて、更新を確認する。sparkle:shortVersionStringは1.0.1のままにしている。
<sparkle:version>1</sparkle:version>
<sparkle:shortVersionString>1.0.1</sparkle:shortVersionString>
すると、Sparkleは更新を通知せず、次のダイアログを表示する。
You’re up to date!
[アプリ名] 1.0.0 is currently the newest version available.
sparkle:versionが同じ、または小さいと、更新は通知されない。ビルド番号は、リリースのたびに増やす。
ビルド番号の比較の挙動
Sparkleは、SUStandardVersionComparatorでバージョン文字列を比較する。Sparkleのフレームワークをリンクした小さなSwiftのスクリプトから、比較の結果を調べる。
import Foundation
import Sparkle
let comparator = SUStandardVersionComparator()
let result = comparator.compareVersion("0.1.9", toVersion: "0.1.10")
print(result == .orderedAscending ? "<" : (result == .orderedDescending ? ">" : "=="))
$ swiftc -F [Sparkle.xcframeworkのmacosディレクトリ] -framework Sparkle \
-Xlinker -rpath -Xlinker [同じディレクトリ] compare.swift -o compare
$ ./compare
<
主な比較の結果は、次のとおりである。
| 左 | 比較 | 右 |
|---|---|---|
0.1.9 | < | 0.1.10 |
1.9 | < | 1.10 |
2 | < | 10 |
200 | < | 1000 |
1.0 | == | 1.0.0 |
3 | == | 3.0.0.0 |
1.0 | < | 1.0.1 |
ドット区切りの数値は、成分ごとに数値で比較される。文字列として比べると1.10は1.9より小さいが、Sparkleは1.10を大きいと判定する。末尾の.0の有無は、比較の結果に影響しない。
英字を含む番号
b1のように、数字の後ろに英字が付く番号は、付かない番号よりも小さい。
| 左 | 比較 | 右 |
|---|---|---|
1.0b1 | < | 1.0b2 |
1.0a1 | < | 1.0b1 |
1.0b1 | < | 1.0 |
1.0rc1 | < | 1.0 |
1.0.0b1 | < | 1.0.0 |
1.0beta2 | < | 1.0beta10 |
ベータ版のビルド番号には、1.0b1や1.0rc1のように、ハイフンなしで英字を付ける。
ハイフンの後ろは比較されない
ハイフンを使うと、意図した順序にならない。ハイフンから後ろの文字列は、比較に使われない。
| 左 | 比較 | 右 |
|---|---|---|
1.0-beta | == | 1.0 |
1.0-beta | == | 1.0-alpha |
1.0-beta2 | == | 1.0-beta10 |
1.0.0-rc1 | == | 1.0.0 |
1.0-betaと1.0が同じ値になるため、ベータ版から正式版への更新が通知されない。ビルド番号には、ハイフンを使わない。
スペースの後ろの文字列は、比較に使われる。1.0 betaは、1.0よりも大きい値になる。
CIでの番号の付け方
ビルド番号は、リリースごとに大きくなる値とする。GitHub Actionsでは、実行のたびに増えるgithub.run_numberを、ビルド番号として使える。バージョン番号は、タグの名前から作る。
- name: Archive
env:
VERSION: ${{ github.ref_name }}
BUILD_NUMBER: ${{ github.run_number }}
run: |
xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] -configuration Release \
-archivePath build/[アプリ名].xcarchive \
MARKETING_VERSION="${VERSION#v}" CURRENT_PROJECT_VERSION="$BUILD_NUMBER" archive
${VERSION#v}は、タグ名の先頭のvを取り除く。v1.0.1のタグから、MARKETING_VERSIONは1.0.1になる。
xcodebuildの引数でMARKETING_VERSIONとCURRENT_PROJECT_VERSIONを渡すと、project.pbxprojを書き換えずに、ビルドする番号を上書きできる。
github.run_numberは、ワークフローごとに採番される。v1.0.0をリリースしたワークフローの1回目の実行では1、v1.0.1をリリースした2回目の実行では2になる。同じリポジトリの別のワークフローを実行していても、リリース用のワークフローの番号は、独立して増える。
$ plutil -p [v1.0.0のアプリ].app/Contents/Info.plist | grep -E 'ShortVersion|BundleVersion'
"CFBundleShortVersionString" => "1.0.0"
"CFBundleVersion" => "1"
$ plutil -p [v1.0.1のアプリ].app/Contents/Info.plist | grep -E 'ShortVersion|BundleVersion'
"CFBundleShortVersionString" => "1.0.1"
"CFBundleVersion" => "2"
ワークフローごとに採番されるため、同じアプリを複数のワークフローでリリースすると、ビルド番号が重複したり、後退したりする場合がある。リリースは、1つのワークフローに集約する。
ローカルでバージョンを管理する方法は、agvtoolの記事で紹介している。
参考: agvtoolでXcodeプロジェクトのバージョンを管理する
