Saturday, 9 October 2021

Epoxy updating the epoxy attributes in the models once they have been built results in crash

App has crashed, executing CustomActivityOnCrash's UncaughtExceptionHandler

com.airbnb.epoxy.ImmutableModelException: The model was changed between being added to the controller and being bound

Controller class

class SortFilterController @Inject constructor(
    private val schedulersFacade: SchedulersFacade,
    private val generateMapOfCategoryFilters: GenerateMapOfCategoryFilters
) : EpoxyController() {

    private val tapCountryRelay: PublishRelay<TopsProductFilter> = PublishRelay.create()

    var sortFilterViewState: SortFilterViewState = SortFilterViewState()
        set(value) {
            field = value
            requestModelBuild()
        }

    var sortFilterType: SortFilterType = SortFilterType.ALL
        set(value) {
            field = value
            requestModelBuild()
        }

    override fun buildModels() {
        sortFilterViewState.let { sortFilterViewState ->
            sortFilterViewState.filterTypes?.forEach { topsProductFilter ->
                when (SortFilterType.getId(topsProductFilter.attributeCode)) {
                    SortFilterType.COUNTRY -> {
                        CountryItemModel_()
                            .id(UUID.randomUUID().toString())
                            .tapCountryChipRelay(tapCountryRelay)
                            .countryFilter(topsProductFilter)
                            .listOfPreSelectedCountryFilters(sortFilterViewState.listOfCurrentlySelectedCountryItems ?: emptyList())
                            .addTo(this)
                    }
                }
            }
        }
    }

    val bindTapCountryRelay: Observable<TopsProductFilter> = tapCountryRelay.hide()
}

// model class

@EpoxyModelClass(layout = R.layout.list_item_country_item)
abstract class CountryItemModel : EpoxyBaseModel() {

    @EpoxyAttribute
    lateinit var tapCountryChipRelay: PublishRelay<TopsProductFilter>

    @EpoxyAttribute
    lateinit var countryFilter: TopsProductFilter

    @EpoxyAttribute
    lateinit var listOfPreSelectedCountryFilters: MutableList<TopsProductFilterItem>

    override fun bind(holder: EpoxyBaseViewHolder) {
        with(holder.itemView) {
        // snippet here 
      }
   }
}

In the DialogFragment oncreate I setup the epoxyRecyclerView.

 epoxyRecyclerView.setController(sortFilterController)
    epoxyRecyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)

And call the setters on the controller and request the model build

sortFilterController.sortFilterViewState = sortFilterViewState
sortFilterController.sortFilterType = SortFilterType.ALL

However, the problem is that I want to change the data that is displayed in the models with some new data. So when the user taps on a country I want to set the setter again.

  private fun onTapClearAll() {
       // sortFilterViewState has some new data so I want to set it again for display.
       
       // This calling these resulted in a crash as the models epoxy attributes have changed with this new data
       sortFilterController.sortFilterViewState = sortFilterViewState
       sortFilterController.sortFilterType = SortFilterType.ALL    
    }
    
// Then I tried to do the same with a interceptor but again the app will crash.
  private fun onTapClearAll() {
            sortFilterController.addInterceptor(object : EpoxyController.Interceptor {
                override fun intercept(models: MutableList<EpoxyModel<*>>) {
                    val countryModel = models[0] as CountryItemModel_

                    countryModel.listOfPreSelectedCountryFilters(sortFilterViewState.listOfCurrentlySelectedCountryItems)
                }
            })
            sortFilterController.requestModelBuild()
        }


from Epoxy updating the epoxy attributes in the models once they have been built results in crash

No comments:

Post a Comment