admin管理员组

文章数量:1310221

I would like to make reusable solution to avoid boilerplate for lazy loading features

I have base state and base view model which describes common

@ObservableState
enum BaseState<Input, BaseContentState> {
  case progress(BaseProgressState<Input>)
  case content(BaseContentState)
  case error(BaseErrorState<Input>)

  init(input: Input) {
    self = .progress(BaseProgressState(input: input))
  }
}

@Reducer
struct BaseViewModel<Input, ContentInput, BaseContentState: BaseContentStateType, BaseContentAction> where ContentInput == BaseContentState.ContentInput {
  typealias State = BaseState<Input, BaseContentState>

  enum Action {
    case progress(BaseProgressAction<Input, ContentInput>)
    case content(BaseContentAction)
    case error(BaseErrorAction<Input>)
  }
}

BaseContentStateType allows to make content state buildable with some result of progress feature

protocol BaseContentStateType {
  associatedtype ContentInput
  init(contentInput: ContentInput)
}

Additional I have common implementation to show progress...

@ObservableState
struct BaseProgressState<Input> {
  let input: Input
}

enum BaseProgressAction<Input, ContentInput> {
  case load(Input)
  case loadSuccess(ContentInput)
  case loadFailure(Input, Error)
}

struct BaseProgressViewModel<Input, ContentInput>: Reducer {
  typealias State = BaseProgressState<Input>
  typealias Action = BaseProgressAction<Input, ContentInput>

  var body: some ReducerOf<Self> {
    EmptyReducer()
  }
}

struct BaseProgressView<Input, ContentInput>: View {
  let store: StoreOf<BaseProgressViewModel<Input, ContentInput>>

  var body: some View {
    ProgressView()
      .onAppear {
        store.send(.load(store.input))
      }
  }
}

... and error

@ObservableState
struct BaseErrorState<Input> {
  let input: Input
  let error: Error
}

enum BaseErrorAction<Input> {
  case retry(Input)
}

struct BaseErrorViewModel<Input>: Reducer {
  typealias State = BaseErrorState<Input>
  typealias Action = BaseErrorAction<Input>

  var body: some ReducerOf<Self> {
    EmptyReducer()
  }
}

struct BaseErrorView<Input>: View {
  let store: StoreOf<BaseErrorViewModel<Input>>

  var body: some View {
    Button("Retry") {
      store.send(.retry(store.input))
    }
  }
}

When I try to use this solution...

@Reducer
struct FeatureViewModel {
  typealias State = BaseViewModel<...>.State
  typealias Action = BaseViewModel<...>.Action

  var body: some ReducerOf<Self> {
    ...
  }
}

struct FeatureView: View {
  let store: StoreOf<FeatureViewModel>

  var body: some View {
    BaseView(store: store) { store in
      FeatureContentView(store: store)
    }
  }
}

... I face problems, like

Instance method 'ifLet(_:action:)' requires that 'FeatureViewModel.State' (aka 'BaseState<FeatureInput, FeatureContentViewModel.State>') conform to 'CaseReducerState'

And no matter how hard I try to get it to look right, I don't understand what I'm doing wrong. How to confirm CaseReducerState?

I would like to make reusable solution to avoid boilerplate for lazy loading features

I have base state and base view model which describes common

@ObservableState
enum BaseState<Input, BaseContentState> {
  case progress(BaseProgressState<Input>)
  case content(BaseContentState)
  case error(BaseErrorState<Input>)

  init(input: Input) {
    self = .progress(BaseProgressState(input: input))
  }
}

@Reducer
struct BaseViewModel<Input, ContentInput, BaseContentState: BaseContentStateType, BaseContentAction> where ContentInput == BaseContentState.ContentInput {
  typealias State = BaseState<Input, BaseContentState>

  enum Action {
    case progress(BaseProgressAction<Input, ContentInput>)
    case content(BaseContentAction)
    case error(BaseErrorAction<Input>)
  }
}

BaseContentStateType allows to make content state buildable with some result of progress feature

protocol BaseContentStateType {
  associatedtype ContentInput
  init(contentInput: ContentInput)
}

Additional I have common implementation to show progress...

@ObservableState
struct BaseProgressState<Input> {
  let input: Input
}

enum BaseProgressAction<Input, ContentInput> {
  case load(Input)
  case loadSuccess(ContentInput)
  case loadFailure(Input, Error)
}

struct BaseProgressViewModel<Input, ContentInput>: Reducer {
  typealias State = BaseProgressState<Input>
  typealias Action = BaseProgressAction<Input, ContentInput>

  var body: some ReducerOf<Self> {
    EmptyReducer()
  }
}

struct BaseProgressView<Input, ContentInput>: View {
  let store: StoreOf<BaseProgressViewModel<Input, ContentInput>>

  var body: some View {
    ProgressView()
      .onAppear {
        store.send(.load(store.input))
      }
  }
}

... and error

@ObservableState
struct BaseErrorState<Input> {
  let input: Input
  let error: Error
}

enum BaseErrorAction<Input> {
  case retry(Input)
}

struct BaseErrorViewModel<Input>: Reducer {
  typealias State = BaseErrorState<Input>
  typealias Action = BaseErrorAction<Input>

  var body: some ReducerOf<Self> {
    EmptyReducer()
  }
}

struct BaseErrorView<Input>: View {
  let store: StoreOf<BaseErrorViewModel<Input>>

  var body: some View {
    Button("Retry") {
      store.send(.retry(store.input))
    }
  }
}

When I try to use this solution...

@Reducer
struct FeatureViewModel {
  typealias State = BaseViewModel<...>.State
  typealias Action = BaseViewModel<...>.Action

  var body: some ReducerOf<Self> {
    ...
  }
}

struct FeatureView: View {
  let store: StoreOf<FeatureViewModel>

  var body: some View {
    BaseView(store: store) { store in
      FeatureContentView(store: store)
    }
  }
}

... I face problems, like

Instance method 'ifLet(_:action:)' requires that 'FeatureViewModel.State' (aka 'BaseState<FeatureInput, FeatureContentViewModel.State>') conform to 'CaseReducerState'

And no matter how hard I try to get it to look right, I don't understand what I'm doing wrong. How to confirm CaseReducerState?

Share Improve this question edited Feb 2 at 19:07 Joakim Danielson 52.1k5 gold badges33 silver badges71 bronze badges asked Feb 2 at 18:18 sergeifabiansergeifabian 537 bronze badges 1
  • You probably only want a generic and quite "opinionated" State type, which can be composed out of several sub types, where you can represent a rich UI, and which features a lot of convenience accessors and modifiers. – CouchDeveloper Commented Feb 2 at 21:44
Add a comment  | 

1 Answer 1

Reset to default 0

I fot to make ViewModel for this case

.ifLet(\.$myFeatureState, action: \.myFeatureAction) {
    MyFeatureViewModel()
}

本文标签: swiftGeneric state for TCAStack Overflow