Uberが出しているDIライブラリ「needle」を使って簡単なDIの検証をしてみました。
https://github.com/uber/needle
インストール
needleを利用するためには、コードジェネレーターとアプリに組み込むライブラリの2点のインストールが必要になります。
Carthageを使うと、双方が一気に使えるようになるので、今回Carthageを用いたインストールを行いました。
Cartfileを作成し、以下を追記します。
このタイミングでの最新バージョンがv0.17.2だったため、そのバージョンを指定しています。
github "https://github.com/uber/needle.git" ~> 0.17.2
実際試して使っていく中で、Carthageでインストールしたコードジェネレーターがうまく動作せず、コードジェネレーター側はHomebrewに切り替えたため、Homebrewを用いたインストールも行いました。
brew install needle
アプリ側ライブラリの導入
Carthageを用いた導入を行います。
上記Cartfileを作成した状態で、ターミナルから以下コマンドを実行します。
carthage update --use-xcframeworks --platform iOS
Carthage/Build/以下にNeedleFoundation.xcframework
が作成されるので、Xcodeから導入したいターゲットのGeneral > Frameworks, Libraries, and Embedded Content 項目に移動し、上で作成されたNeedleFoundation.xcframework
をドラッグアンドドロップします。
アプリの雛形作成
今回のケースでは、Main > Root > Home の3画面構成で、Root > Home の画面内でneedleの機能を利用してみます。
Xcodeで新規プロジェクト作成、テンプレートでiOS > Appを選択、InterfaceはStoryboardを選択したプロジェクトの初期状態から始めます。
データ用の構造体作成
今回の検証では、ユーザーを示す構造体を作成し、ユーザー名だけを保持できるようにします。
struct User {
var name: String
init(name: String) {
self.name = name
}
}
Root画面の作成
Root画面の作成のために、DIの構成情報を定義するComponentクラス、ViewControllerクラス、xibファイルの3点を作成します。
ComponentクラスとViewControllerクラスの実装は以下のようにします。
xibファイルはHome画面への遷移用にUIButton1点だけを配置したシンプルなViewを用意します。
import NeedleFoundation
final class RootComponent: BootstrapComponent {
var user: User {
shared{ User(name: "Taro") }
}
var rootViewController: RootViewController {
return RootViewController(homeBuilder: homeBuilder)
}
var homeBuilder: HomeBuilder {
return HomeComponent(parent: self)
}
}
import UIKit
class RootViewController: UIViewController {
private let homeBuilder: HomeBuilder
required init?(coder: NSCoder) {
fatalError()
}
init(homeBuilder: HomeBuilder) {
self.homeBuilder = homeBuilder
super.init(nibName: String(describing: RootViewController.self), bundle: nil)
}
@IBAction private func homeButtonDidTapped() {
present(homeBuilder.homeViewController, animated: true)
}
}
Home画面の作成
Home画面も同様、DIの構成情報を定義するComponentクラス、ViewControllerクラス、xibファイルの3点を作成します。
ComponentクラスとViewControllerクラスの実装は以下のようにします。
xibファイルはユーザーの名前表示用にUILabel1点だけを配置したシンプルなViewを用意します。
import NeedleFoundation
protocol HomeBuilder {
var homeViewController: HomeViewController { get }
}
protocol HomeDependency: Dependency {
var user: User { get }
}
final class HomeComponent: Component<HomeDependency>, HomeBuilder {
var homeViewController: HomeViewController {
return HomeViewController(user: dependency.user)
}
}
import UIKit
class HomeViewController: UIViewController {
@IBOutlet private var nameLabel: UILabel?
private let user: User
required init?(coder: NSCoder) {
fatalError()
}
init(user: User) {
self.user = user
super.init(nibName: String(describing: HomeViewController.self), bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// ユーザー名表示
nameLabel?.text = user.name
}
}
Main > Rootの遷移作成
プロジェクト新規作成時に作成されているMain.storyboardとViewControllerからRoot画面に繋がる遷移を作成します。
Main.storyboardにRoot遷移用のUIButtonを1点配置します。
ViewControllerの実装は以下の通りです。
import UIKit
class ViewController: UIViewController {
@IBAction private func rootButtonDidTapped() {
let rootComponent = RootComponent()
present(rootComponent.rootViewController, animated: true)
}
}
コードジェネレーターの実行
上記で検証に用いる構成要素が揃ったので、コードジェネレーターでDIの依存関係を解決してくれるファイルの作成を行います。
ターミナルで、プロジェクトフォルダ配下のソースコードが入ってるフォルダ(新規プロジェクト作成後、AppDelegate.swiftがある階層)に移動し以下コマンドを実行します。
needle generate NeedleGenerated.swift ./
このコマンドを実行すると、同じ階層にNeedleGenerated.swiftというファイルが作成されます。
依存関係の解決
上記で生成したNeedleGenerated.swiftをXcode上で、Add Files to "プロジェクト名"の操作を行い、利用できる状態にします。
その後、AppDelegate.swiftのapplication(_:didFinishLaunchingWithOptions:)
内でregisterProviderFactories()
の呼び出しを追加します。
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
registerProviderFactories()
return true
}
}
アプリ実行
実行すると以下のようにHome画面でユーザー名で登録した「Taro」が表示されており、検証想定していた表示が実現できています。