# Transmission brings UIKit presentation controllers to SwiftUI

> Transmission is a Swift package that wraps UIKit's presentation machinery in a SwiftUI shape, giving you a PresentationLink with a catalogue of transitions, a PresentationCoordinator for dismissing programmatically, detent helpers for sheets, and a TransitionReader for building interactive transitions driven by the transition coordinator's progress.

**nathantannar4/Transmission** — Bridges UIKit presentation APIs to a SwiftUI API so you can use presentation controllers, interactive transitions and more.

- Repository: https://github.com/nathantannar4/Transmission
- Stars: 1,188 · Forks: 63
- Language: Swift
- License: BSD-2-Clause
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/nathantannar4-transmission

## What SwiftUI still cannot do on its own

Transmission aims to improve SwiftUI view presentations and transitions, and it does this by bridging UIKit presentation APIs to a SwiftUI API so you can use presentation controllers, interactive transitions and more.

The problem it solves is a specific gap rather than a general dissatisfaction. SwiftUI has a sheet and a fullScreenCover, and they cover most cases. What they do not cover is the presentation controller, which is the object that decides how a view is presented, how it responds to a drag, what the background looks like and how it sizes itself. A designer who has spent time in UIKit knows that a good custom transition is a presentation controller, and in SwiftUI there has historically been no way to install one.

Transmission closes that gap by exposing the same machinery as a SwiftUI API. You write a PresentationLink, you pick a transition, and underneath there is a real UIPresentationController doing the work.

The result is that an interface can keep its SwiftUI state model and still get the interaction design that UIKit has always been better at, and that is a narrow, well defined goal rather than a framework.

The package is BSD-2-Clause licensed, the primary language is Swift, the last push was on 2026-10-08, and the releases are regular on a 2.x line, with 2.18.0 on 2026-09-17, 2.17.1 on 2026-09-16 and 2.17.0 on 2026-09-14.

## Installing it as a Swift package

There are two installation routes, and the Xcode one is the short one.

For an Xcode project, select File, Swift Packages, Add Package Dependency and enter the repository address, which is https://github.com/nathantannar4/Transmission.

For a Swift Package Manager project, you add it to the dependencies of your package and reference the product from your target.

```swift
let package = Package(
    //...
    dependencies: [
        .package(url: "https://github.com/nathantannar4/Transmission"),
    ],
    targets: [
        .target(
            name: "YourPackageTarget",
            dependencies: [
                .product(name: "Transmission", package: "Transmission"),
            ],
            //...
        ),
        //...
    ],
    //...
)
```

The package manifest at the root of the repository is the other half of the picture, and the repository is a clean Swift package layout with a Sources/ directory, a Package.swift, a .spi.yml for the Swift Package Index, an Example/ directory containing an Xcode project, and a Logo.png.

The Example project is the recommended starting point for sample code, and the readme says to build and run the included Example project. For a library whose API surface is a set of unfamiliar types, a runnable example is worth more than another page of documentation, and this one ships with the package.

## The transition catalogue

The central type is PresentationLinkTransition, and it is essentially an enumeration of the presentations UIKit already knows how to do, with a name for each one.

```swift
public struct PresentationLinkTransition {
    public static var `default`: PresentationLinkTransition
    public static let sheet: PresentationLinkTransition
    public static let currentContext: PresentationLinkTransition
    public static let fullscreen: PresentationLinkTransition
    public static let popover: PresentationLinkTransition
    public static let slide: PresentationLinkTransition
    public static let card: PresentationLinkTransition
    public static let zoom: PresentationLinkTransition
    public static let matchedGeometry: PresentationLinkTransition
    public static let toast: PresentationLinkTransition
}
```

The list is the API's summary. currentContext is the UIKit presentation that has no first class SwiftUI equivalent. card and toast are the modern ones, and matchedGeometry is the bridge to SwiftUI's own matched geometry effect.

Two of them carry availability constraints that the type declaration makes explicit. The whole struct is marked available from iOS 14.0, and the zoom transition is marked available from iOS 18.0, because it is built on a presentation style that only exists on that release.

The last case is the escape hatch, a custom function taking a type that conforms to PresentationLinkTransitionRepresentable, and the documentation points at PresentationController and InteractivePresentationController as the types to use. So the catalogue is not a closed list, it is a set of presets over a protocol you can implement yourself.

## The protocol that lets you install your own controller

The custom transition path is a protocol, and its shape is the whole extension mechanism.

```swift
public protocol PresentationLinkTransitionRepresentable {
    typealias Context = PresentationLinkTransitionRepresentableContext
    associatedtype UIPresentationControllerType: UIPresentationController

    func makeUIPresentationController(
        presented: UIViewController,
        presenting: UIViewController?,
        source: UIViewController,
        context: Context
    ) -> UIPresentationControllerType

    func updateUIPresentationController(
        presentationController: UIPresentationControllerType,
        context: Context
    )
}
```

So you implement two methods. The first builds the UIPresentationController, given the presented view controller, the presenting one, the source and a context. The second updates it, which is where you react to the state changing during the presentation, such as the interactive transition being driven or cancelled.

The associated type is what makes this type safe. The concrete controller type is part of the type system, so the value you get back from makeUIPresentationController is statically known, and a library that already has a controller subclass does not have to be wrapped in an existential to be used.

The documentation for the protocol carries a tip pointing at PresentationController and InteractivePresentationController, which is a hint that the package ships ready made implementations of the two cases you would otherwise write yourself, and that the protocol is there for the third case, which is the one only you can write.

## Detents, coordinators and the sheet API

