ahmed3elshaer/everything-claude-code-mobile/kmp-repositories

Claude Code and agent skill by ahmed3elshaer/everything-claude-code-mobile

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Repository pattern for Kotlin Multiplatform. Shared interfaces with platform-specific implementations, clean data layer architecture.

Install

npx vskill@latest install ahmed3elshaer/everything-claude-code-mobile/kmp-repositories

SKILL.md

KMP Repository Pattern

Implement the repository pattern with shared interfaces in commonMain and platform-specific implementations.

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (ViewModels, Composables, SwiftUI) │
└──────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────┐
│ Domain Layer │
│ (Use Cases) │
└──────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────┐
│ Data Layer │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Repository Interface (commonMain) │ │
│ └──────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────┐ │
│ │ Repository Implementation (commonMain) │ │
│ │ - Coordinates data sources │ │
│ │ - Caching strategy │ │
│ │ - Error mapping │ │
│ └──────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────┐ │
│ │ Data Sources │ │
│ │ ┌────────────┐ ┌────────────┐ ┌───────────┐ │ │
│ │ │ Remote │ │ Local │ │ Memory │ │ │
│ │ │ (Network) │ │ (Database) │ │ (Cache) │ │ │
│ │ └────────────┘ └────────────┘ └───────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘

Repository Interface

kotlin
// commonMain/kotlin/data/repository/UserRepository.kt
interface UserRepository {
// Single shot
suspend fun getUser(id: String): Result<User>
suspend fun getUsers(page: Int): Result<PaginatedResponse<User>>
suspend fun saveUser(user: User): Result<User>
suspend fun deleteUser(id: String): Result<Unit>
// Streams
fun observeUser(id: String): Flow<Result<User>>
fun observeUsers(): Flow<Result<List<User>>>
// Cache control
suspend fun refreshUser(id: String): Result<User>
suspend fun refreshAll(): Result<Unit>
suspend fun clearCache(): Result<Unit>
}

Base Repository

kotlin
// commonMain/kotlin/data/repository/BaseRepository.kt
abstract class BaseRepository {
// Execute with error handling
protected suspend fun <T> execute(
block: suspend () -> T
): Result<T> = try {
Result.success(block())
} catch (e: CancellationException) {
throw e
} catch (e: NetworkException) {
Result.failure(e)
} catch (e: Exception) {
Result.failure(DataException(cause = e))
}
// Flow with error handling
protected fun <T> flowResult(
block: suspend () -> Flow<T>
): Flow<Result<T>> = flow {
try {
block().collect { emit(Result.success(it)) }
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
emit(Result.failure(e))
}
}
// Cache-first strategy
protected suspend fun <T> cacheFirst(
cacheKey: String,
ttl: Duration,
cacheBlock: suspend () -> T?,
networkBlock: suspend () -> T
): Result<T> = execute {
// Try cache
cacheBlock()?.let { return@execute it }
// Fetch from network
val result = networkBlock()
// Update cache
// saveToCache(cacheKey, result)
result
}
// Network-first strategy
protected suspend fun <T> networkFirst(
cacheKey: String,
cacheBlock: suspend () -> T?,
networkBlock: suspend () -> T
): Result<T> = execute {
try {
// Try network first
val result = networkBlock()
// Update cache
result
} catch (e: NetworkException) {
// Fall back to cache
cacheBlock() ?: throw e
}
}
// Refresh strategy
protected suspend fun <T> refresh(
networkBlock: suspend () -> T,
cacheBlock: suspend (T) -> Unit
): Result<T> = execute {
val result = networkBlock()
cacheBlock(result)
result
}
}

Concrete Repository

kotlin
// commonMain/kotlin/data/repository/UserRepositoryImpl.kt
class UserRepositoryImpl(
private val remoteDataSource: UserRemoteDataSource,
private val localDataSource: UserLocalDataSource,
private val memoryCache: MemoryCache,
private val dispatcher: CoroutineDispatcher = PlatformDispatchers.io
) : BaseRepository(), UserRepository {
override suspend fun getUser(id: String): Result<User> =
networkFirst(
cacheKey = "user:$id",
cacheBlock = { localDataSource.getUser(id) },
networkBlock = { remoteDataSource.getUser(id).also { user ->
localDataSource.saveUser(user)
memoryCache.put("user:$id", user)
}}
)
override suspend fun getUsers(page: Int): Result<PaginatedResponse<User>> =
execute {
remoteDataSource.getUsers(page).also { response ->
localDataSource.saveUsers(response.items)
}
}
override suspend fun saveUser(user: User): Result<User> =
execute {
remoteDataSource.createUser(user).also { savedUser ->
localDataSource.saveUser(savedUser)
memoryCache.put("user:${savedUser.id}", savedUser)
}
}
override suspend fun deleteUser(id: String): Result<Unit> =
execute {
remoteDataSource.deleteUser(id).also {
localDataSource.deleteUser(id)
memoryCache.remove("user:$id")
}
}
override fun observeUser(id: String): Flow<Result<User>> =
flowResult {
localDataSource.observeUser(id)
}
override fun observeUsers(): Flow<Result<List<User>>> =
flowResult {
localDataSource.observeAllUsers()
}
override suspend fun refreshUser(id: String): Result<User> =
refresh(
networkBlock = { remoteDataSource.getUser(id) },
cacheBlock = { user -> localDataSource.saveUser(user) }
)
override suspend fun refreshAll(): Result<Unit> =
execute {
remoteDataSource.getUsers(page = 1).items.forEach { user ->
localDataSource.saveUser(user)
}
}
override suspend fun clearCache(): Result<Unit> =
execute {
localDataSource.clearAll()
memoryCache.clear()
}
}

