Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use collect() and dematerializeLoadingState() together ? #209

Open
npvisual opened this issue Feb 14, 2019 · 1 comment
Open

How to use collect() and dematerializeLoadingState() together ? #209

npvisual opened this issue Feb 14, 2019 · 1 comment

Comments

@npvisual
Copy link
Contributor

Description

I am trying to map a collection of events (from a datastore) to a list of choices (in a different datastore) made by a user for some / all of the events in that collection.

The collection of events is stored in a LoadingProperty. Something like :

       public let events: LoadingProperty<[Event], ApplicationError>

....

        events = LoadingProperty {
            // some HTTP request that retrieves an array of events from a remote server
            ...
            .toLoadingSignal()
        }

I will display those events in a UITableView with custom cells that show whether or not the user will attend.

In order to provide a mutable observable array to bind that table to, I have to issue a request for every event to check if the user has already made some selection -- I know, not the best solution, but that's what I am dealing with for now.

So I am building that MutableObservableArray from the events property. Everytime it gets refreshed, then the mapping is established again and the table is updated accordingly :

    public let eventsArray: MutableObservableArray<MappingScheduleChoice> = MutableObservableArray([])

    ...

        events
            .dematerializeLoadingState()
            .suppressError(logging: true)
            .with(latestFrom: userService.currentUser.value())
            .map { (arg) -> [(Event, User)] in
                let (evts, user) = arg
                return evts.map { ($0, user) }
            }
            .unwrap()
            .flatMap(.concat) { arg -> Signal<MappingScheduleChoice, ApplicationError> in
                let (evt, user) = arg
                return evt
                    .getEventSelection(for: user.localId)
                    .response(using: client)
                    .map { $0.first }
                    .map { MappingScheduleChoice(schedule: evt, choice: $0) }
            }
            .collect()
            .observeNext { state in
                self.eventsArray.replace(with: state)
            }.dispose(in: bag)

Problem

The main issue is that dematerializeLoadingState() never really completes (just reloads) and therefore collect() can never issue a new array via .next() that would be used to build the MutableObservableArray.

If I add .take(first: 4) right before collect() then the first 4 events will show up in the table with the proper user choices (as intended, but just for those 4 events). I'd like to achieve this for all the events present in the events property.

Now this seems like a very, very complicated solution and I am happy to hear other suggestions on how to achieve the intended result. But, otherwise, any nifty little trick I have forgotten about that could make this work ?

thanks,
N.

@npvisual
Copy link
Contributor Author

npvisual commented May 4, 2020

Following up on this as I have another use case, for collect(), which faces the same problem.

For each new carpool (given by the carpoolSignal) I fetch its participants (a property of the carpool storing a dictionary), flatten the elements of the signal into signals of element on which I perform a few operations (filter, flatmap, etc.) and then bind the result to a mutable observable array (riderArray).

        let carpoolSignal: Property<CarPool>
....
        carpoolSignal
            .compactMap { $0.participants}
            .flattenElements()
            .filter { $0.value.type == .rider }
            .flatMapConcat { carpoolService.getParticipant(for: $0.key, with: $0.value) }
            .collect()
            .suppressError(logging: true)
            .bind(to: riderArray)

However, since carpoolSignal will only send a termination event way after we need to collect the participants for a given carpool, riderArray will always be empty (waiting on "originating" signal to terminate).

So is there a way to use flattenElements() and collect() in a way that will keep the original signal alive (i.e. not using prefix / take) while still emitting a signal of rider arrays ? Or do I need to find a different way to handle a signal of collections.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant