# UIKitUltra: a declarative, SwiftUI-shaped layer over UIKit and AppKit

> UIKitUltra, published as the Ultra module, gives iOS and macOS developers a SwiftUI-like declarative syntax while staying on UIKit and AppKit, with delayed constraints, reactive state bindings and Xcode live previews. It suits a team that wants SwiftUI's shape without giving up UIKit, and it is the wrong choice if you want a declarative system that is not a wrapper around the old view system.

**swiftstream/Ultra** — 🏰 Declarative crossplatform UIKit with LivePreview for iOS9+ (best alternative to SwiftUI)

- Repository: https://github.com/swiftstream/Ultra
- Website: https://swift.stream
- Stars: 629 · Forks: 36
- Language: Swift
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/swiftstream-ultra

## Declarative syntax without leaving the platform view system

UIKitUltra is described as a declarative, protocol-oriented UI framework built on UIKit and AppKit, and the project's own framing is blunt about why it exists: you will love UIKit more than ever. The repository description calls it the best alternative to SwiftUI, and the topic list includes betterthanswiftui and uikit and wrapper.

The point of the library is that it does not replace the platform view system. It wraps it. A view you construct is still a UIView, a control is still a UIControl, and layout is still autolayout. What changes is how you write the code that assembles them.

The requirements are stated precisely. Swift 6.2 or newer, Xcode 26 or newer, iOS 12 or newer, macOS 10.15 or newer, and tvOS 13 or newer, with iPadOS and Catalyst support in the badge row. The current Swift 6 line uses main-actor isolation for its public UI DSL protocols, which means you create and mutate UIKit and AppKit objects on the main actor and crossing that boundary requires an explicit actor hop.

There is a wrinkle worth catching early. The repository description still says iOS 9 and the readme explains that legacy iOS 9 support belongs to the pre-Swift-6 tags and branches, and is not a supported deployment target for the current main branch. A team starting today reads the description, sees iOS 9, and is wrong.

Another detail that affects every new project: application targets own their entry point explicitly, so you annotate the BaseApp or Ultra.App subclass with @main, and the bundled templates already do that.

## Delayed constraints, and why the order of calls stops mattering

The first feature listed is delayed constraints, and it is the one that most changes how the code reads. You declare all the constraints in advance before adding the view to the superview, and the example is a single chained line:

```swift
Button("Click me").width(300).centerInSuperview()
```

In plain UIKit, a constraint is an object you create, translate, activate, and hope the other view is already in the hierarchy. The order of those operations is the source of a familiar class of bugs where a constraint is added before the superview is known. Delaying the constraints removes that ordering requirement, which is why the example reads as one expression rather than a sequence.

The second feature is declarativity in the broad sense, and the examples make the point that anything can be chained. A text view with a color, an alignment and a font:

```swift
UText("Hello world").color(.red).alignment(.center).font(.sfProMedium, 15)
```

The library goes further than a single view, because the same chainable surface is available on plain strings and on multiple pieces composed together, so a label can mix coloured and differently weighted runs of text, and even a string literal can be styled directly. The stated goal for the whole feature set is purity: clean, short code without magic.

The third feature is reactivity through @UState, and the examples show three ways of binding. A plain string state, a number mapped to a string, and a boolean mapped to a conditional string:

```swift
@UState var text = "Hello world"
UText($text)

@UState var number = 5
UText($number.map { "\($0)" })

@UState var bool = false
UText($bool.map { $0 ? "enabled" : "disabled" })
```

That is the part where the design is genuinely different from UIKit's target-action model. A property wrapper holds the state, the value flows into the view, and the mapping lets one state drive a value of a different type, which is what makes a boolean usable directly in a text label without a separate observer.

## The body block, and the U prefix that avoids name clashes

There is a specific reason the library's views carry a U prefix, and the readme explains it plainly. Since the names of views are the same in UIKitUltra and in SwiftUI, aliases such as UButton for Button or UView for View are used, so that everything has a U prefix.

The rule is conditional on what you need. The documentation says the aliasing is only necessary if you want to use live previews, because otherwise there is no need to import SwiftUI and so no name conflict. In other words, in a project with no SwiftUI import, you could call the views Button and View, and the U prefix is the price of having both worlds in one file.

The SwiftUI-shaped part is the body block, where subviews are declared the way SwiftUI does it:

```swift
body {
    View1()
    View2()
    View3()
    // btw it is NOT limited to 10
}
```

The comment about not being limited to ten is a jab at SwiftUI's own ten-view limit in a body, and it is worth noting that the constraint does not apply here.

The readme is explicit that this is still UIKit underneath, and that you should not forget that autolayout is what is doing the work. The SwiftUI-like syntax is a surface, not a new rendering model.

Around that core the library adds the pieces a real application needs. There is a built-in ImageLoader so that setting a URL on an Image is enough, and it is overridable, with variants for a default image shown while loading, for releasing the image before loading starts, for replacing immediately after loading, and for a fade effect, and you can subclass ImageLoader for a custom one. There is device model and type detection with the ability to set values based on the device, expressed with the same chainable syntax. There are localizable strings, where you set a default localization and can override the current one, and the readme is explicit that this is a mechanism rather than a full replacement for the platform's own approach. And there are custom trait collections.

## Live previews, and the Xcode 26 installation path

Installation is through Swift Package Manager, which the readme names as the supported dependency-manager path for the current UIKitUltra release line. In Xcode 26 or newer, you choose File, then Add Package Dependencies, and enter the package URL:

```text
https://github.com/swiftstream/UIKitUltra
```