Two extensions round out the API, and they solve the two things you always need and SwiftUI gives you awkwardly.

The first is detents. There is an extension on SheetTransitionOptions.Detent that adds a large detent, a medium detent, a detent with an auto-resolved height of the view's ideal size, a detent with a constant height created from an identifier and a CGFloat, and a detent whose height is lazily resolved through a closure taking a ResolutionContext and returning an optional CGFloat. The whole extension is marked available from iOS 15.0.

The second is the coordinator. PresentationCoordinator is a struct, frozen, available from iOS 14.0, and it is installed into the environment through an EnvironmentValues extension as presentationCoordinator. It carries an isPresented flag and a weak source view, and it has two dismiss methods, one taking an animation and one taking a transaction, both taking a count.

The count is the detail that matters. Dismissing a presented controller does not dismiss only the top one, because a controller can present another on top of itself, and a dismiss that pops the whole stack when you wanted to close one sheet is a bug nobody finds until a user reports it. Passing a count makes the depth explicit.

The weak source view is there so the coordinator can find the controller to dismiss without keeping the view alive, and the two dismiss overloads give you the choice between the SwiftUI transaction model and the older animation model, which is what a bridging library has to offer.

## TransitionReader turns transition progress into state

The last piece is the one that makes interactive transitions possible, and it is a container view rather than a modifier.

TransitionReader is described as a container view that defines its content as a function of its hosting view's UIViewControllerTransitionCoordinator transition progress, and the documentation tip says to use a TransitionReader to build interactive presentation and dismissal transitions.

Its proxy exposes the progress state of the transition from 0 to 1, where 1 is fully presented, and that value is the input to your own animation. So instead of a canned transition, you write the animation as a function of the drag, and the coordinator tells you how far the gesture has gone.

That is the technique UIKit has always had through the transition coordinator's percentComplete, and SwiftUI did not have a first class way to express. The proxy is the bridge.

It is also where the package earns its keep over the built-in SwiftUI transitions, because a custom sheet that follows the finger, with the content scaling and the background dimming in proportion, is the sort of detail that distinguishes an app, and it is a detail you write in SwiftUI here rather than in a separate view controller.

## Requirements, the family of packages and the preview

The requirements are listed plainly. The deployment target is iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0 or visionOS 1.0, and Xcode 16.4 or newer is required.

The deployment target of iOS 13 is lower than the iOS 14 on the core types, which tells you that the package supports a wider set of systems than the presentation features do. The availability annotations on each type are the mechanism for that, and they are consistent: the main types are iOS 14, the detent extension is iOS 15, and the zoom transition is iOS 18.

The repository is part of a family. The readme has a See Also section listing Engine, which the project says it was built using, plus Turbocharger, Metallic and Ignition, all from the same author. A link to a preview video sits above the requirements, and the readme is a fairly long API reference rather than a narrative, which is a sign of a package whose audience already knows what it is looking for.

The release history is active and incremental. Version 2.17.0, 2.17.1 and 2.18.0 all landed in the same fortnight in September 2026, which is a fast cadence for a library that wraps a platform API, and it suggests active work against the newer system releases that the availability annotations track.

## Conclusion

Adopt Transmission when your SwiftUI interface needs what UIKit still does better, namely custom presentation controllers, interactive dismissal and precise sheet detents, and you want to keep the SwiftUI state model rather than dropping to a UIViewControllerRepresentable. Do not adopt it for a simple sheet, since the built-in SwiftUI modifiers cover that case without a dependency. Verify first that your deployment targets are covered, because the package supports iOS 13, macOS 10.15, tvOS 13, watchOS 6 and visionOS 1 while several of its APIs are marked available only from iOS 14, iOS 15 or iOS 18, and check that your Xcode is 16.4 or newer.

## FAQ

### What does the Transmission package do?

It bridges UIKit presentation APIs to a SwiftUI API so you can use presentation controllers, interactive transitions and more, improving how SwiftUI views are presented and transitioned.

### How do I add Transmission to my project?

In Xcode, select File, Swift Packages, Add Package Dependency and enter the repository address. In a Swift Package Manager project, add .package(url:) to the dependencies and reference the Transmission product from your target's dependencies.

### Which transitions does Transmission provide?

PresentationLinkTransition has a default plus sheet, currentContext, fullscreen, popover, slide, card, zoom, matchedGeometry and toast, and a custom function that takes any type conforming to PresentationLinkTransitionRepresentable.

### How do I write a custom transition?

Conform to PresentationLinkTransitionRepresentable and implement makeUIPresentationController and updateUIPresentationController. The documentation suggests using the package's PresentationController or InteractivePresentationController for the common cases and the protocol for your own.

### What is PresentationCoordinator for?

It is a struct exposed in the environment as presentationCoordinator, with an isPresented flag and a weak source view, and a dismiss method taking a count, an animation or a transaction, so a presentation stack can be dismissed to a chosen depth.

### What are the platform requirements for Transmission?

A deployment target of iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0 or visionOS 1.0, and Xcode 16.4 or newer. Individual APIs carry their own availability, with the main types from iOS 14, the detent helpers from iOS 15 and the zoom transition from iOS 18.

## Sources

- [Issues](https://github.com/nathantannar4/Transmission/issues)
- [License: BSD-2-Clause](https://github.com/nathantannar4/Transmission/blob/main/LICENSE)
- [nathantannar4/Transmission on GitHub](https://github.com/nathantannar4/Transmission)
- [README](https://github.com/nathantannar4/Transmission/blob/main/README.md)
- [Releases](https://github.com/nathantannar4/Transmission/releases)

---

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