SwiftUI-compatible framework for building browser apps with WebAssembly and native apps for other platforms

Overview

Tokamak logo

SwiftUI-compatible framework for building browser apps with WebAssembly

CI status Discord

At the moment Tokamak implements a very basic subset of SwiftUI. Its DOM renderer supports a few view types and modifiers (you can check the current list in the progress document), and a new HTML view for constructing arbitrary HTML. The long-term goal of Tokamak is to implement as much of SwiftUI API as possible and to provide a few more helpful additions that simplify HTML and CSS interactions.

If there's some SwiftUI API that's missing but you'd like to use it, please review the existing issues and PRs to get more details about the current status, or create a new issue to let us prioritize the development based on the demand. We also try to make the development of views and modifiers easier (with the help from the HTML view, see the example below), so pull requests are very welcome! Don't forget to check the "Contributing" section first.

If you'd like to participate in the growing SwiftWasm community, you're also very welcome to join our Discord server, or the #webassembly channel in the SwiftPM Slack.

Example code

Tokamak API attempts to resemble SwiftUI API as much as possible. The main difference is that you use import TokamakShim instead of import SwiftUI in your files. The former makes your views compatible with Apple platforms, as well as platforms supported by Tokamak (currently only WebAssembly/WASI with more coming in the future):

import TokamakShim

struct Counter: View {
  @State var count: Int
  let limit: Int

  var body: some View {
    if count < limit {
      VStack {
        Button("Increment") { count += 1 }
        Text("\(count)")
      }
      .onAppear { print("Counter.VStack onAppear") }
      .onDisappear { print("Counter.VStack onDisappear") }
    } else {
      VStack { Text("Limit exceeded") }
    }
  }
}

struct CounterApp: App {
  var body: some Scene {
    WindowGroup("Counter Demo") {
      Counter(count: 5, limit: 15)
    }
  }
}

// @main attribute is not supported in SwiftPM apps.
// See https://bugs.swift.org/browse/SR-12683 for more details.
CounterApp.main()

Arbitrary HTML

With the HTML view you can also render any HTML you want, including inline SVG:

struct SVGCircle: View {
  var body: some View {
    HTML("svg", ["width": "100", "height": "100"]) {
      HTML("circle", [
        "cx": "50", "cy": "50", "r": "40",
        "stroke": "green", "stroke-width": "4", "fill": "yellow",
      ])
    }
  }
}

HTML doesn't support event listeners, and is declared in the TokamakStaticHTML module, which TokamakDOM re-exports. The benefit of HTML is that you can use it for static rendering in libraries like TokamakVapor and TokamakPublish.

Another option is the DynamicHTML view provided by the TokamakDOM module, which has a listeners property with a corresponding initializer parameter. You can pass closures that can handle onclick, onmouseover and other DOM events for you in the listeners dictionary. Check out MDN docs for the full list.

Arbitrary styles and scripts

While JavaScriptKit is a great option for occasional interactions with JavaScript, sometimes you need to inject arbitrary scripts or styles, which can be done through direct DOM access:

import JavaScriptKit

let document = JSObject.global.document
let script = document.createElement("script")
script.setAttribute("src", "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js")
document.head.appendChild(script)

