Do not bind for not changing value. Use initialState
ViewController
init(reactor: Reactor) {
self._reactor = reactor
super.init(nibName: nil, bundle: nil)
titleLabel.text = reactor.initialState.title
}
iOS Developer
Do not bind for not changing value. Use initialState
ViewController
init(reactor: Reactor) {
self._reactor = reactor
super.init(nibName: nil, bundle: nil)
titleLabel.text = reactor.initialState.title
}
case .tapDeleteButton(let model):
let deleteHandler: () -> Void = { [weak self, weak listAdapter] in
self?.action.onNext(.deletePositionCell(model?.userCash))
listAdapter?.performUpdates(animated: true, completion: { [weak self] _ in // line 1
self?.action.onNext(.delete(model?.userCash))
})
}
self?.action.onNext(.showDeleteAlert(deleteHandler))
}
case .showDeleteAlert(let deleteAction):
guard let deleteAction = deleteAction else { return .empty() }
deleteAction()
return .empty()
case .deletePositionCell(let userCash):
return .just(.deletePositionCell(userCash)) // line 2
line 1 is called before line 2.
.concat(
.just(.setIsLoading(true)),
getAPIData(),
.just(.setIsLoading(false))
private func getAPIData( ) -> Observable<Mutation> {
return repository
.getAPI()
.asObservable()
.catchErrorAndShowToastAndJustComplete()
.map { .setDTO($0, mode) }
}
setIsLoading(false) is executed after API response is gotten.
concat wait for the element to complete
reactor.state.map(\.isRefreshing)
.distinctUntilChanged()
.filter { !$0 }
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] _ in
self?.collectionView.refreshControl?.endRefreshing(delay: .default)
})
.disposed(by: disposeBag)
So if you remove distinctUntilChanged, the subscribe block executes when other variables change, when there’s no need to be executed.
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .updatePortfolioView(let portfolioView):
let newCurrency = portfolioView.currency
let refreshIfCurrencyChanged: Observable<Mutation> = {
if newCurrency != currentState.currency {
return getProfit(timeRange: currentState.timeRange, currency: newCurrency, assetFilter: currentState.assetFilter)
} else {
return .empty()
}
}()
return .merge(
refreshIfCurrencyChanged,
.just(
.batch([
.setPortfolioView(portfolioView),
.setCurrency(portfolioView.currency),
.updateUnrealizedProfitStockModels,
.updateRealizedProfitStockModels,
.updateDividendStockModels,
.updateTotalSumModel,
])
)
)
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .initialize:
return .concat( .just(.setCurrency(userDefaultSettings.portfolioTabDisplayCurrencyPreference)),
getProfit()
)
}
}
private func getProfit(timeRange: ProfitTimeRange, targetCurrency: Currency, assetFilter: AssetFilter) -> Observable<Mutation> {
return profitRepository.getProfit(timeRange: currentState.timeRange, targetCurrency: currentState.currency, assetFilter: currentState.assetFilter)
.asObservable()
.catchErrorAndShowToastAndJustComplete()
.map {
.batch([
.setProfitInfoDTO($0),
.updateUnrealizedProfitStockModels,
.updateRealizedProfitStockModels,
.updateDividendStockModels,
.updateTotalSumModel,
])
}
}
If you have this concat return of mutation like this, calling API in getProfit function does not have changed currency. Current state changed after the all concat mutation is executed.
Therefore you need to put parameter.
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .initialize:
let currency = userDefaultSettings.portfolioTabDisplayCurrencyPreference
return .concat(
.just(
.setCurrency(currency)),
getProfit(timeRange: currentState.timeRange, currency: currency, assetFilter: currentState.assetFilter)
)
}
@Inject private var bankAccountRepository: BankAccountRepositoryType
struct InitParams {
var portfolioView: PortfolioView
}
var initialState: State
init(params: InitParams) {
self.initialState = State(portfolioView: params.portfolioView)
}
final class PortfolioDividendViewController: UIViewController, View {
typealias Reactor = PortfolioDividendViewReactor
typealias Factory = (Reactor.InitParams) -> PortfolioDividendViewController
}
final class ExecutionContext {
init() {
container.register { (params: PortfolioDividendViewReactor.InitParams) -> PortfolioDividendViewController in
let reactor = PortfolioDividendViewReactor(params: params)
return PortfolioDividendViewController(
reactor: reactor
)
}
container.register { [unowned container] (params: AnalysisViewReactor.InitParams) -> AnalysisViewController in
let reactor = AnalysisViewReactor(
initParams: params
)
return AnalysisViewController(
reactor: reactor,
dividendViewControllerFactory: try container.resolve()
)
}
container.register { [unowned container] () -> PortfolioDividendViewController.Factory in
{ [container] in try! container.resolve(arguments: $0) }
}
}
}
final class ExampleCell: UICollectionViewCell, Reusable {
enum Event {
case tapButton(Bool)
}
private func bind() {
button?.rx.tapGesture().when(.recognized)
.asPublisherIgnoringError()
.sink(receiveValue: { [weak self] _ in
guard let isSomething = self?.isSomething else { return }
self?.isSomething.toggle()
self?.onEvent?(.tapButton(isSomething))
})
.store(in: &subscriptions)
}
}
2. SectionController
final class ExampleSectionController: NeoListSectionController {
typealias Model = ExampleModel
typealias Cell = ExampleCell
private var model: Model?
private let eventSubject = PublishSubject<ExampleCell.Event>()
var event: Observable<ExampleCell.Event> {
return eventSubject
}
3. Reactor+IGListKit
extension ExampleViewReactor: ListAdapterDataSource {
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
switch object {
case is DiffableBox<ExampleModel>:
let controller = ExampleSectionController()
controller.event
.subscribe(onNext: { [weak self] event in
switch event {
case .tapButton(let isSomething):
self?.action.onNext(.tapButton(isSomething))
}
})
.disposed(by: disposeBag)
4. Reactor
final class ExampleViewReactor: NSObject, Reactor {
enum Action {
case tapButton(Bool)
}
enum Mutation {
case setIsSomething(Bool)
}
struct State {
var isSomething: Bool
}
// MARK: - Properties
var initialState: State
let disposeBag = DisposeBag()
// MARK: - Dependencies
override init() {
self.initialState = State(listVersion: 0, isSomething: true)
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .tapButton(let isSomething):
return .just(.setIsSomething(isSomething))
}
}
func reduce(state: State, mutation: Mutation) -> State {
let state = state
return state
}
}
self?.reactor?.action.onNext(.tapActionButton())
Use InitialState in setUp
Don’t use currentState if the code is not prepared to any change after Setup
Or you can use subscribe with take(1)