RXSwift chaining observers and catch errors

Viewed 116

I have some code that should listen for when some observable operations is finished, but i'am having trouble writing it in the most optimal way.

It's important that the final subscribed result is event, and that the updateEvent is run last.

Here is my code:

/// Update Event
///
/// Updates an event and invites or removes any members from the event before updating the actual event information. This ensures newly updated event to contain the updated list of attendes
///
/// - Parameters:
///   - event: The event to update
///   - group: The group that the event belongs to
///   - newMembers: A list of new group members that should be invited to the invited
///   - removeMembers: A list of event attendees that should be removed from the event
func updateEvent(_ event: EventModel, _ group: GroupModel, newMembers: [MemberModel], removeMembers: [AttendanceModel]) {

    let updateEventObserver = self.repository.updateEvent(group, event)
    var memberObservers: [Observable<AttendanceModel>] = []

    if newMembers.count > 0 {
        memberObservers.append(contentsOf: newMembers.map { (member) in
            return self.repository.inviteToEvent(member: member, event: event, group: group)
        })
    }

    if removeMembers.count > 0 {
        memberObservers.append(contentsOf: removeMembers.map { (member) in
            return self.repository.removeFromEvent(member: member, event: event, group: group)
        })
    }

    if memberObservers.count > 0 {

        Observable
            .zip(memberObservers)
            .subscribe(onNext: { (_) in
                updateEventObserver
                    .subscribe(onNext: { event in
                        self.presenter.eventSuccessfullyUpdated(event)
                    }, onError: { error in
                        self.presenter.failedWithError(error)
                    }).disposed(by: self.disposeBag)
            }, onError: { (error) in
                self.presenter.failedWithError(error)
            }).disposed(by: self.disposeBag)
    } else {
        updateEventObserver
            .subscribe(onNext: { event in
                self.presenter.eventSuccessfullyUpdated(event)
            }, onError: { error in
                self.presenter.failedWithError(error)
            }).disposed(by: self.disposeBag)
    }

}

I think my code is "ugly" and i feel there is a better way. Thank you!

1 Answers

Well, this is a little cleaner:

func updateEvent(_ event: EventModel, _ group: GroupModel, newMembers: [MemberModel], removeMembers: [AttendanceModel]) {

    let invitedResults = newMembers
        .compactMap { [repository] member in
            repository?.inviteToEvent(member: member, event: event, group: group)
        }

    let removedResults = removeMembers
        .compactMap { [repository] member in
            repository?.removeFromEvent(member: member, event: event, group: group)
        }

    Observable.zip(invitedResults + removedResults)
        .flatMap { [repository] _ in repository?.updateEvent(group, event) ?? .empty() }
        .subscribe(
            onNext: { [presenter] event in
                presenter?.eventSuccessfullyUpdated(event)
            },
            onError: { [presenter] error in
                presenter?.failedWithError(error)
            }
        )
        .disposed(by: disposeBag)
}

It avoids capturing self and making a retain cycle at least. But if any of the invites or removes error it could leave your system in an invalid state...

Related