はじめに - 直面した事象
複数バージョンに対応しているアプリケーションで最新OSのみの機能を導入する際、OSバージョンによる判定が必須になります。そのような場合は例えば、下記のような形でバージョンの判定を行います。
if #available(iOS 11.0, *) {
do something
}
@available(iOS 11.0, *)
class ClassForOS11 { }
class SomeClass {
@available(iOS 11.0, *)
var iOS11ComputedProperty {
retrurn property
}
@available(iOS 11.0, *)
func iOS11Function() {
}
}
しかし、上記のような方法はComputed Property、 Class、 メソッドには使用できますが、Stored Propertyには使用できません。下記のようにするとCompile Errorになります。
class SomeClass {
@available(iOS 11.0, *)
var arView: ARSCNView?
// Stored properties cannot be marked potentially unavailable with '@available'
// 省略
}
このような状態の際に、特定OS以上のみでサポートされているクラスのインスタンスを、特定OSバージョン以外もサポートしているクラスのStored Propertyとして保持したいというケースに遭遇しました。本記事では、暫定で考えた対処法を記載します。
検証環境
以下の環境を使用しています。
macOS High Sierra Version 10.13.4
Xcode Version 10.0.0 beta3
実装
前提:
以下を本記事のケースとします。
- iOS 10以上をサポート
- iOS 10以上をサポートしているクラスのストアドプロパティとして、iOS11以上で使用できるクラスのインスタンスを保持したい
以下のようにして、対処しました。
class SomeClass {
// TODO: iOS 10サポートを外したタイミングで書き換える
// storedPropertyはOSバージョン判定できないため暫定で以下のように対処
private var storedArView: Any?
@available(iOS 11.0, *)
var arView: ARSCNView? {
get {
return storedArView as? ARSCNView
}
set(newArView) {
storedArView = newArView as Any
}
}
}
インスタンスの型として、iOS11以上のクラスを記述するとその時点でコンパイルエラーとなってしまいます。
そのため、暫定でAnyで定義し、実際のアクセスはcomputed property経由で行うようにします。
computed propertyはOSバージョンによる判定が可能なため、この方法で該当プロパティを保持しながら、OSバージョン判定を行うことが可能です。
ただし、 Anyで定義しているため、静的型付けのメリットが失われており、実装は開発者自身が気をつけなければなりません。状態として本来好ましくはないため、TODOコメントなどで残して置くと良いと思われます。
まとめ
Stored PropertyのOSバージョン判定をComputed Propertyを介して間接的に行う方法を紹介いたしました。
もっと良い方法をご存知の方がいらっしゃいましたら、共有いただけますと幸いです。
また、Stored PropertyのOSバージョン判定ができない合理的な理由の説明がなかなかできないため、この辺りお詳しい人は補足いただけますと幸いです。stored property周りの処理(metaデータとしての登録?)などはコンパイル時に実行されるため、そのタイミングではOSバージョンの判定ができない、computed propertyなどのOSバージョン判定は実行時に行われるのでOSの判定が可能、あたりかなとは思っているのですが。。。