Azimjon Abdurasulov

07. TCA (The Composable Architecture) — Point-Free

Arxitektura

iOS Arxitektura 7: TCA (The Composable Architecture) — Point-Free'ning Zamonaviy Yondashuvi

Holat: 2026-yil oktabr. TCA bugungi kunda SwiftUI ilovalari uchun eng kuchli va keng tarqalgan state management va arxitektura yechimlaridan biriga aylangan.

Mundarija

  1. TCA nima?
  2. TCA tarkibiy qismlari
  3. Data Flow (Ma'lumotlar oqimi)
  4. Amaliy loyiha: Counter
  5. Effect'lar bilan ishlash (API Call)
  6. Composition (Kompozitsiya)
  7. Navigation TCA'da
  8. Testing TCA
  9. TCA qachon ishlatiladi va uning kamchiliklari
  10. Xulosa
  11. Manbalar

1. TCA nima? {#tca-nima}

The Composable Architecture (TCA) — Point-Free (Brandon Williams va Stephen Celis) tomonidan yaratilgan bo'lib, SwiftUI va Swift ilovalari uchun maxsus ishlab chiqilgan arxitektura kutubxonasidir.

  • Asosiy g'oya: Unidirectional data flow (bir yo'nalishli ma'lumot oqimi).
  • Tarqalishi: GitHub'da juda mashhur (12,000+ yulduzlar) va ko'plab yirik production ilovalarda ishlatiladi.
  • Maqsad: State boshqaruvini barqarorlashtirish, kompozitsiya orqali modullilikni oshirish va test qilishni mutlaqo deterministik qilish.

2. TCA tarkibiy qismlari {#tarkibiy-qismlar}

TCA asosan bir nechta fundamental tushunchalardan iborat:

  • State: Ekranning yoki modulning joriy holati. Odatda struct ko'rinishida yoziladi va o'zgaruvchilarni saqlaydi.
  • Action: Foydalanuvchi harakatlari (masalan, tugma bosilishi) va tizim hodisalari (masalan, API javobi kelishi). Odatda enum.
  • Reducer: State va Action ni qabul qilib, yangi State ni qaytaruvchi sof funksiya.
  • Store: State ni o'zida saqlaydi va Action'larni qabul qilib Reducer orqali State ni yangilaydi.
  • Effect: Asinxron operatsiyalar (API chaqiruvlari, taymerlar va hk). Ular oxir-oqibat yana Action qaytaradi.
  • Dependencies (yangi Environment): Tashqi bog'lanishlar va servislar.

3. Data Flow (Ma'lumotlar oqimi) {#data-flow}

TCA'da ma'lumotlar oqimi bir yo'nalishli va doiraviy:

The Composable Architecture (TCA) Data Flow

4. Amaliy loyiha: Counter {#amaliy-loyiha}

Eng oddiy TCA namunasi - bu Counter. @Reducer va @ObservableState makrolari (Swift 5.9+ / TCA modern) orqali kodingiz ancha qisqaradi.

import SwiftUI
import ComposableArchitecture

@Reducer
struct CounterFeature {
    // 1. State
    @ObservableState
    struct State: Equatable {
        var count = 0
    }
    
    // 2. Action
    enum Action {
        case incrementButtonTapped
        case decrementButtonTapped
    }
    
    // 3. Reducer
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .incrementButtonTapped:
                state.count += 1
                return .none // Effect qaytarmaydi
                
            case .decrementButtonTapped:
                state.count -= 1
                return .none
            }
        }
    }
}

// 4. View
struct CounterView: View {
    // Store obyekti View'ga beriladi
    let store: StoreOf<CounterFeature>
    
    var body: some View {
        VStack(spacing: 20) {
            Text("\(store.count)")
                .font(.largeTitle)
                
            HStack {
                Button("-") {
                    store.send(.decrementButtonTapped)
                }
                .font(.largeTitle)
                
                Button("+") {
                    store.send(.incrementButtonTapped)
                }
                .font(.largeTitle)
            }
        }
        .padding()
    }
}

5. Effect'lar bilan ishlash (API Call) {#effects}

Asinxron ishlarni bajarish (API chaqiruvlari, bazadan o'qish) uchun Effect ishlatiladi. Effect.run yoki qisqacha .run orqali asinxron blok yoziladi va u yana yangi Action qaytaradi.

TCA Side Effects va Dependencies Boshqaruvi

import SwiftUI
import ComposableArchitecture

@Reducer
struct UserListFeature {
    @ObservableState
    struct State: Equatable {
        var users: [String] = []
        var isLoading = false
    }
    
    enum Action {
        case fetchUsersTapped
        case usersResponse([String])
    }
    
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .fetchUsersTapped:
                state.isLoading = true
                return .run { send in
                    // 1. Asinxron operatsiya (masalan, API call)
                    try await Task.sleep(for: .seconds(2))
                    let fetchedUsers = ["Ali", "Vali", "Hasan"]
                    
                    // 2. Natijani yuborish
                    await send(.usersResponse(fetchedUsers))
                }
                
            case let .usersResponse(users):
                state.isLoading = false
                state.users = users
                return .none
            }
        }
    }
}

struct UserListView: View {
    let store: StoreOf<UserListFeature>
    
    var body: some View {
        List {
            if store.isLoading {
                ProgressView()
            } else {
                ForEach(store.users, id: \.self) { user in
                    Text(user)
                }
            }
            
            Button("Users Olib Kelish") {
                store.send(.fetchUsersTapped)
            }
        }
    }
}

