🎨

[SwiftUI] クロージャスタイルのModifierでバージョン分岐を楽に書く

に公開

SwiftUIでアプリを開発していると、異なるiOSバージョンで異なるスタイルを適用したい場面によく遭遇します。しかし、#available を使ったバージョン分岐は時として冗長なコードを生み出してしまいます。

バージョン分岐による冗長なコード

理想的だが書けないコード

まず、理想的には以下のような書き方ができれば良いのですが、これはSwiftUIの仕様上不可能です。

Button()
if #available(iOS 26.0, *) {
    .buttonStyle(.glassProminent)
} else {
    .buttonStyle(.borderedProminent)
}

現実的だが冗長なコード

実際には、以下のような書き方をする必要があります。

if #available(iOS 26.0, *) {
    Button()
        .someModifier()
        .someModifier()
        .buttonStyle(.glassProminent)
} else {
    Button()
        .someModifier()
        .someModifier()
        .buttonStyle(.borderedProminent)
}

この書き方の問題点は、共通のモディファイア(.someModifier())が重複してしまうことです。共通部分が多くなればなるほど、コードの保守性が悪化します。

クロージャスタイルのModifierで楽に書く

シンプルな実装

この問題を解決するため、クロージャを受け取るシンプルな modifier(_:) を定義します。

extension View {
    func modifier(@ViewBuilder _ closure: (Self) -> some View) -> some View {
        closure(self)
    }
}

この実装は非常にシンプルで、既存のSwiftUIのAPIと同じような感覚で使用できます。

使用例

クロージャスタイルの modifier(_:) を使うと、先ほどのコードを以下のように楽に書き直せます。

Button()
    .someModifier()
    .someModifier()
    .modifier {
        if #available(iOS 26.0, *) {
            $0.buttonStyle(.glassProminent)
        } else {
            $0.buttonStyle(.borderedProminent)
        }
    }

Discussion