if 式と switch 式による SwiftUI のプレビューエラー対策
謝辞
2023年11月14日に開催された勉強会「potatotips #85 iOS/Android開発Tips共有会」での発表内容を基にした記事です。
また、この記事はSwiftWednesday Advent Calendar 2023の6日目の記事です。
昨日は @uhooi さんでした。
Swift 5.9の新機能: 式としてのifとswitch
Swift 5.9からifとswitchを式として表現できるようになりました。これはSE-0380により実現されました。
これによりifとswitchをよりシンプルに書くことが可能になりました。以下に具体的な例を挙げます。
enum内のswitch表現の簡略化
従来のSwiftでは、enumの各ケースに対する値をswitch文を用いて設定していました。以下はその例です。
enum SomeEnum {
case moon
case star
var someValue: String {
switch self {
case .moon: return "moon 🌕"
case .star: return "star ⭐️"
}
}
}
Swift 5.9では、このコードを以下のようにreturnを省略しシンプルに書くことができます。
enum SomeEnum {
case moon
case star
var someValue: String {
switch self {
case .moon: "moon 🌕"
case .star: "star ⭐️"
}
}
}
メソッド内でのswitch表現の改善
メソッド内で特定の値をenumの型によって設定する場合、以前は次のように記述していました。
func someFunction(type: SomeEnum) {
let value: Int
switch type {
case .moon: value = 100
case .star: value = 999
}
// valueを使った処理
}
Swift 5.9では、以下のように直感的にコードを書くことが可能になります。
func someFunction(type: SomeEnum) {
let value = switch type {
case .moon: 100
case .star: 999
}
// valueを使った処理
}
SwiftUIプレビュー時の問題と解決策
SwiftUIのプレビュー時にifとswitchの式を利用すると、特定の状況でコンパイルエラーが発生します。例えば、以下のようなBool値に基づいてTextを返すViewがあります。
struct ContentView: View {
let isSwift: Bool
var body: some View {
makeLanguageView()
}
func makeLanguageView() -> some View {
if isSwift {
Text("Swiftだよ")
} else {
Text("Kotlinだよ")
}
}
}
この場合、以下のようなエラーメッセージが表示されることがあります。
'if' may only be userd as expression in return, throw, or as the source of an assignment
通常のビルドであれば問題なく成功しますが、Preview時のビルドではコンパイルエラーが発生します。これはPreviewProviderを利用した場合でも同様です。
このエラーはXcode起因の問題であり、将来的には修正されることが期待されます。
解決方法
以下は、この問題に対処するための3つの方法です。
@ViewBuilderキーワードの使用
@ViewBuilderキーワードをメソッドに付与することで、Preview時のエラーを回避できます。
struct ContentView: View {
let isSwift: Bool
var body: some View {
makeLanguageView()
}
@ViewBuilder
func makeLanguageView() -> some View {
if isSwift {
Text("Swiftだよ")
} else {
Text("Kotlinだよ")
}
}
}
明確な戻り値の指定
また、以下のようにメソッドの戻り値をOpaque Typeであるsome ViewからTextに変えることでもエラーを回避することができます。
struct ContentView: View {
let isSwift: Bool
var body: some View {
makeLanguageView()
}
func makeLanguageView() -> Text {
if isSwift {
Text("Swiftだよ")
} else {
Text("Kotlinだよ")
}
}
}
Existential Typeへの置き換え
メソッドの戻り値をsome Viewからany Viewに変える方法です。ただし、この方法ではメソッドの呼び出し元での型キャストが必要になるため、上記の2つの方法が推奨されます。
struct ContentView: View {
let isSwift: Bool
var body: some View {
makeLanguageView() as! View
}
func makeLanguageView() -> any View {
if isSwift {
Text("Swiftだよ")
} else {
Text("Kotlinだよ")
}
}
}
まとめ
この記事では、Swift 5.9のアップデートの1つであるifとswitchの式表現について解説しました。また、ifとswitchの式をSwiftUIプレビュー時に使用する際の課題点と、それを解決する方法について説明しました。
明日は @startaiyo さんです!
開発環境
- Swift compiler version info:
Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
- Xcode version info:
Xcode 15.0 Build version 15A240d
Discussion