使ったことがないAPIはミニマムですぐ書く!
開発環境
> xcodebuild -version Xcode 13.3 Build version 13E113
モチベーション
instantiateViewController(identifier:creator:)
を使ったことがないので使ってみたい
instantiateViewController(identifier:creator:) とは
Creates the specified view controller from the storyboard and initializes it using your custom initialization code. https://developer.apple.com/documentation/uikit/uistoryboard/3213989-instantiateviewcontroller
ViewControllerの初期化をカスタマイズできる。
どうカスタマイズできるのか。
本題に入る前に上記を使わない初期化方法を書く。
従来のViewControllerの初期化
ViewController
-> SecondViewController
に値を渡して遷移する場合を例にコードを書く。
ViewController.swift
import UIKit final class ViewController: UIViewController { let number: Int = 1 override func viewDidLoad() { super.viewDidLoad() let vc = SecondBuilder(number: number).build() self.present(vc, animated: true) } }
SecondViewController.swift
import UIKit final class SecondViewController: UIViewController { var number: Int! override func viewDidLoad() { super.viewDidLoad() print("number: \(number)") // "number: Optional(1)" } } struct SecondBuilder { let number: Int func build() -> SecondViewController { let storyboard = UIStoryboard(name: "Second", bundle: nil) let vc = storyboard.instantiateViewController(withIdentifier: "Second") as! SecondViewController vc.number = number return vc } }
所感
悪くないが、 SecondViewController
の number
を外から注入するため変数にしてしまっている。
変数をやめて定数にしたい。
そんな時に instantiateViewController(identifier:creator:)
が有効だぞ!
instantiateViewController(identifier:creator:) を使って書き直す
ViewController.swift
import UIKit final class ViewController: UIViewController { let number: Int = 1 override func viewDidLoad() { super.viewDidLoad() let storyboard = UIStoryboard(name: "Second", bundle: nil) let vc = storyboard.instantiateViewController(identifier: "Second") { coder in return SecondViewController(coder: coder, number: 1) } self.present(vc, animated: true) } }
SecondViewController.swift
import UIKit final class SecondViewController: UIViewController { let number: Int init?(coder: NSCoder, number: Int) { self.number = number super.init(coder: coder) } required init?(coder: NSCoder) { fatalError() } override func viewDidLoad() { super.viewDidLoad() print("number: \(number)") // "number: 1" } }
所感
遷移先のイニシャライザを新規で生やす必要があるが、変数を定数にできるので心理的安全性は高まりそう。
import UIKit final class SecondViewController: UIViewController { let viewModel: ViewModel struct ViewModel { let number: Int let hoge: String let fuga: String let piyo: String } init?(coder: NSCoder, viewModel: ViewModel) { self.viewModel = viewModel super.init(coder: coder) } required init?(coder: NSCoder) { fatalError() } override func viewDidLoad() { super.viewDidLoad() print("number: \(viewModel.number)") } }
注入したいプロパティが多い時は構造体切るなりするとイニシャライザの引数の個数も抑えられるし、iOS13以降なら使わない手はなさそう。