SwiftUI vs UIKit
Apple'ın modern deklaratif framework'ü SwiftUI ile battle-tested UIKit arasındaki kapsamlı karşılaştırma. 2025'te hangi framework'ü seçmelisiniz?
Model-View-ViewModel: sezgisel, esnek, yaygın kullanımlı
The Composable Architecture: fonksiyonel, test edilebilir, öngörülebilir
// MVVM - Ürün listesi
import SwiftUI
import Observation
@Observable
class ProductListViewModel {
var products: [Product] = []
var isLoading = false
var errorMessage: String?
var searchText = ""
private let repository: ProductRepository
init(repository: ProductRepository = .live) {
self.repository = repository
}
var filteredProducts: [Product] {
guard !searchText.isEmpty else { return products }
return products.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}
func loadProducts() async {
isLoading = true
errorMessage = nil
do {
products = try await repository.fetchProducts()
} catch {
errorMessage = "Ürünler yüklenemedi: \(error.localizedDescription)"
}
isLoading = false
}
func deleteProduct(_ product: Product) async {
do {
try await repository.delete(product.id)
products.removeAll { $0.id == product.id }
} catch {
errorMessage = "Silme başarısız: \(error.localizedDescription)"
}
}
}
struct ProductListView: View {
@State private var viewModel = ProductListViewModel()
var body: some View {
NavigationStack {
Group {
if viewModel.isLoading {
ProgressView("Yükleniyor...")
} else {
List(viewModel.filteredProducts) { product in
ProductRow(product: product)
}
.searchable(text: $viewModel.searchText)
}
}
.navigationTitle("Ürünler")
}
.task { await viewModel.loadProducts() }
.alert("Hata", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("Tamam") { viewModel.errorMessage = nil }
} message: {
Text(viewModel.errorMessage ?? "")
}
}
}// TCA - Ürün listesi
import ComposableArchitecture
import SwiftUI
@Reducer
struct ProductListFeature {
@ObservableState
struct State: Equatable {
var products: [Product] = []
var isLoading = false
var errorMessage: String?
var searchText = ""
var filteredProducts: [Product] {
guard !searchText.isEmpty else { return products }
return products.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}
}
enum Action {
case onAppear
case searchTextChanged(String)
case deleteProduct(id: String)
case productsLoaded(Result<[Product], Error>)
case productDeleted(Result<Void, Error>)
case dismissError
}
@Dependency(\.productRepository) var repository
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .onAppear:
state.isLoading = true
return .run { send in
await send(.productsLoaded(
Result { try await repository.fetchProducts() }
))
}
case .searchTextChanged(let text):
state.searchText = text
return .none
case .deleteProduct(let id):
return .run { send in
await send(.productDeleted(
Result { try await repository.delete(id) }
))
}
case .productsLoaded(.success(let products)):
state.isLoading = false
state.products = products
return .none
case .productsLoaded(.failure(let error)):
state.isLoading = false
state.errorMessage = error.localizedDescription
return .none
case .productDeleted(.success):
return .send(.onAppear)
case .productDeleted(.failure(let error)):
state.errorMessage = error.localizedDescription
return .none
case .dismissError:
state.errorMessage = nil
return .none
}
}
}
}
struct ProductListView: View {
let store: StoreOf<ProductListFeature>
var body: some View {
WithPerceptionTracking {
NavigationStack {
List(store.filteredProducts) { product in
Text(product.name)
}
.searchable(text: store.binding(get: \.searchText, send: ProductListFeature.Action.searchTextChanged))
.navigationTitle("Ürünler")
}
.task { store.send(.onAppear) }
}
}
}Küçük-orta projeler için MVVM — daha hızlı geliştirme, düşük giriş eşiği. Büyük, karmaşık ve test-critical projelerde TCA — öngörülebilir state, compose edilebilir mimarinin değeri uzun vadede kendini gösteriyor. Hibrit yaklaşım da mümkün: ana özellikler MVVM, kritik/karmaşık akışlar TCA.
Temel kavramları kavramak için 1-2 hafta, üretken olmak için 1-2 ay. Point-Free'nin videolarını ve TCA examples reposunu incelemenizi öneririz.