Kotlin Multiplatform 2026: Android ve iOS ortak kod

Kotlin Multiplatform 2026'da iş mantığını Android ve iOS arasında nasıl paylaşır: Gradle kurulumu, expect/actual, Ktor ağı, Compose Multiplatform arayüzü ve KMP mülakat soruları.

Kotlin Multiplatform 2026 Android ve iOS uygulamaları arasında kod paylaşımı

Kotlin Multiplatform (KMP), tek bir Kotlin kod tabanının hem Android hem de iOS uygulamalarının iş mantığını çalıştırmasına olanak tanırken her platform tamamen yerel arayüzünü, performansını ve kendi SDK erişimini korur. 2026 yılında KMP artık bir deney değil: kararlı durumda, resmi olarak Google tarafından destekleniyor ve Netflix, McDonald's, Forbes ile Cash App'te üretimde çalışıyor. Bu ayrıntılı inceleme, kod paylaşımının gerçekte nasıl işlediğini, paylaşılan bir modülün nasıl yapılandırılacağını ve bir KMP mülakatından önce anlaşılması gereken ödünleşimleri ele alır.

KMP gerçekte neyi paylaşır

Kotlin Multiplatform, bir çalışma zamanını değil, derlenmiş mantığı paylaşır. Ortak Kotlin, Android için JVM bayt koduna ve iOS için (LLVM aracılığıyla) yerel bir ikili dosyaya derlenir. JavaScript köprüsü, gömülü webview veya yansıma katmanı yoktur, bu nedenle paylaşılan kod her iki platformda da yerel hızda çalışır.

Kotlin Multiplatform kodu yerel performanstan ödün vermeden nasıl paylaşır

Temel mekanizma, kaynak kümelerinden (source sets) oluşan bir hiyerarşidir. commonMain, platformdan bağımsız Kotlin kodunu barındırır: ağ, serileştirme, iş kuralları ve görünüm modelleri. androidMain ve iosMain gibi platform kaynak kümeleri, platform API'lerine dokunan kodu barındırır. Derleyici, Android için commonMain artı androidMain'den bir JVM yapıtı, iOS için ise commonMain artı iosMain'den yerel bir framework üretir.

2026'da Kotlin Multiplatform, tek bir doğruluk kaynağından gerçekten fayda sağlayan şeyleri paylaşmayı tercih eder: modeller, API istemcileri, önbellekleme ve doğrulama. Arayüz, Compose Multiplatform ile paylaşılabilir veya ekran ekran SwiftUI ile tamamen yerel bırakılabilir. Bu "mantığı paylaş, arayüzü seç" modeli, ekiplerin mevcut uygulamaları yeniden yazmadan KMP'yi benimsemesinin nedenidir.

Kotlin Multiplatform, tek bir paylaşılan modülü birden çok hedefe (JVM üzerinde Android, yerel olarak iOS, ayrıca masaüstü ve web) derleyen bir Kotlin teknolojisidir; böylece iş mantığı bir kez yazılırken her platform kendi yerel arayüzünü ve tam SDK erişimini korur. Google artık bunu birinci sınıf bir seçenek olarak destekliyor; Room, DataStore, ViewModel ve Paging gibi Jetpack kütüphaneleri KMP yapıtları sunuyor.

Paylaşılan modüle neyin ait olduğuna karar vermek, bir KMP projesindeki en önemli mimari karardır. Aşağıdaki tablo, çoğu üretim ekibinin 2026'da bu sınırı nasıl çizdiğini yansıtır: sunum katmanının altındaki her şey paylaşım için güçlü bir adaydır, platform UX kurallarına dokunan her şey ise genellikle yerel kalır.

| Katman | commonMain'de paylaşılsın mı? | Neden | |-------|---------------------|--------| | Veri modelleri ve DTO'lar | Evet | Her iki platformda özdeş API sözleşmesi | | Ağ ve önbellekleme | Evet | Tek HTTP yığını, tek hata yüzeyi | | İş kuralları ve doğrulama | Evet | Alan, işletim sistemine göre değişmez | | Görünüm modelleri ve durum | Genellikle | Compose ve SwiftUI paylaşılan durumu tüketir | | Arayüz oluşturma | İsteğe bağlı | Compose ile paylaşılır veya her ekranda yerel | | Platform API'leri (kamera, biyometri) | Hayır | Bunun yerine expect/actual ile açığa çıkarılır |

