Skip to content

ViewModel層の統一化とリファクタリング(BaseViewModel導入、DI対応、重複コード削除) #32

@harutiro

Description

@harutiro

概要

Presentation層の全ViewModelに対して、統一性の向上とコード品質改善を行います。BaseViewModelの導入、依存性注入パターンの統一、重複コードの共通化を実施します。

背景

現在、14個のViewModelが存在していますが、以下の問題が発見されました:

  • @MainActorの使用が不統一(10/14のみ使用)
  • エラーハンドリングの実装が不統一(3/14のみ実装)
  • 依存性注入未対応のViewModelが7個存在
  • 7つの重複パターンが存在
  • 一部ViewModelにビジネスロジックが残存

現状の問題点

1. クラス修飾子の不統一

@MainActor未使用(4個):

  • SensingViewModel
  • SettingsViewModel
  • AdvertiserViewModel
  • TrajectoryViewModel

2. 依存性注入未対応(7個)

以下のViewModelは内部で直接依存を生成しており、テスタビリティが低い:

  • FieldSettingViewModel
  • SensingViewModel
  • SettingsViewModel
  • FloorMapViewModel
  • FloorMapSettingViewModel
  • TrajectoryViewModel
  • AdvertiserViewModel

3. 重複コード(7つのパターン)

  1. ModelContext/SwiftDataRepositoryの初期化 - 7個のViewModelで重複
  2. Timerベースの更新ロジック - 4個のViewModelで重複
  3. UserDefaultsへのデータ保存/読み込み - 4個のViewModelで重複
  4. エラーメッセージ表示 - 2個のViewModelで重複
  5. ファイルパス取得・画像保存 - 2個のViewModelで重複
  6. CSV生成とエクスポート - 2個のViewModelで重複
  7. フロアマップ情報の取得 - 3個のViewModelで重複

4. ビジネスロジックの残存

以下のViewModelにビジネスロジックが含まれており、Usecase層への委譲が不十分:

  • FieldSettingViewModel: アンテナ位置の計算ロジック
  • AntennaPositioningViewModel: 座標変換、アンテナ配置ロジック
  • TrajectoryViewModel: 軌跡データ分析ロジック
  • FloorMapViewModel: カスケード削除ロジック

改善内容

Phase 1: BaseViewModelの導入

共通のプロパティとメソッドを抽出してBaseViewModelクラスを作成:

@MainActor
class BaseViewModel: ObservableObject {
    @Published var errorMessage: String = ""
    @Published var showErrorAlert: Bool = false
    @Published var isLoading: Bool = false
    
    func showError(_ message: String) {
        self.errorMessage = message
        self.showErrorAlert = true
    }
}

Phase 2: 共通ユーティリティクラスの作成

以下のユーティリティクラスを作成して重複コードを共通化:

  1. TimerManager: Timer管理の共通化
  2. UserDefaultsManager: エンコード/デコードの共通化
  3. FileManagerExtension: ファイル操作の共通化
  4. CSVExporter: CSV生成・エクスポートの共通化
  5. SwiftDataManageable Protocol: SwiftDataRepository管理の統一

Phase 3: 依存性注入の全ViewModel適用

DI未対応の7個のViewModelに対して、依存性注入パターンを適用:

  • テスト容易性の向上
  • Mock/Stubによるテストの実現

Phase 4: ビジネスロジックのUsecase層への移譲

ViewModel内に残存しているビジネスロジックをUsecase層に移動:

  • 座標変換ロジック → 新規Usecaseまたは既存Usecaseへ
  • データ分析ロジック → 新規Usecaseへ
  • カスケード削除ロジック → Repository層またはUsecase層へ

Phase 5: @mainactorの統一

すべてのViewModelに@MainActorを適用して、メインスレッドでの実行を保証

受け入れ条件

  • BaseViewModelが実装され、全ViewModelが継承すること
  • 共通ユーティリティクラスが実装され、重複コードが削減されること
  • 全ViewModelが依存性注入に対応すること
  • ビジネスロジックがUsecase層に移譲されること
  • 全ViewModelに@MainActorが適用されること
  • すべてのビルドが成功すること
  • 既存のテストがすべて通ること
  • SwiftFormatを実行してコードフォーマットを統一

実装方針

  1. BaseViewModelを実装
  2. 共通ユーティリティクラスを実装
  3. 各ViewModelをBaseViewModelに移行(段階的に)
  4. DI未対応ViewModelをDI対応に変更
  5. ビジネスロジックをUsecase層に移動
  6. @mainactorを統一

補足事項

  • 段階的なリファクタリングを推奨(Phase 1→2→3→4→5)
  • 各Phaseごとに個別のPRを作成可能
  • 既存の動作を壊さないよう、十分なテストを実施

期待される効果

  • コードの保守性向上
  • テスタビリティの向上
  • コードの一貫性と可読性の向上
  • 重複コードの削減によるバグリスク低減

🤖 このIssueはClaude Codeによって作成されました

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions