Skip to content

EffectID

tgrapperon edited this page Feb 1, 2022 · 1 revision

EffectID

A property wrapper that generates a hashable value suitable to identify Effect's.

@propertyWrapper
public struct EffectID: Hashable 

These identifiers can be bound to the root Store executing the Reducer that produces the Effect's. This can be conveniently exploited in document-based apps for example, where you may have multiple documents and by extension, multiple root Store's coexisting in the same process.

In order to bind the identifiers to a store, you need to namespace their root reducer using the Reducer.namespace() methods. You don't need to declare a namespace if you're using only one instance of a root store in your application.

The value returned when accessing a property wrapped with this type is an opaque hashable value that is constant across Reducer's runs, and which can be used to identify long-running or cancellable effects:

Reducer<State, Action, Environment> { state, action, environment in
 @EffectID var timerID
 switch action {
 case .onAppear:
  return
    .timer(id: timerID, every: 1, on: environment.mainQueue)
    .map { _ in Action.timerTick }
 case .onDisappear:
  return .cancel(id: timerID)
 case .timerTick:
  state.ticks += 1
  return .none
 }
}

These property wrappers can be used without arguments, but you can also provide some contextual data to parametrize them:

Reducer<State, Action, Environment> { state, action, environment in
 @EffectID var timerID = state.timerID
 
}

If you want to share an EffectID across reducers, you should define it as a property in any shared type. You can even use the EffectID type itself to declare a shared identifier:

extension EffectID {
  @EffectID public static var sharedID
}
// And access it as inside reducers as:
EffectID.sharedID
Reducer<State, Action, Environment> { _, _, _ in
 @EffectID var id1 = "A"
 @EffectID var id2 = "A"
 // => id1 != id2
}

Two identifiers are equal iff they are defined at the same place, and with the same contextual data (if any).

Inheritance

Hashable

Initializers

init(wrappedValue:file:line:column:)

Initialize an EffectID carrying some user-defined payload.

public init<UserData>(
    wrappedValue: UserData,
    file: StaticString = #fileID,
    line: UInt = #line,
    column: UInt = #column
  ) where UserData: Hashable 

The EffectID.Value returned when accessing this property is as unique and stable as the value provided. You can assign a value when you want to parametrize the identifier with some State-dependant value for example:

let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, env in
  @EffectID var timerId = state.timerID

  switch action {
  case .startButtonTapped:
    return Effect.timer(id: timerId, every: 1, on: env.mainQueue)
      .map { _ in .timerTicked }

  case .stopButtonTapped:
    return .cancel(id: timerId)

  case let .timerTicked:
    state.count += 1
    return .none
}
@EffectID var id1 = "A"
@EffectID var id2 = "A"
// => id1 != id2

init(file:line:column:)

Initialize an EffectID that returns a unique and stable EffectID.Value when accessed.

public init(
    file: StaticString = #fileID,
    line: UInt = #line,
    column: UInt = #column
  ) 

You don't need to provide any value:

let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, env in
  @EffectID var timerId

  switch action {
  case .startButtonTapped:
    return Effect.timer(id: timerId, every: 1, on: env.mainQueue)
      .map { _ in .timerTicked }

  case .stopButtonTapped:
    return .cancel(id: timerId)

  case let .timerTicked:
    state.count += 1
    return .none
}

Properties

wrappedValue

public var wrappedValue: Value 
Types
Extensions
Clone this wiki locally