Orhak
Administrator
Yönetici
2026'da JetBrains’in Compose Multiplatform’u (Compose for iOS stabil, Web ve Desktop olgun) ve KMP ekosistemiyle artık “write once, run anywhere” gerçek anlamda mümkün. Bu rehberde Android + iOS için ortak bir Not Alma Uygulaması (shared business logic, shared UI, native navigation) yapacağız:
Yeni proje oluştur: File → New → Project → Kotlin Multiplatform → Mobile Application → Compose Multiplatform
shared/src/commonMain/sqldb/notes.sq
NoteDatabase.kt otomatik oluşur.
shared/src/commonMain/kotlin/data/NoteRepository.kt
shared/src/commonMain/kotlin/viewmodel/NotesViewModel.kt
- Shared ViewModel + StateFlow
- SQLDelight ile cross-platform veritabanı
- Ktor Client ile opsiyonel remote sync
- Compose Multiplatform UI (Material 3)
- Android + iOS native entegrasyonu
- IntelliJ IDEA Ultimate / Android Studio 2025.x (KMP wizard destekli)
- Xcode 17+ (iOS için)
- Kotlin 2.3+, Compose Multiplatform 1.7+
Yeni proje oluştur: File → New → Project → Kotlin Multiplatform → Mobile Application → Compose Multiplatform
1. Proje Yapısı (Standart KMP 2026)
Kod:
shared/ # Ortak kod (business logic + UI)
├── src/
│ ├── commonMain/ # Her platformda çalışan kod
│ │ ├── kotlin/
│ │ │ ├── data/ # SQLDelight, models, repository
│ │ │ ├── domain/ # UseCase, entities
│ │ │ ├── ui/ # Compose screens, components
│ │ │ └── viewmodel/
│ ├── androidMain/ # Android spesifik
│ ├── iosMain/ # iOS spesifik
│ └── desktopMain/ # Opsiyonel
androidApp/ # Android uygulaması
iosApp/ # Xcode projesi (SwiftUI wrapper)
2. SQLDelight ile Ortak Veritabanı
build.gradle.kts (shared) → SQLDelight ekle:
Kod:
plugins {
id("app.cash.sqldelight") version "2.0.2"
}
sqldelight {
databases {
create("NoteDatabase") {
packageName = "com.example.notes.db"
dialect("app.cash.sqldelight:android-dialect:2.0.2") // android + native
}
}
}
shared/src/commonMain/sqldb/notes.sq
Kod:
CREATE TABLE Note (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
createdAt INTEGER NOT NULL
);
insertNote:
INSERT INTO Note (title, content, createdAt)
VALUES (?, ?, ?);
getAllNotes:
SELECT * FROM Note ORDER BY createdAt DESC;
deleteNote:
DELETE FROM Note WHERE id = ?;
NoteDatabase.kt otomatik oluşur.
3. Shared Data Layer
shared/src/commonMain/kotlin/data/NoteRepository.kt
Kod:
interface NoteRepository {
suspend fun insertNote(title: String, content: String)
fun getAllNotes(): Flow<List<Note>>
suspend fun deleteNote(id: Long)
}
class NoteRepositoryImpl(
private val db: NoteDatabase
) : NoteRepository {
override suspend fun insertNote(title: String, content: String) {
db.noteQueries.insertNote(title, content, Clock.System.now().toEpochMilliseconds())
}
override fun getAllNotes(): Flow<List<Note>> =
db.noteQueries.getAllNotes().asFlow().mapToList()
override suspend fun deleteNote(id: Long) {
db.noteQueries.deleteNote(id)
}
}
// Domain entity
data class Note(
val id: Long,
val title: String,
val content: String,
val createdAt: Instant
)
4. Shared ViewModel (Decompose veya pure Coroutines)
shared/src/commonMain/kotlin/viewmodel/NotesViewModel.kt
Kod:
class NotesViewModel(
private val repository: NoteRepository
) : ViewModel() { // androidx.lifecycle.ViewModel ortak
val notes: StateFlow<List<Note>> = repository.getAllNotes()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
fun addNote(title: String, content: String) {
viewModelScope.launch {
repository.insertNote(title, content)
}
}
fun deleteNote(note: Note) {
viewModelScope.launch {
repository.deleteNote(note.id)
}
}
}
5. Compose Multiplatform UI
shared/src/commonMain/kotlin/ui/NotesScreen.kt
Kod:
@Composable
fun NotesScreen(
viewModel: NotesViewModel = remember { NotesViewModel(/* inject */) },
onNoteClick: (Note) -> Unit = {}
) {
val notes by viewModel.notes.collectAsStateWithLifecycle()
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
Text("Notlarım", style = MaterialTheme.typography.headlineMedium)
LazyColumn {
items(notes, key = { it.id }) { note ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.clickable { onNoteClick(note) }
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(note.title, style = MaterialTheme.typography.titleMedium)
Text(note.content.take(100) + "...", style = MaterialTheme.typography.bodyMedium)
Text(
note.createdAt.toLocalDateTime(TimeZone.currentSystemDefault())
.format(LocalDateTime.Format.ISO),
style = MaterialTheme.typography.bodySmall
)
}
}
}
}
FloatingActionButton(onClick = { /* yeni not dialogu */ }) {
Icon(Icons.Default.Add, contentDescription = "Ekle")
}
}
}
6. Android Entegrasyonu
androidApp/src/main/kotlin/MainActivity.kt
Kod:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
NotesScreen()
}
}
}
}
7. iOS Entegrasyonu (SwiftUI Wrapper)
iosApp/ContentView.swift
Kod:
import SwiftUI
import shared // shared framework
struct ContentView: View {
let viewModel = NotesViewModel(/* DI */)
var body: some View {
NotesScreenKt.NotesScreen(
viewModel: viewModel,
onNoteClick: { note in
// Detay ekranına git
}
)
}
}
8. İleri Seviye İpuçları (2026)
- Koin veya Kodein ile DI
- Ktor Client ile backend sync (shared network layer)
- Compose for Web ile browser desteği
- Baseline Profiles + Compose Compiler optimizasyonu
- Xcode’da preview için SwiftUI + UIKit interop