Application source then imports the Ultra module.

There is a related detail about SwiftUI-backed previews. They are availability-gated, and Xcode 26 weak-links the later-availability system frameworks automatically, so current projects do not need a manual -weak_framework SwiftUI linker flag. That is a small convenience, but it is the kind of detail that used to cost an afternoon.

The repository also ships three Xcode project templates. There is a UIKitUltra App for iOS and iPadOS at iOS 12 or later, a UIKitUltra macOS App for macOS 10.15 or later, and a UIKitUltra Multiplatform App with one iOS, iPadOS and macOS target with platform-specific entry points and shared UI code. Installing them is a copy into the Xcode templates directory:

```bash
mkdir -p ~/Library/Developer/Xcode/Templates
cp -R "Templates/Project Templates" ~/Library/Developer/Xcode/Templates/
```

After a restart of Xcode you choose File, then New, then Project, and pick the template.

There is a catch the readme is upfront about. The templates do not embed a package reference, because Xcode project-template metadata cannot reliably declare an external local or remote Swift package dependency. You add UIKitUltra yourself through Swift Package Dependencies afterwards, entering the repository URL for a published dependency or choosing Add Local for a checkout you are working in, and then you add the Ultra library product to the generated app target. The multiplatform template has one target, so the product is linked once.

Live preview itself is provided by SwiftUI and is available only since macOS Catalina. The readme notes that you can create as many preview structs as you need, and the example wraps a view controller in a PreviewProvider conforming to a DeclarativePreview protocol.

## Where UIKitUltra is the wrong choice

The first limitation is the most important one to state plainly. This is a wrapper. Every view you build is a UIKit view, and the layout is autolayout, so you inherit UIKit's constraints and its performance characteristics, not SwiftUI's. If the reason you wanted SwiftUI was its rendering model rather than its syntax, this library does not give you that. The SwiftUI-like shape is a convenience layer over the same machinery, and a team that has already measured the limits of UIKit will measure them here too.

The second is the platform floor in practice, not just on paper. Swift 6.2, Xcode 26, iOS 12 and macOS 10.15 is a modern toolchain requirement, and the main-actor isolation on the public UI DSL protocols means that any code touching these views from a background task needs an explicit hop. That is correct Swift concurrency, and it is also friction for code that was previously free of it.

The third is the release history, and it deserves a close look. The published tags are 2.0.0 from 2021-09-22, 1.25.0 from 2020-04-18 and 1.24.0 from 2020-04-17, so the last named release is from five years ago, while the last push was on 2026-09-28. The badges say Swift 6.2 and Xcode 26, which are recent toolchains, so the code is clearly still being worked on, but there is no recent tag to pin. A team that adopts a UI framework on a moving branch without a versioned release is taking on that risk deliberately.

The fourth is the ecosystem. A wrapper around UIKit cannot be used from SwiftUI, so a project that adopts SwiftUI later has to rewrite the interface layer rather than swap the framework, and the community plugins and examples for SwiftUI do not apply here.

The fifth is that the U prefix is a permanent tax on the codebase for the benefit of an optional preview feature, and a codebase that already imports SwiftUI for other reasons has to deal with the aliases in every file that touches the UI layer.

## Conclusion

UIKitUltra fits an iOS team that has decided to stay on UIKit and AppKit but wants the ergonomics of SwiftUI's declarative style, since delayed constraints, chainable modifiers and reactive bindings remove most of the layout code without replacing the platform view system. It is the wrong choice for a greenfield project that would rather start on SwiftUI, and it is the wrong choice if you need iOS 9 support, since that belongs to the pre-Swift-6 tags. Before adopting it, check the toolchain requirements, since the current line needs Swift 6.2, Xcode 26, iOS 12 and macOS 10.15, and note that the last push was on 2026-09-28 with the newest published release being 2.0.0 from 2021-09-22.

## FAQ

### What is UIKitUltra and how does it relate to SwiftUI?

UIKitUltra is a declarative, protocol-oriented UI framework built on UIKit and AppKit, giving SwiftUI-shaped syntax while staying on the platform view system and autolayout. It is published as the Ultra module, and views carry a U prefix so that they do not clash with SwiftUI names when both are imported.

### How do I install UIKitUltra?

Add the package through Swift Package Manager: in Xcode 26 or newer choose File, then Add Package Dependencies, and enter https://github.com/swiftstream/UIKitUltra. Application source then imports the Ultra module.

### What are the system requirements for UIKitUltra?

Swift 6.2 or newer, Xcode 26 or newer, iOS 12 or newer, macOS 10.15 or newer and tvOS 13 or newer, with iPadOS and Catalyst support. Legacy iOS 9 support belongs to the pre-Swift-6 tags and branches and is not a target of the current main branch.

### What do delayed constraints mean in UIKitUltra?

You declare all the constraints in advance, before the view is added to its superview, so a chain such as Button("Click me").width(300).centerInSuperview() can be written as a single expression without worrying about the order in which the view and its constraints are installed.

### What licence is UIKitUltra released under?

MIT, with the licence file in the repository root. The last push was on 2026-09-28, and the newest published release tag is 2.0.0 from 2021-09-22.

## Sources

- [License: MIT](https://github.com/swiftstream/Ultra/blob/master/LICENSE)
- [Project website](https://swift.stream)
- [README](https://github.com/swiftstream/Ultra/blob/master/README.md)
- [Releases](https://github.com/swiftstream/Ultra/releases)
- [swiftstream/Ultra on GitHub](https://github.com/swiftstream/Ultra)

---

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