🦋

SwiftUI: チェーンメソッドの途中で、iOSバージョンにより処理を分けたいときどうする?

2022/06/06に公開

SwiftUIはiOSのバージョンによって使えるAPIが異なったり書き方が異なったりで、複数バージョンに対応しようと思うと煩雑な書き方になると思います。

例えば、ListStyle.insetGroupedはiOS 14以上でないと使えませんが、安直に#available(iOS 14, *)で分岐すると長くなってしまいます。

struct HogeView: View {
    var body: some View {
        if #available(iOS 14, *) {
            List {
                Text("Hello")
                Text("World")
            }
            .listStyle(.insetGrouped)
        } else {
            List {
                Text("Hello")
                Text("World")
            }
            .listStyle(.grouped)
        }
    }
}

中身のViewを変数や関数に切り出せばマシにはなりますが、一部分しか違わないのに全体の構造に影響するのは嫌ですよね。

そこで、Viewに対して拡張で関数を生やすことで綺麗に書けます。

struct HogeView: View {
    var body: some View {
        List {
            Text("Hello")
            Text("World")
        }
        .listStyleInsetGroupedIfPossible()
    }
}

extension View {
    func listStyleInsetGroupedIfPossible() -> some View {
        Group {
            if #available(iOS 14, *) {
                self.listStyle(.insetGrouped)
            } else {
                self.listStyle(.grouped)
            }
        }
    }
}

もしも新しいAPIが使えないときの代わりが決め打ちで決まっているのであればスッキリ書くことができると思います。ポイントはGroupで囲っているところです。こうでないとFunction declares an opaque return type, but has no return statements in its body from which to infer an underlying typeというふうに警告が出てコンパイルが通りません。

Discussion