Kazanç, bir uygulamanın bu tablonun alt yarısının ne kadarına sahip olduğuyla ölçeklenir. İnce arayüzlere sahip veri yoğun ürünler en çoğunu paylaşır; son derece özel, animasyon odaklı uygulamalar en azını paylaşır. Hiçbir uç doğru değildir, bu yüzden KMP her şeyi yeniden yazmak yerine kademeli benimsemeye uygundur.

Paylaşılan modülü Gradle'da yapılandırma

Bir KMP modülü, hedeflerini ve kaynak kümelerini Kotlin DSL içinde bildirir. Aşağıdaki build.gradle.kts, Android'i ve üç iOS mimarisini (fiziksel cihaz, Intel simülatörü, Apple Silicon simülatörü) kurar ve iOS çıktısını Shared adlı bir framework olarak sunar.

shared/build.gradle.ktskotlin
plugins {
    kotlin("multiplatform")
    kotlin("plugin.serialization")
    id("com.android.library")
}

kotlin {
    // Android target compiles commonMain + androidMain to JVM bytecode
    androidTarget()

    // Three iOS targets: device, Intel sim, Apple Silicon sim
    listOf(iosArm64(), iosX64(), iosSimulatorArm64()).forEach { target ->
        target.binaries.framework {
            baseName = "Shared"   // becomes "import Shared" in Swift
            isStatic = true
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation("io.ktor:ktor-client-core:3.3.0")
            implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
        }
        androidMain.dependencies {
            implementation("io.ktor:ktor-client-okhttp:3.3.0")
        }
        iosMain.dependencies {
            implementation("io.ktor:ktor-client-darwin:3.3.0")
        }
    }
}

Her hedef, platforma özgü bir Ktor motoru çeker (okhttp Android'de, darwin iOS'ta), paylaşılan kod ise yalnızca ortak ktor-client-core API'sine bağlıdır. Tüm seçenek kümesi Kotlin Multiplatform belgelerinde bulunur.

expect ve actual ile platforma özgü kod yazma

Bazı mantık, platform API'sine ihtiyaç duyduğu için commonMain içinde yer alamaz. expect/actual mekanizması, şekli ortak kodda bildirir ve her hedef için somut bir uygulama sağlar; bu, derleme zamanında sıfır çalışma zamanı maliyetiyle çözülür.

commonMain/kotlin/Platform.ktkotlin
// commonMain declares what it needs but cannot implement here
expect class Platform() {
    val name: String
    val osVersion: String
}

// Shared logic can now depend on Platform freely
fun analyticsUserAgent(): String {
    val platform = Platform()
    return "SharpSkill/" + platform.name + " " + platform.osVersion
}

Android uygulaması Build sınıfından, iOS uygulaması ise UIDevice'tan okur. Her ikisi de aynı sözleşmeyi karşılar, bu nedenle commonMain hangisiyle konuştuğunu asla bilmez.

androidMain/kotlin/Platform.ktkotlin
import android.os.Build

// actual supplies the Android-specific implementation
actual class Platform actual constructor() {
    actual val name: String = "Android"
    actual val osVersion: String = Build.VERSION.RELEASE
}
iosMain/kotlin/Platform.ktkotlin
import platform.UIKit.UIDevice

// actual uses UIKit directly, no Objective-C bridging code required
actual class Platform actual constructor() {
    actual val name: String = "iOS"
    actual val osVersion: String = UIDevice.currentDevice.systemVersion
}

Kotlin/Native, tüm iOS SDK'sı için türlenmiş bağlamalar (bindings) sağlar; böylece UIDevice, NSUserDefaults ve Foundation türleri elle yazılmış yapıştırıcı kod olmadan Kotlin'den çağrılabilir.

