SwiftUIでもこれまで通りDelegateを使ってみたい

Swift

はじめに

お外を出歩かずに家に引きこもる時間が長くなり、少しお酒の量も増えてまいりました。これまでは焼酎をちびちびでしたが、最近はホットワインにハマっています。
あ、いや、飲みながら仕事をしているわけでは決して……
どうでもいいことですが、卵酒は口に合いませんでした。

さて今日は酒の話をしたかったのではなく……

ここ何日かSwiftUIを使いつつも、これまで通りDelegateも使っていきたいということで試行錯誤をしていました。言語が変わったわけでもなく、理屈上は使えるはずです。

世間に散らかるMVCやらアレやらコレやらに応じてどう取り組むかが変わってくる課題になるので、一概にこれが正解!とは言えないです。
ちょっと心苦しい。

参考までに見ていただければ幸いです。毎度のお約束ですが、間違っているかもしれません。後日書き直しているかもしれません。

SwiftUIを使う前のやり方

SwiftUIを使う前は、Viewと処理部分をある程度区別をつける方法で組んでいました。VC(View Controller)を軸としてM(Model)とV(View)が連携するMVVCもどきです。できるかぎりAppleの公式を参照してそれに合わせるようにしていました。

どうしても手癖というものもあり、それに適合する形で組めていたとは自信を持って言えないので、ゆる〜く、MとVCのグループとVのグループに切り分ける方式とでも申しておきましょう。

極力Vにはユーザに見える部分だけとし、単純にユーザに見せるだけ/ユーザから受け取るだけとしました。税額計算やデータの集計などのロジック的なものは全てVCにまとめ、VCが太ってきたら業務や使い方などでグループ分けをして切り出すような感じです。

本筋に戻りますが、Delegateを使っていたのはこういうパターンです。

  • ボタンアクションなど、Viewで受け付けるユーザからの操作を全てVCに丸投げする
  • VCから、切り出したクラスやMに処理を丸投げする

思想信条の都合上、VからMやVCから切り出したクラスへの直接丸投げはしない方向で進めてました。必ずVCを経由する方式です。

こういうのをSwiftUIを使ってもしたかったんです。

SwiftUIを使うようになってからのやり方

基本的には方法論や考え方は同じはずです。

SwiftUIの導入で、View(見た目)とロジックの切り分けではなく、どちらかというと業務ドメイン的な切り分け方になったように思います。Viewの構造体の中でビューをメインとしてそれに関係するロジックを持っているイメージです。

ならばdelegate不要ではと思われるかもしれませんが、非同期でデータを取得するパターンや、複数のViewを組み合わせている場合などでデータの融通をしないといけないパターンなど、Delegateのお世話になるパターンはいくつも見つかると思います。

移譲先をただのクラスにしてしまうとこれまでと同じですので、移譲先はView(SwiftUI)としてまとめてみます。

クラスでdelegateを使う

まずはデリゲートプロトコルに準拠させます。構造体の宣言の横に並べるアレです。

struct testStruct:View,testDelegate {
 ~略~
}

ここでいうところのViewの横にあるものですね。

次に移譲先Viewのinit()メソッドにてクラスのインスタンス生成とdelegateプロパティへの移譲先の構造体代入を済ませてしまいます。

仕上げに、移譲されたメソッドの中身を実装します。

基本的にこれまで通りです。

View(SwiftUI)でdelegateを使う

今度は移譲先も移譲元もView(SwiftUI)のパターンです。子Viewからのアクションを親Viewにて処理したり、そのまま別クラスへ丸投げしたりするときに使います。

~.delegate = self的なことをする場所に困る

基本的なアプローチは一緒なんですけどね。

SwiftUIを使う前はクラスのインスタンスが持っているdelegateプロパティに対し、委譲先のインスタンスをもたせます。ですが、SwiftUIのViewは構造体ですし、構造体をどうのこうのとするところは無いです。
そもそも構造体は値型。
委譲先を設定するところが、これまでの感覚で行くと見つからないんです。

これで詰まりました。

解決方法

一時は諦めかけたものの、解決できました。

SwiftUIのViewを呼び出すときに引数で初期化すれば良いだけでした。

イメージとしては、例えば委譲元の構造体(View)をTestViewとし、委譲先の構造体を保持するプロパティをdelegateとすると、

TestView(delegate:self)

とするだけです。

構造体(View)が持つプロパティは初期化が必須です。Viewを呼び出すときに初期化してしまえば良いわけです。

理屈がわかれば単純な話です。

※delegateプロパティと言っていますが、変数名は適当に変えられるので、名称はdelegateとは限りません。要は、委譲先を保持できるプロパティのことです。

注意点

ちょっとした点で違いがいくつか。

  • 移譲先のインスタンスを保持するプロパティにはweakを付けない
  • プロトコルにはclassを付けない

1つ目のは、構造体が値型だからという理由ですね。2つ目のは、移譲先がクラスではなく構造体だからです。

delegateの定型パターンを手癖でズババっと書いてしまうとエラーが出てションボリします。

まとめ

これまで通りでDelegateを使うことができます。ただし、構造体相手ということもあり、若干違いがあります。そこは柔軟に対応していきましょう。

基本的にイメージで突き進む右脳人間なので、そもそものところ(Delegateの仕組み)を勘違いしているかもしれません。これまたお約束ですが、「鵜呑みはダメですよ」と申しておきましょう。

そういえばDelegateを使う方法以外にCombineとやらを使う方法もあるみたいですね。これまでの方法一本槍で突き進むだけでなく、新しい方法や違う方法も取り入れてみたいですね。

余談ですが、このご時勢でふさぎ込むことも多々あると思います。百薬の長をお供に、一日一日大切に過ごしていきましょう!!これまでの平常時のように仕事に追われる日々とは違う状況を体感できるのは、そうそう無い機会ですよ!