### Install CoreFlow Xcode Templates Source: https://github.com/choijunyeong-company/coreflow-c7t/blob/main/tooling/README.md Run this script to install the CoreFlow Xcode templates. Templates will be placed in `~/Library/Developer/Xcode/Templates/File Templates/CoreFlow/`. ```bash ./install-xcode-template.sh ``` -------------------------------- ### Install CoreFlow Xcode File Template Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Install the Xcode file template to scaffold new CoreFlow units from File → New → File... ```bash git clone https://github.com/choijunyeong-company/CoreFlow.git cd CoreFlow/tooling ./install-xcode-template.sh ``` -------------------------------- ### Define and Implement Multi-Step Workflows with Procedures Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Define step protocols and implement them on a Core type. Start the procedure from Core.didBecomeActive() or reduce(...). Store the returned Procedure instance to keep the workflow alive. ```swift import CoreFlow import Combine // 1. Define Step protocols protocol RootProcedureStep { func waitForLogin() -> AnyPublisher<(RootProcedureStep, User), Never> func routeToMain(user: User) } // 2. Define the Procedure final class LaunchProcedure: Procedure, @unchecked Sendable { override init() { super.init() onStep { root in root.waitForLogin() // waits until loginStepFinished emits } .finalStep { root, user in root.routeToMain(user: user) // no further steps } } } // 3. Implement the Step protocol on Core extension RootCore: RootProcedureStep { // Subject driven by LoginListener callback private var loginStepFinished: CurrentValueSubject { _loginStepFinished } func waitForLogin() -> AnyPublisher<(RootProcedureStep, User), Never> { router?.routeToLogin() return loginStepFinished .compactMap(\.self) .map { user in (self as RootProcedureStep, user) } .eraseToAnyPublisher() } func routeToMain(user: User) { router?.routeToMain(user: user) } } extension RootCore: LoginListener { func loginFinished(user: User) { router?.dismissLogin() _loginStepFinished.send(user) // triggers next Procedure step } } // 4. Start the Procedure from Core.didBecomeActive() or reduce(...) final class RootCore: Core { private let _loginStepFinished = CurrentValueSubject(nil) private var launchProcedure: LaunchProcedure? override func didBecomeActive() { let proc = LaunchProcedure() launchProcedure = proc // must retain proc.start(self) { print("Launch workflow complete") } } } ``` -------------------------------- ### Implement LoginCore with State Reduction Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Implements a `LoginCore` by subclassing `Core`. It defines dependencies using `@Autowired` and overrides `reduce(state:action:)` to handle actions and return `Effect`s for side effects. `didBecomeActive` is called when the Core is ready. ```swift import CoreFlow // --- Types --- enum LoginAction { case viewDidLoad case loginButtonTapped case _loginFinished(User) // internal derived action (convention: _ prefix) } struct LoginState: Equatable { var isLoading = false var loggedInUser: User? var buttonTitle: String = "" } // --- Core Implementation --- final class LoginCore: Core { // Dependencies injected via @Autowired (resolved from ServiceLocator at init time) @Autowired var loginService: LoginService // Wires to parent Flow for upward communication weak var listener: LoginListener? // Wires to owning Flow for navigation requests weak var router: LoginRouting? override func didBecomeActive() { // Called by Flow immediately after Core is created send(.viewDidLoad) } override func reduce(state: inout LoginState, action: LoginAction) -> Effect { switch action { case .viewDidLoad: state.buttonTitle = "Tap to log in" return .none case .loginButtonTapped: state.isLoading = true // .run launches a detached async task; `send` re-enters the action stream return .run { [weak self] send in guard let self else { return } let user = await loginService.login() await send(._loginFinished(user)) } case ._loginFinished(let user): state.isLoading = false state.loggedInUser = user listener?.loginFinished(user: user) // notify parent return .none } } } ``` -------------------------------- ### Define Login Flow with Core and Screen Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Subclass Flow to manage Core and Screen lifecycles. Override createCore() and createScreen() to initialize components and wire listeners and routers. Child flows are retained as properties to build the feature tree. ```swift import CoreFlow import UIKit // Routing protocol — implemented by Flow, called from Core protocol LoginRouting: AnyObject { func routeToPasswordReset() } // Listener protocol — implemented by parent Core, called from child Core protocol LoginListener: AnyObject { func loginFinished(user: User) } final class LoginFlow: Flow { // Child flows are held here to keep them alive private var passwordResetFlow: PasswordResetFlow? private weak var listener: LoginListener? init(listener: LoginListener) { self.listener = listener super.init() } // Called once when `flow.core` is first accessed (lazy) override func createCore() -> LoginCore { let core = LoginCore(initialState: .init()) core.listener = listener // upward communication core.router = self // navigation requests come back here return core } // Called once when `flow.screen` is first accessed (lazy) override func createScreen() -> LoginScreen { let screen = LoginScreen(reactor: core) screen.bind() return screen } } // MARK: - LoginRouting extension LoginFlow: LoginRouting { func routeToPasswordReset() { let flow = PasswordResetFlow(listener: core) passwordResetFlow = flow // retain child flow screen.present(flow.screen, animated: true) } } ``` -------------------------------- ### Implement Logic-Only Features with ScreenLessCore Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Use ScreenLessCore and ScreenLessFlow for features without UI, such as background services. ScreenLessCore provides lifecycle hooks (didBecomeActive, willResignActive) but no Action or State. ScreenLessFlow participates in LeakDetector tracking. ```swift import CoreFlow // Logic-only core — no Action/State unidirectional flow final class AnalyticsCore: ScreenLessCore { @Autowired private var tracker: AnalyticsTracker override func didBecomeActive() { tracker.start() } override func willResignActive() { tracker.stop() } } // Logic-only flow — Screen type is UIViewController (never presented) final class AnalyticsFlow: ScreenLessFlow { override func createCore() -> AnalyticsCore { AnalyticsCore() } } // Usage in a parent Flow: // analyticsFlow = AnalyticsFlow() // retains AnalyticsCore alive ``` -------------------------------- ### Define Login Actions and State for Core Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Defines the `Action` enum and `State` struct for a `LoginCore`. Actions represent events, and State holds the UI data. Internal actions are conventionally prefixed with an underscore. ```swift enum LoginAction { case viewDidLoad case loginButtonTapped case _loginFinished(User) // internal derived action (convention: _ prefix) } struct LoginState: Equatable { var isLoading = false var loggedInUser: User? var buttonTitle: String = "" } ``` -------------------------------- ### Bind UI Events and State in Login Screen Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Screen is a UIViewController subclass that binds UI elements to Core actions and state. Use observeState() for single key paths and observeLatestStates() for combined key paths. Forward UI events as actions using map(). ```swift import CoreFlow import UIKit final class LoginScreen: UIViewController, @MainActor Screenable { typealias Action = LoginAction typealias State = LoginState let reactor: AnyReactor private let loginButton = UIButton(type: .system) private let loadingView = UIActivityIndicatorView(style: .medium) private let statusLabel = UILabel() init(reactor: any Reactable) { self.reactor = reactor.eraseToAnyReactor() super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { nil } /// Called by Flow after init — wires state → UI and UI → actions func bind() { // Single state key path observeState(\.buttonTitle, receiver: self) { screen, title in screen.loginButton.setTitle(title, for: .normal) } // Combine two key paths observeLatestStates(\.isLoading, \.loggedInUser, receiver: self) { screen, loading, user in screen.loadingView.isHidden = !loading if loading { screen.loadingView.startAnimating() } } // Forward UI events as actions forward(actions: [ map(loginButton.touchUpInside, to: .loginButtonTapped) ]) } override func viewDidLoad() { super.viewDidLoad() layoutUI() reactor.send(.viewDidLoad) } private func layoutUI() { /* Auto Layout setup */ } } // Xcode preview support #Preview { LoginScreen(reactor: PreviewReactor(initialState: .init(buttonTitle: "Log in"))) } ``` -------------------------------- ### Dependency Injection with ServiceLocator and @Autowired Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Register services in Assembly implementations at app startup and resolve them anywhere using @Autowired. At test time, bypass ServiceLocator by assigning mocks directly to @Autowired properties. ```swift import CoreFlow // 1. Define service protocol and implementation protocol LoginService { func login() async -> User } final class LoginServiceImpl: LoginService { func login() async -> User { User(id: UUID(), name: "Alice") } } // 2. Register in an Assembly struct LoginAssembly: Assembly { func assemble(container: Container) { container.register(LoginService.self) { _ in LoginServiceImpl() } .inObjectScope(.container) // singleton scope } } // 3. Bootstrap at app launch @main final class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ app: UIApplication, didFinishLaunchingWithOptions opts: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ServiceLocator.shared.assemble([ LoginAssembly(), // HomeAssembly(), SettingsAssembly(), ... ]) return true } } // 4. Inject into Core via @Autowired final class LoginCore: Core { @Autowired private var service: LoginService // resolved lazily at first access override func reduce(state: inout LoginState, action: LoginAction) -> Effect { switch action { case .loginButtonTapped: return .run { [weak self] send in guard let self else { return } let user = await service.login() await send(._loginFinished(user)) } default: return .none } } } // 5. Override in tests — no ServiceLocator needed @Test func loginSuccess() async throws { let sut = LoginCore(initialState: .init()) sut.service = StubLoginService(user: User(id: UUID(), name: "Test")) // direct injection sut.enableTestMode() sut.send(.loginButtonTapped) try await sut.exhaust() #expect(sut.currentState.loggedInUser?.name == "Test") } struct StubLoginService: LoginService { let user: User func login() async -> User { user } } ``` -------------------------------- ### Enable Test Mode and Exhaust Effects in Swift Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Use enableTestMode() before dispatching actions and exhaust() to wait for all in-flight Effect.run tasks and derived actions to settle. This ensures the state is stable before assertions. ```swift import Testing @testable import YourApp import CoreFlow @MainActor struct LoginCoreTests { @Test("Login button triggers async login and updates state") func loginFlow() async throws { // Arrange let testUser = User(id: UUID(), name: "Alice") let sut = LoginCore(initialState: .init()) sut.service = StubLoginService(user: testUser) sut.enableTestMode() // Act sut.send(.loginButtonTapped) // isLoading == true here (synchronous mutation in reduce) try await sut.exhaust(timeout: 5) // waits for .run task + ._loginFinished action // Assert #expect(sut.currentState.isLoading == false) #expect(sut.currentState.loggedInUser == testUser) } @Test("Effect chain resolves to final state") func effectChain() async throws { // SutCore: .step1 → .step2 → .step3 → .step4 (each via Effect.run) let sut = SutCore(initialState: .init(step: 0)) sut.enableTestMode() sut.send(.step1) try await sut.exhaust(timeout: 3) #expect(sut.currentState.step == 4) } } // Example chained Core used in testing final class SutCore: Core { override func reduce(state: inout SutState, action: SutAction) -> Effect { switch action { case .step1: state.step = 1; return .run { await $0(.step2) } case .step2: state.step = 2; return .run { await $0(.step3) } case .step3: state.step = 3; return .run { await $0(.step4) } case .step4: state.step = 4; return .none } } } ``` -------------------------------- ### Reusable Button Component Emitting Typed Actions Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt A reusable button component that emits typed actions and binds to a scoped Reactor. Use this as a base for custom UI components that need to interact with CoreFlow. ```swift import CoreFlow import UIKit // A reusable button component that emits typed actions final class LoginSection: ScopedView { // Typed action the component can emit enum Action { case loginButtonTapped } // State the component receives from a scoped Reactor struct State: Equatable { var buttonTitle: String = "" var isLoading: Bool = false } private let titleLabel = UILabel() private let loginButton = UIButton(type: .system) private let spinner = UIActivityIndicatorView(style: .medium) // Called by SimpleInitUIView.init() — set up layout here override func initialize() { addSubview(titleLabel) addSubview(loginButton) addSubview(spinner) // ... Auto Layout constraints ... } // ReactorBindable: bind a scoped Reactor (Core sliced to State?) func bind(reactor: any Reactable) { // Input — state changes → UI updates reactor.state .compactMap(\.self) .map(\.buttonTitle) .sink { [weak self] title in self?.titleLabel.text = title } .store(in: &store) reactor.state .compactMap(\.self) .map(\.isLoading) .sink { [weak self] loading in self?.loginButton.isUserInteractionEnabled = !loading loading ? self?.spinner.startAnimating() : self?.spinner.stopAnimating() } .store(in: &store) // Output — UI events → reactor actions map(loginButton.touchUpInside, to: .loginButtonTapped) .sink { reactor.send($0) } .store(in: &store) } } // Wiring inside LoginScreen.bind(): // loginSection.bind( // reactor: reactor.scope( // state: \.loginSectionState, // transform: { LoginAction.loginSection($0) } // ) // ) ``` -------------------------------- ### Add CoreFlow to Package.swift Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Add CoreFlow as a dependency in your Swift Package Manager's Package.swift file. ```swift // Package.swift // swift-tools-version: 6.0 import PackageDescription let package = Package( name: "YourApp", platforms: [.iOS(.v13)], dependencies: [ .package( url: "https://github.com/choijunyeong-company/CoreFlow.git", from: "1.0.0" ) ], targets: [ .target( name: "YourApp", dependencies: ["CoreFlow"] ) ] ) ``` -------------------------------- ### Scoped Sub-Reactors with Reactable.scope() Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Slices a parent Core's state and maps child actions back up. Use this to bind reusable ScopedView components to a portion of a parent Core's state without the component knowing about the parent. ```swift import CoreFlow // Parent state containing a nested component state struct LoginState: Equatable { var loginSectionState: LoginSection.State? var isLoading: Bool = false } // In LoginScreen.bind(): func bind() { // Slice state: LoginState.loginSectionState -> LoginSection.State? // Map action: LoginSection.Action -> LoginAction let sectionReactor = reactor.scope( state: \.loginSectionState, transform: { LoginAction.loginSection($0) } ) // loginSection only knows about LoginSection.State — fully decoupled loginSection.bind(reactor: sectionReactor) } ``` -------------------------------- ### Uninstall CoreFlow Xcode Templates Source: https://github.com/choijunyeong-company/coreflow-c7t/blob/main/tooling/README.md Execute this command to remove the CoreFlow Xcode templates from your system. This command deletes the template directory. ```bash rm -rf ~/Library/Developer/Xcode/Templates/File\ Templates/CoreFlow ``` -------------------------------- ### Describe Side Effects with Effect Enum Source: https://context7.com/choijunyeong-company/coreflow-c7t/llms.txt Illustrates the usage of the `Effect` enum to describe side effects returned from the `reduce` function. It covers `.none` for pure state changes, `.send` for synchronous action chaining, and `.run` for asynchronous operations that can dispatch derived actions. ```swift import CoreFlow // .none — pure state mutation, no async work case .increment: state.count += 1 return .none // .send — synchronous action chaining case .reset: return .send(.loadDefaults) // .run — async work with derived action case .fetchUser(let id): state.isLoading = true return .run(priority: .userInitiated) { send in let user = await UserService.shared.fetch(id: id) await send(.userLoaded(user)) // re-enters reducer on MainActor } case .userLoaded(let user): state.isLoading = false state.user = user return .none ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.