【SwiftUI】View のロジック部分を外出しにする構成

ModelView を作成することで

「ロジック部分を外出し」

して View の見通しを良くすることができます。

同時に @State も外部クラスに任せますか。


@Observable final class StateHolder {
  var text = ""
}


var stateHolder = StateHolder()

TextField("text", text: Binding(
  get: { stateHolder.text },
  set: { newValue in stateHolder.text = newValue })
)


var stateHolder = StateHolder()

@Bindable var sh = stateHolder
TextField("text", text: $sh.text)

複雑なデータ処理ロジック部分も置いてしまうと、

実質の今どきの ModelView となりますね。

基本、ModelView は View に対して 1:1 なことを忘れないでください。



👉 【SwiftUI】@State と 単方向データフロー hatena-bookmark
👉 【SwiftUI】ModelView は 単なる StateHolder なのか ? - ModelView の役割 hatena-bookmark


【SwiftUI】ViewModel は 単なる StateHolder なのか ? - ViewModel の役割

「ViewModel は必要なのか」というような話のときに、

「State置き場としての ViewModel」

というような言葉を聞いたりしていましたが。


@Observable final class StateHolder {
  var text = ""
}


var stateHolder = StateHolder()

TextField("text", text: Binding(
  get: { stateHolder.text },
  set: { newValue in stateHolder.text = newValue })
)


var stateHolder = StateHolder()

@Bindable var sh = stateHolder
TextField("text", text: $sh.text)

ドメイン部分というか複雑なデータ処理ロジック部分も置けますよね。

しかし。。。



👉 【SwiftUI】@State と 単方向データフロー hatena-bookmark
👉 【SwiftUI】ModelView は StateHolder なのか ? - ModelView の役割 hatena-bookmark


【SwiftUI】 スクレイピングで GitHub Contribution Graph をつくる

WEB-API もあるようですが。


❯ curl -s https://github.com/users/benigumocom/contributions

で、HTMLが取得できるというのでやってみる。

わーい。

guard ってなんとなく嫌いだなあ。

👉 GitHub Gist のコードをスクリプトから更新する方法 hatena-bookmark


【SwiftUI】onAppear() ・onChange(initial: true) ・task() の実行順序

プロパティをいじるのに便利ですが。

実行の順序ですよ。

これは、予想通りですが。


Text("hello")
  .onAppear { 
    print("Text onAppear") 
  }
  .onChange(of: true, initial: true) { 
    print("Text onChange") 
  }
  .task {
    print("Text task") 
  }

// Text onAppear
// Text onChange
// Text task

記述の順序にかかわらず task() は最後に実行されます。


Text("hello")
  .task { print("Text task") }
  .onAppear { print("Text onAppear") }
  .onChange(of: true, initial: true) { print("Text onChange") }

// Text onAppear
// Text onChange
// Text task

onAppear() と .onChange(initial: true) は記述順。


Text("hello")
  .task { print("Text task") }
  .onChange(of: true, initial: true) { print("Text onChange") }
  .onAppear { print("Text onAppear") }

// Text onChange
// Text onAppear
// Text task

では、View のネスト。


Group {
  VStack {
    Text("hello")
      .task { print("Text task") }
      .onChange(of: true, initial: true) { print("Text onChange") }
      .onAppear { print("Text onAppear") }
  }
  .task { print(" VStack task") }
  .onChange(of: true, initial: true) { print(" VStack onChange") }
  .onAppear { print(" VStack onAppear") }
}
.task { print("Group task") }
.onChange(of: true, initial: true) { print("Group onChange") }
.onAppear { print("Group onAppear") }

// Text onChange
// Text onAppear
// VStack onChange
// VStack onAppear
// Group onChange
// Group onAppear
// Text task
// VStack task
// Group task

task() は 最上位の View の表示後にまとめて実行されることに驚きです。

 

■ まとめ


- ネストの深いものから順番に実行される。
- onAppear(), onChange(initial: true) は記述順に実行される。
- task() は最上位 View の表示後にまとめて実行される。

結構、手が止まるんですよね、ここらへん。


【SwiftUI】 onChange() の 書き方

iOS17でクロージャ引数が1つのものは Deprecated と警告が出たりするので、


👉 onChange(of:perform:) | Apple Developer Documentation hatena-bookmark

こんなかんじで書いてました。


Text("\(count)")
  .onChange(of: token) { _, newValue in
    // do something ...
  }

クソですね !

 

■ クロージャの引数

クロージャの引数なしと2つのものがあるんですね !


func onChange<V>(
    of value: V,
    initial: Bool = false,
    _ action: @escaping () -> Void
) -> some View where V : Equatable

👉 onChange(of:initial:_:) | Apple Developer Documentation hatena-bookmark


func onChange<V>(
    of value: V,
    initial: Bool = false,
    _ action: @escaping (V, V) -> Void
) -> some View where V : Equatable

👉 onChange(of:initial:_:) | Apple Developer Documentation hatena-bookmark

よって、引数なしのほうを使って、

フツーに以下のように書けば良かったのか。


Text("\(count)")
  .onChange(of: token) {
    print("\(token)")
  }

なるほど。

IDE の選択肢の一番上をなんとなく選択してたのだろうと思います。

きちんと、ドキュメントを見ることって大事。

考えてみると、引数2つの記述


Text("\(count)")
  .onChange(of: token) { oldValue, newValue in
    // do something ...
  }

は、oldValue が必要でない限り使う必要がないな。