-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add @Environment support + documentation. (#12)
This change introduces the ability to define, read, and write environment properties within any Slipstream View type.
- Loading branch information
Showing
26 changed files
with
868 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
79 changes: 79 additions & 0 deletions
79
Sources/Slipstream/Documentation.docc/Architecture/HowSlipstreamWorks.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# How Slipstream works | ||
|
||
A from the ground up explanation of Slipstream's architecture. | ||
|
||
Slipstream is designed to offer a SwiftUI-like approach to building HTML documents | ||
that are compatible with [Tailwind CSS](http://tailwindcss.com). | ||
|
||
## Core Concepts | ||
|
||
### Result Builders | ||
|
||
Slipstream uses Swift [result builders](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0289-result-builders.md) | ||
to enable the construction of HTML documents in a syntax similar to SwiftUI's. | ||
Result builders encourage defining structural data in a hierarchical and declarative | ||
manner, separating intent (what the site should look like) from implementation (how | ||
it's turned into HTML). This separation of concerns allows you to focus on the | ||
design and structure of your web pages without getting bogged down by the | ||
intricacies of HTML generation. | ||
|
||
### View Protocol | ||
|
||
The primary type that Slipstream's result builders work with is the ``View`` protocol. | ||
Like SwiftUI, a View represents a part of an HTML document that can be combined | ||
with other View instances to create a website. The protocol defines a contract | ||
that all views must adhere to, ensuring consistency in how views are constructed and | ||
rendered as HTML. | ||
|
||
```swift | ||
public protocol View { | ||
associatedtype Content: View | ||
@ViewBuilder var body: Self.Content { get } | ||
|
||
func render(_ container: Element) throws | ||
} | ||
``` | ||
|
||
The ``View/body`` property returns the content of the view, and in most cases, is | ||
the only part of the View protocol that you need to implement. The ``ViewBuilder`` | ||
attribute is what enables our use of the SwiftUI-like syntax in the body implementation. | ||
|
||
The ``View/render(_:environment:)`` method, on the other hand, is responsible for converting the | ||
view’s content into HTML elements. You'll only need to implement this method if you | ||
need to generate new types of HTML. | ||
|
||
### W3C HTML Views | ||
|
||
Slipstream provides a catalog of standard [W3C HTML](https://html.spec.whatwg.org/multipage/) | ||
View implementations that can be used to build your website. Read <doc:Fundamentals> to | ||
learn more about the different Views available in Slipstream. | ||
|
||
### Rendering a View as HTML | ||
|
||
The combination of result builders, the View protocol, and a ``Text`` view is all we | ||
need to build a simple "Hello, world!" example: | ||
|
||
```swift | ||
struct HelloWorld: View { | ||
var body: some View { | ||
Text("Hello, world!") | ||
} | ||
} | ||
|
||
print(try renderHTML(HelloWorld())) | ||
``` | ||
|
||
In this example, the Text view is treated as a single "block" in HelloWorld's body. | ||
|
||
Slipstream depends on [SwiftSoup](https://scinfu.github.io/SwiftSoup/) for rendering valid | ||
HTML. Each call to ``renderHTML(_:)`` follows the same rough flow: | ||
|
||
1. The ``renderHTML(_:)`` method creates a SwiftSoup `Document` object. | ||
2. This object is then passed to HelloWorld's ``View/render(_:environment:)`` method, | ||
which in turn calls Text's `render(_:environment:)` method, which appends the string | ||
to the document. This step happens recursively until the entire view | ||
hierarchy has had a chance to render its contents into the document. | ||
3. The Document, at this point an in-memory Document Object Model (DOM) representation, | ||
is then rendered as HTML using SwiftSoup and returned. | ||
|
||
You can then save the resulting html string to the appropriate file. |
7 changes: 7 additions & 0 deletions
7
Sources/Slipstream/Documentation.docc/DataAndStorage/Environment.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# ``Environment`` | ||
|
||
## Topics | ||
|
||
### Getting the value | ||
|
||
- ``Environment/wrappedValue`` |
7 changes: 7 additions & 0 deletions
7
Sources/Slipstream/Documentation.docc/DataAndStorage/EnvironmentValues.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# ``EnvironmentValues`` | ||
|
||
## Topics | ||
|
||
### Creating and accessing values | ||
|
||
- ``EnvironmentValues/init()`` |
84 changes: 84 additions & 0 deletions
84
Sources/Slipstream/Documentation.docc/DataAndStorage/EnvironmentValuesSection.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# Environment values | ||
|
||
Share data throughout a view hierarchy using the environment. | ||
|
||
Like SwiftUI, views in Slipstream can react to configuration information | ||
that they read from the environment using an ``Environment`` property wrapper. | ||
|
||
A view inherits its environment from its container view, subject to explicit | ||
changes from an ``View/environment(_:_:)`` view modifier. As a result, you | ||
can configure an entire hierarchy of views by modifying the environment of | ||
the group’s container. | ||
|
||
## Defining custom environment values | ||
|
||
To create a custom environment value, you first define a type that conforms | ||
to the ``EnvironmentKey`` protocol. This type will be used to uniquely | ||
identify the value in the environment. | ||
|
||
```swift | ||
struct PathEnvironmentKey: EnvironmentKey { | ||
static let defaultValue: String = "/" | ||
} | ||
``` | ||
|
||
You must then provide a way to read and write the environment value: | ||
|
||
```swift | ||
extension EnvironmentValues { | ||
var path: String { | ||
get { self[PathEnvironmentKey.self] } | ||
set { self[PathEnvironmentKey.self] = newValue } | ||
} | ||
} | ||
``` | ||
|
||
While not required, it's also a good practice to provide a ``View`` | ||
extension that modifies the environment value: | ||
|
||
```swift | ||
extension View { | ||
func path(_ path: String) -> some View { | ||
environment(\.path, path) | ||
} | ||
} | ||
``` | ||
|
||
### How to read environment properties | ||
|
||
In any ``View``, you can read an environment value using the ``Environment`` | ||
property wrapper: | ||
|
||
```swift | ||
struct MyView: View { | ||
@Environment(\.path) var path | ||
|
||
// ... | ||
} | ||
``` | ||
|
||
You can then read the environment property like any other property on | ||
the view. When the view is rendered, the value of the property will | ||
reflect the environment this view is being rendered within. | ||
|
||
### How to change environment properties | ||
|
||
Within the ``View/body`` of any view you can use the ``View/environment(_:_:)`` | ||
modifier to set the environment value for that view and its descendants. | ||
|
||
```swift | ||
MyView() | ||
.environment(\.path, "/home") | ||
``` | ||
|
||
|
||
## Topics | ||
|
||
### Accessing environment values | ||
|
||
- ``Environment`` | ||
- ``EnvironmentValues`` | ||
|
||
### Creating custom environment values | ||
|
||
- ``EnvironmentKey`` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
Sources/Slipstream/Views/Fundamentals/DataAndStorage/Environment.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/// A property wrapper that reads a value from a view's environment. | ||
/// | ||
/// Use the `Environment` property wrapper to read a value stored in a view's | ||
/// environment. Indicate the value to read using an ``EnvironmentValues`` | ||
/// key path in the property declaration. | ||
/// | ||
/// You can condition a view's content on the associated value, which | ||
/// you read from the declared property's ``wrappedValue``. As with any property | ||
/// wrapper, you access the wrapped value by directly referring to the property: | ||
/// | ||
/// You can use this property wrapper to read --- but not set --- an environment | ||
/// value. You can override existing environment values, as well as set custom | ||
/// environment values that you define, using the ``View/environment(_:_:)`` | ||
/// view modifier. | ||
@available(iOS 17.0, macOS 14.0, *) | ||
@propertyWrapper | ||
public struct Environment<Value> { | ||
/// Creates an environment property to read the specified key path. | ||
/// | ||
/// Don’t call this initializer directly. Instead, declare a property | ||
/// with the ``Environment`` property wrapper, and provide the key path of | ||
/// the environment value that the property should reflect: | ||
/// | ||
/// ```swift | ||
/// struct MyView: View { | ||
/// @Environment(\.path) var path | ||
/// | ||
/// // ... | ||
/// } | ||
/// ``` | ||
/// | ||
/// You can't modify the environment value using a property like this. Instead, | ||
/// use the ``View/environment(_:_:)`` view modifier on a view to set | ||
/// a value for a view hierarchy. | ||
/// | ||
/// - Parameter keyPath: A key path to a specific resulting value. | ||
public init(_ keyPath: KeyPath<EnvironmentValues, Value>) { | ||
self.keyPath = keyPath | ||
} | ||
|
||
/// The current value of the environment property. | ||
/// | ||
/// The wrapped value property provides primary access to the value's data. | ||
/// However, you don't access `wrappedValue` directly. Instead, you read the | ||
/// property variable created with the ``Environment`` property wrapper. | ||
public var wrappedValue: Value { | ||
get { environmentValues[keyPath: keyPath] } | ||
set { fatalError("Wrapped value should not be used.") } | ||
} | ||
|
||
/// The environment storage, from which the value of the property will be retrieved. | ||
/// | ||
/// When a view is rendered, this property will be modified with the contextual environment values. | ||
private var environmentValues: EnvironmentValues = EnvironmentValues() | ||
|
||
/// The key path to the property represented by this Environment wrapper. | ||
private let keyPath: KeyPath<EnvironmentValues, Value> | ||
} |
Oops, something went wrong.