Open-source project
RxSwiftCommunity/RxOptional avatar
RxSwiftCommunity/RxOptional

RxOptional: RxSwift operators that unwrap optionals and empty collections

RxSwift extensions for Swift optionals and "Occupiable" types

693 stars91 forksSwiftMIT

At a glance

What is it?
RxOptional is a small RxSwift extension library that adds operators for optional values and for occupiable types such as String, Array, Dictionary and Set, including filterNil, replaceNilWith, errorOnNil, catchOnNil and their empty-collection counterparts. It is MIT licensed and available through CocoaPods, Carthage and Swift Package Manager.
Who is it for?
RxOptional fits a Swift team using RxSwift who keeps hitting the same friction point, an optional arriving on a stream and the awkward choice between dropping it, substituting it or failing the stream. It does not fit a project that has moved to structured concurrency with async/await, or a codebase that would rather model absence in the type.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One recurring problem in RxSwift code, solved with operators

RxOptional is a set of RxSwift extensions for Swift optionals and for what the project calls Occupiable types. The problem it addresses is narrow and extremely common. When a stream carries an optional value, the reactive chain has to decide what to do with nil, and the type system helps only so far: an Observable of String? is a different type from an Observable of String, so something has to bridge them. Most teams bridge it by hand, in a map or a compact, and end up with the same decision repeated across the codebase. RxOptional packages those decisions as named operators. The audience is Swift developers already using RxSwift and RxCocoa, which the README lists as requirements, and who want the absence-handling to be visible in the chain rather than buried in a closure. The library is MIT licensed, authored by Thane Gill, and lives in the RxSwiftCommunity organisation, which is where the community-maintained extensions to RxSwift live.

Four operators for optionals, each with a different policy

The optional operators are the core of the library, and there are four of them, corresponding to four policies. filterNil drops nil values and narrows the type to the wrapped one, so an Observable of String? becomes an Observable of String and only the non-nil elements are delivered. replaceNilWith substitutes a value, keeping the stream alive and the type unwrapped. errorOnNil turns a nil into an error, and by default it errors with RxOptionalError.foundNilWhileUnwrappingOptional. catchOnNil replaces the nil with a different Observable, which is the one to reach for when a missing value should trigger a fallback source rather than an error. The README notes that all operators are also available on Driver and Signal unless stated otherwise, and that exception is errorOnNil, which is unavailable on Driver because a Driver cannot error out.

filterNil and errorOnNil show the type narrowing

The examples in the README are short enough to read as a unit, and they show the type change that is the point of the library. Filtering nils off a stream of optionals narrows the element type:

swift
Observable<String?>
    .of("One", nil, "Three")
    .filterNil()
    // Type is now Observable<String>
    .subscribe { print($0) }

The erroring variant stops the stream instead:

swift
Observable<String?>
    .of("One", nil, "Three")
    .errorOnNil()
    // Type is now Observable<String>
    .subscribe { print($0) }

In that second case the output shown in the README is next(One) followed by an error, with the message Found nil while trying to unwrap type <Optional<String>>. The type is narrowed in both cases, so the subscriber deals with a String rather than an optional, which is the practical difference between this and a hand-written closure.

Occupiable covers the empty collection case

The second half of the library handles emptiness rather than nil, and it works on a protocol called Occupiable. The types that conform to it are String, Array, Dictionary and Set, and the README is explicit about why the list is short: Swift protocols cannot currently be extended to conform to other protocols, so the built-in types had to be enumerated rather than covered by a blanket extension. Custom types can conform to Occupiable themselves, so the protocol is not limited to the four. The operators mirror the optional ones with empty rather than nil semantics. filterEmpty drops empty values, errorOnEmpty fails the stream with RxOptionalError.emptyOccupiable, and catchOnEmpty substitutes a fallback Observable. The same Driver restriction applies to errorOnEmpty, for the same reason, since Drivers cannot error.

Installation through CocoaPods, Carthage or Swift Package Manager

The library is distributed through all three of the usual Swift package managers, and the README gives a snippet for each. With CocoaPods the Podfile gets a single line:

ruby
pod 'RxOptional'

With Carthage the Cartfile gets a dependency line and then an update:

