# Quick/Nimble: a matcher framework for Swift and Objective-C tests

> Nimble replaces XCTAssert calls with readable expectation expressions. Here is what it does, how to install it, and where its design costs you.

**Quick/Nimble** — A Matcher Framework for Swift and Objective-C

- Repository: https://github.com/Quick/Nimble
- Website: https://quick.github.io/Nimble/documentation/nimble/
- Stars: 4,836 · Forks: 609
- Language: Swift
- License: Apache-2.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/quick-nimble

## What Nimble changes about assertion syntax

XCTest ships a family of XCTAssert functions, each with its own parameter list. Nimble replaces that with one expression form: expect(value).to(matcher), where the matcher is a function such as equal, beCloseTo, contain or beTruthy. The README's own example shows the shape: expect(1 + 1).to(equal(2)), expect(1.2).to(beCloseTo(1.1, within: 0.1)), expect("seahorse").to(contain("sea")), and expect(["Atlantic", "Pacific"]).toNot(contain("Mississippi")).

The audience is narrow and specific. This is a library for test targets in Swift and Objective-C projects, and the README states it can be used alone or alongside Quick, its sister project. It is not shipped in app binaries. The README's privacy statement says Nimble "is a library that is only used for testing and should never be included in the binary submitted to App Store Connect," and adds that it collects no analytics or tracking. That matters for teams that have to justify every dependency in a shipping binary: Nimble is designed to never be in one.

The project is Apache-2.0 licensed and its documentation has moved into a DocC catalog at Sources/Nimble/Nimble.docc, browsable at quick.github.io/Nimble/documentation/nimble/. The README points there rather than carrying a matcher reference itself, so the README alone will not tell you which matchers exist in your version.

## The mechanism: expectations, matchers and operator overloads

Nimble's core is a generic expectation object. expect(...) wraps a value, .to(...) and .toNot(...) apply a matcher to it, and the matcher decides whether the value satisfies the condition. Matchers are functions, so they compose with arguments: beCloseTo takes a within tolerance, contain takes a substring or element, beTruthy takes nothing. Because Swift generics drive the wrapping, the same expect call works across value types, collections and booleans without a separate function per type.

The repository's topic list names the pieces the project itself considers central: matcher-functions, operator-overloads, asynchronous-expectations, failure-messages, swift-generics. Operator overloads are visible in the README example: expect(3) > 2 uses a comparison operator instead of a matcher function. Asynchronous expectations appear as toEventually, shown in the README as expect(ocean.isClean).toEventually(beTruthy()). That form polls an expression over time rather than asserting once, which is the mechanism behind testing state that settles asynchronously.

Failure messages are listed as a topic, which is the practical reason teams pick this over raw XCTAssert: a failing expectation can report the expression and the matcher rather than a generic assertion failure. The README does not document the message format, so check the DocC catalog if message text matters to your CI output.

## Installing Nimble with Swift Package Manager

The README lists four installation routes: Swift Package Manager, CocoaPods, Carthage and git submodules. In Xcode, the README's steps are to select your project configuration, then the project tab, then the Package Dependencies tab, click the plus button, and specify https://github.com/Quick/Nimble.git as the url. It adds one instruction worth repeating: add Nimble as a dependency of your unit test target, not your app target.

For a package manifest, the README gives this dependency and test target wiring:

```swift
// swift-tools-version:5.7
import PackageDescription

let package = Package(
    name: "MyAwesomeLibrary",
    dependencies: [
        .package(url: "https://github.com/Quick/Nimble.git", from: "13.0.0"),
    ],
    targets: [
        .testTarget(
            name: "MyAwesomeLibraryTests",
            dependencies: ["MyAwesomeLibrary", "Nimble"]),
    ]
)
```

Note the version floor in that snippet: the README's example pins from "13.0.0", while the most recent release listed in the repository is v14.0.0 from 2025-11-28. If you copy the snippet verbatim you will resolve to a 13.x line unless you raise the floor yourself.

After the dependency resolves, the first real use is a single expectation in a test method. The README's opening example is the smallest useful thing to write:

```swift
expect(1 + 1).to(equal(2))
expect("seahorse").to(contain("sea"))
expect(ocean.isClean).toEventually(beTruthy())
```

If the expectations compile and run, the target is wired correctly. A failure to resolve equal, contain or beTruthy means Nimble is not linked to that test target.

## CocoaPods, Carthage and submodule installs

CocoaPods is the route the README spells out for macOS, iOS, tvOS and watchOS apps. Add Nimble to your Podfile inside a test target, include the use_frameworks! line for Swift support, then run pod install. The README's example uses platform :ios, '13.0' and the Specs source, with the pod declared in a target named for your app's tests. The README does not list a minimum deployment target beyond that example, so treat the version there as illustrative of the snippet rather than a stated floor.

Carthage takes a different declaration. The README says to add Nimble to your Cartfile.private with github "Quick/Nimble" ~> 13.2, then follow the Carthage Quick Start and link Nimble with your unit tests. The private Cartfile convention is the right one here: it keeps a test-only dependency out of the public Cartfile that consumers of your framework would resolve.

The fourth route is git submodules, and the README's four steps are to clone the Nimble repository, add Nimble.xcodeproj to your Xcode workspace, link Nimble.framework to your test target, and start writing expectations. It then points at Quick's installation documentation and tells you to ignore the steps involving adding Quick if you only want Nimble. That cross-reference is the weakest part of the README: the submodule instructions are a redirect to another project's instructions rather than a self-contained walkthrough.

## raiseException and other limits worth knowing before you commit

