Skip to content

Commit

Permalink
Update and format documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
tgrapperon committed Aug 5, 2021
1 parent 84c90ec commit aa1207e
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 66 deletions.
15 changes: 8 additions & 7 deletions Example/Shared/Level0Feature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import ComposableArchitecture
import ComposableEnvironment
import SwiftUI

// "Level0" Feature, which embeds a `Level1` feature

struct Level0State: Equatable {
var level1: Level1State
var isReady: Bool = false
Expand Down Expand Up @@ -40,14 +42,14 @@ let level0Reducer = Reducer<Level0State, Level0Action, Level0Environment>.combin
.delay(for: 1, scheduler: environment.background) // Simulate something lengthy…
.receive(on: environment.main)
.eraseToEffect()

// Alternatively, we can directly tap into the environment's dependepencies using
// their global KeyPath, meaning that we can even bypass declarations like
// `@Dependency(\.mainQueue) var main` in the environment.
// their global property name, meaning that we can even bypass declarations like
// `@Dependency(\.mainQueue) var main` in the environment to write:
//
// return Effect(value: .isReady)
// .delay(for: 1, scheduler: environment[\.backgroundQueue])
// .receive(on: environment[\.mainQueue])
// .delay(for: 1, scheduler: environment.backgroundQueue)
// .receive(on: environment.mainQueue)
// .eraseToEffect()
}
}
Expand Down Expand Up @@ -98,9 +100,8 @@ struct Level0View_Preview: PreviewProvider {
second: .init(randomNumber: nil)
)),
reducer: level0Reducer,
// An environment default dependencies:
environment: .init())
)
}
}


29 changes: 16 additions & 13 deletions Example/Shared/Level1Feature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import ComposableArchitecture
import ComposableEnvironment
import SwiftUI

// "Level1" Feature, which embeds two similar `Level2` features