code
github "RxSwiftCommunity/RxOptional" ~> 4.1.0
code
$ carthage update

With Swift Package Manager the Package.swift file gets the dependency:

swift
import PackageDescription

let package = Package(
    name: "ProjectName",
    dependencies: [
        .Package(url: "https://github.com/RxSwiftCommunity/RxOptional")
    ]
)

Two details in that last snippet are worth flagging, because they are older than the library's current release. The PackageDescription API shown, with a capital P on .Package and no tools version, is the pre-5.0 style, and a current Swift toolchain will want the lowercase .package form. The Carthage line pins the 4.1.0 range while the most recent release is 5.0.6. Neither stops you adopting the library, but both mean the README's install instructions are not a copy-paste for a modern project, and the repository ships its own Package.swift, a podspec and a Cartfile, so the current coordinates are available there.

Running the examples and the shape of the repository

The README documents how to exercise the library through a playground rather than a test target. You run pod install in the Example directory, select the RxOptional Examples target, build, show the Debug Area with cmd+shift+Y, and click the blue play button. The repository is more conventional than the README's install section suggests, with Sources, Tests, a Package.swift, a Package.resolved, an RxOptional.podspec, a Cartfile and Cartfile.resolved, an Xcode project, a playground directory, a .jazzy.yaml for documentation generation, a .swiftlint.yml and a .swiftformat configuration, a Dangerfile, a Gemfile, a Brewfile and a cleanup.sh script. There is also a CHANGELOG.md and a .versionrc, which is the standard tool for version bumps and changelog entries. The mix of CocoaPods, Carthage and SwiftPM artefacts in one tree is typical of a library that has to keep working for projects stuck on older tooling, which is the same constraint that produces the dated install snippets.

A 5.0 line with the last release in 2024

The release history is short and sits on the 5.0 line. v5.0.4 was published on 2021-10-05, v5.0.5 on 2022-04-14 and v5.0.6 on 2024-10-06, and the last push to the repository was on 2026-04-08, which is more than six months before this writing. In other words the repository is not archived and is not being worked on at the moment, and the most recent published version is roughly two years old. For a library whose entire surface is a handful of operators over standard types, that is less alarming than it sounds, since there is little to change and the dependency on RxSwift is what really determines compatibility. But the honest framing is that RxOptional is a stable, unchanging extension rather than an actively developed one, and the version range of RxSwift it builds against is the thing to check before adding it to a new project.

Editorial conclusion

RxOptional fits a Swift team using RxSwift who keeps hitting the same friction point, an optional arriving on a stream and the awkward choice between dropping it, substituting it or failing the stream. It does not fit a project that has moved to structured concurrency with async/await, or a codebase that would rather model absence in the type. Before adopting it, check that your RxSwift version still supports the operators you intend to use, decide whether you want the erroring variants, which are unavailable on Driver, and note that the installation instructions in the README still show the older PackageDescription API and an old Carthage version.

Frequently asked questions

What does RxOptional add to RxSwift?

It adds operators for optional values and for Occupiable types. For optionals there are filterNil, replaceNilWith, errorOnNil and catchOnNil, and for occupiable types such as String, Array, Dictionary and Set there are filterEmpty, errorOnEmpty and catchOnEmpty.

How do I install RxOptional?

Through CocoaPods with pod 'RxOptional' in the Podfile, through Carthage with a Cartfile entry and carthage update, or through Swift Package Manager by adding the dependency to Package.swift. Note that the Package.swift snippet in the README uses the older PackageDescription API and the Carthage line pins the 4.1.0 range, while the latest release is 5.0.6.

Can errorOnNil be used with a Driver?

No. The README states that errorOnNil is unavailable on Driver because Drivers cannot error out, and the same restriction applies to errorOnEmpty. All the other operators are available on Driver and Signal unless noted otherwise.

What types conform to Occupiable?

String, Array, Dictionary and Set, listed explicitly because Swift protocols cannot be extended to conform to other protocols. Custom types can conform to Occupiable themselves, so the empty-collection operators are not limited to those four.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. RxSwiftCommunity/RxOptional on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rxswiftcommunity-rxoptional.svg)](https://hysenlabs.com/projects/rxswiftcommunity-rxoptional)

Was this useful?