Week 7-8: Pattern 3 Implementation (Agent 1)

Timeline: 2025-10-26 Agent: Agent 1 (AAP - Platform Abstractions) Status: ✅ COMPLETE

Overview

Pattern 3 (Context Abstraction) designed, implemented, and validated over Week 6-8.

Week 6: Design

Deliverable: CONTEXT_ABSTRACTION_PATTERN.md (1,108 lines)

3 Core Abstractions Designed:

  1. PreferenceStorage - SharedPreferences → KMP
  2. ResourceProvider - String resources → KMP
  3. FileSystemProvider - File I/O → KMP

7-Step Migration Workflow:

  1. Identify Context dependencies
  2. Choose/create abstraction
  3. Use Android implementation
  4. Update service constructor
  5. Update manual DI
  6. Migrate to shared:domain
  7. Test with fakes

Week 7 Phase 1: Core Abstractions

PR #152 (MERGED)

PreferenceStorage

Purpose: Abstract SharedPreferences for KMP

Android Implementation:

  • Uses SharedPreferences.apply() (non-blocking)
  • Proper threading (Dispatchers.IO)
  • Application Context (no leaks)

API:

interface PreferenceStorage {
    suspend fun getString(key: String, defaultValue: String = ""): String
    suspend fun putString(key: String, value: String)
    suspend fun getInt(key: String, defaultValue: Int = 0): Int
    // ... Boolean, Long, Float, remove, clear, contains
}

iOS Future: NSUserDefaults

Tests: 21 test cases


ResourceProvider

Purpose: Abstract string resources for KMP

Android Implementation:

  • Reflection-based resource ID lookup
  • LRU cache (maxSize=50) for performance
  • Fallback to android.R.string.* for system resources

API:

interface ResourceProvider {
    fun getString(key: String): String
    fun getString(key: String, vararg args: Any): String
    fun getStringArray(key: String): List<String>
}

iOS Future: NSBundle.localizedStringForKey()

Tests: 11 test cases


FileSystemProvider

Purpose: Abstract file I/O for KMP

Android Implementation:

  • Uses context.filesDir, context.cacheDir, external storage
  • Proper threading (Dispatchers.IO)
  • Automatic parent directory creation

API:

interface FileSystemProvider {
    suspend fun getDirectoryPath(directory: Directory): String
    suspend fun writeText(filename: String, content: String, directory: Directory)
    suspend fun readText(filename: String, directory: Directory): String?
    suspend fun exists(filename: String, directory: Directory): Boolean
    // ... binary I/O, directory management, list, delete, clear
}

iOS Future: NSDocumentDirectory, NSCachesDirectory

Tests: 21 test cases


Week 7 Phase 2: ExportUtils Migration

PR #154 (Created)

Service: ExportUtils (CSV export utility)

Migration:

  • From: app module (Android-specific)
  • To: shared:domain (KMP-compatible)

Changes:

  • ❌ Removed Context dependency
  • ✅ Added FileSystemProvider injection
  • ❌ Removed java.io.File (Android-only)
  • ✅ Uses String paths (KMP-compatible)
  • ✅ Added suspend functions

Before:

object ExportUtils {
    fun exportCsv(context: Context, filename: String, rows: List<List<String>>): File {
        val f = File(context.cacheDir, filename)
        // Write CSV using java.io.File
    }
}

After:

class ExportUtils(private val fileSystemProvider: FileSystemProvider) {
    suspend fun exportCsv(filename: String, rows: List<List<String>>): String {
        val csvContent = buildString { /* CSV generation */ }
        fileSystemProvider.writeText(filename, csvContent, Directory.CACHE)
        return fileSystemProvider.getDirectoryPath(Directory.CACHE) + "/" + filename
    }
}

Tests: 11 test cases with FakeFileSystemProvider


Pattern 3 Validation

Workflow Proven:

  1. ✅ Design abstraction (Week 6)
  2. ✅ Implement Android version (Week 7 Phase 1)
  3. ✅ Create test fake (Week 7 Phase 1)
  4. ✅ Migrate service (Week 7 Phase 2)
  5. ✅ Test with fake (Week 7 Phase 2)

Results:

  • Zero Android dependencies in shared:domain
  • 100% test pass rate (68 new tests)
  • Zero regressions
  • Pattern ready for Week 8 reuse

Week 8 Preview

Next: Migrate 3-5 More Services

Candidates:

  1. OfflineScoreQueueManager (file storage)
  2. PlatformProvider extensions (Context deps)
  3. Settings utilities (SharedPreferences)

Estimated Effort: 5-7 hours

Pattern: Reuse existing abstractions (no new design needed)


Architecture Impact

Before Pattern 3:

  • ~15% of services blocked by Context dependencies
  • Services stuck in app module (Android-specific)

After Pattern 3:

  • Context-dependent services can migrate to shared:domain
  • Services testable with fakes (no Robolectric needed)
  • iOS implementation path clear (expect/actual)

Next Patterns:

  • Pattern 4: Additional platform abstractions (TBD)
  • Week 9: FirebaseDataSource abstraction (Pattern 2 implementation)

Status: ✅ COMPLETE Impact: 15% of services now migratable to KMP Next: Week 8 service migration sprint