PONOS Advent Calendar 2021の2日目の記事です。
昨日は@nissy_gpさんの「UnityのGC AllocateとWaitForSeconds」でした。
はじめに
UnityのSystemInfo.operatingSystem
をご存知でしょうか。
アプリの実行環境のOS情報を取得するプロパティであり、OS名とバージョンを「iOS 15.1」のようなフォーマットの文字列で返してくれます。
SystemInfo-operatingSystem - Unity スクリプトリファレンス
今回は、iPadOS 15.1
以降のiPad端末でSystemInfo.operatingSystem
を使用していて発生した問題について共有させていただきます。
なお、筆者の手元で今回確認した端末のOSバージョンがiPadOS 15.1
でしたので、そちらのバージョンについてのみ記述しています。
iPadOS 15.0 ~ 15.0.2
での動作については未確認ですが、同様の挙動をする可能性があります。
また、確認時のビルド環境はUnity 2019.4.32f1
およびXcode 13.0
です。
発生した問題
前提
SystemInfo.operatingSystem
をiOS環境で呼ぶと、「iOS 15.1」のように**「iOS」から始まる文字列を返してくれます。
これは、端末の種類がiPhoneであってもiPadであっても同様の挙動でした。
(ちなみに、iOS 10
未満の場合は「iPhone OS」**から始まる文字列を返していましたが、今回は特に触れません)
私の携わっていたプロジェクトでは、このSystemInfo.operatingSystem
から取得できる文字列を利用して、
以下のようなコードでiOSのバージョンを取得して一部機能の利用可否を判定していました。
/// <summary>
/// 端末のiOSのバージョンを取得。
/// </summary>
/// <returns>iOSのバージョン。</returns>
static Version GetiOSVersion()
{
var operatingSystem = SystemInfo.operatingSystem;
// 「iOS」から始まる文字列が返ってくる前提で、「iOS」部分を削除してバージョンの部分のみを残す。
// "iOS 15.1" -> "15.1"
var versionString = operatingSystem.Replace("iOS ", "");
// バージョンの文字列をSystem.Versionオブジェクトにパースする。
Version version;
if (Version.TryParse(versionString, out version))
{
return version;
}
return null;
}
#if UNITY_IOS
var version = GetiOSVersion();
if(version == null)
{
return false;
}
return 13 <= version.majarVersion; // iOS 13以降であれば機能が利用できる。
#endif
iPadOS 15.1で問題が発生
アプリのiOS 15
対応を進めていたある日、
**「iPadOS 15.1のiPad端末で検証しているが、該当機能が有効化されていない」**という不具合報告が飛び込んできました。
「該当機能については手を入れていないはずなのに不思議だな…」と思いつつ調査を進めていくと、衝撃の事実が。
なんと、「iPadOS 15.1
を搭載したiPad端末」においてSystemInfo.operatingSystem
が
「iPadOS 15.1」のような「iPadOS」から始まる文字列を返していることが判明したのです…!
先ほど上で紹介したGetiOSVersion()
メソッドはSystemInfo.operatingSystem
の前半部分が「iOS」でないと、
バージョンの文字列を抽出することができないので、その後のSystem.Version.TryParse()
に失敗します。
GetiOSVersion()
メソッドがバージョン情報を返せなかったために、利用可能なバージョンかどうかの判定を行うことができず、
該当機能が「利用不可」として処理されてしまっていた、というわけです…。
iPadOS 15.1
上のSystemInfo.operatingSystem
の挙動に対応するため、GetiOSVersion()
メソッドを修正することになりました。
iPadOSから始まる文字列に対応するための修正
修正方法としてすぐに思いついたのは、「iOS」と「iPadOS」の両方をReplace()
で削除する方法でした。
// 「iOS」部分を削除してバージョン文字列の部分のみを残す。
var versionString = operatingSystem.Replace("iOS ", "");
// 「iPadOS」部分を削除してバージョン文字列の部分のみを残す。
versionString = operatingSystem.Replace("iPadOS ", "");
しかし、この修正の場合、今後のバージョンでまた前半の文字列が変化した場合に再度コードを修正する必要が生じてしまいます。
これ以上、前半の文字列の内容に振り回されたくはないため、正規表現でバージョン情報を取得する方法で修正することにしました。
/// <summary>
/// 端末のiOSのバージョンを取得。
/// </summary>
/// <returns>iOSのバージョン。</returns>
static Version GetiOSVersion()
{
var operatingSystem = SystemInfo.operatingSystem;
// 「数字」から始まり、「.(ピリオド)」と「数字」が連続する文字列を探し出す。
var regex = new System.Text.RegularExpressions.Regex("([0-9]+)(\\.[0-9]+)*");
var match = regex.Match(operatingSystemString);
if (!match.Success)
{
return null;
}
Version version;
if (Version.TryParse(match.Value, out version))
{
return version;
}
return null;
}
これなら、前半の文字列が「iOS」でも「iPhoneOS」でも「iPadOS」でもバージョン情報を抽出することができます!
まとめ
SystemInfo.operatingSystem
を利用してOSバージョンを判定している場合、
判定方法によっては新しいOSバージョンがリリースされたときに動作不備が発生することがありますので注意しましょう。
明日の担当も私@e73ryoです。