【実装MEMO】PropertyWrappersの機能を利用したDependency Injectionのコードに触れた際の備忘録

Fumiya Sakai
12 min readMay 24, 2020

改めてiOS13から登場したProprtyWrappersついて実際のコードを交えて触れてみた記録

最近はコロナの影響で、外出頻度はめっきりと減り、お仕事もリモートワークが主体の形になっていますが、ありがたいことに元気に日々を過ごすことができております。日々新たな発見や新しい技術を知る機会が増えてエキサイティングに感じる一方で、「まだまだ触れ切れてない事はたくさんあるな」と思いながら過ごしている次第です。

完全に余談ですが、料理がすっかり趣味になってしまいました。

今回は表題の通りではあるんですが、自分で取り組んでいるサンプルの中でClean Architectureを導入して試してみようと思い立って開発していた中でDependency Injectionをする必要性があり、またこれまで利用した経験のある手法とは異なった方法を試してみようと思ったきっかけから取り組んだことが始まりです。

今回Dependency Injectionについて考えた際の参考資料

Dependency Injectionに関するWikipediaの記載を見ると、

依存性の注入とは、コンポーネント間の依存関係をプログラムのソースコードから排除するために、外部の設定ファイルなどでオブジェクトを注入できるようにするソフトウェアパターンである。

となっています。本当に大雑把に言うと「あるクラスに必要なオブジェクトを外から渡してあげること」とも言えるのではないかと思います。

まずは例として例として外部APIリクエストを利用してお知らせ情報を取得する場合を考えてみることにします。下記の様に必要な処理をそれぞれのドメインに分離して管理する形にしていた場合には、それぞれのクラスが依存し合う関係となります。

このような依存関係が生まれるので初期化する際に必要なものを渡す必要が出てくる

ここで画面表示を組み立てるAnnouncementViewController内で対応するAnnouncementViewModelを利用する際には、下記のような感じでそれぞれの依存関係を解決するために必要なクラスのインスタンスを初期化の際に引き渡す必要が出てきます。

// MEMO: Dependency Injectionをしないで書いた場合
// これより更に依存するものが増えてしまうと管理ができぬ...
let viewModel = AnnouncementViewModel( // ① ViewModelが依存するUseCaseの初期化
announcementDataUseCase: AnnouncementDataUseCase(
// ② UseCaseが依存するRepositoryの初期化
annoucementRepository: AnnoucementRepository(
// ③ Repositoryが依存するAPIRequestManagerの初期化
apiRequestManager: APIRequestManager.shared
)
)
)

この例ではまだ、それほど依存関係が多くはない形ではありますが、実際の画面ではこれ以上に複雑な処理をしなければいけない場合の方が多いと思いますので、それに伴って依存するクラスも増えていくことになります。そうなってしまうと上記のような方法ではとても管理・把握が大変になってしまうことは想像がつくかと思います。

このような状態に陥ることを解決するために、実装クラスを初期化してその都度インスタンスを生成するのではなく、Protocolを利用して、実際に利用するクラスはProtocolに適合したクラスを初期化して利用する形とし、外側のコードから依存性を渡してあげる形にするという点がポイントになるかと思います。

Dependency Injectionに関する復習をする際には下記の@takasekさんの資料が本当に参考になりました。

本当にいつもtakasekさんの資料にはお世話になっております!

※上記資料については実際のコードも掲載されているので、実際のイメージもしやすくとても理解しやすかったです!

Swiftを本格的に触る以前にもバックエンド側でLaravel/Symfony等のPHPフレームワークに触れていたこともあり、馴染みがない訳ではなかったのですが、改めて考察してみると、実は理解が曖昧なままであった点や改めて理解が深まり腹落ちができた気がします。

※上記リンクではLaravelでのアーキテクチャの考察に関する例です。昨年はSwiftと一緒にLaravelにもお仕事で触れていたこともあって、その際の実装経験があったことも理解の手助けになりました。

Dependency Injectionを実現するためのライブラリ・参考資料

