SwiftUI مقابل UIKit
مقارنة شاملة بين إطار عمل Apple الحديث التصريحي SwiftUI وUIKit المُختبَر ميدانيًا. أي إطار عمل يجب اختياره في 2025؟
Model-View-ViewModel: بديهي ومرن وواسع الانتشار
The Composable Architecture: وظيفية (functional)، قابلة للاختبار، قابلة للتنبؤ
| الفئة | MVVM | TCA |
|---|---|---|
| الأداء | 8/10 | 8/10 |
| سهولة التعلّم | 9/10 | 4/10 |
| النظام البيئي | 9/10 | 7/10 |
| المجتمع | 9/10 | 7/10 |
| سوق العمل | 10/10 | 7/10 |
| الاستدامة المستقبلية | 8/10 | 9/10 |
// MVVM - قائمة المنتجات
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 = "تعذر تحميل المنتجات: \(error.localizedDescription)"
}
isLoading = false
}
func deleteProduct(_ product: Product) async {
do {
try await repository.delete(product.id)
products.removeAll { $0.id == product.id }
} catch {
errorMessage = "فشل الحذف: \(error.localizedDescription)"
}
}
}
struct ProductListView: View {
@State private var viewModel = ProductListViewModel()
var body: some View {
NavigationStack {
Group {
if viewModel.isLoading {
ProgressView("جارٍ التحميل...")
} else {
List(viewModel.filteredProducts) { product in
ProductRow(product: product)
}
.searchable(text: $viewModel.searchText)
}
}
.navigationTitle("المنتجات")
}
.task { await viewModel.loadProducts() }
.alert("خطأ", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("حسنًا") { viewModel.errorMessage = nil }
} message: {
Text(viewModel.errorMessage ?? "")
}
}
}// TCA - قائمة المنتجات
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("المنتجات")
}
.task { store.send(.onAppear) }
}
}
}للمشاريع الصغيرة والمتوسطة، يُفضّل MVVM لسرعة التطوير وانخفاض عتبة الدخول. أما في المشاريع الكبيرة والمعقدة وحيث يكون الاختبار أمرًا حرجًا، فإن TCA يثبت قيمته على المدى الطويل بفضل الحالة (state) القابلة للتنبؤ والمعمارية القابلة للتركيب (composable). يمكن أيضًا اتباع نهج هجين: الميزات الأساسية بـ MVVM، والتدفقات الحرجة/المعقدة بـ TCA.
احصل على استشارة مجانيةأسبوع إلى أسبوعين لفهم المفاهيم الأساسية، وشهر إلى شهرين لتصبح منتجًا بشكل كامل. يُنصح بمتابعة فيديوهات Point-Free واستعراض مستودع أمثلة TCA.