_ = document.head.insertAdjacentHTML("beforeend", #"""
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css">
"""#)

This way both Semantic UI styles and moment.js localized date formatting (or any arbitrary style/script/font added that way) are available in your app.

Requirements for app developers

  • macOS 10.15 and Xcode 11.4 or later. macOS 11.0 and Xcode 12.0 or later are required if you're building a multi-platform app with Tokamak that also needs to support SwiftUI on macOS.
  • Swift 5.2 or later and Ubuntu 18.04 if you'd like to use Linux. Other Linux distributions are currently not supported.

Requirements for app users

Any browser that supports WebAssembly should work, which currently includes:

  • Edge 16+
  • Firefox 53+
  • Chrome 57+
  • (Mobile) Safari 11+

Not all of these were tested though, compatibility reports are very welcome!

Getting started

Tokamak relies on carton as a primary build tool. As a part of these steps you'll install carton via Homebrew on macOS (unfortunately you'll have to build it manually on Linux). Assuming you already have Homebrew installed, you can create a new Tokamak app by following these steps:

  1. Install carton:
brew install swiftwasm/tap/carton

If you had carton installed before this, make sure you have version 0.9.0 or greater:

carton --version
  1. Create a directory for your project and make it current:
mkdir TokamakApp && cd TokamakApp
  1. Initialize the project from a template with carton:
carton init --template tokamak
  1. Build the project and start the development server, carton dev can be kept running during development:
carton dev
  1. Open http://127.0.0.1:8080/ in your browser to see the app running. You can edit the app source code in your favorite editor and save it, carton will immediately rebuild the app and reload all browser tabs that have the app open.

You can also clone this repository and run carton dev --product TokamakDemo in its root directory. This will build the demo app that shows almost all of the currently implemented APIs.

Troubleshooting

unable to find utility "xctest" error when building

This error can only happen on macOS, so make sure you have Xcode installed as listed in the requirements. If you do have Xcode installed but still get the error, please refer to this StackOverflow answer.

Syntax highlighting and autocomplete don't work in Xcode

Open Package.swift of your project that depends on Tokamak with Xcode and build it for macOS. As Xcode currently doesn't support cross-compilation for non-Apple platforms, your project can't be indexed if it doesn't build for macOS, even if it isn't fully function on macOS when running. If you need to exclude some WebAssembly-specific code in your own app that doesn't compile on macOS, you can rely on #if os(WASI) compiler directives.

All relevant modules of Tokamak (including TokamakDOM) should compile on macOS. You may see issues with TokamakShim on macOS Catalina, where relevant SwiftUI APIs aren't supported, but replacing import TokamakShim with import TokamakDOM should resolve the issue until you're able to update to macOS Big Sur.

If you stumble upon code in Tokamak that doesn't build on macOS and prevents syntax highlighting or autocomplete from working in Xcode, please report it as a bug.

Syntax highlighting and autocomplete don't work in VSCode

Make sure you have the SourceKit LSP extension installed. If you don't trust this unofficial release, please follow the manual building and installation guide. Apple currently doesn't provide an official build of the extension on the VSCode Marketplace unfortunately.

Contributing

Modular structure

Tokamak is built with modularity in mind, providing a multi-platform TokamakCore module and separate modules for platform-specific renderers. Currently, the only available renderer modules are TokamakDOM and TokamakStaticHTML, the latter can be used for static websites and server-side rendering. If you'd like to implement your own custom renderer, please refer to our renderers guide for more details.

Tokamak users only need to import a renderer module they would like to use, while TokamakCore is hidden as an "internal" Tokamak package target. Unfortunately, Swift does not allow us to specify that certain symbols in TokamakCore are private to a package, but they need to stay public for renderer modules to get access to them. Thus, the current workaround is to mark those symbols with underscores in their names to indicate this. It can be formulated as these "rules":

  1. If a symbol is restricted to a module and has no public access control, no need for an underscore.
  2. If a symbol is part of a public renderer module API (e.g. TokamakDOM), no need for an underscore, users may use those symbols directly, and it is re-exported from TokamakCore by the renderer module via public typealias.
  3. If a function or a type have public on them only by necessity to make them available in TokamakDOM, but unavailable to users (or not intended for public use), underscore is needed to indicate that.

The benefit of separate modules is that they allow us to provide separate renderers for different platforms. Users can pick and choose what they want to use, e.g. purely static websites would use only TokamakStaticHTML, single-page apps would use TokamakDOM, maybe in conjuction with TokamakStaticHTML for pre-rendering. As we'd like to try to implement a native renderer for Android at some point, probably in a separate TokamakAndroid module, Android apps would use TokamakAndroid with no need to be aware of any of the web modules.

Coding Style

This project uses SwiftFormat and SwiftLint to enforce formatting and coding style. SwiftFormat 0.45.3 and SwiftLint 0.39.2 or later versions are recommended. We encourage you to run SwiftFormat and SwiftLint within a local clone of the repository in whatever way works best for you. You can do that either manually, or automatically with VSCode extensions for SwiftFormat and SwiftLint respectively, or with the Xcode extension, or build phase.

To guarantee that these tools run before you commit your changes on macOS, you're encouraged to run this once to set up the pre-commit hook:

brew bundle # installs SwiftLint, SwiftFormat and pre-commit
pre-commit install # installs pre-commit hook to run checks before you commit

Refer to the pre-commit documentation page for more details and installation instructions for other platforms.

SwiftFormat and SwiftLint also run on CI for every PR and thus a CI build can fail with inconsistent formatting or style. We require CI builds to pass for all PRs before merging.

Code of Conduct

This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to [email protected].

Sponsorship

If this library saved you any amount of time or money, please consider sponsoring the work of its maintainers on their sponsorship pages: @carson-katri, @kateinoigakukun, and @MaxDesiatov. While some of the sponsorship tiers give you priority support or even consulting time, any amount is appreciated and helps in maintaining the project.

Maintainers

In alphabetical order: Carson Katri, David Hunt, Jed Fox, Max Desiatov, Morten Bek Ditlevsen, Yuta Saito.

Acknowledgments

  • Thanks to the Swift community for building one of the best programming languages available!
  • Thanks to everyone who developed React with its reconciler/renderer architecture that inspired Tokamak in the first place.
  • Thanks to the designers of the SwiftUI API who showed us how to write UI apps in Swift declaratively (arguably even in a better way than React did).
  • Thanks to SwiftWebUI for reverse-engineering some of the bits of SwiftUI and kickstarting the front-end Swift ecosystem for the web.
  • Thanks to Render, ReSwift, Katana UI and Komponents for inspiration!

SwiftUI is a trademark owned by Apple Inc. Software maintained as a part of the Tokamak project is not affiliated with Apple Inc.

License

Tokamak is available under the Apache 2.0 license. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the LICENSE file for more info.

Comments
  • Initial `NavigationView` implementation

    Initial `NavigationView` implementation

    Fixes #129.

    Current status:

    • [x] Displays the navigation links
    • [x] Accessible
    • [x] NavigationLink is clearly clickable
    • [x] Destination is visible after clicking
    SwiftUI compatibility 
    opened by j-f1 35
  • Expose TokamakCore as a library (for GTK renderer)

    Expose TokamakCore as a library (for GTK renderer)

    Expose TokamakCore as a library so that it can be used directly as an external dependency.

    I tried building a GTK wrapper using TokamakCore and lack the possibility of referencing the library directly.

    enhancement 
    opened by mortenbekditlevsen 24
  • Support meta tags in StaticHTMLRenderer

    Support meta tags in StaticHTMLRenderer

    This PR adds the ability to control <head> tags using a new HTMLTitle view and HTMLMeta view. Taking inspiration from Next.js <NextHead>, you can use these views anywhere in your view hierarchy and they will be hoisted to the top <head> section of the html.

    Use as a view:

    var body: some View {
      VStack {
        ...
        HTMLTitle("Hello, Tokamak")
        HTMLMeta(charset: "utf-8")
        ...
      }
    }
    

    Use as a view modifier:

    var body: some View {
      VStack {
        ...
      }
      .htmlTitle("Hello, Tokamak")
      .htmlMeta(charset: "utf-8")
    }
    

    And the resulting html (no matter where these are used in your view hierarchy):

    <html>
      <head>
        <title>Hello, Tokamak</title>
        <meta charset="utf-8">
      </head>
      <body>
        ...
      </body>
    </html>
    
    enhancement 
    opened by AndrewBarba 23
  • Add TextField, SecureField, and a progress document

    Add TextField, SecureField, and a progress document

    Currently, it seems that changing the text binding doesn’t actually affect the rendered input. I’m not sure why this happens, but I’m guessing it’s because the value attribute isn’t being set as <input>.value.

    I’ve added two TextFields to the demo, which are supposed to have their values synced. Pressing enter in the second one will increment the commit counter.

    SwiftUI compatibility 
    opened by j-f1 19
  • Set up code coverage reports on GitHub Actions

    Set up code coverage reports on GitHub Actions

    I think stability of our existing code should be of higher priority. We need to be diligent about keeping test coverage high and not reducing it significantly (ideally increasing it, as it's probably already too low) in any PRs. GitHub Actions job that runs in a PR and posts current coverage as a comment would be super helpful.

    After that is done we can set up more complex workflows that compute coverage deltas. I know that proprietary solutions like Codecov can already do this, but I'm wary of relying on them as they tend to shut down free access for open-source projects after some period of initial unsustainable growth. GitHub Actions seems to be a more reliable tool for this long term.

    good first issue continuous integration 
    opened by MaxDesiatov 17
  • Use setAttribute, not properties to fix SVG update

    Use setAttribute, not properties to fix SVG update

    Property assignment does not update SVG elements. We may want to use properties instead of setAttribute in the future for optimizations. For now I think that setAttribute works in all cases, including SVG, and seems safer to me.

    Resolves #278.

    bug 
    opened by MaxDesiatov 14
  • Add Link view, update JavaScriptKit to 0.8.0

    Add Link view, update JavaScriptKit to 0.8.0

    The Link View is added in this PR. I also upgraded JavaScriptKit to 0.8.0.

    NOTE: Publish integration is being moved to TokamakUI/TokamakPublish

    This adds the ability to use TokamakStaticHTML in place of Plot.

    A tokamakFoundation theme is provided, and custom ones can be created with the TokamakHTMLFactory protocol.

    That requires implementing the following functions:

    func makeIndexView(for index: Index, context: PublishingContext<Self.Site>) throws -> IndexView
    func makeSectionView(
        for section: Publish.Section<Self.Site>,
        context: PublishingContext<Self.Site>
      ) throws -> SectionView
    func makeItemView(for item: Item<Self.Site>, context: PublishingContext<Self.Site>) throws
        -> ItemView
    func makePageView(for page: Page, context: PublishingContext<Self.Site>) throws -> PageView
    func makeTagListView(for page: TagListPage, context: PublishingContext<Self.Site>) throws
        -> TagListView
    func makeTagDetailsView(for page: TagDetailsPage, context: PublishingContext<Self.Site>) throws
        -> TagDetailsView
    

    These will be rendered to HTML, and embedded in Plot.HTML elements.

    You can then declare your theme with an extension on the Theme struct:

    extension Theme {
      public static var yourThemeName: Self {
        Theme(htmlFactory: FoundationTokamakFactory())
      }
    }
    

    If you haven't used Publish before, you can install the CLI with:

    git clone https://github.com/JohnSundell/Publish.git
    cd Publish
    make
    

    Then run publish new and add TokamakStaticHTML as a dependency.

    enhancement 
    opened by carson-katri 14
  • Allow gtk modifiers to be expressed as css attributes.

    Allow gtk modifiers to be expressed as css attributes.

    Apply this to background modifier and added rectangle overlay (border) modifier.

    I don't know if it's the best API to have to conform to both WidgetModifier and WidgetAttributeModifier, but it does the trick...

    GTK renderer 
    opened by mortenbekditlevsen 13
  • Experimental

    Experimental "React Fiber"-like Reconciler

    This PR adds a new reconciler modeled after React Fiber. It has a few potential benefits over the current stack reconciler:

    1. Non-recursive. This may help mitigate some of the stack overflow issues.
    2. Reduces use of AnyView. We can instead use a ViewVisitor to traverse the View tree without type-erasing all of the children.
    3. Mounted element updates are batched, which may reduce some of the cost of bridging to JavaScript by deferring it until all mutations have been gathered, although I'm not sure if this is the case or not.
    4. Can be used to experiment with a custom layout engine that matches SwiftUI 1-1. I've been experimenting with this in the fiber/layout branch, but it does not work all that well yet.

    We will need to test it to figure out if these are actually of any benefit.

    This exists alongside the current StackReconciler class and Renderer protocol as FiberReconciler and FiberRenderer. I'd like to get some feedback on this to know if its something that would be worth the effort, and whether or not I've overlooked anything major in the design.

    I have only tested it with very simple Views. App and Scene are not supported, as well as preferences. However, preferences may be simple to implement with this model as we traverse down then back up the tree in the main reconciler loop.

    Note: To try the DOM renderer you must pass the custom index.html to Carton, as all DOM mutations are performed on the JavaScript side.

    refactor 
    opened by carson-katri 12
  • Add View Traits and transitions

    Add View Traits and transitions

    This adds _ViewTraitKey and related internal modifiers. Then transition builds on top of these traits.

    Here's the transition demo: https://user-images.githubusercontent.com/13581484/126044318-b2856cbb-56ed-4196-a0fd-6ea5c6c606c7.mov

    It works by applying the transition's modifier to the a View on mount/update/unmount.

    SwiftUI compatibility DOM/HTML renderer 
    opened by carson-katri 11
  • Simple Code Coverage analysis

    Simple Code Coverage analysis

    Closes https://github.com/TokamakUI/Tokamak/issues/350.

    Adds simple code coverage analysis. Until the GitHub token is set up in this repo, you can see the results including a comment on the PR here: https://github.com/mattpolzin/Tokamak/pull/2

    I only commented out the tests to make the coverage diff with main interesting. Obviously not expecting that change to get merged :)

    continuous integration 
    opened by mattpolzin 11
  • Implement Path.addArc(tangent1End:...).

    Implement Path.addArc(tangent1End:...).

    The addArc(tangent1End:tangent2End:radius:transform:) method was left unimplemented and added nothing to the path components. This implementation uses some vector arithmetic on CGPoint to create an arc. There are probably better approaches out there, but I think this a good starting point.

    enhancement 
    opened by filip-sakel 5
  • [wip] fix #523

    [wip] fix #523

    This is what I have done so far to make the example (with a little modification) from #523 work.

    Currently, things work until returning to the initial state, after which pressing the button will leave an empty screen.

    Some comments in no particular order:

    • The reproducer target is of course only for debugging and to be removed before merge.
    • I've left the logs as normal prints for now, since swift-log doesn't build on wasm rn, we can remove those as well if things get ready before swift-log is fixed.
    • Pay no attention to the individual commits, I'll clean those up once things look more mergeable.
    • The implementation of ReconcilePass.reconcile(_: , in:, caches:) obviously still needs to be refactored.
    • The reconciliation pass currently emits mutations that seem to assume replacing an element transfers any children to the replacement. I've adjusted the DOM and test renderers, but maybe the emitted mutations should be changed instead.
    • I tried turning the reproducer into a test case, but that fails one step earlier, while returning to the initial state, due to an out of range index in an insert mutation.
    • Another issue I observed: There are cases where a remove mutation is emitted with the wrong parent. The DOMRenderer doesn't use the parent, so it's not an issue there, but it does materialize with the test renderer.
    • The parent in the remove mutation is optional, but all places where such a value is created pass a non-nil parent. Is there a reason for this, or can this be turned non-optional?
    bug Fiber 
    opened by ahti 3
  • Fiber layout/reconciliation issues

    Fiber layout/reconciliation issues

    Sample code

    import TokamakDOM
    import Foundation
    
    @main
    struct TokamakApp: App {
        static let _configuration: _AppConfiguration = .init(reconciler: .fiber(useDynamicLayout: false))
        var body: some Scene { WindowGroup("Tokamak App") { ContentView() } }
    }
    
    enum State {
        case a
        case b([String])
        case c(String, [Int])
        case d(String, [Int], String)
    }
    
    final class StateManager: ObservableObject {
        private init() { }
        static let shared = StateManager()
    
        @Published var state = State.a
    }
    
    struct ContentView: View {
        @ObservedObject var sm = StateManager.shared
    
        var body: some View {
            switch sm.state {
            case .a:
                // VStack {
                    Button("go to b") {
                        sm.state = .b(["eins", "zwei", "drei"])
                    }
                // }
            case .b(let arr):
                VStack {
                    Text("b:")
                    ForEach(arr, id: \.self) { s in
                        Button(s) {
                            sm.state = .c(s, s == "zwei" ? [1, 2] : [1])
                        }
                    }
                }
            case .c(let str, let ints):
                VStack {
                    Text("c \(str)")
                        .font(.headline)
                    Text("hello there")
                    ForEach(ints, id: \.self) { i in
                        let d = "i = \(i)"
                        Button(d) {
                            sm.state = .d(str, ints, d)
                        }
                    }
                }
            case .d(_, let ints, let other):
                VStack {
                    Text("\(other)")
                    Text("count \(ints.count)")
                }
            }
        }
    }
    

    Describe the bug Using the fiber reconciler, the sample code above misbehaves in various ways:

    To Reproduce Steps to reproduce the behavior:

    1. Run the sample code
    2. Click "go to b"
    3. => The correct buttons for state b appear, but the text does not
    4. Click any button
    5. => The "i = number" buttons for state c appear, but some from state b are left over, still no text

    Changing the configuration to use dynamic layout puts all the buttons on top of each other.

    If you add the VStack to wrap the button for state a, and apply the change from #522 the buttons update properly, but the text is still missing.

    Expected behavior The way the code behaves when commenting out the _configuration. No lingering buttons, all texts show up.

    Desktop:

    • macOS 12.6
    • Safari 16
    • Version of Tokamak: main branch
    bug 
    opened by ahti 1
  • fix replaceChild parameter order

    fix replaceChild parameter order

    replaceChild expects the new child first, old child second (see MDN).

    To observe this issue in action, take the sample code from #523 and add back the VStack wrapping the body for state a, then run and click the button. In Safari, this throws the following:

    [Error] NotFoundError: The object can not be found here.
    	replaceChild (index.mjs:280)
    	swjs_call_function_with_this_no_catch (index.mjs:280)
    	wasm-stub
    	<?>.wasm-function[$s13JavaScriptKit27invokeNonThrowingJSFunction_9arguments4thisSo10RawJSValueaAA0G0C_SayAA013ConvertibleToK0_pGAA8JSObjectCSgtFAFSayAFGXEfU_AFSRyAFGXEfU_]
    	<?>.wasm-function[$s13JavaScriptKit27invokeNonThrowingJSFunction_9arguments4thisSo10RawJSValueaAA0G0C_SayAA013ConvertibleToK0_pGAA8JSObjectCSgtFAFSayAFGXEfU_AFSRyAFGXEfU_TA]
    	<?>.wasm-function[$sSRySo10RawJSValueaGABs5Error_pIgydzo_AcBsAD_pIegyrzo_TR]
    	<?>.wasm-function[$sSRySo10RawJSValueaGABs5Error_pIgydzo_AcBsAD_pIegyrzo_TRTA]
    	<?>.wasm-function[$sSa23withUnsafeBufferPointeryqd__qd__SRyxGKXEKlF]
    	<?>.wasm-function[$s13JavaScriptKit27invokeNonThrowingJSFunction_9arguments4thisSo10RawJSValueaAA0G0C_SayAA013ConvertibleToK0_pGAA8JSObjectCSgtFAFSayAFGXEfU_]
    	<?>.wasm-function[$s13JavaScriptKit27invokeNonThrowingJSFunction_9arguments4thisSo10RawJSValueaAA0G0C_SayAA013ConvertibleToK0_pGAA8JSObjectCSgtFAFSayAFGXEfU_TA]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF01_ghI0L_yqd0__SayAaB_pG_SiAFzqd0__AFXEtAaB_pRszr___lF]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF01_ghI0L_yqd0__SayAaB_pG_SiAFzqd0__AFXEtAaB_pRszr___lFqd0__AEXEfU_]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF01_ghI0L_yqd0__SayAaB_pG_SiAFzqd0__AFXEtAaB_pRszr___lFqd0__AEXEfU_TA]
    	<?>.wasm-function[$s13JavaScriptKit7JSValueO07withRawD0yxxSo0fD0aXElF]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF01_ghI0L_yqd0__SayAaB_pG_SiAFzqd0__AFXEtAaB_pRszr___lF]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF01_ghI0L_yqd0__SayAaB_pG_SiAFzqd0__AFXEtAaB_pRszr___lFqd0__AEXEfU_]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF01_ghI0L_yqd0__SayAaB_pG_SiAFzqd0__AFXEtAaB_pRszr___lFqd0__AEXEfU_TA]
    	<?>.wasm-function[$s13JavaScriptKit7JSValueO07withRawD0yxxSo0fD0aXElF]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF01_ghI0L_yqd0__SayAaB_pG_SiAFzqd0__AFXEtAaB_pRszr___lF]
    	<?>.wasm-function[$sSa13JavaScriptKitAA20ConvertibleToJSValue_pRszlE15withRawJSValuesyqd__qd__SaySo0hF0aGXElF]
    	<?>.wasm-function[$s13JavaScriptKit27invokeNonThrowingJSFunction_9arguments4thisSo10RawJSValueaAA0G0C_SayAA013ConvertibleToK0_pGAA8JSObjectCSgtF]
    	<?>.wasm-function[$s13JavaScriptKit10JSFunctionC14callAsFunction4this9argumentsAA7JSValueOAA8JSObjectCSg_SayAA013ConvertibleToJ0_pGtF]
    	<?>.wasm-function[$s13JavaScriptKit8JSObjectCyAA7JSValueOAA013ConvertibleToE0_pd_tcSgSScigAeaF_pd_tcfU_]
    	<?>.wasm-function[$s13JavaScriptKit8JSObjectCyAA7JSValueOAA013ConvertibleToE0_pd_tcSgSScigAeaF_pd_tcfU_TA]
    	<?>.wasm-function[$s10TokamakDOM16DOMFiberRendererV6commityySay0A4Core8MutationOyACGGF]
    	<?>.wasm-function[$s10TokamakDOM16DOMFiberRendererV0A4Core05FiberD0AadEP6commityySayAD8MutationOyxGGFTW]
    	<?>.wasm-function[$s11TokamakCore15FiberReconcilerC9reconcileyyF]
    	<?>.wasm-function[$s11TokamakCore15FiberReconcilerC12fiberChangedyyAC0C0Cyx_GFyycfU_]
    	<?>.wasm-function[$s11TokamakCore15FiberReconcilerC12fiberChangedyyAC0C0Cyx_GFyycfU_TA]
    	<?>.wasm-function[$s13OpenCombineJS11JSSchedulerC8schedule7options_yAC16SchedulerOptionsVSg_yyctFyycfU_]
    	<?>.wasm-function[$s13OpenCombineJS11JSSchedulerC8schedule7options_yAC16SchedulerOptionsVSg_yyctFyycfU_TA]
    	<?>.wasm-function[$s13JavaScriptKit7JSTimerC17millisecondsDelay11isRepeating8callbackACSd_SbyyctcfcAA7JSValueOSayAHGcfU0_]
    	<?>.wasm-function[$s13JavaScriptKit7JSTimerC17millisecondsDelay11isRepeating8callbackACSd_SbyyctcfcAA7JSValueOSayAHGcfU0_TA]
    	<?>.wasm-function[$s13JavaScriptKit16JSOneshotClosureC_4file4lineAcA7JSValueOSayAGGc_SSs6UInt32VtcfcAgHcfU0_]
    	<?>.wasm-function[$s13JavaScriptKit16JSOneshotClosureC_4file4lineAcA7JSValueOSayAGGc_SSs6UInt32VtcfcAgHcfU0_TA]
    	<?>.wasm-function[$sSay13JavaScriptKit7JSValueOGACIeggo_AdCIegnr_TR]
    	<?>.wasm-function[$sSay13JavaScriptKit7JSValueOGACIeggo_AdCIegnr_TRTA]
    	<?>.wasm-function[$sSay13JavaScriptKit7JSValueOGACIegnr_AdCIeggo_TR]
    	<?>.wasm-function[$sSay13JavaScriptKit7JSValueOGACIegnr_AdCIeggo_TRTA]
    	<?>.wasm-function[$s13JavaScriptKit24_call_host_function_implySbs6UInt32V_SPySo10RawJSValueaGs5Int32VADtF]
    	<?>.wasm-function[_call_host_function_impl]
    	<?>.wasm-function[swjs_call_host_function]
    	wasm-stub
    	callHostFunction (index.mjs:404)
    
    bug Fiber 
    opened by ahti 1
  • Add item in collection property wrapper support

    Add item in collection property wrapper support

    Describe the bug In SwiftUI 3, support was added for using a binding of an element in a collection.

    struct ExampleView: View {
      @State var directions: [Direction] = [SomeIdentifiableStructWithFields()]
    
      var body: some View {
        List($directions) { $direction in
          TextField("Instructions", text: $direction.text)
          Text(direction.text)
        }
      }
    }
    

    This allows for using a binding to a value and reading the value. Currently this behavior is not supported in Tokamak. In the example above, I get an error on $direction in saying

    Cannot use property wrapper projection parameter

    This isn't a big issue since you can technically use a ForEach on the count and index into the collection, but would be a nice to have!

    Expected behavior Using a binding would work in the above code snippet.

    Desktop (please complete the following information):

    • OS: macOS 12.6
    • Browser: Safari
    • Version of the browser: 16
    • Version of Tokamak: 0.11.0

    Thanks!

    bug 
    opened by Sammcb 1
Releases(0.11.1)
  • 0.11.1(Nov 29, 2022)

    This is a minor update to fix building from Xcode 14.1 (#528)

    What's Changed

    • Enable async/await in DOM Fiber renderer by @ahti in https://github.com/TokamakUI/Tokamak/pull/516
    • [bug] Missing CoreGraphics import for Swift 5.7.1 / Xcode 14.1 by @gregcotten in https://github.com/TokamakUI/Tokamak/pull/528

    New Contributors

    • @ahti made their first contribution in https://github.com/TokamakUI/Tokamak/pull/516
    • @gregcotten made their first contribution in https://github.com/TokamakUI/Tokamak/pull/528

    Full Changelog: https://github.com/TokamakUI/Tokamak/compare/0.11.0...0.11.1

    Source code(tar.gz)
    Source code(zip)
  • 0.11.0(Sep 27, 2022)

    This release adds support for SwiftWasm 5.7. It also includes a new reconciler modeled after React's Fiber reconciler. It can provide faster updates and allow for larger View hierarchies. It also includes layout steps that can match SwiftUI layouts closer than CSS approximations.

    You can specify which reconciler to use in your App's configuration:

    struct CounterApp: App {
      static let _configuration: _AppConfiguration = .init(
        // Specify `useDynamicLayout` to enable the layout steps in place of CSS approximations.
        reconciler: .fiber(useDynamicLayout: true)
      )
    
      var body: some Scene {
        WindowGroup("Counter Demo") {
          Counter(count: 5, limit: 15)
        }
      }
    }
    

    What's Changed

    • Experimental "React Fiber"-like Reconciler by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/471
    • Support meta tags in StaticHTMLRenderer by @AndrewBarba in https://github.com/TokamakUI/Tokamak/pull/483
    • Reenable macOS CI builds, using macOS 12 and Xcode 13.4 by @ezraberch in https://github.com/TokamakUI/Tokamak/pull/484
    • Add html5 doctype to static renderer by @AndrewBarba in https://github.com/TokamakUI/Tokamak/pull/486
    • Revert "Add html5 doctype to static renderer (#486)" by @MaxDesiatov in https://github.com/TokamakUI/Tokamak/pull/487
    • Clarify testing commands in CONTRIBUTING.md by @MaxDesiatov in https://github.com/TokamakUI/Tokamak/pull/488
    • Support foregroundColor for TextField by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/453
    • Custom Layout Engine for Fiber Reconciler by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/472
    • Add support for 'Fiber' label in CI by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/493
    • Tweak formatting rules for attributes by @MaxDesiatov in https://github.com/TokamakUI/Tokamak/pull/492
    • Add configuration options to App to choose reconciler by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/495
    • Add _ShapeView and background modifiers support to Fiber renderers by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/491
    • Add _PaddingLayout support to layout engine by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/485
    • Add file size to benchmark script by @ezraberch in https://github.com/TokamakUI/Tokamak/pull/496
    • Implement AnyShape by @filip-sakel in https://github.com/TokamakUI/Tokamak/pull/497
    • Fix build and tests for 5.7 toolchain by @kateinoigakukun in https://github.com/TokamakUI/Tokamak/pull/499
    • Add Layout protocol for FiberReconciler by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/498
    • Support Image in Fiber renderers by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/500
    • Add support for preferences, @StateObject, @EnvironmentObject, and custom DynamicProperty types by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/501
    • Allow window resizing in Fiber renderers by @carson-katri in https://github.com/TokamakUI/Tokamak/pull/502
    • Use Swift 5.7 nightly on CI, fix build issues by @MaxDesiatov in https://github.com/TokamakUI/Tokamak/pull/507
    • Update default assignee in bug_report.md by @MaxDesiatov in https://github.com/TokamakUI/Tokamak/pull/510
    • Disable GTK macOS builds by @MaxDesiatov in https://github.com/TokamakUI/Tokamak/pull/512
    • Fix typos in doc comments across the codebase by @BenedictSt in https://github.com/TokamakUI/Tokamak/pull/513
    • Create FAQ.md by @MaxDesiatov in https://github.com/TokamakUI/Tokamak/pull/511

    New Contributors

    • @AndrewBarba made their first contribution in https://github.com/TokamakUI/Tokamak/pull/483
    • @BenedictSt made their first contribution in https://github.com/TokamakUI/Tokamak/pull/513

    Full Changelog: https://github.com/TokamakUI/Tokamak/compare/0.10.1...0.11.0

    Source code(tar.gz)
    Source code(zip)
  • 0.10.1(May 20, 2022)

  • 0.10.0(Apr 9, 2022)

    This release adds support for SwiftWasm 5.6. It also updates JavaScriptKit and OpenCombineJS dependencies. Due to issues with support for older SwiftWasm releases in the carton/SwiftPM integration, Tokamak now requires SwiftWasm 5.6 or later, while SwiftWasm 5.4 and 5.5 are no longer supported.

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.9.1(Feb 16, 2022)

  • 0.9.0(Nov 26, 2021)

    This release adds support for SwiftWasm 5.5 and bumps the minimum required version to Swift 5.4. It now depends on JavaScriptKit 0.11.1, which no longer requires manual memory management of JSClosure instances. The downside of that update is that minimum browser version requirements are significantly higher now. See README.md for more details.

    Additionally, a few new features were added to the DOM renderer:

    • Canvas and TimelineView;
    • onHover modifier;
    • task modifier for running async functions;
    • Sanitizers for Text view.

    Many thanks (in alphabetical order) to @agg23, @carson-katri, @ezraberch, and @mbrandonw for their contributions to this release!

    Closed issues:

    • TextField Not Rendering the field (#455)
    • Can't find CGSize or CGFloat type (#450)
    • UnitPoint constants don't match SwiftUI (#443)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Aug 17, 2021)

    This release adds support for more SwiftUI types and modifiers, and fixes bugs. Including, but not limited to:

    • Toolbar type and toolbar modifier
    • ProgressView type
    • Animation and related types and modifiers
    • opacity, scaleEffect, aspectRatio, and controlSize modifiers
    • Material and Gradient types
    • HierarchicalShapeStyle (.primary/.secondary/.tertiary/.quaternary) type
    • ContainerRelativeShape type
    • spacing argument support for initializers of HStack and VStack
    • support for standard Foundation types, such as CGRect, CGSize (we previously used our own implementation of those, which weren't fully compatible with Foundation)
    • ability to sort HTML attributes when generating static HTML, which is essential for end-to-end tests that cover generated output.

    Many thanks to @carson-katri, @ezraberch, and @yonihemi for their contributions to this release!

    Closed issues:

    • Is there anyway to compile this from Xcode? (#406)
    • Xcode doesn't compile — gtk/gtk.h not found (#405)
    • Use NSGeometry types from Foundation (#404)
    • Adding padding to a view contained in a Button causes the Button to disappear (#403)
    • .background modifier with contained shape causes view to expand to full vertical size of the screen (#402)
    • Multi-line string handling in Text views (#400)
    • Content with spacer jumps when blurring and focusing the page (#395)
    • Frame sizes do not match expected behavior. (#387)
    • URL hash change demo crashes (#369)
    • Infinite loops w/ 100% CPU usage caused by stack overflows (#367)
    • TokamakDemo breaks after use of _domRef (#326)
    • Add support for toolbar modifier and related types (#316)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(May 3, 2021)

    This release introduces new view types such as DatePicker, new modifiers such as shadow, improves test coverage, updates dependencies, and fixes multiple bugs and crashes. Additionally, a proof of concept GTK renderer is now available in the TokamakGTK module.

    Many thanks to (in alphabetical order) @carson-katri, @filip-sakel @foscomputerservices, @literalpie, @mattpolzin, @mortenbekditlevsen, and @Snowy1803 for their contributions to this release!

    Closed issues:

    • @ObservedObject is a get-only property (#392)
    • What is the difference between HTML and DynamicHTML? (#388)
    • Reduce View.body Visibility (#385)
    • Verify that type constructor names contain contain module names (#368)
    • Crash when using a View with optional content (#362)
    • Set up code coverage reports on GitHub Actions (#350)
    • Shadow support (#324)
    • Implement DatePicker view in the DOM renderer (#320)
    • TokamakDemo build failed (#305)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.6.1(Dec 6, 2020)

  • 0.6.0(Dec 4, 2020)

    This release introduces support for the Image view, which can load images bundled as SwiftPM resources. It also adds the PreferenceKey protocol and preference(key:value:), onPreferenceChange, backgroundPreferenceValue, transformPreference, and overlayPreferenceValue modifiers. Many thanks to @carson-katri and @j-f1 for implementing this!

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.5.3(Nov 28, 2020)

    A bugfix release that fixes Toggle values not updated when reset from a binding. Additionally, the embedded internal implementation of JSScheduler is replaced with one from OpenCombineJS. This library is a new dependency of Tokamak used in the DOM renderer.

    Closed issues:

    • Toggle value not updated when it's reset from a binding (#287)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.5.2(Nov 12, 2020)

    This is a bugfix release that fixes in-tree updates in cases where type of a view changes with conditional updates. Thanks to @vi4m for reporting the issue!

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.5.1(Nov 9, 2020)

  • 0.5.0(Nov 8, 2020)

    This is a compatibility release with small feature additions. Namely the Link view is now available, and our JavaScriptKit dependency has been updated. The latter change now allows you to open Package.swift package manifests of your Tokamak projects with working auto-complete in Xcode. Also, our dark mode implementation now more closely follows SwiftUI behavior.

    Many thanks to @carson-katri and @kateinoigakukun for their contributions to this release!

    Closed issues:

    • Can't build Tokamak project - carton dev command (#296)
    • Colors should change depending on light/dark color scheme (#290)
    • Pattern for handling global dom events (#284)
    • 0.4.0 upgrade / regression? (#283)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Sep 30, 2020)

    This is mainly a bugfix and compatibility release with a small feature addition. Namely, Slider view is introduced in the DOM renderer, and binding updates for SVG elements are working now. During this development cycle efforts of our team were devoted to recently released JavaScriptKit 0.7 and carton 0.6. Both of those releases are pretty big updates that improve developer experience significantly, and this version of Tokamak requires those as minimum versions.

    Many thanks to @j-f1 and @kateinoigakukun for their contributions to these updates!

    Closed issues:

    • HTML + Binding (#278)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Aug 19, 2020)

    This release improves compatibility with the SwiftUI API and fixes bugs in our WebAssembly/DOM renderer, included but not limited to:

    • support for App/Scene lifecycle;
    • ColorScheme detection and environment setting;
    • dark mode styles;
    • @StateObject property wrapper implementation;
    • SidebarListStyle, ButtonStyle, GeometryProxy types;
    • NavigationView and GeometryReader views.

    Additionally, new TokamakStaticHTML renderer was added that supports rendering stateless views into static HTML that doesn't include any JavaScript or WebAssembly dependencies. This is useful for static websites and in the future could be used together with TokamakDOM for server-side rendering.

    Tokamak 0.3.0 now requires 5.3 snapshots of SwiftWasm, which in general should be more stable than the development snapshots that were previously used, and is also compatible with Xcode 12 betas. If you have a .swift-version file in your project, you should specify wasm-5.3-SNAPSHOT-2020-07-27-a in it or a later 5.3 snapshot, otherwise carton 0.5 selects a compatible 5.3 snapshot for you automatically. Allowing carton to select a default snapshot is the recommended approach, so in general we recommend avoiding .swif-version files in projects that use Tokamak.

    Many thanks to @carson-katri, @j-f1, and @Outcue for their contributions to this release.

    The complete list of changes included in this release is available below.

    Closed issues:

    • Command "carton dev" failed (#258)
    • Dark mode detection causes crashes in Safari (#245)
    • Add dark color scheme style (#237)
    • Establish App lifecycle as the only way to start rendering (#224)
    • Runtime issues with dynamic properties in App types (#222)
    • List appearance changes when reloaded (#212)
    • List scrolling does not work on Firefox 78 on macOS (#211)
    • Scrolling broken when List is child of NavigationView (#208)
    • Rectangle frame is not being set properly (#185)
    • Implement SidebarListStyle (#180)
    • Implement GeometryReader/GeometryProxy (#176)
    • @StateObject support (#158)
    • NavigationView/NavigationLink (#129)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Jul 21, 2020)

    This is the first release that supports WebAssembly and browsers with the new TokamakDOM module. The API now closely follows SwiftUI, while the new React-like API is no longer available. Unfortunately, since older versions of iOS don't support opaque types, and you already can use SwiftUI on iOS versions that do support it, iOS and macOS renderers are no longer available. Many thanks to @carson-katri, @j-f1, @helje5, @hodovani, @Outcue, @filip-sakel and @noontideox for their contributions to this release!

    Closed issues:

    • State vars have to be first (#190)
    • Implicit 8 pixel margin added to html body (#188)
    • Unable to set Color value (#186)
    • Crash in protocol conformance code (#167)
    • Extend Path to match the SwiftUI API (#156)
    • Some primitive Views cannot access @Environment (#139)
    • Logo for the project (#132)
    • ZStack? (#111)
    • View has - by default - a Body of type Never. (#110)
    • Getting value of type 'String' has no member 'components' (#108)
    • Does iOS 10 work? (#105)
    • Add Tokamak project linter (#77)
    • Ambiguous reference to member 'node' (#68)

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Mar 18, 2019)

  • 0.1.1(Mar 18, 2019)

  • 0.1.0(Mar 18, 2019)

Owner
TokamakUI
SwiftUI-compatible framework for building browser apps
TokamakUI
Swift Apps in a Swoosh! A modern framework for creating iOS apps, inspired by Redux.

Katana is a modern Swift framework for writing iOS applications' business logic that are testable and easy to reason about. Katana is strongly inspire

Bending Spoons 2.2k Jan 1, 2023
Very simple Observable and Publisher implementation for iOS apps.

Very simple Observable and Publisher implementation for iOS apps.

Igor Kulman 7 Jun 11, 2022
A simple example of the VIPER architecture for iOS apps

Counter Counter is a simple app showing the basics of the VIPER architecture, a version of Uncle Bob’s Clean Architecture for iOS apps. Counter shows

Mutual Mobile 353 Nov 6, 2022
Bond is a Swift binding framework that takes binding concepts to a whole new level.

Bond, Swift Bond Update: Bond 7 has been released! Check out the migration guide to learn more about the update. Bond is a Swift binding framework tha

Declarative Hub 4.2k Jan 5, 2023
UI event handling using Apple's combine framework.

Description Combinative is a library for UI event handling using Apple's combine framework. It doesn't need many dependencies because it is written ma

noppefoxwolf 106 Jan 29, 2022
Open source implementation of Apple's Combine framework for processing values over time.

OpenCombine Open-source implementation of Apple's Combine framework for processing values over time. The main goal of this project is to provide a com

OpenCombine 2.4k Dec 26, 2022
RxReduce is a lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way.

About Architecture concerns RxReduce Installation The key principles How to use RxReduce Tools and dependencies Travis CI Frameworks Platform Licence

RxSwift Community 125 Jan 29, 2022
🐌 snail - An observables framework for Swift

?? snail A lightweight observables framework, also available in Kotlin Installation Carthage You can install Carthage with Homebrew using the followin

Compass 179 Nov 21, 2022
EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

Ali Samaiee 4 Oct 5, 2022
A New, Modern Reactive State Management Library for Swift and SwiftUI (The iOS implementation of Recoil)

RecoilSwift RecoilSwift is a lightweight & reactive swift state management library. RecoilSwift is a SwiftUI implementation of recoil.js which powered

Holly Li 160 Dec 25, 2022
Predictable state management for SwiftUI applications.

SwiftDux Predictable state management for SwiftUI applications. SwiftDux is a state container inspired by Redux and built on top of Combine and SwiftU

Steven Lambion 148 Jul 4, 2022
Modern thread-safe and type-safe key-value observing for Swift and Objective-C

Now Archived and Forked PMKVObserver will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork o

Postmates Inc. 708 Jun 29, 2022
Define and chain Closures with Inputs and Outputs

Closure Define and chain Closures with Inputs and Outputs Examples No Scoped State let noStateCount = Closure<String, String> { text in String(repea

Zach Eriksen 3 May 18, 2022
Write great asynchronous code in Swift using futures and promises

BrightFutures How do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer. BrightFutures implements proven fu

Thomas Visser 1.9k Dec 20, 2022
A Swift based Future/Promises Library for IOS and OS X.

FutureKit for Swift A Swift based Future/Promises Library for IOS and OS X. Note - The latest FutureKit is works 3.0 For Swift 2.x compatibility use v

null 759 Dec 2, 2022
A library for reactive and unidirectional Swift applications

ReactorKit is a framework for a reactive and unidirectional Swift application architecture. This repository introduces the basic concept of ReactorKit

ReactorKit 2.5k Dec 28, 2022
A micro-library for creating and observing events.

Signals Signals is a library for creating and observing events. It replaces delegates, actions and NSNotificationCenter with something much more power

Tuomas Artman 454 Dec 21, 2022
Tempura - A holistic approach to iOS development, inspired by Redux and MVVM

Tempura is a holistic approach to iOS development, it borrows concepts from Redux (through Katana) and MVVM. ?? Installation Requirements CocoaPods ??

Bending Spoons 692 Dec 17, 2022
VueFlux is the architecture to manage state with unidirectional data flow for Swift, inspired by Vuex and Flux.

Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux Introduction VueFlux is the architecture to manage state with unidi

Ryo Aoyama 324 Dec 17, 2022