if 式と switch 式による SwiftUI のプレビューエラー対策
謝辞
2023年11月14日に開催された勉強会「potatotips #85 iOS/Android開発Tips共有会」での発表内容を基にした記事です。
また、この記事はSwiftWednesday Advent Calendar 2023の6日目の記事です。
昨日は @uhooi さんでした。
if
とswitch
Swift 5.9の新機能: 式としての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