208
117

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOS13ではStoryboardでもDIができる件について

Last updated at Posted at 2019-07-10

はじめに

これまでのiOS13未満のUIKitのAPIでは、Storyboardで定義したUIViewControllerではinitializerでのDI(Dependency Injection|依存性の注入)ができないという課題がありました。

※initializerでのDIはコンストラクタインジェクションとも呼ばれますが、Swiftでは慣習的にコンストラクタという用語はあまり使わないので、本記事では「initializerでのDI」とします。

UIViewControllerの実装方法とinitializerでのDIの可否をまとめると以下のようになります。
※黒魔術的なライブラリなどを利用しない前提

レイアウト実装方法 initializer DI可否(〜iOS12) initializer DI可否(iOS13〜)
コードベース
XIB
Storyboard ×

iOS13未満での実装方法の詳細については以下の記事で説明しています。
Qiita - iOSとコードベースレイアウト

勿論、プロパティインジェクションならばiOS12以下でもStoryboardで定義したUIViewControllerで可能ですが、initializerでのDIをする設計にすることで以下の恩恵が得られます。

  • DIする値をプロパティで保持する場合に、varでなくletで定義できるのでより安全
  • DIする値をプロパティで保持する場合に、Optional型でなく非Optional型で定義できるので無駄なUnwrapが不要
  • UIViewControllerの生成を行うために、必ずDIが必要になるので、プロパティインジェクションのように設定忘れが起こり得ない

iOS13でStoryboardでイニシャライザでDIする

iOS13では地味にこんなAPIが加わっています。

instantiateInitialViewController(creator:)

func instantiateInitialViewController<ViewController>(creator: ((NSCoder) -> ViewController?)? = nil) 
-> ViewController? where ViewController : UIViewController

このAPIでは、creatorというクロージャーを引数として渡すことで、これまで隠蔽されていたStoryboardからのUIViewControllerの生成過程に介入することができます。

以下のようにUIViewControllerの継承クラスで以下のようにNSCoderを引数にとるinitializerを実装します。

class ViewController: UIViewController {
    @IBOutlet fileprivate weak var textView: UITextView!
    
    private let dependency: Int
    
    // DI用のinitializer
    init?(coder: NSCoder, dependency: Int) {
        self.dependency = dependency
        super.init(coder: coder)
    }
    
    // 独自のinitializerを実装するときにrequiredとして実装が要求されるinitializer
    // 利用しないので、fatalError()として実装を省略する 
    required init?(coder: NSCoder) {
        fatalError()
    }
}

上記のように定義したinitializerをcreatorのクロージャーで利用します。

let storyboard = UIStoryboard(name: "ViewController", bundle: nil)
let viewController = storyboard.instantiateInitialViewController { coder in
    ViewController(coder: coder, dependency: 10)
}

以上のようにすることで、Storyboardで定義したUIViewControllerでもinitializerでのDIが可能です。

208
117
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
208
117

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?