Skip to content

Commit

Permalink
basic readme
Browse files Browse the repository at this point in the history
  • Loading branch information
tib committed Mar 11, 2022
1 parent fbdefb1 commit b5992f0
Showing 1 changed file with 26 additions and 179 deletions.
205 changes: 26 additions & 179 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,218 +1,65 @@
# SwiftHttp

An awesome Swift HTTP library to rapidly setup the communication layer with API endpoints.

```swift
import SwiftHttp

print(html)
```

An awesome Swift HTTP library to rapidly create communication layers with API endpoints.

## Install

You can simply use `SwiftHtml` as a dependency via the Swift Package Manager:

```swift
.package(url: "https://github.com/binarybirds/swift-html", from: "1.6.0"),
.package(url: "https://github.com/binarybirds/swift-http", from: "1.0.0"),
```

Add the `SwiftHtml` product from the `swift-html` package as a dependency to your target:
Add the `SwiftHttp` product from the `swift-http` package as a dependency to your target:

```swift
.product(name: "SwiftHtml", package: "swift-html"),
.product(name: "SwiftHttp", package: "swift-http"),
```

Import the framework:

```swift
import SwiftHtml
import SwiftHttp
```

That's it.


## Creating custom tags

You can define your own custom tags by subclassing the `Tag` or `EmptyTag` class.

You can follow the same pattern if you take a look at the core tags.

```swift
open class Div: Tag {

}

// <div></div> - standard tag

open class Br: EmptyTag {

}
// <br> - no closing tag

```

By default the name of the tag is automatically derived from the class name (lowercased), but you can also create your own tag type & name by overriding the `createNode()` class function.

```swift
open class LastBuildDate: Tag {

open override class func createNode() -> Node {
Node(type: .standard, name: "lastBuildDate")
}
}

// <lastBuildDate></lastBuildDate> - standard tag with custom name
```

It is also possible to create tags with altered content or default attributes.

```swift
open class Description: Tag {

public init(_ contents: String) {
super.init()
setContents("<![CDATA[" + contents + "]]>")
}
}
// <description><![CDATA[lorem ipsum]]></description> - content wrapped in CDATA

open class Rss: Tag {

public init(@TagBuilder _ builder: () -> [Tag]) {
super.init(builder())
setAttributes([
.init(key: "version", value: "2.0"),
])
}
}
// <rss version="2.0">...</rss> - tag with a default attribute
```

## Attribute management
## Basic usage

You can set, add or delete the attributes of a given tag.
It is really easy to setup a communication layer with an API endpoint.

```swift
Leaf("example")
// set (override) the current attributes
.setAttributes([
.init(key: "a", value: "foo"),
.init(key: "b", value: "bar"),
.init(key: "c", value: "baz"),
])
// add a new attribute using a key & value
.attribute("foo", "example")
// add a new flag attribute (without a value)
.flagAttribute("bar")
// delete an attribute by using a key
.deleteAttribute("b")

// <leaf a="foo" c="baz" foo="example" bar></leaf>
```

You can also manage the class atrribute through helper methods.

```swift
Span("foo")
// set (override) class values
.class("a", "b", "c")
// add new class values
.class(add: ["d", "e", "f"])
// add new class value if the condition is true
.class(add: "b", true)
/// remove multiple class values
.class(remove: ["b", "c", "d"])
/// remove a class value if the condition is true
.class(remove: "e", true)

// <span class="a f"></span>
```

You can create your own attribute modifier via an extension.

```swift
public extension Guid {

func isPermalink(_ value: Bool = true) -> Self {
attribute("isPermalink", String(value))
}
}
```

There are other built-in type-safe attribute modifiers available on tags.


## Composing tags

You can come up with your own `Tag` composition system by introducing a new protocol.

```swift
protocol TagRepresentable {

func build() -> Tag
}

struct ListComponent: TagRepresentable {
import SwiftHttp

let items: [String]

init(_ items: [String]) {
self.items = items
}

@TagBuilder
func build() -> Tag {
Ul {
items.map { Li($0) }
}
}
struct Todo: Codable {
let id: Int
let title: String
let completed: Bool
}

let tag = ListComponent(["a", "b", "c"]).build()
```

This way it is also possible to extend the `TagBuilder` to support the new protocol.
struct TodoApi: HttpCodablePipelineCollection {

```swift
extension TagBuilder {
let client: HttpClient = UrlSessionHttpClient(log: true)
let apiBaseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")

static func buildExpression(_ expression: TagRepresentable) -> Tag {
expression.build()
}

static func buildExpression(_ expression: TagRepresentable) -> [Tag] {
[expression.build()]
}

static func buildExpression(_ expression: [TagRepresentable]) -> [Tag] {
expression.map { $0.build() }
}

static func buildExpression(_ expression: [TagRepresentable]) -> Tag {
GroupTag {
expression.map { $0.build() }
}
}
func list() async throws -> [Todo] {
try await decodableRequest(executor: client.dataTask,
url: apiBaseUrl.path("todos"),
method: .get)
}
}
```

Sometimes you'll need extra parameters for the build function, so you have to call the build method by hand.
// api usage
let todos = try await api.list()

In those cases it is recommended to introduce a `render` function instead of using build.
// curl log
// curl "https://jsonplaceholder.typicode.com/todos/"

```swift

let tag = WebIndexTemplate(ctx) {
ListComponent(["a", "b", "c"])
.render(req)
}
.render(req)
```

If you want to create a lightweight template engine for the [Vapor](https://vapor.codes/) web framework using SwiftHtml, you can see a working example inside the [Feather CMS core](https://github.com/FeatherCMS/feather-core) repository.

The HttpClient provides the executors to perform data, download or upload tasks.

## Credits & references
You can create decodable, encodable, codable or raw request when using a codable pipeline collection.

- [HTML Reference](https://www.w3schools.com/tags/default.asp)

0 comments on commit b5992f0

Please sign in to comment.