Data Sources

Remote Data Source
kotlin
// commonMain/kotlin/data/datasource/remote/UserRemoteDataSource.kt
interface UserRemoteDataSource {
suspend fun getUser(id: String): User
suspend fun getUsers(page: Int): PaginatedResponse<User>
suspend fun createUser(user: User): User
suspend fun updateUser(id: String, user: User): User
suspend fun deleteUser(id: String)
}
class UserRemoteDataSourceImpl(
private val api: UserApi
) : UserRemoteDataSource {
override suspend fun getUser(id: String): User = api.getUser(id)
override suspend fun getUsers(page: Int): PaginatedResponse<User> = api.getUsers(page)
override suspend fun createUser(user: User): User = api.createUser(user)
override suspend fun updateUser(id: String, user: User): User = api.updateUser(id, user)
override suspend fun deleteUser(id: String) = api.deleteUser(id)
}
Local Data Source
kotlin
// commonMain/kotlin/data/datasource/local/UserLocalDataSource.kt
interface UserLocalDataSource {
suspend fun getUser(id: String): User?
suspend fun getAllUsers(): List<User>
suspend fun saveUser(user: User)
suspend fun saveUsers(users: List<User>)
suspend fun deleteUser(id: String)
suspend fun clearAll()
fun observeUser(id: String): Flow<User>
fun observeAllUsers(): Flow<List<User>>
}
class UserLocalDataSourceImpl(
private val database: AppDatabase
) : UserLocalDataSource {
override suspend fun getUser(id: String): User? =
database.userQueries().getById(id).executeAsOneOrNull()?.toDomain()
override suspend fun getAllUsers(): List<User> =
database.userQueries().getAll().executeAsList().map { it.toDomain() }
override suspend fun saveUser(user: User) =
database.userQueries().insert(user.toEntity())
override suspend fun saveUsers(users: List<User>) =
database.transaction {
users.forEach { saveUser(it) }
}
override suspend fun deleteUser(id: String) =
database.userQueries().deleteById(id)
override suspend fun clearAll() =
database.userQueries().deleteAll()
override fun observeUser(id: String): Flow<User> =
database.userQueries().getById(id).asFlow().map { it.executeAsOneOrNull()?.toDomain() }
.filterNotNull()
override fun observeAllUsers(): Flow<List<User>> =
database.userQueries().getAll().asFlow().map { it.executeAsList().map { entity -> entity.toDomain() } }
}

Use Cases

kotlin
// commonMain/kotlin/domain/usecase/GetUserUseCase.kt
class GetUserUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(id: String): Result<User> =
repository.getUser(id)
}
// commonMain/kotlin/domain/usecase/ObserveUserUseCase.kt
class ObserveUserUseCase(
private val repository: UserRepository
) {
operator fun invoke(id: String): Flow<Result<User>> =
repository.observeUser(id)
}
// commonMain/kotlin/domain/usecase/RefreshUserDataUseCase.kt
class RefreshUserDataUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(id: String): Result<User> =
repository.refreshUser(id)
}

Dependency Injection

kotlin
// commonMain/kotlin/di/DataModule.kt
val dataModule = module {
// Data sources
single { UserApi(get()) }
single<UserRemoteDataSource> { UserRemoteDataSourceImpl(get()) }
// Local data source
single { createDatabase(get()) }
single<UserLocalDataSource> { UserLocalDataSourceImpl(get()) }
// Memory cache
single { MemoryCache() }
// Repositories
single<UserRepository> { UserRepositoryImpl(get(), get(), get()) }
// Use cases
factory { GetUserUseCase(get()) }
factory { ObserveUserUseCase(get()) }
factory { RefreshUserDataUseCase(get()) }
}

Platform-Specific Database

kotlin
// commonMain/kotlin/database/DatabaseFactory.kt
expect class DatabaseFactory {
fun create(): AppDatabase
}
// Android implementation
actual class DatabaseFactory(private val context: Context) {
actual fun create(): AppDatabase =
Room.databaseBuilder(
context,
AppDatabase::class.java,
"app.db"
).build()
}
// iOS implementation
actual class DatabaseFactory {
actual fun create(): AppDatabase {
val dbPath = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask,
true
).first() as String
return AppDatabase("$dbPath/app.db")
}
}

Repository Testing

kotlin
// commonTest/kotlin/data/repository/UserRepositoryTest.kt
class UserRepositoryTest {
private lateinit var repository: UserRepository
private lateinit var mockRemote: UserRemoteDataSource
private lateinit var mockLocal: UserLocalDataSource

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
32
Repository forks
3
7-day trend score
17

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 17 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/ahmed3elshaer/everything-claude-code-mobile/kmp-repositories/badge)](https://verified-skill.com/skills/ahmed3elshaer/everything-claude-code-mobile/kmp-repositories)

Something wrong with this skill or its page? Tell us.