Tokamak

React-like framework for native UI written in pure Swift.

Tokamak provides a declarative, testable and scalable API for building UI components backed by fully native views. You can use it for your new iOS apps or add to existing apps with little effort and without rewriting the rest of the code or changing the app's overall architecture.

Tokamak recreates React Hooks API improving it with Swift's strong type system, high performance and efficient memory management thanks to being compiled to a native binary.

When compared to standard UIKit MVC or other patterns built on top of it (MVVM, MVP, VIPER etc), Tokamak provides:

  • Declarative DSL for native UI: no more conflicts caused by Storyboards, no template languages or XML. Describe UI of your app concisely in Swift and get views native to iOS with full support for accessibility, auto layout and native navigation gestures.

  • Easy to use one-way data binding: tired of didSet, delegates, notifications or KVO? UI components automatically update in response to state changes.

  • Clean composable architecture: components can be passed to other components as children with an established API focused on code reuse. You can easily embed Tokamak components within your existing UIKit code or vice versa: expose that code as Tokamak components. No need to decide whether you should subclass UIView or UIViewController to make your UI composable.

  • Off-screen rendering for unit-tests: no need to maintain slow and flaky UI tests that render everything on a simulator screen and simulate actual touch events to just test UI logic. Components written with Tokamak can be tested off-screen with tests completing in a fraction of a second. If your UI doesn't require any code specific to UIKit (and Tokamak provides helpers to achieve that) you can even run your UI-related unit-tests on Linux!

  • Platform-independent core: our main goal is to eventually support as many platforms as possible. Starting with iOS and UIKit, we plan to add renderers for macOS/AppKit, WebAssembly/DOM and native Android in future versions. As the core API is cross-platform, UI components written with Tokamak won't need to change to become available on newly added platforms unless you need UI logic specific to a device or OS. And if they do, you can still cleanly separate platform-specific components thanks to easy composition.

  • Architecture proven to work: React has been available for years and gained a lot of traction and is still growing. We've seen so many apps successfully rebuilt with it and heard positive feedback on React itself, but we also see a lot of complaints about its overreliance on JavaScript. Tokamak makes architecture of React with its established patterns available to you in Swift.

Important: Tokamak is relatively stable at this point, as in not having any blocking or critical bugs that the maintainers are aware of. The core API of Component and Hooks types is frozen, and there's a plenty of standard components to start building useful apps. If in the future there's a breaking change that's absolutely needed, deprecating old APIs in a source-compatible way and introducing a replacement gradually is the top priority. Nevertheless, its important to note this can't always be achieved.

Example code

An example of a Tokamak component that binds a button to a label, embedded within an existing UIKit app, looks like this:

import Tokamak
import TokamakUIKit

struct Counter: LeafComponent {
  struct Props: Equatable {
    let countFrom: Int
  }

  static func render(props: Props, hooks: Hooks) -> AnyNode {
    let count = hooks.state(props.countFrom)

    return StackView.node(.init(axis: .vertical,
                                distribution: .fillEqually,
                                Style(Edges.equal(to: .parent))), [
      Button.node(.init(onPress: Handler { count.set { $0 + 1 } }),
                  "Increment"),
      Label.node(.init(alignment: .center), "\(count.value)")
    ])
  }
}

final class ViewController: TokamakViewController {
  override var node: AnyNode {
    return Counter.node(.init(countFrom: 1))
  }
}

Tokamak

To run the example project, clone the repo, and run pod install from the Example directory first. Then you can open Example/Tokamak.xcworkspace and run the main executable target Tokamak-Example.

GitHub