【Swift】よくある enum extension の switch ~ case のイメージ

頻繁に見かけるんですけど、

いまいち分かってない以下のような記述。

これは一体なんなのか。

すごく冗長に見えるし。

ぱっと見、理解できなかったので、

基本的な部分を少し噛み砕いてみました。

順番にやっていきます。

 

🤔 enum

まず、列挙型の enum。


enum Animal {
  case dog
  case cat
  case monkey
}

また、次のように書いても同じ。


enum Animal {
  case dog, cat, monkey
}

case がいるんですね !

 

🤔 extension で switch ~ case 追加

extension で拡張して追加。


enum Animal {
  case dog, cat, monkey
}

extension Animal {
  var hiragana: String {
    switch self {
      case .dog: "いぬ"
      case .cat: "ねこ"
      case .monkey: "さる"
    }
  }
}

print(Animal.cat.hiragana)
// ねこ

紐づくんですね。

それぞれの enum 要素に。

 

🤔 さらにもう一つ追加


enum Animal {
  case dog, cat, monkey
}

extension Animal {
  var hiragana: String {
    switch self {
      case .dog: "いぬ"
      case .cat: "ねこ"
      case .monkey: "さる"
    }
  }

  var katakana: String {
    switch self {
      case .dog: "イヌ"
      case .cat: "ネコ"
      case .monkey: "サル"
    }
  }
}

print(Animal.cat.hiragana)
print(Animal.monkey.katakana)
// ねこ
// サル

そういうことか !

enum の「それぞれの要素から枝が生えていく」のか !

 

🤔 まとめ

どうやら考え方としては、

ベースの enum のそれぞれの要素に extension で枝を生やしていく

ようなイメージでいいのでしょうか。

ネストしたデータ構成の編集時に便利に使えそうです。

推測できれば、クラス名の省略ができることも良い !

しかし、Swift の enum は機能豊富らしいけど、

まずは、これくらいでいいかな。


【SwiftUI】@Observable と @Binding vs @Bindable 👀

例えば、Binding を含むこういうのがあったとして。


struct Parent: View {
  @State private var isOn = false

  var body: some View {
    VStack {
      Rectangle().fill(isOn ? .yellow : .gray)
      Toggle(isOn ? "ON" : "OFF", isOn: $isOn)
    }
  }
}

親子分けした場合。


struct Parent: View {
  @State private var isOn = false

  var body: some View {
    VStack {
      Rectangle().fill(isOn ? .yellow : .gray)
      Child(isOn: $isOn)
    }
  }
}

struct Child: View {
  @Binding var isOn: Bool

  var body: some View {
    Toggle(isOn ? "ON" : "OFF", isOn: $isOn)
  }
}

👉 【SwiftUI】再描画の伝播 - @State と @Binding hatena-bookmark

@State の数が増えてくるとつらいので、

まとめて外出しにする方法はないのかな、と。

そして、子 View などへ上手に持ち運びたい。

 

👀 @Observable と @Bindable

@Observable と @Bindable を使います。

Observable

Observable プロトコルの適合性を定義および実装します。

概要
このマクロは、カスタム タイプに監視サポートを追加し、タイプをObservableプロトコルに準拠させます。

👉 Observable() | Apple Developer Documentation hatena-bookmark

Bindable

監視可能なオブジェクトの可変プロパティへのバインディングの作成をサポートするプロパティラッパータイプ。

概要
このプロパティ ラッパーを使用して、Observable プロトコルに準拠するデータ モデル オブジェクトの可変プロパティへのバインディングを作成します。

👉 Bindable | Apple Developer Documentation hatena-bookmark

ドキュメントのコードを参考にしながら、

とりあえずは、クラスにまとめて外出しします。


@Observable
class StateModel: Identifiable {
  var isOn = false
  var label: String { isOn ? "ON" : "OFF" }
  var color: Color { isOn ? .yellow : .gray }
}

struct Parent: View {
  @Bindable private var model = StateModel()

  var body: some View {
    VStack {
      Rectangle().fill(model.color)
      Toggle(model.label, isOn: $model.isOn)
    }
  }
}

 

👀 子に渡す

Binding を含む @State は、子の @Binding で受けてましたが。

@Observable クラスはどうやって渡すのか。

知ってる範囲であれこれ試してみました。

どうやら、これが標準的で良さげです。


@Observable
class StateModel: Identifiable {
  var isOn = false
  var label: String { isOn ? "ON" : "OFF" }
  var color: Color { isOn ? .yellow : .gray }
}

struct Parent: View {
  private let model = StateModel()

  var body: some View {
    VStack {
      Rectangle().fill(model.color)
      Child(model: model) // *
    }
  }
}

struct Child: View {
  var model: StateModel

  var body: some View {
    @Bindable var model = model // *
    Toggle(model.label, isOn: $model.isOn)
  }
}

試しながら思ったのは、

「クラスインスタンスのまま持ち歩いて、使う直前で @Bindable で Binding 化する」

のが良いですわ。

持ち回りがシンプルで楽だから。

データ自体は @Bindable なくても変化を検知して順方向には流れる。

ただ、いきなり出てきて意味不明で気持ちが悪かった。


@Bindable var model = model

公式ドキュメントでもあちこちにあって、気にはなっていました。

 

👀 まとめ

最後に、ゴミを置いておきます。

どのコメントブロックもきちんと動きます。

いや、動いてるように見えているだけかもしれません。


【SwiftUI】今どきの データモデル (Model data) のマクロ記述 📝

ネットで参考になりそうなコード記述を探すと、

新旧入り乱れてる感じがしたので、

まず読んでおいたほうがいいような気がした。

👉 Platforms State of the Union (ASL) - WWDC23 - Videos - Apple Developer hatena-bookmark

公式ドキュメントにもしっかり説明があるようなので、ざっくり古いかもしれない記述を整理しておく。


Managing user interface state | Apple Developer Documentation
👉 https://developer.apple.com/documentation/swiftui/managing-user-interface-state

Managing model data in your app | Apple Developer Documentation
👉 https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

Migrating from the Observable Object protocol to the Observable macro | Apple Developer Documentation
👉 https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro

 

📝 まとめ


: ObservableObject   →   @Observable
@Published           →   不要
@StateObject         →   @State
.environmentObject() →   .environment()
@EnvironmentObject   →   @Environment
@ObservedObject      →   不要 or @Bindable

→ 「@Published*Object があれば古い。」

SwiftData を使うにしても、まずは SwiftUI のみの記述の変遷の知識もいるよな ?

先人たちの作ったネットリソースや AI系コードサジェスチョンを効率的に利用するためにも。

次は SwiftData です。