Ağ ve serileştirme mantığını paylaşma

Ağ, paylaşılacak en yüksek değerli koddur, çünkü API sözleşmeleri her iki platformda özdeştir. Tek bir Ktor istemcisi ile kotlinx.serialization, iki paralel uygulamanın yerini alır. suspend fonksiyonları, her iki tarafta da async/await'e temiz biçimde eşlenen Kotlin korutinlerine dayanır.

commonMain/kotlin/data/InterviewRepository.ktkotlin
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable

// One serializable model, zero duplication across platforms
@Serializable
data class Question(val id: String, val prompt: String, val difficulty: String)

// One HttpClient, one JSON parser, one repository for both apps
class InterviewRepository {
    private val client = HttpClient {
        install(ContentNegotiation) { json() }
    }

    suspend fun fetchQuestions(tech: String): List<Question> =
        client.get("https://api.sharpskill.dev/questions/" + tech).body()
}

Bu depo, yeniden deneme mantığı ve veri modelleri bir kez yazılır. Ayrıştırıcıda düzeltilen bir hata, Android ve iOS'ta aynı anda düzeltilir; KMP kurulum maliyetini işte burada geri öder. Aynı desen, SQLDelight ile paylaşılan bir veritabanına, paylaşılan anahtar-değer depolamasına ve Koin ile paylaşılan bağımlılık enjeksiyonuna genişler; böylece tüm veri katmanı, arayüzün bağlı olduğu arabirimlerin arkasında commonMain içinde yer alabilir.

Yaygın bir hata, çok erken çok fazla şey paylaşmaya çalışmaktır. Pragmatik yol, tek bir depoyla başlamak, her iki platformda derleme ve CI hattını kanıtlamak, ardından güven arttıkça özellikleri birer birer taşımaktır.

Android mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Compose Multiplatform ile paylaşılan arayüz oluşturma

Compose Multiplatform, Mayıs 2025'te (sürüm 1.8.0) iOS'ta kararlı duruma ulaştı ve 2026 ortasına kadar yerel metin girişi ve varsayılan olarak açık eşzamanlı oluşturmayla 1.11.0'a ulaştı. commonMain içindeki bir @Composable, her iki platformda oluşturulur ve paylaşılan görünüm modelleri, bir MVVM veya MVI mimarisiyle doğal biçimde eşleşir.

commonMain/kotlin/ui/QuestionList.ktkotlin
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.layout.padding

// This list renders identically on Android and iOS
@Composable
fun QuestionList(questions: List<Question>) {
    LazyColumn {
        items(questions) { question ->
            Card(modifier = Modifier.padding(8.dp)) {
                Text(text = question.prompt, modifier = Modifier.padding(16.dp))
            }
        }
    }
}

Arayüz paylaşımı isteğe bağlıdır ve ekran ekran yapılır. Bir ekip, arayüzün tamamını Compose Multiplatform ile paylaşabilir, tüm uygulama için SwiftUI'yi koruyup yalnızca mantığı paylaşabilir veya ikisini birleştirebilir. Widget'lar veya App Clips gibi platforma özgü UX'e bağlı ekranlar genellikle yerel bırakılır.

Paylaşılan koda iOS'ta Swift'ten erişme

Paylaşılan modül, Swift'in herhangi bir kütüphane gibi içe aktardığı bir framework'e derlenir. Tarihsel olarak sürtünme, Kotlin suspend fonksiyonlarının ve Flow'un beceriksiz geri arama API'leri olarak ortaya çıkmasıydı. Touchlab'dan SKIE, deyimsel Swift üreterek bunu çözer: suspend yerel async/await'e, Flow ise AsyncSequence'e dönüşür.

iosApp/ContentView.swiftswift
import SwiftUI
import Shared   // the KMP framework, baseName = "Shared"

struct ContentView: View {
    @State private var questions: [Question] = []
    private let repository = InterviewRepository()

    var body: some View {
        List(questions, id: \.id) { question in
            Text(question.prompt)
        }
        .task {
            // With SKIE, the Kotlin suspend fun is a Swift async fun
            questions = (try? await repository.fetchQuestions(tech: "android")) ?? []
        }
    }
}