struct Level1State: Equatable {
var first: Level2State
var second: Level2State
Expand All @@ -15,10 +17,11 @@ enum Level1Action {
class Level1Environment: ComposableEnvironment {
@DerivedEnvironment<Level2Environment> var first
@DerivedEnvironment<Level2Environment> var second
// In this case, we could have used a shared DerivedEnvironment property instead:

// In this case, we could have used a shared `@DerivedEnvironment` property instead:
// @DerivedEnvironment<Level2Environment> var level2

// And used its `KeyPath` `\.level2` twice when pulling-back in `level1Reducer`

// This environment doesn't have exposed dependencies, but this doesn't prevent derived
// environments to inherit dependencies that were set higher in the parents' chain, nor
// to access them using their global KeyPath.
Expand All @@ -32,26 +35,20 @@ let level1Reducer = Reducer<Level1State, Level1Action, Level1Environment>.combin
level2Reducer.pullback(state: \.first,
action: /Level1Action.first,
environment: \.first), // (or \.level2 if we had used only one property)

level2Reducer.pullback(state: \.second,
action: /Level1Action.second,
environment: \.second) // (or \.level2 if we had used only one property)

// Alternatively, we can use the environment-less pullback variants:

// Alternatively, we can forgo the `@DerivedEnvironment` declarations in `Level1Environment`, and
// use the environment-less pullback variants:
// level2Reducer.pullback(state: \.first,
// action: /Level1Action.first)
//
// level2Reducer.pullback(state: \.second,
// action: /Level1Action.second)

)

#if os(macOS)
fileprivate typealias Stack = HStack
#else
fileprivate typealias Stack = VStack
#endif

struct Level1View: View {
let store: Store<Level1State, Level1Action>
init(store: Store<Level1State, Level1Action>) {
Expand All @@ -74,6 +71,12 @@ struct Level1View: View {
}
}
}

#if os(macOS)
typealias Stack = HStack
#else
typealias Stack = VStack
#endif
}

struct Level1View_Preview: PreviewProvider {
Expand Down
7 changes: 5 additions & 2 deletions Example/Shared/Level2Feature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ private struct RNGKey: DependencyKey {
}
}

// Install it in `ComposableDependencies`:
public extension ComposableDependencies {
var rng: RandomNumberGenerator {
get { self[RNGKey.self] }
set { self[RNGKey.self] = newValue }
}
}

// "Level2" Feature

struct Level2State: Equatable {
var randomNumber: Int?
}
Expand Down Expand Up @@ -47,8 +50,8 @@ let level2Reducer = Reducer<Level2State, Level2Action, Level2Environment> {
state.randomNumber = number
return .none
case .requestRandomNumber:
// Note that we don't have defined any `@Dependency(\.mainQueue)` in environment, but we can use
// its global property name instead.
// Note that we don't have defined any `@Dependency(\.mainQueue)` in environment.
// We use its global property name instead:
return environment
.randomNumber()
.map(Level2Action.randomNumber)
Expand Down
41 changes: 25 additions & 16 deletions Sources/ComposableEnvironment/ComposableEnvironment.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import Foundation
/// The base class of your environments.
///
/// Subclass this class to define your feature's environment. You can expose ``ComposableDependencies`` values using the
/// ``Dependency`` property wrapper and declare child environment using the ``DerivedEnvironment`` property wrapper.
/// Subclass this class to define your feature's environment. You can expose
/// ``ComposableDependencies`` values using the ``Dependency`` property wrapper and declare child
/// environment using the ``DerivedEnvironment`` property wrapper.
///
/// For example, if you defined:
/// For example, if you define:
/// ```swift
/// extension ComposableDependencies {
/// var uuidGenerator: () -> UUID {…}
/// var mainQueue: AnySchedulerOf {…}
/// },
/// ```
/// you can declare the `LocalEnvironment` class, with `ChildEnvironment1` and `ChildEnvironment2` like:
/// you can declare the `LocalEnvironment` class, with `ChildEnvironment1` and `ChildEnvironment2`
/// like:
/// ```swift
/// class LocalEnvironment: ComposableEnvironment {
/// @Dependency(\.uuidGenerator) var uuidGenerator
Expand All @@ -20,15 +22,18 @@ import Foundation
/// @DerivedEnvironment<ChildEnvironment2> var child2
/// }
/// ```
/// - Warning: All child environment must be themself subclasses of ``ComposableEnvironment``. If the environments chain is
/// broken, an environment will retrieve the value of a dependency from its farthest direct ascendant, or use the default value if none
/// was specificied. It will not "jump" over ascendants that are not ``ComposableEnvironment`` to retrieve the value of a dependency.
/// - Warning: All child environment must be themself subclasses of ``ComposableEnvironment``. If
/// the environments chain is broken, an environment will retrieve the value of a dependency from
/// its farthest direct ascendant, or use the default value if none was specificied. It will not
/// "jump" over ascendants that are not ``ComposableEnvironment`` to retrieve the value of a
/// dependency.
@dynamicMemberLookup
open class ComposableEnvironment {
/// Instantiate a ``ComposableEnvironment`` instance with all dependencies sets to their defaults.
///
/// After using this initializer, you can chain ``with(_:_:)`` calls to set the values of individual dependencies. These values
/// will propagate to each child``DerivedEnvironment`` as well as their own children ``DerivedEnvironment``.
/// After using this initializer, you can chain ``with(_:_:)`` calls to set the values of
/// individual dependencies. These values ill propagate to each child``DerivedEnvironment`` as
/// well as their own children ``DerivedEnvironment``.
public required init() {}

var dependencies: ComposableDependencies = .init() {
Expand All @@ -51,12 +56,14 @@ open class ComposableEnvironment {
return self
}

/// Use this function to set the values of a given dependency for this environment and all its descendants.
/// Use this function to set the values of a given dependency for this environment and all its
/// descendants.
///
/// Calls to this function are chainable, and you can specify any ``ComposableDependencies`` `KeyPath`, even if the current
/// environment instance does not expose the corresponding dependency itself.
/// Calls to this function are chainable, and you can specify any ``ComposableDependencies``
/// `KeyPath`, even if the current environment instance does not expose the corresponding
/// dependency itself.
///
/// For example, if you defined:
/// For example, if you define:
/// ```swift
/// extension ComposableDependencies {
/// var uuidGenerator: () -> UUID {…}
Expand All @@ -75,15 +82,17 @@ open class ComposableEnvironment {
return self
}

/// A read-write subcript to directly access a dependency from its `KeyPath` in ``ComposableDependencies``.
/// A read-write subcript to directly access a dependency from its `KeyPath` in
/// ``ComposableDependencies``.
public subscript<Value>(keyPath: WritableKeyPath<ComposableDependencies, Value>) -> Value {
get { dependencies[keyPath: keyPath] }
set { dependencies[keyPath: keyPath] = newValue }
}

/// A read-only subcript to directly access a dependency from ``ComposableDependencies``.
/// - Remark: This direct access can't be used to set a dependency, as it will try to go through a the setter part of a ``Dependency``
/// property wrapper, which is not allowed yet. You can use ``with(_:_:)`` or ``subscript(_:)`` instead.
/// - Remark: This direct access can't be used to set a dependency, as it will try to go through
/// the setter part of a ``Dependency`` property wrapper, which is not allowed yet. You can use
/// ``with(_:_:)`` or ``subscript(_:)`` instead.
public subscript<Value>(dynamicMember keyPath: KeyPath<ComposableDependencies, Value>) -> Value {
get { dependencies[keyPath: keyPath] }
}
Expand Down
26 changes: 13 additions & 13 deletions Sources/ComposableEnvironment/Dependencies.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/// Conform types to this protocol to define dependencies as ``ComposableDependencies`` computed properties.
/// Conform types to this protocol to define dependencies as ``ComposableDependencies`` computed
/// properties.
///
/// You use this protocol like `EnvironmentKey` are used in SwiftUI. Types conforming to this protocol can then
/// be used to declare the dependency in the ``ComposableDependencies`` namespace.
/// You use this protocol like `EnvironmentKey` are used in SwiftUI. Types conforming to this
/// protocol can then be used to declare the dependency in the ``ComposableDependencies`` namespace.
public protocol DependencyKey {
associatedtype Value
/// The default value returned when accessing the corresponding dependency when no value was defined by
/// one of its parents.
/// The default value returned when accessing the corresponding dependency when no value was
/// defined by one of its parents.
static var defaultValue: Self.Value { get }
}

/// This type acts as a namespace to reference your dependencies.
///
/// To declare a dependency, create a ``DependencyKey``, and declare a computed property in this type like
/// you would declare a custom `EnvironmentValue` in SwiftUI. For example, if `UUIDGeneratorKey` is a
/// ``DependencyKey`` with ``DependencyKey/Value`` == `() -> UUID`:
///
/// To declare a dependency, create a ``DependencyKey``, and declare a computed property in this
/// type like you would declare a custom `EnvironmentValue` in SwiftUI. For example, if
/// `UUIDGeneratorKey` is a ``DependencyKey`` with ``DependencyKey/Value`` == `() -> UUID`:
/// ```swift
/// extension ComposableDependencies {
/// var uuidGenerator: () -> UUID {
Expand All @@ -23,11 +23,11 @@ public protocol DependencyKey {
/// }
/// }
/// ```
/// This dependency can then be referenced by its keypath `\.uuidGenerator` when invoking the ``Dependency``
/// property wrapper in some ``ComposableEnvironment`` subclass.
/// This dependency can then be referenced by its keypath `\.uuidGenerator` when invoking the
/// ``Dependency`` property wrapper in some ``ComposableEnvironment`` subclass.
public struct ComposableDependencies {
/// This wrapper enum allows to distinguish dependencies that where defined explicitely for a given environment from
/// dependencies that were inherited from their parent environment.
/// This wrapper enum allows to distinguish dependencies that where defined explicitely for a
/// given environment from dependencies that were inherited from their parent environment.
enum DependencyValue {
case defined(Any)
case inherited(Any)
Expand Down
11 changes: 6 additions & 5 deletions Sources/ComposableEnvironment/Dependency.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/// Use this property wrapper to declare depencies in a ``ComposableEnvironment`` subclass.
///
/// You reference the dependency by its `KeyPath` originating from ``ComposableDependencies``, and you declare its
/// name in the local environment. The dependency should not be instantiated, as it is either inherited from a ``ComposableEnvironment`` parent,
/// or installed with ``ComposableEnvironment/with(_:_:)``.
/// You reference the dependency by its `KeyPath` originating from ``ComposableDependencies``, and
/// you declare its name in the local environment. The dependency should not be instantiated, as it
/// is either inherited from a ``ComposableEnvironment`` parent, or installed with
/// ``ComposableEnvironment/with(_:_:)``.
///
/// For example, if the dependency is declared as:
/// ```swift
Expand All @@ -19,8 +20,8 @@
/// @Dependency(\.uuidGenerator) var uuid
/// }
/// ```
/// This exposes a `var uuid: () -> UUID` readonly property in the `LocalEnvironment`. This property can then be used as
/// any vanilla dependency.
/// This exposes a `var uuid: () -> UUID` read-only property in the `LocalEnvironment`. This
/// property can then be used as any vanilla dependency.
@propertyWrapper
public struct Dependency<Value> {
/// Alternative to ``wrappedValue`` with access to the enclosing instance.
Expand Down
15 changes: 9 additions & 6 deletions Sources/ComposableEnvironment/DerivedEnvironment.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/// Use this property wrapper to declare child ``ComposableEnvironment`` in a ``ComposableEnvironment`` subclass.
/// Use this property wrapper to declare child ``ComposableEnvironment`` in a
/// ``ComposableEnvironment`` subclass.
///
/// You only need to specify the subclass used and its name. You don't need to instantiate the subclass. For example, if `ChildEnvironment`
/// is a ``ComposableEnvironment`` subclass, you can install a representant in `ParentEnvironment` as:
/// You only need to specify the subclass used and its name. You don't need to instantiate the
/// subclass. For example, if `ChildEnvironment` is a ``ComposableEnvironment`` subclass, you can
/// install a representant in `ParentEnvironment` as:
/// ```swift
/// class ParentEnvironment: ComposableEnvironment {
/// @DerivedEnvironment<ChildEnvironment> var child
/// }.
///```
/// This exposes a `var child: ChildEnvironment` readonly property in the `ParentEnvironment`. This child environment inherits
/// the current dependencies of all its ancestor. They can be exposed using the ``Dependency`` property wrapper.
/// ```
/// This exposes a `var child: ChildEnvironment` read-only property in the `ParentEnvironment`.
/// This child environment inherits the current dependencies of all its ancestor. They can be
/// exposed using the ``Dependency`` property wrapper.
@propertyWrapper
public final class DerivedEnvironment<Value> where Value: ComposableEnvironment {
/// Alternative to ``wrappedValue`` with access to the enclosing instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public extension Reducer where Environment: ComposableEnvironment {
func pullback<GlobalState, GlobalAction, GlobalEnvironment>(
state toLocalState: WritableKeyPath<GlobalState, State>,
action toLocalAction: CasePath<GlobalAction, Action>
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment> where GlobalEnvironment: ComposableEnvironment {
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment>
where GlobalEnvironment: ComposableEnvironment {
let local = Environment()
return pullback(
state: toLocalState,
Expand Down Expand Up @@ -51,7 +52,8 @@ public extension Reducer where Environment: ComposableEnvironment {
breakpointOnNil: Bool = true,
_ file: StaticString = #file,
_ line: UInt = #line
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment> where GlobalEnvironment: ComposableEnvironment {
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment>
where GlobalEnvironment: ComposableEnvironment {
let local = Environment()
return pullback(
state: toLocalState,
Expand Down Expand Up @@ -84,7 +86,8 @@ public extension Reducer where Environment: ComposableEnvironment {
breakpointOnNil: Bool = true,
_ file: StaticString = #file,
_ line: UInt = #line
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment> where GlobalEnvironment: ComposableEnvironment {
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment>
where GlobalEnvironment: ComposableEnvironment {
let local = Environment()
return forEach(
state: toLocalState,
Expand Down Expand Up @@ -116,7 +119,8 @@ public extension Reducer where Environment: ComposableEnvironment {
breakpointOnNil: Bool = true,
_ file: StaticString = #file,
_ line: UInt = #line
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment> where GlobalEnvironment: ComposableEnvironment {
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment>
where GlobalEnvironment: ComposableEnvironment {
let local = Environment()
return forEach(
state: toLocalState,
Expand Down

0 comments on commit aa1207e

Please sign in to comment.