Skip to content

Instantly share code, notes, and snippets.

@barabashd
Last active August 21, 2025 12:41
Show Gist options
  • Select an option

  • Save barabashd/33b64676195ce41f4bb73c327ea512a8 to your computer and use it in GitHub Desktop.

Select an option

Save barabashd/33b64676195ce41f4bb73c327ea512a8 to your computer and use it in GitHub Desktop.

Revisions

  1. barabashd revised this gist May 12, 2024. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions TCA_README_UKR.md
    Original file line number Diff line number Diff line change
    @@ -55,8 +55,8 @@ The Composable Architecture була розроблена протягом ба
    Ви можете переглянути всі епізоди [тут][tca-episode-collection], а також присвячений [багатошаровий тур][tca-tour]
    по архітектурі з нуля.

    <a href="https://www.pointfree.co/collections/tours/composable-architecture-1-0">
    <img alt="video poster image" src="https://d3rccdn33rt8ze.cloudfront.net/episodes/0243.jpeg" width="600">
    <a href="https://www.pointfree.co/collections/composable-architecture">
    <img alt="video poster image" src="https://i.vimeocdn.com/video/1492464398-1f5189a415136097aabf5b0b4845928bbb5d425a149069e4e0a848b67618a6f3-d?mw=1900&mh=1069&q=70" width="600">
    </a>

    ## Приклади
    @@ -318,7 +318,8 @@ struct MyApp: App {
    ### Тестування

    > [!Нотаточка]
    > Для докладнішої інформації щодо тестування перегляньте окрему статтю про [тестування][testing-article].
    > Для докладнішої інформації щодо тестування перегляньте окрему статтю про
    > [тестування][testing-article].
    Щоб тестів, використовуйте `TestStore`, який можна створити з тією самою інформацією, що й `Store`, але
    він робить додаткову роботу, щоб дозволити вам перевірити, як розвивається ваша фіча при відправці дій:
    @@ -704,7 +705,7 @@ The Composable Architecture була побудована на основі ід
    [mbrandonw]: https://twitter.com/mbrandonw
    [stephencelis]: https://twitter.com/stephencelis
    [tca-episode-collection]: https://www.pointfree.co/collections/composable-architecture
    [tca-tour]: https://www.pointfree.co/collections/tours/composable-architecture-1-0
    [tca-tour]: https://www.pointfree.co/collections/tours/composable-architecture
    [gh-isowords]: https://github.com/pointfreeco/isowords
    [gh-discussions]: https://github.com/pointfreeco/swift-composable-architecture/discussions
    [swift-forum]: https://forums.swift.org/c/related-projects/swift-composable-architecture
  2. barabashd revised this gist May 12, 2024. 1 changed file with 157 additions and 143 deletions.
    300 changes: 157 additions & 143 deletions TCA_README_UKR.md
    Original file line number Diff line number Diff line change
    @@ -76,6 +76,7 @@ The Composable Architecture була розроблена протягом ба
    * [Motion manager](https://github.com/pointfreeco/composable-core-motion/tree/main/Examples/MotionManager)
    * [Search](./Examples/Search)
    * [Speech Recognition](./Examples/SpeechRecognition)
    * [SyncUps app](./Examples/SyncUps)
    * [Tic-Tac-Toe](./Examples/TicTacToe)
    * [Todos](./Examples/Todos)
    * [Voice memos](./Examples/VoiceMemos)
    @@ -85,7 +86,7 @@ The Composable Architecture була розроблена протягом ба

    ## Базове використання

    > **Примітка**
    > [!Нотаточка]
    > Для інтерактивного посібника крок за кроком обов'язково перегляньте [Meet the Composable
    > Architecture][meet-tca].
    @@ -108,41 +109,47 @@ The Composable Architecture була розроблена протягом ба

    Як базовий приклад, розглянемо користувацький інтерфейс, що відображає число разом з кнопками "+" та "−", що
    інкрементують та декрементують число. Щоб зробити річ цікавішою, припустимо, яка при натисканні виконує запит API для отримання
    випадкового факту про це число, а потім відображає факт у вигляді сповіщення.
    випадкового факту про це число, і відображає його на вью.

    Для реалізації цього функціоналу ми створюємо новий тип, який буде містити домен та поведінку цієї
    фічі, реалізував протокол `Reducer`:
    фічі, яка буде анотована через макро `@Reducer`:

    ```swift
    import ComposableArchitecture

    struct Feature: Reducer {
    @Reducer
    struct Feature {
    }
    ```

    Тут нам потрібно визначити тип для стану фічі, який складається з цілого числа для поточного
    лічильника і необов'язкового рядка, який представляє заголовок сповіщення, яке ми хочемо
    показати (необов'язково, оскільки `nil` означає, що сповіщення не показується):
    лічильника і необов'язкового рядка, який представляє відображення факторіалу:

    ```swift
    struct Feature: Reducer {
    @Reducer
    struct Feature {
    @ObservableState
    struct State: Equatable {
    var count = 0
    var numberFactAlert: String?
    var numberFact: String?
    }
    }
    ```

    > [!Нотаточка]
    > Ми застосували макро `@ObservableState` до `State`, для того щоб використати
    > механізм спостерігача в бібліотеці.
    Також нам потрібно визначити тип для дій фічі. Є очевидні дії, такі як натискання кнопки
    зменшення, кнопки збільшення або кнопки факту. Але також є деякі не такі очевидні дії, такі як дія
    користувача, коли він закриває сповіщення, і дія, яка відбувається при отриманні відповіді від
    зменшення, кнопки збільшення або кнопки факту. Але також є деякі не такі очевидні дії, такі як дія, яка відбувається при отриманні відповіді від
    API-запиту факту:

    ```swift
    struct Feature: Reducer {
    @Reducer
    struct Feature {
    @ObservableState
    struct State: Equatable { }
    enum Action: Equatable {
    case factAlertDismissed
    enum Action {
    case decrementButtonTapped
    case incrementButtonTapped
    case numberFactButtonTapped
    @@ -151,107 +158,96 @@ struct Feature: Reducer {
    }
    ```

    Потім ми реалізуємо метод `reduce`, який відповідає за обробку фактичної логіки та поведінки
    фічі. Він описує, як змінити поточний стан на наступний стан та описує, які ефекти потрібно
    Потім ми реалізуємо змінну `body`, яка відповідає за створення фактичної логіки та поведінки
    фічі. В ній ми можемо використати `Reduce` щоб описаи, як змінити поточний стан на наступний стан, та які ефекти потрібно
    виконати. Деякі дії не потребують виконання ефектів, і вони можуть повертати значення `.none`, щоб
    показати це:

    ```swift
    struct Feature: Reducer {
    @Reducer
    struct Feature {
    @ObservableState
    struct State: Equatable { }
    enum Action: Equatable { }
    enum Action { /* ... */ }

    func reduce(into state: inout State, action: Action) -> Effect<Action> {
    switch action {
    case .factAlertDismissed:
    state.numberFactAlert = nil
    return .none

    case .decrementButtonTapped:
    state.count -= 1
    return .none

    case .incrementButtonTapped:
    state.count += 1
    return .none

    case .numberFactButtonTapped:
    return .run { [count = state.count] send in
    await send(
    .numberFactResponse(
    TaskResult {
    String(
    decoding: try await URLSession.shared
    .data(from: URL(string: "http://numbersapi.com/\(count)/trivia")!).0,
    as: UTF8.self
    )
    }
    var body: some Reducer<State, Action> {
    Reduce { state, action in
    switch action {
    case .decrementButtonTapped:
    state.count -= 1
    return .none

    case .incrementButtonTapped:
    state.count += 1
    return .none

    case .numberFactButtonTapped:
    return .run { [count = state.count] send in
    await send(
    .numberFactResponse(
    TaskResult {
    String(
    decoding: try await URLSession.shared
    .data(from: URL(string: "http://numbersapi.com/\(count)/trivia")!).0,
    as: UTF8.self
    )
    }
    )
    )
    )
    }
    }

    case let .numberFactResponse(.success(fact)):
    state.numberFactAlert = fact
    return .none
    case let .numberFactResponse(.success(fact)):
    state.numberFactAlert = fact
    return .none

    case .numberFactResponse(.failure):
    state.numberFactAlert = "Хай йому грець! Не можу загрузити число факт :("
    return .none
    case .numberFactResponse(.failure):
    state.numberFact = "Хай йому грець! Не можу загрузити число факт :("
    return .none
    }
    }
    }
    }
    ```

    І наостанок ми визначаємо представлення, яке відображає фічу. Воно зберігає посилання на `StoreOf<Feature>`,
    щоб відстежувати усі зміни стану і здійснювати перерендеринг, і ми можемо надсилати усі дії користувача в сховище,
    щоб змінювати стан. Також ми повинні створити обгортку структури навколо факту сповіщення, щоб зробити його `Identifiable`,
    що вимагає використання модифікатора `.alert`:
    щоб змінювати стан:

    ```swift
    struct FeatureView: View {
    let store: StoreOf<Feature>

    var body: some View {
    WithViewStore(self.store, observe: { $0 }) { viewStore in
    VStack {
    HStack {
    Button("") { viewStore.send(.decrementButtonTapped) }
    Text("\(viewStore.count)")
    Button("+") { viewStore.send(.incrementButtonTapped) }
    }

    Button("Number fact") { viewStore.send(.numberFactButtonTapped) }
    Form {
    Section {
    Text("\(store.count)")
    Button("Decrement") { store.send(.decrementButtonTapped) }
    Button("Increment") { store.send(.incrementButtonTapped) }
    }
    Section {
    Button("Number fact") { store.send(.numberFactButtonTapped) }
    }
    if let fact = store.numberFact {
    Text(fact)
    }
    .alert(
    item: viewStore.binding(
    get: { $0.numberFactAlert.map(FactAlert.init(title:)) },
    send: .factAlertDismissed
    ),
    content: { Alert(title: Text($0.title)) }
    )
    }
    }
    }

    struct FactAlert: Identifiable {
    var title: String
    var id: String { self.title }
    }
    ```

    Також нескладно мати контролер UIKit, який працює на основі цього cховища. Ви підписуєтеся на сховище у `viewDidLoad`,
    щоб оновлювати інтерфейс користувача та показувати сповіщення. Код трохи довший, ніж версія для SwiftUI, тому ми згорнули його тут:
    Також нескладно мати контролер UIKit, який працює на основі цього cховища. Ви можете спостерігати за змінами стану в сховищі у `viewDidLoad`,
    і тоді наповнити UI компонентів даними зі сховища. Код трохи довший, ніж версія для SwiftUI, тому ми згорнули його тут:

    <details>
    <summary>Тицьни щоб розкрити!</summary>

    ```swift
    class FeatureViewController: UIViewController {
    let viewStore: ViewStoreOf<Feature>
    let store: StoreOf<Feature>
    var cancellables: Set<AnyCancellable> = []

    init(store: StoreOf<Feature>) {
    self.viewStore = ViewStore(store, observe: { $0 })
    self.store = store
    super.init(nibName: nil, bundle: nil)
    }

    @@ -263,42 +259,29 @@ struct FactAlert: Identifiable {
    super.viewDidLoad()

    let countLabel = UILabel()
    let incrementButton = UIButton()
    let decrementButton = UIButton()
    let factButton = UIButton()
    let incrementButton = UIButton()
    let factLabel = UILabel()

    // Пропущено: Додаєм сабвьюхи і встановлюємо констрейнти...

    self.viewStore.publisher
    .map { "\($0.count)" }
    .assign(to: \.text, on: countLabel)
    .store(in: &self.cancellables)

    self.viewStore.publisher.numberFactAlert
    .sink { [weak self] numberFactAlert in
    let alertController = UIAlertController(
    title: numberFactAlert, message: nil, preferredStyle: .alert
    )
    alertController.addAction(
    UIAlertAction(
    title: "Окі докі",
    style: .default,
    handler: { _ in self?.viewStore.send(.factAlertDismissed) }
    )
    )
    self?.present(alertController, animated: true, completion: nil)
    }
    .store(in: &self.cancellables)
    observe { [weak self] in
    guard let self
    else { return }

    countLabel.text = "\(self.store.text)"
    factLabel.text = self.store.numberFact
    }
    }

    @objc private func incrementButtonTapped() {
    self.viewStore.send(.incrementButtonTapped)
    self.store.send(.incrementButtonTapped)
    }
    @objc private func decrementButtonTapped() {
    self.viewStore.send(.decrementButtonTapped)
    self.store.send(.decrementButtonTapped)
    }
    @objc private func factButtonTapped() {
    self.viewStore.send(.numberFactButtonTapped)
    self.store.send(.numberFactButtonTapped)
    }
    }
    ```
    @@ -334,7 +317,7 @@ struct MyApp: App {

    ### Тестування

    > **Нотаточка**
    > [!Нотаточка]
    > Для докладнішої інформації щодо тестування перегляньте окрему статтю про [тестування][testing-article].
    Щоб тестів, використовуйте `TestStore`, який можна створити з тією самою інформацією, що й `Store`, але
    @@ -365,13 +348,13 @@ await store.send(.decrementButtonTapped) {

    Далі, якщо крок спричиняє виконання ефекту, який повертає дані назад до сховища, ми повинні перевірити
    його. Наприклад, якщо ми симулюємо натискання кнопки факту, ми очікуємо отримати відповідь з фактом,
    яке потім призведе до показу cповіщення:
    яке потім спричинить зміну стану `numberFact`:

    ```swift
    await store.send(.numberFactButtonTapped)

    await store.receive(.numberFactResponse(.success(???))) {
    $0.numberFactAlert = ???
    await store.receive(\.numberFactResponse) {
    $0.numberFact = ???
    }
    ```

    @@ -386,7 +369,8 @@ await store.receive(.numberFactResponse(.success(???))) {
    це, додавши властивість до редуктора `Feature`:

    ```swift
    struct Feature: Reducer {
    @Reducer
    struct Feature {
    let numberFact: (Int) async throws -> String
    }
    @@ -409,18 +393,20 @@ case .numberFactButtonTapped:
    @main
    struct MyApp: App {
    var body: some Scene {
    FeatureView(
    store: Store(
    initialState: Feature.State(),
    reducer: Feature(
    numberFact: { number in
    let (data, _) = try await URLSession.shared
    .data(from: .init(string: "http://numbersapi.com/\(number)")!)
    return String(decoding: data, as: UTF8.self)
    }
    WindowGroup {
    FeatureView(
    store: Store(
    initialState: Feature.State(),
    reducer: Feature(
    numberFact: { number in
    let (data, _) = try await URLSession.shared
    .data(from: .init(string: "http://numbersapi.com/\(number)")!)
    return String(decoding: data, as: UTF8.self)
    }
    )
    )
    )
    )
    }
    }
    }
    ```
    @@ -438,13 +424,13 @@ func testFeature() async {
    ```

    Завдяки цьому невеликому етапу роботи ми можемо завершити тест, симулюючи натискання користувачем на
    кнопку факту, отримання відповіді від залежності для спрацювання сповіщення, а потім закриття сповіщенняЖ
    кнопку факту, і тоді отримання відповіді від залежності для для відображення факторіалу:

    ```swift
    await store.send(.numberFactButtonTapped)

    await store.receive(.numberFactResponse(.success("0 це є курва, неймовірне число, Остапе"))) {
    $0.numberFactAlert = "0 це є курва, неймовірне число, Остапе"
    await store.receive(\.numberFactResponse) {
    $0.numberFact = "0 це є курва, неймовірне число, Остапе"
    }

    await store.send(.factAlertDismissed) {
    @@ -458,7 +444,7 @@ await store.send(.factAlertDismissed) {
    слідувати, щоб "зареєструвати" залежності з бібліотекою, що дозволить їм негайно стати доступними в
    будь-якому рівні застосунку.

    > **Нотаточка**
    > [!Нотаточка]
    > Для більш детальної інформації, дивитися присвячену статтю про
    > [залежності][dependencies-article].
    @@ -496,7 +482,8 @@ extension DependencyValues {
    будь-якій функціональності, використовуючи обгортку властивості `@Dependency`:

    ```diff
    struct Feature: Reducer {
    @Reducer
    struct Feature {
    - let numberFact: (Int) async throws -> String
    + @Dependency(\.numberFact) var numberFact

    @@ -517,11 +504,13 @@ extension DependencyValues {
    @main
    struct MyApp: App {
    var body: some Scene {
    FeatureView(
    store: Store(initialState: Feature.State()) {
    Feature()
    }
    )
    WindowGroup {
    FeatureView(
    store: Store(initialState: Feature.State()) {
    Feature()
    }
    )
    }
    }
    }
    ```
    @@ -548,23 +537,32 @@ let store = TestStore(initialState: Feature.State()) {

    Документація для релізів та гілки `main` доступна тут:

    * [`main`](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture)
    * [1.0.0](https://pointfreeco.github.io/swift-composable-architecture/1.0.0/documentation/composablearchitecture/)
    * [`main`](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/)
    * [1.10.0](https://pointfreeco.github.io/swift-composable-architecture/1.10.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.10))

    <details>
    <summary>
    Інші версії
    </summary>

    * [1.9.0](https://pointfreeco.github.io/swift-composable-architecture/1.9.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.9))

    * [1.8.0](https://pointfreeco.github.io/swift-composable-architecture/1.8.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.8))

    * [1.7.0](https://pointfreeco.github.io/swift-composable-architecture/1.7.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.7))

    * [1.6.0](https://pointfreeco.github.io/swift-composable-architecture/1.6.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.6))

    * [1.5.0](https://pointfreeco.github.io/swift-composable-architecture/1.5.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.5))

    * [1.4.0](https://pointfreeco.github.io/swift-composable-architecture/1.4.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.4))
    * [1.3.0](https://pointfreeco.github.io/swift-composable-architecture/1.3.0/documentation/composablearchitecture/)
    * [1.2.0](https://pointfreeco.github.io/swift-composable-architecture/1.2.0/documentation/composablearchitecture/)
    * [1.1.0](https://pointfreeco.github.io/swift-composable-architecture/1.1.0/documentation/composablearchitecture/)
    * [1.0.0](https://pointfreeco.github.io/swift-composable-architecture/1.0.0/documentation/composablearchitecture/)
    * [0.59.0](https://pointfreeco.github.io/swift-composable-architecture/0.59.0/documentation/composablearchitecture/)
    * [0.58.0](https://pointfreeco.github.io/swift-composable-architecture/0.58.0/documentation/composablearchitecture/)
    * [0.57.0](https://pointfreeco.github.io/swift-composable-architecture/0.57.0/documentation/composablearchitecture/)
    * [0.56.0](https://pointfreeco.github.io/swift-composable-architecture/0.56.0/documentation/composablearchitecture/)
    * [0.55.0](https://pointfreeco.github.io/swift-composable-architecture/0.55.0/documentation/composablearchitecture/)
    * [0.54.0](https://pointfreeco.github.io/swift-composable-architecture/0.54.0/documentation/composablearchitecture/)
    * [0.53.0](https://pointfreeco.github.io/swift-composable-architecture/0.53.0/documentation/composablearchitecture/)
    * [0.52.0](https://pointfreeco.github.io/swift-composable-architecture/0.52.0/documentation/composablearchitecture/)
    * [0.50.0](https://pointfreeco.github.io/swift-composable-architecture/0.50.0/documentation/composablearchitecture/)
    * [0.49.0](https://pointfreeco.github.io/swift-composable-architecture/0.49.0/documentation/composablearchitecture/)
    </details>

    <br>
    @@ -574,9 +572,10 @@ let store = TestStore(initialState: Feature.State()) {
    * [З чого розпочати][getting-started-article]
    * [Залежності][dependencies-article]
    * [Тестування][testing-article]
    * [Performance][performance-article]
    * [Concurrency][concurrency-article]
    * [Bindings][bindings-article]
    * [Подільний стан][sharing-state-article]
    * [Ефективність][performance-article]
    * [Паралелізм][concurrency-article]
    * [Зв'язування][bindings-article]

    ## Спільнота

    @@ -590,7 +589,7 @@ let store = TestStore(initialState: Feature.State()) {

    Ви можете додати ComposableArchitecture як залежність пакету до вашого проекту в Xcode.

    1. У меню **File** в Xcode виберіть **Add Packages...**
    1. У меню **File** в Xcode виберіть **Add Package Dependencies...**
    2. У полі введення URL-адреси репозиторію пакету введіть "https://github.com/pointfreeco/swift-composable-architecture"
    3. Залежно від структури вашого проекту:
    - Якщо у вас є одит таргет додатку, який потребує доступу до бібліотеки, додайте
    @@ -601,6 +600,20 @@ let store = TestStore(initialState: Feature.State()) {
    [Tic-Tac-Toe](./Examples/TicTacToe) для прикладу такого підходу, де різні фічі розбиті на модулі та
    використовують статичну бібліотеку за допомогою пакету Swift **tic-tac-toe**.


    ## Бібліотеки-партнери

    The Composable Architecture побудована з думкою про розширення, а також існує низка бібліотек,
    які підтримуються спільнотою та доступні для зручності у вашому додатку:

    * [Composable Architecture Extras](https://github.com/Ryu0118/swift-composable-architecture-extras):
    Партнерська бібліотека для Composable Architecture.
    * [TCAComposer](https://github.com/mentalflux/tca-composer): Макро бібліотека для генерування
    шаблонного коду у Composable Architecture.
    * [TCACoordinators](https://github.com/johnpatrickmorgan/TCACoordinators): Шаблон координатор у Composable Architecture.

    Якщо ви бажаєте додати свою бібліотеку, будь ласка [відкрийте ріквест на зміни](https://github.com/pointfreeco/swift-composable-architecture/edit/main/README.md) через посилання до нього!!!

    ## Переклади

    Переклади цього README були зроблені учасниками спільноти:
    @@ -610,8 +623,8 @@ let store = TestStore(initialState: Feature.State()) {
    * [Hindi](https://gist.github.com/akashsoni01/b358ee0b3b747167964ef6946123c88d)
    * [Indonesian](https://gist.github.com/wendyliga/792ea9ac5cc887f59de70a9e39cc7343)
    * [Italian](https://gist.github.com/Bellaposa/5114e6d4d55fdb1388e8186886d48958)
    * [Japanese](https://gist.github.com/kalupas226/bdf577e4a7066377ea0a8aaeebcad428)
    * [Korean](https://gist.github.com/pilgwon/ea05e2207ab68bdd1f49dff97b293b17)
    * [Japanese](https://gist.github.com/Achoo-kr/2d0712deb77f78b3379551ac7baea3e4)
    * [Korean](https://gist.github.com/Achoo-kr/5d8936d12e71028fcc4a7c5e078ca038)
    * [Polish](https://gist.github.com/MarcelStarczyk/6b6153051f46912a665c32199f0d1d54)
    * [Portuguese](https://gist.github.com/SevioCorrea/2bbf337cd084a58c89f2f7f370626dc8)
    * [Simplified Chinese](https://gist.github.com/sh3l6orrr/10c8f7c634a892a9c37214f3211242ad)
    @@ -634,7 +647,7 @@ let store = TestStore(initialState: Feature.State()) {

    В інших аспектах TCA є трохи більш гнучкою, ніж інші бібліотеки. Наприклад, Elm контролює, які типи
    ефектів можна створювати за допомогою типу `Cmd`, але TCA дозволяє будь-які типи ефектів, оскільки
    Effect реалізовує протокол Combine `Publisher`.
    Effect обгортається навколо асинхронної операції.

    І, нарешті, є певні речі, на яких TCA надає велику увагу, а які не є основними для Redux, Elm та
    більшості інших бібліотек. Наприклад, композиція є дуже важливим аспектом TCA, яка полягає в процесі
    @@ -702,4 +715,5 @@ The Composable Architecture була побудована на основі ід
    [concurrency-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/swiftconcurrency
    [bindings-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/bindings
    [navigation-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/navigation
    [sharing-state-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/sharingstate
    [meet-tca]: https://pointfreeco.github.io/swift-composable-architecture/main/tutorials/meetcomposablearchitecture
  3. barabashd revised this gist Aug 17, 2023. 1 changed file with 30 additions and 32 deletions.
    62 changes: 30 additions & 32 deletions TCA_README_UKR.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@
    [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpointfreeco%2Fswift-composable-architecture%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/pointfreeco/swift-composable-architecture)
    [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpointfreeco%2Fswift-composable-architecture%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/pointfreeco/swift-composable-architecture)

    The Composable Architecture (скорочено TCA) - це бібліотека для побудови додатків у послідовновному
    The Composable Architecture (скорочено TCA) - це бібліотека для побудови додатків у послідовному
    та зрозумілому підході з урахуванням композиції, тестування та ергономіки. Вона може бути використана у
    SwiftUI, UIKit та інших фреймворках на будь-якій платформі Apple (iOS, macOS, tvOS та watchOS).

    @@ -55,8 +55,8 @@ The Composable Architecture була розроблена протягом ба
    Ви можете переглянути всі епізоди [тут][tca-episode-collection], а також присвячений [багатошаровий тур][tca-tour]
    по архітектурі з нуля.

    <a href="https://www.pointfree.co/collections/composable-architecture">
    <img alt="video poster image" src="https://i.vimeocdn.com/video/1492464398-1f5189a415136097aabf5b0b4845928bbb5d425a149069e4e0a848b67618a6f3-d?mw=1900&mh=1069&q=70" width="600">
    <a href="https://www.pointfree.co/collections/tours/composable-architecture-1-0">
    <img alt="video poster image" src="https://d3rccdn33rt8ze.cloudfront.net/episodes/0243.jpeg" width="600">
    </a>

    ## Приклади
    @@ -110,13 +110,13 @@ The Composable Architecture була розроблена протягом ба
    інкрементують та декрементують число. Щоб зробити річ цікавішою, припустимо, яка при натисканні виконує запит API для отримання
    випадкового факту про це число, а потім відображає факт у вигляді сповіщення.

    Для реалізації цього функціоналу ми створюємо новий тип, який буде містити домену та поведінку цієї
    фічі, реалізував протоколу `ReducerProtocol`:
    Для реалізації цього функціоналу ми створюємо новий тип, який буде містити домен та поведінку цієї
    фічі, реалізував протокол `Reducer`:

    ```swift
    import ComposableArchitecture

    struct Feature: ReducerProtocol {
    struct Feature: Reducer {
    }
    ```

    @@ -125,7 +125,7 @@ struct Feature: ReducerProtocol {
    показати (необов'язково, оскільки `nil` означає, що сповіщення не показується):

    ```swift
    struct Feature: ReducerProtocol {
    struct Feature: Reducer {
    struct State: Equatable {
    var count = 0
    var numberFactAlert: String?
    @@ -139,7 +139,7 @@ struct Feature: ReducerProtocol {
    API-запиту факту:

    ```swift
    struct Feature: ReducerProtocol {
    struct Feature: Reducer {
    struct State: Equatable { }
    enum Action: Equatable {
    case factAlertDismissed
    @@ -157,11 +157,11 @@ struct Feature: ReducerProtocol {
    показати це:

    ```swift
    struct Feature: ReducerProtocol {
    struct Feature: Reducer {
    struct State: Equatable { }
    enum Action: Equatable { }

    func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
    func reduce(into state: inout State, action: Action) -> Effect<Action> {
    switch action {
    case .factAlertDismissed:
    state.numberFactAlert = nil
    @@ -251,7 +251,7 @@ struct FactAlert: Identifiable {
    var cancellables: Set<AnyCancellable> = []

    init(store: StoreOf<Feature>) {
    self.viewStore = ViewStore(store)
    self.viewStore = ViewStore(store, observe: { $0 })
    super.init(nibName: nil, bundle: nil)
    }

    @@ -334,6 +334,7 @@ struct MyApp: App {

    ### Тестування

    > **Нотаточка**
    > Для докладнішої інформації щодо тестування перегляньте окрему статтю про [тестування][testing-article].
    Щоб тестів, використовуйте `TestStore`, який можна створити з тією самою інформацією, що й `Store`, але
    @@ -385,7 +386,7 @@ await store.receive(.numberFactResponse(.success(???))) {
    це, додавши властивість до редуктора `Feature`:

    ```swift
    struct Feature: ReducerProtocol {
    struct Feature: Reducer {
    let numberFact: (Int) async throws -> String
    }
    @@ -457,7 +458,9 @@ await store.send(.factAlertDismissed) {
    слідувати, щоб "зареєструвати" залежності з бібліотекою, що дозволить їм негайно стати доступними в
    будь-якому рівні застосунку.

    > Для більш детальної інформації, дивитися присвячену статтю про [залежності][dependencies-article].
    > **Нотаточка**
    > Для більш детальної інформації, дивитися присвячену статтю про
    > [залежності][dependencies-article].
    Ми можемо розпочати, обгортаючи функціональності факту про число в новий тип:

    @@ -467,7 +470,7 @@ struct NumberFactClient {
    }
    ```

    А потім реєструємо цей тип у системі керування залежностями, реалізовуючи протоколу `DependencyKey`, який
    А потім реєструємо цей тип у системі керування залежностями, реалізовуючи протокол `DependencyKey`, який
    вимагає вказати живе значення для використання під час запуску додатка на симуляторах або на девайсах:

    ```swift
    @@ -493,7 +496,7 @@ extension DependencyValues {
    будь-якій функціональності, використовуючи обгортку властивості `@Dependency`:

    ```diff
    struct Feature: ReducerProtocol {
    struct Feature: Reducer {
    - let numberFact: (Int) async throws -> String
    + @Dependency(\.numberFact) var numberFact

    @@ -546,40 +549,34 @@ let store = TestStore(initialState: Feature.State()) {
    Документація для релізів та гілки `main` доступна тут:

    * [`main`](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture)
    * [0.53.0](https://pointfreeco.github.io/swift-composable-architecture/0.53.0/documentation/composablearchitecture/)
    * [1.0.0](https://pointfreeco.github.io/swift-composable-architecture/1.0.0/documentation/composablearchitecture/)

    <details>
    <summary>
    Інші версії
    </summary>

    * [0.58.0](https://pointfreeco.github.io/swift-composable-architecture/0.58.0/documentation/composablearchitecture/)
    * [0.57.0](https://pointfreeco.github.io/swift-composable-architecture/0.57.0/documentation/composablearchitecture/)
    * [0.56.0](https://pointfreeco.github.io/swift-composable-architecture/0.56.0/documentation/composablearchitecture/)
    * [0.55.0](https://pointfreeco.github.io/swift-composable-architecture/0.55.0/documentation/composablearchitecture/)
    * [0.54.0](https://pointfreeco.github.io/swift-composable-architecture/0.54.0/documentation/composablearchitecture/)
    * [0.53.0](https://pointfreeco.github.io/swift-composable-architecture/0.53.0/documentation/composablearchitecture/)
    * [0.52.0](https://pointfreeco.github.io/swift-composable-architecture/0.52.0/documentation/composablearchitecture/)
    * [0.50.0](https://pointfreeco.github.io/swift-composable-architecture/0.50.0/documentation/composablearchitecture/)
    * [0.49.0](https://pointfreeco.github.io/swift-composable-architecture/0.49.0/documentation/composablearchitecture/)
    * [0.48.0](https://pointfreeco.github.io/swift-composable-architecture/0.48.0/documentation/composablearchitecture/)
    * [0.47.0](https://pointfreeco.github.io/swift-composable-architecture/0.47.0/documentation/composablearchitecture/)
    * [0.46.0](https://pointfreeco.github.io/swift-composable-architecture/0.46.0/documentation/composablearchitecture/)
    * [0.45.0](https://pointfreeco.github.io/swift-composable-architecture/0.45.0/documentation/composablearchitecture/)
    * [0.44.0](https://pointfreeco.github.io/swift-composable-architecture/0.44.0/documentation/composablearchitecture/)
    * [0.43.0](https://pointfreeco.github.io/swift-composable-architecture/0.43.0/documentation/composablearchitecture/)
    * [0.42.0](https://pointfreeco.github.io/swift-composable-architecture/0.42.0/documentation/composablearchitecture/)
    * [0.41.0](https://pointfreeco.github.io/swift-composable-architecture/0.41.0/documentation/composablearchitecture/)
    * [0.40.0](https://pointfreeco.github.io/swift-composable-architecture/0.40.0/documentation/composablearchitecture/)
    * [0.39.0](https://pointfreeco.github.io/swift-composable-architecture/0.39.0/documentation/composablearchitecture/)
    * [0.38.0](https://pointfreeco.github.io/swift-composable-architecture/0.38.0/documentation/composablearchitecture/)
    </details>

    <br>

    У документації є кілька статей, які можуть бути корисними, коли ви стаєте більш знайомими з бібліотекою:

    * [З чого розпочати][getting-started-article]
    * [Керування залежностями][dependencies-article]
    * [Залежності][dependencies-article]
    * [Тестування][testing-article]
    * [Performance][performance-article]
    * [Concurrency][concurrency-article]
    * [Bindings][bindings-article]
    * [Міграція до протоколу редуктора][migrating-article]

    ## Спільнота

    @@ -610,6 +607,7 @@ let store = TestStore(initialState: Feature.State()) {

    * [Arabic](https://gist.github.com/NorhanBoghdadi/1b98d55c02b683ddef7e05c2ebcccd47)
    * [French](https://gist.github.com/nikitamounier/0e93eb832cf389db12f9a69da030a2dc)
    * [Hindi](https://gist.github.com/akashsoni01/b358ee0b3b747167964ef6946123c88d)
    * [Indonesian](https://gist.github.com/wendyliga/792ea9ac5cc887f59de70a9e39cc7343)
    * [Italian](https://gist.github.com/Bellaposa/5114e6d4d55fdb1388e8186886d48958)
    * [Japanese](https://gist.github.com/kalupas226/bdf577e4a7066377ea0a8aaeebcad428)
    @@ -693,7 +691,7 @@ The Composable Architecture була побудована на основі ід
    [mbrandonw]: https://twitter.com/mbrandonw
    [stephencelis]: https://twitter.com/stephencelis
    [tca-episode-collection]: https://www.pointfree.co/collections/composable-architecture
    [tca-tour]: https://www.pointfree.co/collections/tours/composable-architecture
    [tca-tour]: https://www.pointfree.co/collections/tours/composable-architecture-1-0
    [gh-isowords]: https://github.com/pointfreeco/isowords
    [gh-discussions]: https://github.com/pointfreeco/swift-composable-architecture/discussions
    [swift-forum]: https://forums.swift.org/c/related-projects/swift-composable-architecture
    @@ -703,5 +701,5 @@ The Composable Architecture була побудована на основі ід
    [performance-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/performance
    [concurrency-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/swiftconcurrency
    [bindings-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/bindings
    [migrating-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingtothereducerprotocol
    [meet-tca]: https://pointfreeco.github.io/swift-composable-architecture/main/tutorials/meetcomposablearchitecture
    [navigation-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/navigation
    [meet-tca]: https://pointfreeco.github.io/swift-composable-architecture/main/tutorials/meetcomposablearchitecture
  4. barabashd revised this gist May 27, 2023. 1 changed file with 0 additions and 7 deletions.
    7 changes: 0 additions & 7 deletions TCA_README_UKR.md
    Original file line number Diff line number Diff line change
    @@ -451,11 +451,6 @@ await store.send(.factAlertDismissed) {
    }
    ```

    We can also improve the ergonomics of using the `numberFact` dependency in our application. Over
    time the application may evolve into many features, and some of those features may also want access
    to `numberFact`, and explicitly passing it through all layers can get annoying. There is a process
    you can follow to “register” dependencies with the library, making them instantly available to any
    layer in the application.
    Ми також можемо поліпшити ергономіку використання залежності `numberFact` в нашому додатку. З часом
    додаток може розвиватися і містити багато фіч, і деякі з них також можуть потребувати доступу до
    `numberFact`, а явне передавання його через всі рівні може бути незручним. Існує процес, який можна
    @@ -494,8 +489,6 @@ extension DependencyValues {
    }
    ```

    With that little bit of upfront work done you can instantly start making use of the dependency in
    any feature by using the `@Dependency` property wrapper:
    Завдяки цьому невеликому початковому етапу ви можете негайно почати використовувати залежність в
    будь-якій функціональності, використовуючи обгортку властивості `@Dependency`:

  5. barabashd renamed this gist May 19, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  6. barabashd created this gist May 19, 2023.
    714 changes: 714 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,714 @@
    # The Composable Architecture

    [![CI](https://github.com/pointfreeco/swift-composable-architecture/workflows/CI/badge.svg)](https://github.com/pointfreeco/swift-composable-architecture/actions?query=workflow%3ACI)
    [![Slack](https://img.shields.io/badge/slack-chat-informational.svg?label=Slack&logo=slack)](https://www.pointfree.co/slack-invite)
    [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpointfreeco%2Fswift-composable-architecture%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/pointfreeco/swift-composable-architecture)
    [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpointfreeco%2Fswift-composable-architecture%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/pointfreeco/swift-composable-architecture)

    The Composable Architecture (скорочено TCA) - це бібліотека для побудови додатків у послідовновному
    та зрозумілому підході з урахуванням композиції, тестування та ергономіки. Вона може бути використана у
    SwiftUI, UIKit та інших фреймворках на будь-якій платформі Apple (iOS, macOS, tvOS та watchOS).

    * [Що таке Composable Architecture?](#що-таке-composable-architecture)
    * [Дізнатися більше](#дізнатися-більше)
    * [Приклади](#приклади)
    * [Базове використання](#базове-використання)
    * [Документація](#документація)
    * [Спільнота](#спільнота)
    * [Встановлення](#встановлення)
    * [Переклади](#переклади)

    ## Що таке Composable Architecture?

    Ця бібліотека надає декілька основних інструментів, що можуть використовуватися для побудови додатків
    різної цілі та складності. Вона надає підходи, які можна використовувати для вирішення багатьох проблем,
    з якими ви стикаєтеся щодня при розробці додатків, таких як:

    * **Управління станом**
    <br> Як керувати станом вашого додатку, використовуючи прості типи значень та передавати стан між
    багатьма екранами так, щоб зміни на одному екрані миттєво спостерігалися на іншому екрані.

    * **Композиція**
    <br> Як розбивати великі фічі на менші компоненти, які можуть бути витягнуті у власні ізольовані
    модулі і легко збиратися назад для формування фічі.

    * **Побічні ефекти**
    <br> Як дозволити окремим частинам додатку спілкуватися з зовнішнім світом у найтестованіший та
    зрозумілий спосіб.

    * **Тестування**
    <br> Як не лише тестувати фічу, побудовану цією архітектурою, але й писати інтеграційні тести для
    фіч, які складаються з багатьох частин, а також писати end-to-end тести, щоб розуміти, як побічні
    ефекти впливають на ваш додаток. Це дозволяє вам впевненіше гарантувати, що ваша бізнес-логіка працює
    так, як очікується.

    * **Ергономіка**
    <br> Як досягнути всього вищезазначеного за допомогою простого API з якомога меншою кількістю
    концепцій та рухомих частин.

    ## Дізнатися більше

    The Composable Architecture була розроблена протягом багатьох епізодів на [Point-Free][pointfreeco],
    відеосерії, присвяченій вивченю функціонального програмування та мові Swift, веденої
    [Брендоном Вільямсом][mbrandonw] та [Стефеном Селісом][stephencelis].

    Ви можете переглянути всі епізоди [тут][tca-episode-collection], а також присвячений [багатошаровий тур][tca-tour]
    по архітектурі з нуля.

    <a href="https://www.pointfree.co/collections/composable-architecture">
    <img alt="video poster image" src="https://i.vimeocdn.com/video/1492464398-1f5189a415136097aabf5b0b4845928bbb5d425a149069e4e0a848b67618a6f3-d?mw=1900&mh=1069&q=70" width="600">
    </a>

    ## Приклади

    [![Screen shots of example applications](https://d3rccdn33rt8ze.cloudfront.net/composable-architecture/demos.png)](./Examples)

    У цьому репозиторії є _багато_ прикладів, які демонструють, як вирішувати загальні і складні проблеми за
    допомогою Composable Architecture. Перегляньте [цю](./Examples) папку, щоб побачити їх всі, включаючи:

    * [Кейси](./Examples/CaseStudies)
    * Початок роботи
    * Ефекти
    * Навігація
    * Редуктори вищого порядку
    * Перевикористовувані компоненти
    * [Location manager](https://github.com/pointfreeco/composable-core-location/tree/main/Examples/LocationManager)
    * [Motion manager](https://github.com/pointfreeco/composable-core-motion/tree/main/Examples/MotionManager)
    * [Search](./Examples/Search)
    * [Speech Recognition](./Examples/SpeechRecognition)
    * [Tic-Tac-Toe](./Examples/TicTacToe)
    * [Todos](./Examples/Todos)
    * [Voice memos](./Examples/VoiceMemos)

    Шукаєте щось більш значуще? Перегляньте вихідний код для [isowords][gh-isowords], гри пошуку слів для iOS,
    побудованої на SwiftUI та Composable Architecture.

    ## Базове використання

    > **Примітка**
    > Для інтерактивного посібника крок за кроком обов'язково перегляньте [Meet the Composable
    > Architecture][meet-tca].

    Для побудови фічі за допомогою Composable Architecture ви визначаєте деякі типи та значення, які моделюють ваш
    домен:

    * **Стан (State)**: Тип, який описує дані, які потребні вашій фічі для виконання логіки та відображення свого
    користувацького інтерфейсу.
    * **Дія (Action)**: Тип, що представляє всі можливі дії, які можуть статися у вашій фічі, такі як дії
    користувача, сповіщення, джерела подій тощо.
    * **Редуктор (Reducer)**: Функція, що описує, як змінити поточний стан додатка до наступного стану на основі
    отриманої дії. Редуктор також відповідає за повернення будь-яких ефектів, які потрібно виконати, таких як запити API,
    що можуть бути виконані, повертаючи значення Effect.
    * **Сховише (Store)**: Середовище виконання (runtime), що фактично керую вашою фічею. Ви надсилаєте всі дії користувача в
    сховище, для того, щоб сховише могло запустити редуктор та ефекти, а також ви можете спостерігати за
    змінами стану у сховищі, щоб можна було оновлювати користувацький інтерфейс.

    Переваги цього підходу полягають у тому, що ви миттєво зможете проводити тестування вашого функціоналу,
    а також ви зможете розбивати великі та складні функціонали на менші домени, які можна злити разом.

    Як базовий приклад, розглянемо користувацький інтерфейс, що відображає число разом з кнопками "+" та "−", що
    інкрементують та декрементують число. Щоб зробити річ цікавішою, припустимо, яка при натисканні виконує запит API для отримання
    випадкового факту про це число, а потім відображає факт у вигляді сповіщення.

    Для реалізації цього функціоналу ми створюємо новий тип, який буде містити домену та поведінку цієї
    фічі, реалізував протоколу `ReducerProtocol`:

    ```swift
    import ComposableArchitecture

    struct Feature: ReducerProtocol {
    }
    ```

    Тут нам потрібно визначити тип для стану фічі, який складається з цілого числа для поточного
    лічильника і необов'язкового рядка, який представляє заголовок сповіщення, яке ми хочемо
    показати (необов'язково, оскільки `nil` означає, що сповіщення не показується):

    ```swift
    struct Feature: ReducerProtocol {
    struct State: Equatable {
    var count = 0
    var numberFactAlert: String?
    }
    }
    ```

    Також нам потрібно визначити тип для дій фічі. Є очевидні дії, такі як натискання кнопки
    зменшення, кнопки збільшення або кнопки факту. Але також є деякі не такі очевидні дії, такі як дія
    користувача, коли він закриває сповіщення, і дія, яка відбувається при отриманні відповіді від
    API-запиту факту:

    ```swift
    struct Feature: ReducerProtocol {
    struct State: Equatable { … }
    enum Action: Equatable {
    case factAlertDismissed
    case decrementButtonTapped
    case incrementButtonTapped
    case numberFactButtonTapped
    case numberFactResponse(TaskResult<String>)
    }
    }
    ```

    Потім ми реалізуємо метод `reduce`, який відповідає за обробку фактичної логіки та поведінки
    фічі. Він описує, як змінити поточний стан на наступний стан та описує, які ефекти потрібно
    виконати. Деякі дії не потребують виконання ефектів, і вони можуть повертати значення `.none`, щоб
    показати це:

    ```swift
    struct Feature: ReducerProtocol {
    struct State: Equatable { … }
    enum Action: Equatable { … }

    func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
    switch action {
    case .factAlertDismissed:
    state.numberFactAlert = nil
    return .none

    case .decrementButtonTapped:
    state.count -= 1
    return .none

    case .incrementButtonTapped:
    state.count += 1
    return .none

    case .numberFactButtonTapped:
    return .run { [count = state.count] send in
    await send(
    .numberFactResponse(
    TaskResult {
    String(
    decoding: try await URLSession.shared
    .data(from: URL(string: "http://numbersapi.com/\(count)/trivia")!).0,
    as: UTF8.self
    )
    }
    )
    )
    }

    case let .numberFactResponse(.success(fact)):
    state.numberFactAlert = fact
    return .none

    case .numberFactResponse(.failure):
    state.numberFactAlert = "Хай йому грець! Не можу загрузити число факт :("
    return .none
    }
    }
    }
    ```

    І наостанок ми визначаємо представлення, яке відображає фічу. Воно зберігає посилання на `StoreOf<Feature>`,
    щоб відстежувати усі зміни стану і здійснювати перерендеринг, і ми можемо надсилати усі дії користувача в сховище,
    щоб змінювати стан. Також ми повинні створити обгортку структури навколо факту сповіщення, щоб зробити його `Identifiable`,
    що вимагає використання модифікатора `.alert`:

    ```swift
    struct FeatureView: View {
    let store: StoreOf<Feature>

    var body: some View {
    WithViewStore(self.store, observe: { $0 }) { viewStore in
    VStack {
    HStack {
    Button("−") { viewStore.send(.decrementButtonTapped) }
    Text("\(viewStore.count)")
    Button("+") { viewStore.send(.incrementButtonTapped) }
    }

    Button("Number fact") { viewStore.send(.numberFactButtonTapped) }
    }
    .alert(
    item: viewStore.binding(
    get: { $0.numberFactAlert.map(FactAlert.init(title:)) },
    send: .factAlertDismissed
    ),
    content: { Alert(title: Text($0.title)) }
    )
    }
    }
    }

    struct FactAlert: Identifiable {
    var title: String
    var id: String { self.title }
    }
    ```

    Також нескладно мати контролер UIKit, який працює на основі цього cховища. Ви підписуєтеся на сховище у `viewDidLoad`,
    щоб оновлювати інтерфейс користувача та показувати сповіщення. Код трохи довший, ніж версія для SwiftUI, тому ми згорнули його тут:

    <details>
    <summary>Тицьни щоб розкрити!</summary>

    ```swift
    class FeatureViewController: UIViewController {
    let viewStore: ViewStoreOf<Feature>
    var cancellables: Set<AnyCancellable> = []

    init(store: StoreOf<Feature>) {
    self.viewStore = ViewStore(store)
    super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
    super.viewDidLoad()

    let countLabel = UILabel()
    let incrementButton = UIButton()
    let decrementButton = UIButton()
    let factButton = UIButton()

    // Пропущено: Додаєм сабвьюхи і встановлюємо констрейнти...

    self.viewStore.publisher
    .map { "\($0.count)" }
    .assign(to: \.text, on: countLabel)
    .store(in: &self.cancellables)

    self.viewStore.publisher.numberFactAlert
    .sink { [weak self] numberFactAlert in
    let alertController = UIAlertController(
    title: numberFactAlert, message: nil, preferredStyle: .alert
    )
    alertController.addAction(
    UIAlertAction(
    title: "Окі докі",
    style: .default,
    handler: { _ in self?.viewStore.send(.factAlertDismissed) }
    )
    )
    self?.present(alertController, animated: true, completion: nil)
    }
    .store(in: &self.cancellables)
    }

    @objc private func incrementButtonTapped() {
    self.viewStore.send(.incrementButtonTapped)
    }
    @objc private func decrementButtonTapped() {
    self.viewStore.send(.decrementButtonTapped)
    }
    @objc private func factButtonTapped() {
    self.viewStore.send(.numberFactButtonTapped)
    }
    }
    ```
    </details>

    Коли ми готові відобразити це представлення, наприклад, в точці входу програми, ми можемо створити
    сховище. Це можна зробити, вказавши початковий стан, з якого почнеться програма, а також редуктор,
    який буде приводити програму в дію:

    ```swift
    import ComposableArchitecture

    @main
    struct MyApp: App {
    var body: some Scene {
    WindowGroup {
    FeatureView(
    store: Store(initialState: Feature.State()) {
    Feature()
    }
    )
    }
    }
    }
    ```

    І цього вистачить, щоб отримати щось на екрані, з чим можна поекспериментувати. Звичайно, це трохи
    більше кроків, ніж якщо ви робили б це в стандартний спосіб SwiftUI, але є кілька переваг. Він надає
    нам послідовний спосіб застосування мутацій стану, замість розсіювання логіки в деяких спостережуваних
    об'єктах та у різних замиканнях дій компонентів користувацького інтерфейсу. Він також надає нам
    лаконічний спосіб вираження побічних ефектів. І ми можемо негайно тестувати цю логіку, включаючи ефекти,
    без додаткових зусиль.

    ### Тестування

    > Для докладнішої інформації щодо тестування перегляньте окрему статтю про [тестування][testing-article].

    Щоб тестів, використовуйте `TestStore`, який можна створити з тією самою інформацією, що й `Store`, але
    він робить додаткову роботу, щоб дозволити вам перевірити, як розвивається ваша фіча при відправці дій:

    ```swift
    @MainActor
    func testFeature() async {
    let store = TestStore(initialState: Feature.State()) {
    Feature()
    }
    }
    ```

    Після створення тестового сховища ми можемо використовувати його для перевірки послідовності кроків
    користувача. На кожному кроці ми повинні переконатися, що стан змінився так, як очікувалося. Наприклад,
    ми можемо симулювати послідовність натискань на кнопки "збільшити" і "зменшити":

    ```swift
    // Тест, що тицьнувши на "збільшити" і "зменшити" змінюється лічильник
    await store.send(.incrementButtonTapped) {
    $0.count = 1
    }
    await store.send(.decrementButtonTapped) {
    $0.count = 0
    }
    ```

    Далі, якщо крок спричиняє виконання ефекту, який повертає дані назад до сховища, ми повинні перевірити
    його. Наприклад, якщо ми симулюємо натискання кнопки факту, ми очікуємо отримати відповідь з фактом,
    яке потім призведе до показу cповіщення:

    ```swift
    await store.send(.numberFactButtonTapped)

    await store.receive(.numberFactResponse(.success(???))) {
    $0.numberFactAlert = ???
    }
    ```

    Однак, як ми можемо знати, який факт буде надісланий нам?

    Наразі наш редуктор використовує ефект, який звертається до реального світу для отримання доступу до
    сервера API, і це означає, що ми не маємо можливості контролювати його поведінку. Ми залежимо від нашого
    інтернет-з'єднання та доступності сервера API, щоб написати цей тест.

    Краще було б передавати цю залежність до редуктора, щоб ми могли використовувати реальну залежність під
    час виконання програми на пристрої, а для тестів використовувати підроблену залежність. Ми можемо зробити
    це, додавши властивість до редуктора `Feature`:

    ```swift
    struct Feature: ReducerProtocol {
    let numberFact: (Int) async throws -> String
    }
    ```

    Таким чином, ми можемо використовувати у `reduce` імплементацію:

    ```swift
    case .numberFactButtonTapped:
    return .run { [count = state.count] send in
    await send(
    .numberFactResponse(TaskResult { try await self.numberFact(count) })
    )
    }
    ```

    І на точці входу додатку ми можемо надати версію залежності, яка фактично взаємодіє з реальним сервером API:

    ```swift
    @main
    struct MyApp: App {
    var body: some Scene {
    FeatureView(
    store: Store(
    initialState: Feature.State(),
    reducer: Feature(
    numberFact: { number in
    let (data, _) = try await URLSession.shared
    .data(from: .init(string: "http://numbersapi.com/\(number)")!)
    return String(decoding: data, as: UTF8.self)
    }
    )
    )
    )
    }
    }
    ```

    Але в тестах ми можемо використовувати імітовану залежність, яка негайно повертає визначений,
    передбачуваний факт:

    ```swift
    @MainActor
    func testFeature() async {
    let store = TestStore(initialState: Feature.State()) {
    Feature(numberFact: { "\($0) is a good number Brent" })
    }
    }
    ```

    Завдяки цьому невеликому етапу роботи ми можемо завершити тест, симулюючи натискання користувачем на
    кнопку факту, отримання відповіді від залежності для спрацювання сповіщення, а потім закриття сповіщенняЖ

    ```swift
    await store.send(.numberFactButtonTapped)

    await store.receive(.numberFactResponse(.success("0 це є курва, неймовірне число, Остапе"))) {
    $0.numberFactAlert = "0 це є курва, неймовірне число, Остапе"
    }

    await store.send(.factAlertDismissed) {
    $0.numberFactAlert = nil
    }
    ```

    We can also improve the ergonomics of using the `numberFact` dependency in our application. Over
    time the application may evolve into many features, and some of those features may also want access
    to `numberFact`, and explicitly passing it through all layers can get annoying. There is a process
    you can follow to “register” dependencies with the library, making them instantly available to any
    layer in the application.
    Ми також можемо поліпшити ергономіку використання залежності `numberFact` в нашому додатку. З часом
    додаток може розвиватися і містити багато фіч, і деякі з них також можуть потребувати доступу до
    `numberFact`, а явне передавання його через всі рівні може бути незручним. Існує процес, який можна
    слідувати, щоб "зареєструвати" залежності з бібліотекою, що дозволить їм негайно стати доступними в
    будь-якому рівні застосунку.

    > Для більш детальної інформації, дивитися присвячену статтю про [залежності][dependencies-article].

    Ми можемо розпочати, обгортаючи функціональності факту про число в новий тип:

    ```swift
    struct NumberFactClient {
    var fetch: (Int) async throws -> String
    }
    ```

    А потім реєструємо цей тип у системі керування залежностями, реалізовуючи протоколу `DependencyKey`, який
    вимагає вказати живе значення для використання під час запуску додатка на симуляторах або на девайсах:

    ```swift
    extension NumberFactClient: DependencyKey {
    static let liveValue = Self(
    fetch: { number in
    let (data, _) = try await URLSession.shared
    .data(from: .init(string: "http://numbersapi.com/\(number)")!)
    return String(decoding: data, as: UTF8.self)
    }
    )
    }

    extension DependencyValues {
    var numberFact: NumberFactClient {
    get { self[NumberFactClient.self] }
    set { self[NumberFactClient.self] = newValue }
    }
    }
    ```

    With that little bit of upfront work done you can instantly start making use of the dependency in
    any feature by using the `@Dependency` property wrapper:
    Завдяки цьому невеликому початковому етапу ви можете негайно почати використовувати залежність в
    будь-якій функціональності, використовуючи обгортку властивості `@Dependency`:

    ```diff
    struct Feature: ReducerProtocol {
    - let numberFact: (Int) async throws -> String
    + @Dependency(\.numberFact) var numberFact


    - try await self.numberFact(count)
    + try await self.numberFact.fetch(count)
    }
    ```

    Цей код працює точно так само, як і раніше, але вам більше не потрібно явно передавати залежність при
    створенні редуктора функціональності. При запуску програми у превью, на симуляторі або на пристрої,
    жива залежність буде надана редуктору, а під час тестування буде надано тестову залежність.

    Це означає, що точка входу в додаток більше не потребує створення залежностей:

    ```swift
    @main
    struct MyApp: App {
    var body: some Scene {
    FeatureView(
    store: Store(initialState: Feature.State()) {
    Feature()
    }
    )
    }
    }
    ```

    І тестовий об'єкт-сховище може бути створений без вказівки будь-яких залежностей, але ви все ще
    можете перевизначити будь-яку залежність, яка вам потрібна для тестування:

    ```swift
    let store = TestStore(initialState: Feature.State()) {
    Feature()
    } withDependencies: {
    $0.numberFact.fetch = { "\($0) це є курва неймовірне число, Остапе" }
    }

    ```

    Це основи створення та тестування функціональності у Composable Architecture. Є ще багато інших
    речей, які варто дослідити, таких як композиція, модульність, адаптивність і складні ефекти.
    У каталозі [Examples](./Examples) є кілька проектів, які можна дослідити, щоб побачити більш
    продвинуті використання.

    ## Документація

    Документація для релізів та гілки `main` доступна тут:

    * [`main`](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture)
    * [0.53.0](https://pointfreeco.github.io/swift-composable-architecture/0.53.0/documentation/composablearchitecture/)

    <details>
    <summary>
    Інші версії
    </summary>

    * [0.52.0](https://pointfreeco.github.io/swift-composable-architecture/0.52.0/documentation/composablearchitecture/)
    * [0.50.0](https://pointfreeco.github.io/swift-composable-architecture/0.50.0/documentation/composablearchitecture/)
    * [0.49.0](https://pointfreeco.github.io/swift-composable-architecture/0.49.0/documentation/composablearchitecture/)
    * [0.48.0](https://pointfreeco.github.io/swift-composable-architecture/0.48.0/documentation/composablearchitecture/)
    * [0.47.0](https://pointfreeco.github.io/swift-composable-architecture/0.47.0/documentation/composablearchitecture/)
    * [0.46.0](https://pointfreeco.github.io/swift-composable-architecture/0.46.0/documentation/composablearchitecture/)
    * [0.45.0](https://pointfreeco.github.io/swift-composable-architecture/0.45.0/documentation/composablearchitecture/)
    * [0.44.0](https://pointfreeco.github.io/swift-composable-architecture/0.44.0/documentation/composablearchitecture/)
    * [0.43.0](https://pointfreeco.github.io/swift-composable-architecture/0.43.0/documentation/composablearchitecture/)
    * [0.42.0](https://pointfreeco.github.io/swift-composable-architecture/0.42.0/documentation/composablearchitecture/)
    * [0.41.0](https://pointfreeco.github.io/swift-composable-architecture/0.41.0/documentation/composablearchitecture/)
    * [0.40.0](https://pointfreeco.github.io/swift-composable-architecture/0.40.0/documentation/composablearchitecture/)
    * [0.39.0](https://pointfreeco.github.io/swift-composable-architecture/0.39.0/documentation/composablearchitecture/)
    * [0.38.0](https://pointfreeco.github.io/swift-composable-architecture/0.38.0/documentation/composablearchitecture/)
    </details>

    <br>

    У документації є кілька статей, які можуть бути корисними, коли ви стаєте більш знайомими з бібліотекою:

    * [З чого розпочати][getting-started-article]
    * [Керування залежностями][dependencies-article]
    * [Тестування][testing-article]
    * [Performance][performance-article]
    * [Concurrency][concurrency-article]
    * [Bindings][bindings-article]
    * [Міграція до протоколу редуктора][migrating-article]

    ## Спільнота

    Якщо ви бажаєте обговорити Composable Architecture або мати питання щодо використання його для вирішення
    певної проблеми, є кілька місць, де ви можете обговорити це з іншими прихильниками [Point-Free](http://www.pointfree.co):

    * Для довгих обговорень ми рекомендуємо перейти до вкладки [обговорення][gh-discussions] цього репозиторію.
    * Для неформального спілкування ми рекомендуємо [Point-Free Community slack](http://pointfree.co/slack-invite).

    ## Встановлення

    Ви можете додати ComposableArchitecture як залежність пакету до вашого проекту в Xcode.

    1. У меню **File** в Xcode виберіть **Add Packages...**
    2. У полі введення URL-адреси репозиторію пакету введіть "https://github.com/pointfreeco/swift-composable-architecture"
    3. Залежно від структури вашого проекту:
    - Якщо у вас є одит таргет додатку, який потребує доступу до бібліотеки, додайте
    **ComposableArchitecture** безпосередньо до вашого додатку.
    - Якщо ви хочете використовувати бібліотеку з кількох таргетів Xcode або комбінувати Xcode та
    SPM таргети, вам потрібно створити спільний фреймворк, який залежить від **ComposableArchitecture** і
    потім залежити від цього фреймворку в усіх ваших таргетах. Ви можете переглянути демонстраційній додаток
    [Tic-Tac-Toe](./Examples/TicTacToe) для прикладу такого підходу, де різні фічі розбиті на модулі та
    використовують статичну бібліотеку за допомогою пакету Swift **tic-tac-toe**.

    ## Переклади

    Переклади цього README були зроблені учасниками спільноти:

    * [Arabic](https://gist.github.com/NorhanBoghdadi/1b98d55c02b683ddef7e05c2ebcccd47)
    * [French](https://gist.github.com/nikitamounier/0e93eb832cf389db12f9a69da030a2dc)
    * [Indonesian](https://gist.github.com/wendyliga/792ea9ac5cc887f59de70a9e39cc7343)
    * [Italian](https://gist.github.com/Bellaposa/5114e6d4d55fdb1388e8186886d48958)
    * [Japanese](https://gist.github.com/kalupas226/bdf577e4a7066377ea0a8aaeebcad428)
    * [Korean](https://gist.github.com/pilgwon/ea05e2207ab68bdd1f49dff97b293b17)
    * [Polish](https://gist.github.com/MarcelStarczyk/6b6153051f46912a665c32199f0d1d54)
    * [Portuguese](https://gist.github.com/SevioCorrea/2bbf337cd084a58c89f2f7f370626dc8)
    * [Simplified Chinese](https://gist.github.com/sh3l6orrr/10c8f7c634a892a9c37214f3211242ad)
    * [Spanish](https://gist.github.com/pitt500/f5e32fccb575ce112ffea2827c7bf942)

    Якшо б ви хотіли внести свій вклад у переклад, будь ласка [створіть PR](https://github.com/pointfreeco/swift-composable-architecture/edit/main/README.md)
    з посиланням на [Gist](https://gist.github.com)!

    ## FAQ

    * Як Composable Architecture у порівнянні Elm, Redux та іншим бібліотекам?
    <details>
    <summary>Тицьни щоб відкрити відповіль</summary>
    The Composable Architecture (TCA) базується на ідеях, популяризованих Elm Architecture (TEA)
    та Redux, але створений так, щоб відчуватися комфортно у мові Swift та на платформах Apple.

    В деяких аспектах TCA є трохи більш визначеною, ніж інші бібліотеки. Наприклад, Redux не накладає
    вимог щодо того, як виконувати побічні ефекти, але в TCA всі побічні ефекти повинні бути сформовані
    у типі `Effect` та повернуті з редуктора.

    В інших аспектах TCA є трохи більш гнучкою, ніж інші бібліотеки. Наприклад, Elm контролює, які типи
    ефектів можна створювати за допомогою типу `Cmd`, але TCA дозволяє будь-які типи ефектів, оскільки
    Effect реалізовує протокол Combine `Publisher`.

    І, нарешті, є певні речі, на яких TCA надає велику увагу, а які не є основними для Redux, Elm та
    більшості інших бібліотек. Наприклад, композиція є дуже важливим аспектом TCA, яка полягає в процесі
    розбиття великих функціональностей на менші одиниці, які можуть бути з'єднані разом. Це досягається за
    допомогою побудови редукторів та операторів, таких як `Scope`, і допомагає управляти складними
    функціональностями, а також модуляризацією для кращої ізольованості коду та поліпшенням часу компіляції.
    </details>

    ## Подяки

    Наступні люди надали зворотній зв'язок про бібліотеку на ранніх етапах та допомогли зробити бібліотеку
    такою яка вона є сьогодні:

    Paul Colton, Kaan Dedeoglu, Matt Diephouse, Josef Doležal, Eimantas, Matthew Johnson, George
    Kaimakas, Nikita Leonov, Christopher Liscio, Jeffrey Macko, Alejandro Martinez, Shai Mishali, Willis
    Plummer, Simon-Pierre Roy, Justin Price, Sven A. Schmidt, Kyle Sherman, Petr Šíma, Jasdev Singh,
    Maxim Smirnov, Ryan Stone, Daniel Hollis Tavares, та усім підписничам сім'ї [Point-Free][pointfreeco] 😁.

    Особлива подяка [Chris Liscio](https://twitter.com/liscio), який допоміг нам пропрацювати багато дивних
    SwiftUI особливостей та допоміг уточнити остаточний API.

    І подяка [Shai Mishali](https://github.com/freak4pc) і проект
    [CombineCommunity](https://github.com/CombineCommunity/CombineExt/), з якого ми взяли їх реалізацію
    `Publishers.Create`, яку ми використали в `Effect` щоб полегшити поєднання делегату та API на замиканнях,
    що полегшило роботу інтерфейсу зі сторонніми фрейморками.

    ## Інші бібліотеки

    The Composable Architecture була побудована на основі ідей, розпочатих іншими бібліотеками,
    зокрема [Elm](https://elm-lang.org) та [Redux](https://redux.js.org/).

    Також існує багато бібліотек архітектури в Swift та iOS-спільноті. Кожна з цих бібліотек має
    свій набір пріоритетів та компромісів, які відрізняються від Composable Architecture.

    * [RIBs](https://github.com/uber/RIBs)
    * [Loop](https://github.com/ReactiveCocoa/Loop)
    * [ReSwift](https://github.com/ReSwift/ReSwift)
    * [Workflow](https://github.com/square/workflow)
    * [ReactorKit](https://github.com/ReactorKit/ReactorKit)
    * [RxFeedback](https://github.com/NoTests/RxFeedback.swift)
    * [Mobius.swift](https://github.com/spotify/mobius.swift)
    * <details>
    <summary>Та інші</summary>

    * [Fluxor](https://github.com/FluxorOrg/Fluxor)
    * [PromisedArchitectureKit](https://github.com/RPallas92/PromisedArchitectureKit)
    </details>

    ## Права

    Ця бібліотека випущена під MIT правами. Дивись [Права](LICENSE) за деталями.

    [pointfreeco]: https://www.pointfree.co
    [mbrandonw]: https://twitter.com/mbrandonw
    [stephencelis]: https://twitter.com/stephencelis
    [tca-episode-collection]: https://www.pointfree.co/collections/composable-architecture
    [tca-tour]: https://www.pointfree.co/collections/tours/composable-architecture
    [gh-isowords]: https://github.com/pointfreeco/isowords
    [gh-discussions]: https://github.com/pointfreeco/swift-composable-architecture/discussions
    [swift-forum]: https://forums.swift.org/c/related-projects/swift-composable-architecture
    [testing-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/testing
    [dependencies-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/dependencymanagement
    [getting-started-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/gettingstarted
    [performance-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/performance
    [concurrency-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/swiftconcurrency
    [bindings-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/bindings
    [migrating-article]: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingtothereducerprotocol
    [meet-tca]: https://pointfreeco.github.io/swift-composable-architecture/main/tutorials/meetcomposablearchitecture