The clearest documented limitation is installation-dependent. The README states plainly: "Please note that if you install Nimble using Swift Package Manager, then raiseException is not available." If your suite asserts on Objective-C exceptions, that single sentence rules out the most common modern install path, and you are pushed toward CocoaPods, Carthage or submodules to keep the capability.

The second limitation is scope. Nimble is an assertion library, not a test runner. It does not discover tests, manage fixtures or produce a report; it sits inside whatever runner you already use, XCTest or Quick. Teams looking for a full BDD stack should read Quick's documentation, which the README links for the combined install. Nimble alone gives you matchers and nothing else.

The third is documentation surface. The README has moved its reference material into Sources/Nimble/Nimble.docc and sends readers to the hosted catalog. Everything about matcher availability, failure message formatting and asynchronous expectation timing lives there, not in the README. If you evaluate a dependency by reading its README, you will come away knowing the syntax shape but not the matcher inventory.

## Nimble against plain XCTest assertions

The real alternative is not another matcher library; it is XCTest's own assertions, which ship with the toolchain and require no dependency, no Podfile entry and no manifest change. XCTAssertEqual(1 + 1, 2) does the same job as expect(1 + 1).to(equal(2)). The difference is in what happens as assertions get more complex.

XCTest needs a distinct function per comparison shape: equality, nil checks, boolean checks, floating point accuracy, collection membership. Nimble collapses those into expect(...).to(matcher), and the matcher list is extensible because matchers are just functions. Floating point tolerance is the clearest case: beCloseTo(1.1, within: 0.1) reads as a single assertion, while the XCTest equivalent is a dedicated accuracy overload.

The second difference is asynchronous testing. toEventually polls an expression until it succeeds or times out, which is the pattern for asserting on state that settles after a network call or animation. XCTest has its own expectation API for this, so the choice is stylistic rather than a capability gap, but the Nimble form stays inside the same expect syntax you use everywhere else. Against Quick specifically, the split is clean: Quick supplies the describe/it structure, Nimble supplies the assertions inside it. Using Nimble without Quick is documented and supported.

## Maintenance, upgrades and licence

The repository is not archived, and the last push was on 2026-05-04. The release history shows v14.0.0 on 2025-11-28, v13.8.0 on 2025-10-01 and v13.7.1 on 2024-12-16, so the 13.x line had a long gap before 13.8.0 landed. Major-version bumps are the upgrade cost to plan for: the README's own manifest example still pins from "13.0.0" while v14.0.0 exists, which means the documentation lags the release line. Check the release notes for v14 before moving a suite across a major boundary, because the README does not describe what changed.

Platform support is stated in the README's install sections: macOS, iOS, tvOS and watchOS for CocoaPods and Carthage. The manifest snippet uses swift-tools-version:5.7, and the repository carries both Package.swift and Package@swift-5.9.swift, so there is a separate manifest for the newer toolchain. There is also a Dockerfile.test and a script/ directory at the top level, which suggests a container-based test path, though the README does not document either.

Nimble is Apache-2.0. That is a permissive licence with an explicit patent grant, and it is compatible with the common practice of linking a test-only framework into a test target. This is not legal advice; if your organisation has a dependency review process, the licence identifier to submit is Apache-2.0, and the README's privacy statement is the relevant text for the question of whether Nimble ships to users.

## Conclusion

Nimble fits teams already writing XCTest or Quick suites in Swift or Objective-C who want expectation syntax instead of XCTAssert overloads. Skip it if you depend on raiseException and install through Swift Package Manager, since the README states that combination is not available, or if your tests are plain value checks that XCTAssertEqual already covers. Before adopting, confirm your test target rather than your app target is the one linking Nimble, and check the .docc catalog under Sources/Nimble/Nimble.docc for the matcher list your version ships.

## FAQ

### How do I install Nimble in a Swift package?

Add a package dependency on https://github.com/Quick/Nimble.git in your Package.swift and list "Nimble" in your test target's dependencies. The README's example uses from: "13.0.0", but the most recent release is v14.0.0, so raise the floor if you want the 14.x line.

### How do I use Nimble in a test?

Wrap a value in expect and apply a matcher with to or toNot, as in expect(1 + 1).to(equal(2)) or expect("seahorse").to(contain("sea")). For state that settles over time, the README shows expect(ocean.isClean).toEventually(beTruthy()).

### Can I install Nimble with Swift Package Manager and still use raiseException?

No. The README states that if you install Nimble using Swift Package Manager, raiseException is not available. Use CocoaPods, Carthage or git submodules if your suite needs it.

### Does Nimble work without Quick?

Yes. The README says Nimble can be used on its own or in conjunction with its sister project Quick, and the git submodule instructions tell you to ignore the steps involving adding Quick if you only want Nimble.

### Is Nimble included in the app binary submitted to the App Store?

According to the README's privacy statement, Nimble is only used for testing and should never be included in the binary submitted to App Store Connect. The same statement says it collects no analytics or tracking.

### Where is Nimble's matcher documentation?

The README says the documentation now lives in Sources/Nimble/Nimble.docc as a Documentation Catalog, browsable at quick.github.io/Nimble/documentation/nimble/. The README itself shows only a small set of example matchers.

## Sources

- [License: Apache-2.0](https://github.com/Quick/Nimble/blob/main/LICENSE)
- [Project website](https://quick.github.io/Nimble/documentation/nimble/)
- [Quick/Nimble on GitHub](https://github.com/Quick/Nimble)
- [README](https://github.com/Quick/Nimble/blob/main/README.md)
- [Releases](https://github.com/Quick/Nimble/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/quick-nimble