僕がこれまでに利用した経験があるDependency Injectionを実現するために利用した経験があるのは下記にも掲載している「Swinject + SwinjectStoryboard」だけではありますが、その他に知っているものを簡単に列挙してみるとこんな感じになります。

また、外部ライブラリを利用しないでDependency Injectionを実現する方法としては今年2月に参加した「みてねのMeetup #6 iOS/Android」の発表で紹介されていた下記資料の様な実装方針を取るのも良さそうに思います。

Interfaceの実装Objectを返すFactoryを別途作成して引き渡す方針が紹介されています

Stackoverflowで発見したコードを参考にして組み立ててみる

ここから、タイトルの本題に入っていきます。今回はライブラリを利用しない形で実現できないかと考えていて色々と調べていた際にたまたまなのですがStackoverflowでPropertyWrappersを利用して実装している事例を見つけたので、そこで紹介されていたコードにについて考察していきます。

またPropertyWrappersの基本的な部分や実際に活用している事例は下記に列挙した資料を参考にするとよりイメージがつきやすいのではないかと思います。

(特に下2つのQiitaで書かれているPropertyWrappersを利用してUserDefaultへのデータアクセスを便利にする例は今回のコードを理解して紐解く上でも参考になりました!)

例1. UserDefaultsで活用した事例

こちらで紹介している事例については、UserDefaultを型安全に取り扱うという点や、UserDefaultを利用する処理の記述を綺麗にするという部分で大きなメリットを感じられるのではないかと思います。

例2. UILabel等のUIに関する部品で活用した事例

こちらは、UILabelを継承したクラスに対してPropertyWrappersを活用した事例になります。

実際のコードはこんな感じ

下記のコードはStackoverflow内で紹介されていたDependency InjectionをPropertyWrappersを利用して実現しているコードにコメントを加えたものになります。ポイントとなる部分は、各ドメイン層における依存関係の登録をする際に一緒にProtocol名も渡している点になります。こうすることで、Protocol名をキーにして実装クラスを管理するような形を取ることができるので、もし各ドメイン側で必要な実装クラスとプロトコル名に誤りがある場合には、「名前との紐付けがおかしいですよ!」ということでクラッシュが発生するような形にしています。

(1) Dependency Injectionに関する処理

ポイントとして解説した箇所はinject<T>での定義部分になります。

Swift/Kotlin愛好会Vol.51での発表の際に教えて頂いた、resolve<T>の処理部分の別解もGistに追記しておきました。本当にありがとうございました!)

(2) PropertyWrappersを利用した依存関係の定義

(3) 依存関係の登録と適用

こちらのコードに関しては現在サンプル開発をしているリポジトリでのコードを抜粋したものになりますので、まだまだ改良や改善の余地がたくさんありそうな気もしますが、iOS13 & Swift5.1からの新機能に触れると同時に実際に手を動かしながら、何気なく触れてきた部分の復習や今まで僕自身が触れる機会がなかったアーキテクチャの体験や予習ができたと感じています。

※補足: 下記の記事でもPropertyWrappersを利用したDependency Injectionの事例に関する解説がされておりました。

簡単なまとめと振り返り

今回は参考にしたコードをベースにして、自分なりにちょいと工夫して利用してみた形にはなりますが、気になってはいたけれれども活用しきれていなかった部分に改めてフォーカスを当ててみると発見があって、僕自身も本当に良い勉強になったと感じております。(久しぶりの文章でのアウトプット、なかなか時間がかかってしまった…)また、実際のお仕事の中でも今回取り組んでいる部分の知識が経験が生きて、キャッチアップが早くできた点も良かったです。

※今回の記事をご覧になった際に、「この部分に誤りがあるよ!」とか「説明が不十分な点があるから補足が必要かも!」といった点がありましたらご指摘頂けますと幸いです。

--

--

Fumiya Sakai

iOSアプリのUI実装が好きな元デザイナーからジョブチェンジをしたエンジニア。QiitaやGithubなどでもUI実装に関するサンプルや解説記事を投稿したり、たまに登壇しています。こちらでは実装の過程で出てきたメモとかを展開します。