Swift vs Kotlin
AppleプラットフォームのSwiftとAndroidのKotlinを比較。モダンな言語機能、構文、エコシステムの観点から詳細に分析。
コンパイル時にデータ競合を排除するActorベースのモデル
ライブラリベースの、柔軟なsuspend関数モデル
「状況次第」ではなく、意識的なトレードオフだ。Swift 6はコンパイル時により多くのエラーを検出するが、その代償は高い移行コストである。Kotlin coroutinesはより柔軟で段階的に導入でき、共有stateの管理はランタイムの規律に委ねられる。キャンセルに関しては差がない——どちらも協調的(cooperative)だ。 グリーンフィールドのApple専用プロジェクトでは、最初からSwift 6言語モードを有効にしよう。既存コードベースでの移行はターゲット単位で計画し、最も孤立したモジュールから始めること。KMPではブリッジ層を独立したアーキテクチャフェーズとして扱うこと。
| カテゴリー | Swift 6 Concurrency | Kotlin Coroutines |
|---|---|---|
| パフォーマンス | 8/10 | 8/10 |
| 学習のしやすさ | 5/10 | 7/10 |
| エコシステム | 7/10 | 9/10 |
| コミュニティ | 7/10 | 8/10 |
| 求人市場 | 8/10 | 8/10 |
| 将来性 | 9/10 | 8/10 |
// Swift 6 — Sendableモデル + actorによる並列ダウンロード
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 — 構造化並行性 + Mutexによる並列ダウンロード
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()
}
// キャンセルは協調的:長時間実行するタスクはisActiveをチェックすべき
suspend fun loadWithTimeout(client: HttpClient, ids: List<Int>, cache: ProfileCache) =
withTimeoutOrNull(5_000) { loadProfiles(client, ids, cache) } ?: emptyList()「状況次第」ではなく、意識的なトレードオフだ。Swift 6はコンパイル時により多くのエラーを検出するが、その代償は高い移行コストである。Kotlin coroutinesはより柔軟で段階的に導入でき、共有stateの管理はランタイムの規律に委ねられる。キャンセルに関しては差がない——どちらも協調的(cooperative)だ。 グリーンフィールドのApple専用プロジェクトでは、最初からSwift 6言語モードを有効にしよう。既存コードベースでの移行はターゲット単位で計画し、最も孤立したモジュールから始めること。KMPではブリッジ層を独立したアーキテクチャフェーズとして扱うこと。
無料相談を受けるSwift 6は、SendableプロトコルとActor分離によってデータ競合をコンパイル時に検出することを強制する(SE-0302、SE-0306)。一方Kotlin coroutinesは、kotlinx.coroutinesライブラリを介して動作するsuspend関数モデルであり、データ競合のチェックはコンパイラではなく開発者の規律(Mutex、dispatcherの選択)に委ねられる。