Skip to main content

Overview

The @Presents macro is used to mark properties that represent optional child feature state in tree-based navigation. It wraps the property with PresentationState and provides observation support when used with the @ObservableState macro. Use this macro instead of the @PresentationState property wrapper when adopting the @ObservableState macro, which is incompatible with property wrappers.

Declaration

@attached(accessor, names: named(init), named(get), named(set))
@attached(peer, names: prefixed(`$`), prefixed(_))
public macro Presents()

Usage

Basic Presentation

Use @Presents to mark optional child state in your feature’s state:
@Reducer
struct ParentFeature {
  @ObservableState
  struct State {
    @Presents var child: ChildFeature.State?
  }
  
  enum Action {
    case child(PresentationAction<ChildFeature.Action>)
  }
  
  var body: some ReducerOf<Self> {
    Reduce { state, action in
      // Parent logic
    }
    .ifLet(\.$child, action: \.child) {
      ChildFeature()
    }
  }
}

Multiple Destinations with Enum

When supporting multiple presentation destinations, use @Presents with an enum:
@Reducer
struct ContactsFeature {
  @Reducer
  enum Destination {
    case addContact(AddContactFeature)
    case alert(AlertState<Alert>)
    
    enum Alert {
      case deleteConfirmed
    }
  }
  
  @ObservableState
  struct State {
    @Presents var destination: Destination.State?
    var contacts: IdentifiedArrayOf<Contact> = []
  }
  
  enum Action {
    case destination(PresentationAction<Destination.Action>)
  }
  
  var body: some ReducerOf<Self> {
    Reduce { state, action in
      switch action {
      case .addButtonTapped:
        state.destination = .addContact(AddContactFeature.State())
        return .none
        
      case .destination(.presented(.addContact(.saveButtonTapped))):
        // Handle save
        state.destination = nil
        return .none
        
      case .destination:
        return .none
      }
    }
    .ifLet(\.$destination, action: \.destination)
  }
}

Accessing Presented State

The macro provides a projected value (with $ prefix) of type PresentationState:
// Direct access to the optional value
if let childState = state.child {
  // Work with child state
}

// Access via projected value for use with ifLet
.ifLet(\.$child, action: \.child) {
  ChildFeature()
}

// Access nested enum cases
state.$destination[case: \.edit]?.syncUp = updatedSyncUp

Key Points

  • Observation Support: Automatically integrates with SwiftUI’s observation system when used with @ObservableState
  • Projected Value: Provides a $ prefixed projected value of type PresentationState
  • Navigation Integration: Works seamlessly with the ifLet reducer operator for tree-based navigation
  • Type Safety: Ensures compile-time safety for optional child state management
  • PresentationState - The underlying property wrapper type
  • PresentationAction - Action wrapper for presented features
  • @ObservableState - Macro for observable state conformance
  • ifLet - Reducer operator for optional state

See Also

Build docs developers (and LLMs) love