Swift vs Kotlin
Swift für Apple-Plattformen trifft auf Kotlin für Android. Eine detaillierte Analyse hinsichtlich moderner Sprachfeatures, Syntax und Ökosystem.
Aktorbasiertes Modell, das Datenwettläufe zur Kompilierzeit ausschließt
Bibliotheksbasiertes, flexibles Suspend-Funktionsmodell
Keine Situationsfrage, sondern ein bewusster Kompromiss: Swift 6 findet mehr Fehler zur Kompilierzeit, der Preis dafür sind hohe Migrationskosten. Kotlin Coroutines sind flexibler und lassen sich schrittweise einführen, überlassen den geteilten State jedoch der Runtime-Disziplin. Beim Abbruch gibt es keinen Unterschied — beide sind kooperativ. Bei einem Greenfield-Projekt nur für Apple-Plattformen aktiviere den Swift-6-Sprachmodus von Anfang an. Plane die Migration in einer bestehenden Codebasis target-basiert und beginne mit dem isoliertesten Modul. Behandle die Bridging-Schicht in KMP als eigene Architekturphase.
| Kategorie | Swift 6 Concurrency | Kotlin Coroutines |
|---|---|---|
| Performance | 8/10 | 8/10 |
| Erlernbarkeit | 5/10 | 7/10 |
| Ökosystem | 7/10 | 9/10 |
| Community | 7/10 | 8/10 |
| Arbeitsmarkt | 8/10 | 8/10 |
| Zukunftssicherheit | 9/10 | 8/10 |
// Swift 6 — Sendable-Modell + Actor für paralleles Herunterladen
import Foundation
struct UserProfile: Sendable, Decodable {
let id: Int
let name: String
}
actor ProfileCache {
private var storage: [Int: UserProfile] = [:]
func value(for id: Int) -> UserProfile? {
storage[id]
}
func insert(_ profile: UserProfile) {
storage[profile.id] = profile
}
}
enum ProfileError: Error {
case invalidResponse
}
func fetchProfile(id: Int) async throws -> UserProfile {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, response) = try await URLSession.shared.data(from: url)
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
throw ProfileError.invalidResponse
}
return try JSONDecoder().decode(UserProfile.self, from: data)
}
func loadProfiles(ids: [Int], cache: ProfileCache) async throws -> [UserProfile] {
try await withThrowingTaskGroup(of: UserProfile.self) { group in
for id in ids {
group.addTask {
if let cached = await cache.value(for: id) {
return cached
}
let profile = try await fetchProfile(id: id)
await cache.insert(profile)
return profile
}
}
var results: [UserProfile] = []
for try await profile in group {
results.append(profile)
}
return results
}
}// Kotlin Coroutines — strukturierte Nebenläufigkeit + Mutex für paralleles Herunterladen
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.Serializable
import io.ktor.client.*
import io.ktor.client.call.body
import io.ktor.client.request.get
@Serializable
data class UserProfile(val id: Int, val name: String)
class ProfileCache {
private val mutex = Mutex()
private val storage = mutableMapOf<Int, UserProfile>()
suspend fun get(id: Int): UserProfile? = mutex.withLock { storage[id] }
suspend fun put(profile: UserProfile) = mutex.withLock {
storage[profile.id] = profile
}
}
suspend fun fetchProfile(client: HttpClient, id: Int): UserProfile =
client.get("https://api.example.com/users/$id").body()
suspend fun loadProfiles(
client: HttpClient,
ids: List<Int>,
cache: ProfileCache
): List<UserProfile> = coroutineScope {
ids.map { id ->
async {
cache.get(id) ?: fetchProfile(client, id).also { cache.put(it) }
}
}.awaitAll()
}
// Cancellation ist kooperativ: lang laufende Arbeit muss isActive prüfen
suspend fun loadWithTimeout(client: HttpClient, ids: List<Int>, cache: ProfileCache) =
withTimeoutOrNull(5_000) { loadProfiles(client, ids, cache) } ?: emptyList()Keine Situationsfrage, sondern ein bewusster Kompromiss: Swift 6 findet mehr Fehler zur Kompilierzeit, der Preis dafür sind hohe Migrationskosten. Kotlin Coroutines sind flexibler und lassen sich schrittweise einführen, überlassen den geteilten State jedoch der Runtime-Disziplin. Beim Abbruch gibt es keinen Unterschied — beide sind kooperativ. Bei einem Greenfield-Projekt nur für Apple-Plattformen aktiviere den Swift-6-Sprachmodus von Anfang an. Plane die Migration in einer bestehenden Codebasis target-basiert und beginne mit dem isoliertesten Modul. Behandle die Bridging-Schicht in KMP als eigene Architekturphase.
Kostenlose Beratung erhaltenSwift 6 erzwingt mit dem Sendable-Protokoll und der Actor-Isolation, dass Datenwettläufe zur Kompilierzeit erkannt werden (SE-0302, SE-0306). Kotlin Coroutines hingegen sind ein Suspend-Function-Modell, das über die Bibliothek kotlinx.coroutines läuft; die Datenwettlauf-Prüfung bleibt nicht dem Compiler, sondern der Disziplin des Entwicklers (Mutex, Dispatcher-Wahl) überlassen.