はじめに
アプリの機能でよく実装する強制バージョンアップですが、
毎回実装すると、地味に不具合が出ることに気がつきました...
(ロジックとしては定型的なんですが、意外とロジックミスをしやすい...この記事を書いていてもいくつか...)
使える場面は限定的ですが、強制バージョンアップのロジックを整理して残しておきます。
※こちらは参考のみとして、業務ではコピペで使わないようお願いいたします。
覚書とテストコード
- バージョンルールは決めておく
Android公式サイト では以下のように記載があります。
versionName - ユーザーに表示されるバージョン番号として使用される文字列。この値は、未加工の文字列または文字列リソースへの参照として設定できます。
この値は文字列であり、アプリのバージョンを <メジャー>.<マイナー>.<ポイント> 文字列として、または他のタイプの絶対または相対バージョン識別子として記述できます。versionName はユーザーへの表示のみを目的としています。
1.0.0 のように桁数や形式を予め決めておくことで運用が楽になります。
- ロジックはシンプルに書く
- 例外条件(桁数が異なる、数値以外のものが含まれている)は早期リターン
- 処理の行数は短く、見通しをよくする
class ForceVersionUpLogic {
fun checkForceVersionUp(serverVersion: String, currentVersion: String): Boolean {
val splitServerVersion = serverVersion.split(".")
val splitCurrentVersion = currentVersion.split(".")
if (splitServerVersion.size != 3 || splitCurrentVersion.size != 3) {
return false
}
// check all parts is digit
for (i in 0 until 3) {
checkNotNull(splitServerVersion[i].toIntOrNull()) { return false }
checkNotNull(splitCurrentVersion[i].toIntOrNull()) { return false }
}
for (i in 0 until 3) {
val serverVersionPart = splitServerVersion[i].toInt()
val currentVersionPart = splitCurrentVersion[i].toInt()
if (serverVersionPart > currentVersionPart) {
return true
} else if (serverVersionPart < currentVersionPart) {
return false
}
}
// version is equal
return false
}
}
- 比較ロジックはテストコードでテストする
最後はなんだかんだ言ってそこであらゆるケースに対応するのが大事だと思います。
そうなると、あとはダイアログが表示される / 表示されない の2ケースを確認すればよくなります。
まとめ
強制アップデートは各プロジェクトで毎回作ってたので、一度まとめてみました。
ロジック自体は短いですが、意外とミスりやすいのでサンプルを作っておけると後の気持ちが楽になりそうです。
その他
- Androidでよく使うのは、ActivityのonResumeのタイミングで強制アップデートの通信をして、 結果をみて、ダイアログを表示する方法をよく使います(もっといい方法あるかも...)
- github で https://raw.githubusercontent.com/ を使うと、githubにpushしたファイルにアクセスできます。
object ApiManager {
private val retrofit = Retrofit.Builder()
.baseUrl("https://raw.githubusercontent.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service: ApiService = retrofit.create<ApiService>(ApiService::class.java)
}
interface ApiService {
@GET("shinya-takano/ForceVersionUpSample/master/app/src/main/res/raw/version.json")
fun getVersionJson(): Call<VersionModel>
}
data class VersionModel(
val version: String
)