Skip to content

Instantly share code, notes, and snippets.

@hayek
Last active November 18, 2024 08:55
//
// Recursive.swift
//
//
// Created by Amir Hayek on 17/11/2024.
//
import Foundation
/// Allows recursive state in a TCA feature.
///
/// Usage example:
/// ```swift
/// @Reducer
/// public struct SomeFeature {
/// public init() {}
/// @ObservableState
/// public struct State: Equatable {
/// var recursive = Recursive<SomeFeature.State>()
/// }
/// public enum Action {
/// indirect case recursive(SomeFeature.Action)
/// }
/// public var body: some Reducer<State, Action> {
/// Reduce { state, action in
/// switch action {
/// case let .recursive(action):
/// return .none
/// }
/// }
/// .ifLet(\.recursive.accessor, action: \.recursive) { SomeFeature() }
/// }
/// }
/// ```
/// - Note: This is a workaround for the limitation of swift structs to have recursive variables. TCA uses arrays commonly to solve recursive state.
public struct Recursive<State: Equatable>: Equatable {
private class Box: Equatable{
var storage: State
init(_ storage: State) {
self.storage = storage
}
static func == (lhs: Box, rhs: Box) -> Bool {
lhs.storage == rhs.storage
}
}
public static func == (lhs: Recursive<State>, rhs: Recursive<State>) -> Bool {
lhs.box == rhs.box
}
private var box: Box? = nil
public init(_ state: State? = nil) {
if let state {
box = Box(state)
}
}
public var accessor: State? {
get { box?.storage }
set { if let newValue = newValue { box = Box(newValue) } else { box = nil } }
}
public func callAsFunction() -> State? {
box?.storage
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment