Last active
August 21, 2025 12:41
-
-
Save barabashd/33b64676195ce41f4bb73c327ea512a8 to your computer and use it in GitHub Desktop.
Revisions
-
barabashd revised this gist
May 12, 2024 . 1 changed file with 5 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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/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]. Щоб тестів, використовуйте `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 [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 -
barabashd revised this gist
May 12, 2024 . 1 changed file with 157 additions and 143 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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`: ```swift import ComposableArchitecture @Reducer struct Feature { } ``` Тут нам потрібно визначити тип для стану фічі, який складається з цілого числа для поточного лічильника і необов'язкового рядка, який представляє відображення факторіалу: ```swift @Reducer struct Feature { @ObservableState struct State: Equatable { var count = 0 var numberFact: String? } } ``` > [!Нотаточка] > Ми застосували макро `@ObservableState` до `State`, для того щоб використати > механізм спостерігача в бібліотеці. Також нам потрібно визначити тип для дій фічі. Є очевидні дії, такі як натискання кнопки зменшення, кнопки збільшення або кнопки факту. Але також є деякі не такі очевидні дії, такі як дія, яка відбувається при отриманні відповіді від API-запиту факту: ```swift @Reducer struct Feature { @ObservableState struct State: Equatable { … } enum Action { case decrementButtonTapped case incrementButtonTapped case numberFactButtonTapped @@ -151,107 +158,96 @@ struct Feature: Reducer { } ``` Потім ми реалізуємо змінну `body`, яка відповідає за створення фактичної логіки та поведінки фічі. В ній ми можемо використати `Reduce` щоб описаи, як змінити поточний стан на наступний стан, та які ефекти потрібно виконати. Деякі дії не потребують виконання ефектів, і вони можуть повертати значення `.none`, щоб показати це: ```swift @Reducer struct Feature { @ObservableState struct State: Equatable { … } enum Action { /* ... */ } 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 .numberFactResponse(.failure): state.numberFact = "Хай йому грець! Не можу загрузити число факт :(" return .none } } } } ``` І наостанок ми визначаємо представлення, яке відображає фічу. Воно зберігає посилання на `StoreOf<Feature>`, щоб відстежувати усі зміни стану і здійснювати перерендеринг, і ми можемо надсилати усі дії користувача в сховище, щоб змінювати стан: ```swift struct FeatureView: View { let store: StoreOf<Feature> var body: some View { 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) } } } } ``` Також нескладно мати контролер UIKit, який працює на основі цього cховища. Ви можете спостерігати за змінами стану в сховищі у `viewDidLoad`, і тоді наповнити UI компонентів даними зі сховища. Код трохи довший, ніж версія для SwiftUI, тому ми згорнули його тут: <details> <summary>Тицьни щоб розкрити!</summary> ```swift class FeatureViewController: UIViewController { let store: StoreOf<Feature> var cancellables: Set<AnyCancellable> = [] init(store: StoreOf<Feature>) { self.store = store super.init(nibName: nil, bundle: nil) } @@ -263,42 +259,29 @@ struct FactAlert: Identifiable { super.viewDidLoad() let countLabel = UILabel() let decrementButton = UIButton() let incrementButton = UIButton() let factLabel = UILabel() // Пропущено: Додаєм сабвьюхи і встановлюємо констрейнти... observe { [weak self] in guard let self else { return } countLabel.text = "\(self.store.text)" factLabel.text = self.store.numberFact } } @objc private func incrementButtonTapped() { self.store.send(.incrementButtonTapped) } @objc private func decrementButtonTapped() { self.store.send(.decrementButtonTapped) } @objc private func factButtonTapped() { self.store.send(.numberFactButtonTapped) } } ``` @@ -334,7 +317,7 @@ struct MyApp: App { ### Тестування > [!Нотаточка] > Для докладнішої інформації щодо тестування перегляньте окрему статтю про [тестування][testing-article]. Щоб тестів, використовуйте `TestStore`, який можна створити з тією самою інформацією, що й `Store`, але @@ -365,13 +348,13 @@ await store.send(.decrementButtonTapped) { Далі, якщо крок спричиняє виконання ефекту, який повертає дані назад до сховища, ми повинні перевірити його. Наприклад, якщо ми симулюємо натискання кнопки факту, ми очікуємо отримати відповідь з фактом, яке потім спричинить зміну стану `numberFact`: ```swift await store.send(.numberFactButtonTapped) await store.receive(\.numberFactResponse) { $0.numberFact = ??? } ``` @@ -386,7 +369,8 @@ await store.receive(.numberFactResponse(.success(???))) { це, додавши властивість до редуктора `Feature`: ```swift @Reducer struct Feature { let numberFact: (Int) async throws -> String … } @@ -409,18 +393,20 @@ case .numberFactButtonTapped: @main struct MyApp: App { var body: some Scene { 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) { $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 @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 { 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.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/) </details> <br> @@ -574,9 +572,10 @@ let store = TestStore(initialState: Feature.State()) { * [З чого розпочати][getting-started-article] * [Залежності][dependencies-article] * [Тестування][testing-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 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/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 обгортається навколо асинхронної операції. І, нарешті, є певні речі, на яких 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 -
barabashd revised this gist
Aug 17, 2023 . 1 changed file with 30 additions and 32 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,7 +5,7 @@ [](https://swiftpackageindex.com/pointfreeco/swift-composable-architecture) [](https://swiftpackageindex.com/pointfreeco/swift-composable-architecture) 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/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 для отримання випадкового факту про це число, а потім відображає факт у вигляді сповіщення. Для реалізації цього функціоналу ми створюємо новий тип, який буде містити домен та поведінку цієї фічі, реалізував протокол `Reducer`: ```swift import ComposableArchitecture struct Feature: Reducer { } ``` @@ -125,7 +125,7 @@ struct Feature: ReducerProtocol { показати (необов'язково, оскільки `nil` означає, що сповіщення не показується): ```swift struct Feature: Reducer { struct State: Equatable { var count = 0 var numberFactAlert: String? @@ -139,7 +139,7 @@ struct Feature: ReducerProtocol { API-запиту факту: ```swift struct Feature: Reducer { struct State: Equatable { … } enum Action: Equatable { case factAlertDismissed @@ -157,11 +157,11 @@ struct Feature: ReducerProtocol { показати це: ```swift struct Feature: Reducer { struct State: Equatable { … } enum Action: Equatable { … } 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, 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: Reducer { let numberFact: (Int) async throws -> String … } @@ -457,7 +458,9 @@ await store.send(.factAlertDismissed) { слідувати, щоб "зареєструвати" залежності з бібліотекою, що дозволить їм негайно стати доступними в будь-якому рівні застосунку. > **Нотаточка** > Для більш детальної інформації, дивитися присвячену статтю про > [залежності][dependencies-article]. Ми можемо розпочати, обгортаючи функціональності факту про число в новий тип: @@ -467,7 +470,7 @@ struct NumberFactClient { } ``` А потім реєструємо цей тип у системі керування залежностями, реалізовуючи протокол `DependencyKey`, який вимагає вказати живе значення для використання під час запуску додатка на симуляторах або на девайсах: ```swift @@ -493,7 +496,7 @@ extension DependencyValues { будь-якій функціональності, використовуючи обгортку властивості `@Dependency`: ```diff 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) * [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/) </details> <br> У документації є кілька статей, які можуть бути корисними, коли ви стаєте більш знайомими з бібліотекою: * [З чого розпочати][getting-started-article] * [Залежності][dependencies-article] * [Тестування][testing-article] * [Performance][performance-article] * [Concurrency][concurrency-article] * [Bindings][bindings-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-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 [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 -
barabashd revised this gist
May 27, 2023 . 1 changed file with 0 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -451,11 +451,6 @@ await store.send(.factAlertDismissed) { } ``` Ми також можемо поліпшити ергономіку використання залежності `numberFact` в нашому додатку. З часом додаток може розвиватися і містити багато фіч, і деякі з них також можуть потребувати доступу до `numberFact`, а явне передавання його через всі рівні може бути незручним. Існує процес, який можна @@ -494,8 +489,6 @@ extension DependencyValues { } ``` Завдяки цьому невеликому початковому етапу ви можете негайно почати використовувати залежність в будь-якій функціональності, використовуючи обгортку властивості `@Dependency`: -
barabashd renamed this gist
May 19, 2023 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
barabashd created this gist
May 19, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,714 @@ # The Composable Architecture [](https://github.com/pointfreeco/swift-composable-architecture/actions?query=workflow%3ACI) [](https://www.pointfree.co/slack-invite) [](https://swiftpackageindex.com/pointfreeco/swift-composable-architecture) [](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> ## Приклади [](./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