Help us understand the problem. What is going on with this article?

MicroViewControllerのInjectableとInteractableプロトコルを使ってみたら良かった話

最近少し入力が多くて画面が多く複雑な画面を実装することになったので、iOSDC2018で発表されてたMicroViewControllerを思い出して、ViewController間のやりとりを入力と出力のプロトコルで縛る方式で行うようにした。

「MicroViewControllerのやり方良いよね」というと、正しく相手に通じないかもしれないので何が良いかというのを書いておこうと思います。私は別にButtonとかCellとかをViewControllerにしたいわけじゃなくて、単に入力と出力のプロトコルが良いよね、コンセンサスとりやすいよね、これをベースとさせてもらって自分で好きにやっていけば良いよねということが私の感想です。

全体的なイメージ

この話のサンプルとして次の4つのViewControllerを考えました

  • 何かを編集する目的のある画面のちょっと複雑なEditViewController
    • 2パターンの値を編集する画面で2パターン切り替える画面のEditValueContainerViewController
    • 値を編集するコンテナの中のEditViewController
  • 値を計算したりするCalcViewViewController

MicroViewController.001.png

入力

https://github.com/mercari/Mew/blob/master/Sources/Mew/Protocols/Injectable.swift

public protocol Injectable {
    associatedtype Input
    func input(_ input: Input)
}

サンプルとしてはEditValueContainerViewControllerからEditValueViewControllerに初期値を渡すような際にも使える。

class EditValueViewController: UIViewController {
    struct Input {
        let value: Int
    }

    ... 
}

extension EditValueViewController: Injectable {
    func input(_ input: Input) {
        // self.input = input として使い回すときもあるけどしなかったりもする
        if isViewLoaded {
            label.text = input.value
        }
    }
}

func input(_ input: Input)が単なるメソッドなのが良い。これをvar input: Inputにしてしまうと入力をgetできてしまう。

// プロパティだったらgetできるのでprintしてみる
print(viewController.input)

getして誰かが困るかっつうと困らない。困らないんだけど入力をgetする意味がないのでできない方が良くて、メソッドなのでセットしかできないのがいい。言い換えると、セットしかしないんだからセッターしかない。良い。

出力

https://github.com/mercari/Mew/blob/master/Sources/Mew/Protocols/Interactable.swift

public protocol Interactable {
    associatedtype Output
    func output(_ handler: ((Output) -> Void)?)
}

例えば、EditValueViewControllerがコンテナに結果を伝えたい

class EditValueViewController: UIViewController {
    struct Input {
        let value: Int
    }

    // OutputはInputと同じにもできる。つまり入力の型と出力の型は同じ
    typealias Output = Input
    private var outputHandler: ((Output) -> Void)?
    ... 

    func textFieldが入力されて値が変わったよ(_ changedValue: Int) {
        outputHandler?(Output(value: changedValue))
    }
}

extension EditValueViewController: Interactable {
    func output(_ handler: ((Output) -> Void)?) {
        outputHandler = handler
    }
}

これも入力と同じで出力がメソッドなのがいい。

その他の良い点

その他の良い点も書いておく。

  • そもそもこのprotocolはViewControllerに限定せずViewでも使えるのがいい
  • 複数人でも入力と出力だけやり方を揃えてぶらさず、そこを重点的にチェックすれば良くなるはず
  • また、入力と出力さえ公開されていればよくて他はすべてprivateでいい。人のコードをチェックする際に考えることが減りそうなのもいい
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away