iOS ekibi sıradan Swift ve SwiftUI yazar. Paylaşılan modelleri ve depoları tıpkı yerel bir Swift paketi gibi kullanır, bu da KMP'yi iOS kod tabanının çoğu için görünmez kılar.

Bir mülakatta adı anılmaya değer iki pürüz hâlâ var. İlki, derleme süreleri: Kotlin/Native derlemesi saf bir Swift derlemesinden daha yavaştır, ancak Gradle derleme önbelleği ve önceden derlenmiş XCFramework dağıtımı günlük maliyeti yumuşatır. İkincisi, Swift-Kotlin sınırı boyunca hata ayıklama gelişiyor ancak henüz tek bir dilde kalmak kadar sorunsuz değil; bu da ekiplerin paylaşılan yüzeyi yayılmak yerine bilinçli tutmasının bir başka nedenidir.

Beklenebilecek Kotlin Multiplatform mülakat soruları

KMP mülakat soruları Android ve mobil rollerde giderek daha sık göründüğünden, daha geniş bir Android mülakat hazırlığının yanında birkaç tekrar eden soruyu prova etmek faydalıdır:

  • expect/actual bir arabirimden nasıl farklıdır? expect/actual, her hedef için derleme zamanında çözülür, çalışma zamanında dağıtım yapmaz ve bir sınıfı, fonksiyonu, özelliği veya typealias'ı destekleyebilir. Bir arabirim ise çalışma zamanında dinamik olarak çözülür. Platform API'leri için expect/actual, paylaşılan kod içindeki çok biçimlilik için arabirimler kullanılır.
  • Kotlin/Native belleği ve iş parçacıklarını nasıl yönetir? Kotlin 1.7.20'den beri yeni bellek yöneticisi eski nesne dondurma modelini kaldırdı, bu nedenle paylaşılan değiştirilebilir durum ve korutinler, JVM'deki gibi iş parçacıkları arasında çalışır.
  • Mevcut bir uygulama KMP'yi kademeli olarak benimseyebilir mi? Evet. Paylaşılan modül, Android için bir AAR ve iOS için bir XCFramework olarak dağıtılır, böylece tek bir depo veya özellik yeniden yazma olmadan birer birer taşınabilir.
  • Arayüz ne zaman Compose Multiplatform kullanmak yerine yerel kalmalıdır? Bir ekran platforma özgü UX'e dayandığında veya iOS ekibi sunum katmanına sahip olup onu kontrol etmeyi tercih ettiğinde.
2026'da araç zinciri

K2 derleyicisiyle Kotlin 2.3, ağ için Ktor 3.x, paylaşılan veritabanı için SQLDelight ve bağımlılık enjeksiyonu için Koin hedeflenmelidir. iOS birlikte çalışabilirliği için SKIE erken eklenmelidir; Swift katmanı yazıldıktan sonra sonradan eklemek çok daha zahmetlidir.

Sonuç

2026'da Kotlin Multiplatform, yerel arayüzden veya performanstan ödün vermeden Android ile iOS arasında yinelenen mantığı azaltmanın pragmatik bir yoludur:

  • Tek bir doğruluk kaynağı olan mantığı paylaşın (modeller, ağ, doğrulama) ve her ekranda yerel veya Compose arayüz seçin.
  • Platform API'leri için expect/actual kullanın; derleme zamanında çözülür, çalışma zamanı yükü olmaz.
  • Ağı Ktor ve kotlinx.serialization ile bir kez yazın, böylece düzeltmeler her iki platforma aynı anda ulaşır.
  • Yeniden yazmaya bağlanmak yerine bir AAR ve XCFramework aracılığıyla kademeli olarak benimseyin.
  • Swift'in suspend ve Flow'u yerel async/await ve AsyncSequence olarak kullanması için SKIE'yi baştan ekleyin.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Etiketler

#Kotlin Multiplatform
#KMP
#Android
#iOS
#Cross-platform

Paylaş

İlgili makaleler