HealthKitアプリSwiftコードの紹介(1) – DIコンテナへのサービス登録

Apple

はじめに

この投稿では、現在Swiftで開発中のiPhoneアプリの設計やコードの一部を紹介していきます。このアプリは、Apple WatchやiPhoneでHealthKitのデータベースに保存されたヘルスデータをサーバーに送信することが主な目的です。アプリがバックグラウンド中でも定期的にデータを送信することを目標にしています。また、アプリ上で簡単なグラフの確認もできるように実装しています。

シーケンス図

今回説明する部分のクラスの関係がわかりやすいようにシーケンス図を記載しました。

シングルトンとしてのコンテナの実装

このコードは、SwiftのDIコンテナライブラリであるSwinjectを使用して依存性注入を行うためのコンテナを定義しています。まず、Containerのインスタンスを生成し、Assemblerを使用してそのコンテナに複数のアセンブリを適用します。各アセンブリは、特定の機能や機能グループの依存関係を登録します。例えば、RepositoryAssemblyはリポジトリの依存関係を、MainNaviAssemblyはメインナビゲーション関連の依存関係を登録します。

import Swinject

fileprivate let container: Container = {
    let container = Container()
    let assembler = Assembler(container: container)
    assembler.apply(assemblies: [
        RepositoryAssembly(),
        MainNaviAssembly(),
        // 省略
    ])
    return container
}()

extension Container {
    static var shared: Container {
        return container.synchronize() as! Container
    }
}

コンテナはシングルトンとして実装されており、sharedという静的プロパティ経由でアクセスできるように実装しています。これにより、アプリケーション内の異なる場所から同じコンテナインスタンスにアクセスできます。.synchronize()はコンテナをスレッドセーフにするためのメソッドであり、これにより複数のスレッドからのアクセスが安全になります。

このコードは、アプリケーションの異なる部分で同じ依存関係を共有するために使用されます。依存関係の登録と解決は、アプリケーション全体で一貫性を保ち、柔軟性を高めます。

コンテナへバックグラウンドタスクサービスの登録

このコードは、Swinjectを使用してDI(Dependency Injection)コンテナにサービスを登録するためのアセンブリクラスを定義しています。assembleメソッド内では、BackgroundTaskServiceProtocolの実装を登録しています。

import Swinject

class RepositoryAssembly: Assembly {
    func assemble(container: Container) {
        // 省略
        container.register(BackgroundTaskServiceProtocol.self) { r in
            let userDefault = r.resolve(UserDefaultUtilsProtocol.self)!
            let auth0 = r.resolve(Auth0ServiceProtocol.self)!
            let dataSyncAPI = r.resolve(DataSyncAPIServiceProtocol.self)!
            let localDatabase = r.resolve(LocalDatabaseProtocol.self)!
            return BackgroundTaskService(userDefault: userDefault, auth0: auth0, dataSyncAPI: dataSyncAPI, localDatabase: localDatabase)
        }.inObjectScope(.container)
    }
}

この実装は、DIコンテナから必要な依存関係を解決して作成され、DIコンテナに登録されます。この方法を使用することで、他のクラスやコンポーネントがBackgroundTaskServiceProtocolの実装を必要とする場合、DIコンテナから簡単に取得できるようになります。DIコンテナは、アプリケーション内のさまざまな場所で依存関係を管理し、適切なタイミングでインスタンスを提供する役割を果たします。これにより、コードの柔軟性とテスト容易性が向上し、依存関係の変更に対処しやすくなります。

このコードは、依存性注入(DI)コンテナであるcontainerに対して、BackgroundTaskServiceProtocolの実装を登録しています。登録する際に、クロージャーが使用されています。

クロージャー内のrは、DIコンテナによって提供されるResolverオブジェクトを指します。このResolverオブジェクトを使用して、他の依存性を解決するためのメソッドであるresolveを呼び出しています。

registerメソッドの引数として渡されたクロージャーは、登録された型が解決される際に呼び出されます。このクロージャー内では、UserDefaultUtilsProtocolAuth0ServiceProtocolDataSyncAPIServiceProtocolLocalDatabaseProtocolの各プロトコルに対して、それぞれの実装がDIコンテナから解決されます。その後、BackgroundTaskServiceのインスタンスが生成され、それらの依存性を注入して返されます。

.inObjectScope(.container)は、登録されたインスタンスのライフサイクルを設定しています。この場合、.containerはコンテナのライフサイクルを表し、コンテナ内で一意のインスタンスが保持されます。

アプリ起動時にバックグラウンドタスクを登録

このコードは、アプリの起動時にバックグラウンドでHealthKitからヘルスデータを読み出すための一部です。AppDelegateクラスは、UIApplicationDelegateプロトコルを採用し、application(_:didFinishLaunchingWithOptions:)メソッドを実装しています。このメソッドは、アプリが起動する際に呼び出されます。

このメソッドの中では、Swinjectフレームワークを使用してDI(Dependency Injection)コンテナからBackgroundTaskServiceProtocolに準拠したサービスのインスタンスを解決しています。解決したサービスが存在する場合は、そのサービスのregisterBackgroundTasks()メソッドを呼び出して、バックグラウンドでのタスクの登録を行います。これにより、アプリがバックグラウンドでヘルスデータを読み出すための準備が整います。

import UIKit
import Swinject

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        if let backgroundTaskService = Container.shared.resolve(BackgroundTaskServiceProtocol.self) {
            backgroundTaskService.registerBackgroundTasks()
        }
        return true
    }
}

Container.shared.resolveは、DI(Dependency Injection)コンテナから特定の型のインスタンスを解決するためのメソッドです。このメソッドは、コンテナに登録されているサービスや依存オブジェクトを取得するのに使用されます。解決されたインスタンスは、アプリケーションの他の部分で使用され、依存関係の注入やシングルトンの管理などの目的に役立ちます。

関連記事

カテゴリー

アーカイブ

Lang »