はじめに
目次
- iOSクライアント実装概要
- アーキテクチャ
- 使用技術
- 最後に
iOSクライアント実装概要
AIとの会話のやり取り
AIによる会話、音声のやり取りをサーバーAPIと行っており、以下のような流れになっています。
- 「ユーザー入力したメッセージ」から「AIの回答文字列」を取得
- 「AI回答文字列」から「変換した音声データ」を取得
- WebViewへのリクエスト送信、アバターが喋る
データ管理
メッセージや性格、親密度など、ユーザーデータは主にFirebase Firestoreで管理しています。 チーム構成や開発リソース、アプリの仕様を考慮し、データベースはFirebaseFirestoreを活用。サーバーがデータベースを保有しない構成としました。 クライアントがアプリの機能に関する、コアなドメインロジックを管理しています。
アーキテクチャ
モジュール構成と各レイヤーの役割
CleanArchitectureを基盤にしたアーキテクチャを採用し、実装を進めてまいりました。アプリのコア機能であるドメインレイヤーとアプリケーションレイヤーを依存関係の中心に配置し、UIレイヤーやDataレイヤーをその周辺に配置することで、変更に対する耐性と高い拡張性を維持するクリーンなコードを実現しました。具体的なモジュールの構成は以下の通りです
• UICompoennt: UIの表示レイヤーで、SwiftUI.Viewを使用
• UILogic: 各アプリ画面のViewに1対1で対応するViewModel
• UseCase: アプリのユースケースを定義
• Repository: データの取得を抽象化し、APIやFirebaseの呼び出し、キャッシュデータの保持を行う
• Domain: 性格ロジック、親密度、ユーザーやメッセージなどのエンティティを含むドメインロジック
これにより、アプリのコアロジック(ドメインやユースケース)が技術詳細に依存しないクリーンな実装を維持することができました。ただし、一部の機能追加ではUseCaseやDomainを経由する必要があるため、ボイラープレートが増えてしまうこともあり、機能開発におけるレイヤー分割が過剰に感じられる場合もありました。
DIコンテナ
オブジェクトのインスタンス化や依存関係の解決については、DIコンテナを採用しました。これにより、モジュール間をまたぐオブジェクトの依存関係の解決やインスタンス化を行う際に、疎結合を保ちつつ実装することが可能となりました。
オブジェクトのインスタンス化と、依存の解決をする
protocol DIContainerProtocol: Registrant, Resolver {}
public protocol Registrant {
func register(type: ObjectType.Type, factory: @escaping (Resolver) -> ObjectType)
func register<ObjectType, Args>(type: ObjectType.Type, factory: @escaping (Resolver, Args) -> ObjectType)
}
public protocol Resolver {
func resolve(type: ObjectType.Type) -> ObjectType
func resolve<ObjectType, Args>(type: ObjectType.Type, arg: Args) -> ObjectType
}
シングルトンで保持
public final class DIContainer: DIContainerProtocol {
public static let shared = DIContainer()
private init() {}
・ ・ ・
利用例
依存注入する各オブジェクトをコンテナへ登録
// View
container.register<MessagingScreen, MessagingScreen.Input>(type: MessagingScreen.self) { resolver, arg in
MessagingScreen(
viewModel: resolver.resolve(type: MessagingViewModel.self),
input: arg
)
}
// ViewModel
container.register(type: MessagingViewModel.self) { resolver in
MessagingViewModel(
useCase: resolver.resolve(type: MessagingUseCase.self),
girlfriendUseCase: resolver.resolve(type: GirlfriendUseCase.self),
intimacyUC: resolver.resolve(type: IntimacyUseCase.self),
personalityUC: resolver.resolve(type: PersonalityUseCase.self),
authUC: resolver.resolve(type: AuthenticationUseCase.self)
)
}
使用技術
開発環境として、利用ツールは以下になります- XCode15
- SPM
- XCodeCloud