Skip to content

Commit

Permalink
Remove DefaultApp, make DOMRenderer internal (#227)
Browse files Browse the repository at this point in the history
Removes the `View`-based initializer of `DOMRenderer` which no longer leaves any `public` initializers on it, means we can make it fully internal. `DOMNode` is now internal too, which is great as it was an implementation detail anyway. Corollary, `DefaultApp` is no longer needed.

`Target` was cleaned up is it doesn't need to hold `App` or `Scene` values, now it's just a simple protocol.

I've updated `README.md` to show usage of the `App` protocol in the basic example.

Closes #224.
  • Loading branch information
MaxDesiatov authored Aug 1, 2020
1 parent e37d130 commit 40804d4
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 100 deletions.
39 changes: 17 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,18 @@ struct Counter: View {
}
}
}
```

You can then render your view in any DOM node captured with
[JavaScriptKit](https://github.com/kateinoigakukun/JavaScriptKit/), just
pass it as an argument to the `DOMRenderer` initializer together with your view:

```swift
import JavaScriptKit
import TokamakDOM

let document = JSObjectRef.global.document.object!

let divElement = document.createElement!("div").object!
let renderer = DOMRenderer(Counter(count: 5, limit: 15), divElement)
struct CounterApp: App {
var body: some Scene {
WindowGroup("Counter Demo") {
Counter(count: 5, limit: 15)
}
}
}

let body = document.body.object!
_ = body.appendChild!(divElement)
// @main attribute is not supported in SwiftPM apps.
// See https://bugs.swift.org/browse/SR-12683 for more details.
CounterApp.main()
```

### Arbitrary HTML
Expand Down Expand Up @@ -108,9 +103,9 @@ app.
## Requirements for app developers

- macOS 10.15 and Xcode 11.4/11.5/11.6 for macOS. Xcode betas are currently not supported. You can have
those installed, but please make sure you use
[`xcode-select`](https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-HOW_DO_I_SELECT_THE_DEFAULT_VERSION_OF_XCODE_TO_USE_FOR_MY_COMMAND_LINE_TOOLS_)
to point it to a release version of Xcode.
those installed, but please make sure you use
[`xcode-select`](https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-HOW_DO_I_SELECT_THE_DEFAULT_VERSION_OF_XCODE_TO_USE_FOR_MY_COMMAND_LINE_TOOLS_)
to point it to a release version of Xcode.
- [Swift 5.2 or later](https://swift.org/download/) for Linux.

## Requirements for app users
Expand All @@ -128,7 +123,7 @@ Not all of these were tested though, compatibility reports are very welcome!

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

1. Install `carton`:
Expand Down Expand Up @@ -156,15 +151,15 @@ carton init --template tokamak
```

4. Build the project and start the development server, `carton dev` can be kept running
during development:
during development:

```
carton dev
```

5. Open [http://127.0.0.1:8080/](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.
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` in its root directory. This
will build the demo app that shows almost all of the currently implemented APIs.
Expand Down
50 changes: 2 additions & 48 deletions Sources/TokamakCore/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,52 +15,6 @@
// Created by Max Desiatov on 10/02/2019.
//

open class Target {
var element: MountedElementKind
public internal(set) var app: _AnyApp {
get {
if case let .app(app) = element {
return app
} else {
fatalError("`Target` has type \(element) not `App`")
}
}
set {
element = .app(newValue)
}
}

public internal(set) var scene: _AnyScene {
get {
if case let .scene(scene) = element {
return scene
} else {
fatalError("`Target` has type \(element) not `Scene`")
}
}
set {
element = .scene(newValue)
}
}

public internal(set) var view: AnyView {
get {
if case let .view(view) = element {
return view
} else {
fatalError("`Target` has type \(element) not `View`")
}
}
set {
element = .view(newValue)
}
}

public init<V: View>(_ view: V) {
element = .view(AnyView(view))
}

public init<A: App>(_ app: A) {
element = .app(_AnyApp(app))
}
public protocol Target: AnyObject {
var view: AnyView { get set }
}
10 changes: 0 additions & 10 deletions Sources/TokamakDOM/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,3 @@ extension App {
ScenePhaseObserver.publisher
}
}

struct DefaultApp<V: View>: App {
var content: V?

var body: some Scene {
WindowGroup {
content
}
}
}
12 changes: 6 additions & 6 deletions Sources/TokamakDOM/DOMNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@
import JavaScriptKit
import TokamakCore

public final class DOMNode: Target {
final class DOMNode: Target {
let ref: JSObjectRef
private var listeners: [String: JSClosure]
var view: AnyView

init<V: View>(_ view: V, _ ref: JSObjectRef, _ listeners: [String: Listener] = [:]) {
self.ref = ref
self.listeners = [:]
super.init(view)
self.view = AnyView(view)
reinstall(listeners)
}

init<A: App>(_ app: A, _ ref: JSObjectRef, _ listeners: [String: Listener] = [:]) {
init(_ ref: JSObjectRef) {
self.ref = ref
self.listeners = [:]
super.init(app)
reinstall(listeners)
view = AnyView(EmptyView())
listeners = [:]
}

/// Removes all existing event listeners on this DOM node and install new ones from
Expand Down
20 changes: 6 additions & 14 deletions Sources/TokamakDOM/DOMRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,33 +74,25 @@ func appendRootStyle(_ rootNode: JSObjectRef) {
_ = head.appendChild!(rootStyle)
}

public final class DOMRenderer: Renderer {
public private(set) var reconciler: StackReconciler<DOMRenderer>?
final class DOMRenderer: Renderer {
private(set) var reconciler: StackReconciler<DOMRenderer>?

private let rootRef: JSObjectRef

public convenience init<V: View>(
_ view: V,
_ ref: JSObjectRef,
_ rootEnvironment: EnvironmentValues? = nil
) {
self.init(DefaultApp(content: view), ref, rootEnvironment)
}

init<A: App>(_ app: A, _ ref: JSObjectRef, _ rootEnvironment: EnvironmentValues? = nil) {
rootRef = ref
appendRootStyle(ref)

reconciler = StackReconciler(
app: app,
target: DOMNode(app, ref),
target: DOMNode(ref),
environment: .defaultEnvironment,
renderer: self,
scheduler: timeoutScheduler
)
}

public func mountTarget(to parent: DOMNode, with host: MountedHost) -> DOMNode? {
func mountTarget(to parent: DOMNode, with host: MountedHost) -> DOMNode? {
guard let (outerHTML, listeners) = mapAnyView(
host.view,
transform: { (html: AnyHTML) in (html.outerHTML, html.listeners) }
Expand Down Expand Up @@ -133,14 +125,14 @@ public final class DOMRenderer: Renderer {
return DOMNode(host.view, lastChild, listeners)
}

public func update(target: DOMNode, with host: MountedHost) {
func update(target: DOMNode, with host: MountedHost) {
guard let html = mapAnyView(host.view, transform: { (html: AnyHTML) in html })
else { return }

html.update(dom: target)
}

public func unmount(
func unmount(
target: DOMNode,
from parent: DOMNode,
with host: MountedHost,
Expand Down

0 comments on commit 40804d4

Please sign in to comment.