Skip to content

apply operator #73

Closed
Closed
@acchou

Description

@acchou

Name and description

I'd like to propose a very simple operator, apply, which takes a transformation function that takes an Observable and returns an Observable, and simply runs it and returns its value:

extension ObservableType {
    func apply<T>(_ transform: (Observable<Self.E>) -> Observable<T>) -> Observable<T> {
        return transform(self.asObservable())
    }
}

Motivation for inclusion

It's idiomatic to write new operators as extensions of ObservableType. This makes sense for many custom operators because it preserves the chaining syntax of RxSwift. But sometimes this style of extension operator is uncomfortable when the operator is not generic in nature, but might perform some combination of application-specific actions, and may even have side-effects.

In these cases it makes sense to write a simple function to add operations to an Observable, for example:

// Take an ordinary Rx-style request and add retry, application-specific side-effect, and error parsing.
func requestPolicy(_ request: Observable<Void>) -> Observable<Response> {
    return request.retry(maxAttempts)
        .do(onNext: sideEffect)
        .map { Response.success }
        .catchError { error in Observable.just(parseRequestError(error: error)) }

This function can then be applied to several requests to apply consistent retries, side-effects, and error handling:

let request1 = Observable<Void>.create { ... }
let request2 = Observable<Void>.create { ... } 
let request1Resilient = requestPolicy(request1)
let request2Resilient = requestPolicy(request2)

But this syntax is awkward because it requires each policy to be wrapped around the observable. The use of apply returns this to a more familiar chaining syntax:

let request1Resilient = request1.apply(requestPolicy)
let request2Resilient = request2.apply(requestPolicy)

This is especially useful when composing multiple transform functions:

// Without apply
let multiplePolicy = policy3(policy2(policy1(request)))
// With apply
let multiplePolicy = request
  .apply(policy1)
  .apply(policy2)
  .apply(policy3)

There may also be a desire to have a version with one or more arguments to apply:

    func apply<A, T>(arg: A, _ transform: (A, Observable<Self.E>) -> Observable<T>) -> Observable<T> {
        return transform(arg, self.asObservable())
    }

Example of use

See above.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions