Skip to content

LoadableForEachStore

m-housh edited this page Aug 20, 2021 · 1 revision

LoadableForEachStore

A loadable list view that gives the caller access to a ForEachStore, once the elements have loaded.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public struct LoadableForEachStore<
  Element: Equatable,
  ElementAction: Equatable,
  Id: Hashable,
  Failure: Error,
  Row: View
>: View where Failure: Equatable 

Example

// setup types.
public struct User: Equatable, Identifiable {
   public let id: UUID = UUID()
   public var name: String
   public var isFavorite: Bool

   public init(name: String, isFavorite: Bool = false) {
     self.name = name
     self.isFavorite = isFavorite
   }

   public static let blob = User.init(name: "blob")
   public static let blobJr = User.init(name: "blob-jr")
   public static let blobSr = User.init(name: "blob-sr")
}

extension Array where Element == User {
  public static let users: Self = [.blob, .blobJr, .blobSr]
}

public enum UserAction: Equatable {
  case binding(BindingAction<User>)
}

public let userReducer = Reducer<User, UserAction, Void>.empty
  .binding(action: /UserAction.binding)

public enum LoadError: Error, Equatable {
  case loadingFailed
}

// Create the environment dependency.
extension LoadableForEachEnvironment where
  Element == User,
  Id = User.ID,
  LoadRequest = EmptyLoadRequest,
  Failure == LoadError
{
  state let live = Self.init(
    load: { _ in
      Just(.init(uniqueElements: .users))
        .delay(for: .seconds(1), scheduler: DispatchQueue.main) // simulate loading.
        .setFailureType(to: LoadError.self)
        .eraseToEffect()
    },
    mainQueue: .main
  )
}


// Create the reducer.
let appReducer = Reducer<
  LoadableForEachStateFor<User, LoadError>,
  LoadableForEachStoreActionFor<User, UserAction, LoadError>,
  LoadableForEachEnvironmentFor<User, LoadError>
>.empty
  .loadableForEachStore(
    state: \.self,
    action: /LoadableForEachAction.self,
    environment: { $0 },
    forEach: userReducer
  )

// Use the for each view, you would typically not create the store inside the body, but just
// an example of what it looks like.
struct AppView: View {
  var body: some View {
    LoadableForEachStore(
      store: .init(
        initialState: .init(),
        reducer: appReducer,
        environment: .live
      )
    ) { store in
      WithViewStore(store) { viewStore in
        HStack {
          Text(viewStore.name)
          Spacer()
          Toggle(
            "Favorite"
             isOn: viewStore.binding(keyPath: \.isFavorite, send: UserAction.binding)
          )
        }
      }
    }
  }
}

Inheritance

View

Initializers

init(store:autoLoad:content:)

Create a new loadable list view.

public init(
    store: Store<
      LoadableForEachState<Element, Id, Failure>,
      LoadableForEachAction<Element, ElementAction, Id, Failure>
    >,
    autoLoad: Bool = true,
    @ViewBuilder content row: @escaping (Store<Element, ElementAction>) -> Row
  ) 

Parameters

  • store: The store for the view state.
  • autoLoad: Whether we automatically load items when the view first appears.
  • row: The view builder for an individual row in the list.

Properties

store

The store for our state and actions.

public let store:
    Store<
      LoadableForEachState<Element, Id, Failure>,
      LoadableForEachAction<Element, ElementAction, Id, Failure>
    >

body

public var body: some View 
Clone this wiki locally