Senior eslatmasi: Har doim API call natijalarini to'g'ridan-to'g'ri Effect ichida o'zgartirmang! Effect ichidan .send() orqali Action qaytaring, State faqat Reducer'ning o'zida sinxron o'zgarishi kerak.

6. Composition (Kompozitsiya) {#composition}

TCA ning eng kuchli xususiyati — bu composition. Katta ilova bir qancha kichik va mustaqil Feature'larga (Reducer'larga) bo'linadi va ular parent tomonidan birlashtiriladi.

import SwiftUI
import ComposableArchitecture

@Reducer
struct AppFeature {
    @ObservableState
    struct State: Equatable {
        var counter = CounterFeature.State()
        var userList = UserListFeature.State()
    }
    
    enum Action {
        case counter(CounterFeature.Action)
        case userList(UserListFeature.Action)
    }
    
    var body: some ReducerOf<Self> {
        Scope(state: \.counter, action: \.action.counter) {
            CounterFeature()
        }
        Scope(state: \.userList, action: \.action.userList) {
            UserListFeature()
        }
        Reduce { state, action in
            // Parent leveldagi o'zgarishlar (agar kerak bo'lsa)
            return .none
        }
    }
}

7. Navigation TCA'da {#navigation}

TCA'da navigatsiya ham State tomonidan boshqariladi (State-driven navigation). Yangi makrolar yordamida bu juda osonlashdi.

@Reducer
struct ContactsFeature {
    @ObservableState
    struct State: Equatable {
        // Modal uchun
        @Presents var addContact: ContactDetailFeature.State?
        var contacts: [String] = []
    }
    
    enum Action {
        case addButtonTapped
        // Modal actionlari uchun
        case addContact(PresentationAction<ContactDetailFeature.Action>)
    }
    
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .addButtonTapped:
                // state.addContact = ContactDetailFeature.State(name: "")
                return .none
                
            case let .addContact(.presented(.saveTapped(name))):
                state.contacts.append(name)
                state.addContact = nil // modalni yopish
                return .none
                
            case .addContact:
                return .none
            }
        }
        .ifLet(\.$addContact, action: \.addContact) {
            // ContactDetailFeature()
        }
    }
}

// View-da .sheet modifier misoli:
/*
.sheet(item: $store.scope(state: \.addContact, action: \.addContact)) { store in
    // ContactDetailView(store: store)
}
*/

8. Testing TCA {#testing}

TCA da test yozish shu qadar oson va ishonchli-ki, bu uning eng katta plyusi hisoblanadi. Siz TestStore ishlatasiz va action yuborib, state qanday o'zgarganini tasdiqlaysiz.

import XCTest
import ComposableArchitecture

@MainActor
final class CounterTests: XCTestCase {
    func testIncrement() async {
        let store = TestStore(
            initialState: CounterFeature.State(count: 0)
        ) {
            CounterFeature()
        }
        
        await store.send(.incrementButtonTapped) {
            $0.count = 1
        }
    }
    
    func testDecrement() async {
        let store = TestStore(
            initialState: CounterFeature.State(count: 1)
        ) {
            CounterFeature()
        }
        
        await store.send(.decrementButtonTapped) {
            $0.count = 0
        }
    }
}

Muhim: TCA testlarida siz yuborilayotgan barcha Action'lar va State o'zgarishlarini 100% ketma-ketlikda yozishingiz shart. Aks holda test fail bo'ladi. Bu tizimda yashirin (hidden) o'zgarishlar bo'lishini oldini oladi.

9. TCA qachon ishlatiladi va uning kamchiliklari {#qachon-ishlatish}

Qachon ishlatiladi?

  • Murakkab state boshqaruvi kerak bo'lgan yirik ilovalar.
  • Test coverage eng muhim masala bo'lganida.
  • SwiftUI-first loyihalar (ayniqsa iOS 16+ va undan yuqori uchun).
  • Predictable data flow kerak bo'lgan katta jamoalarda.

Kamchiliklari:

  • Learning curve (O'rganish qiyinligi): Juda ko'p yangi tushunchalar mavjud (Reducer, Effect, Dependencies).
  • Boilerplate code: Har bir kichik element uchun State, Action, Reducer yaratish ko'p vaqt va qator kod talab qiladi.
  • UIKit integratsiyasi: UIKit bilan ham ishlatsa bo'ladi, lekin uning to'liq salohiyati SwiftUI bilan namoyon bo'ladi.
  • Performance: Katta struct (State) qayta yozilaverganligi sababli barcha view'lar render bo'lib ketishi mumkin (albatta, to'g'ri Scope va @ObservableState ishlatilsa buning oldini olish mumkin).

10. Xulosa {#xulosa}

TCA shunchaki Redux'ning SwiftUI nusxasi emas, balki asinxron operatsiyalar, dependencies inyeksiyasi va testlashni o'zida birlashtirgan to'liq va kuchli arxitekturadir. Yangi @Reducer va @ObservableState makrolari tufayli boilerplate kod sezilarli darajada kamaydi, bu esa uni endilikda nafaqat katta korporativ ilovalar, balki o'rtacha o'lchamdagi loyihalar uchun ham ajoyib tanlovga aylantiradi.

11. Manbalar {#manbalar}

  1. TCA GitHub Repository - Point-Free
  2. Point-Free Video Tutorials (TCA Playlist)
  3. TCA Documentation & Tutorials

Markdown manba

iOS Lessons