|
1 | 1 | # SwiftHttp
|
2 | 2 |
|
3 |
| -A description of this package. |
| 3 | +An awesome Swift HTTP library to rapidly setup the communication layer with API endpoints. |
| 4 | + |
| 5 | +```swift |
| 6 | +import SwiftHttp |
| 7 | + |
| 8 | +print(html) |
| 9 | +``` |
| 10 | + |
| 11 | + |
| 12 | +## Install |
| 13 | + |
| 14 | +You can simply use `SwiftHtml` as a dependency via the Swift Package Manager: |
| 15 | + |
| 16 | +```swift |
| 17 | +.package(url: "https://github.com/binarybirds/swift-html", from: "1.6.0"), |
| 18 | +``` |
| 19 | + |
| 20 | +Add the `SwiftHtml` product from the `swift-html` package as a dependency to your target: |
| 21 | + |
| 22 | +```swift |
| 23 | +.product(name: "SwiftHtml", package: "swift-html"), |
| 24 | +``` |
| 25 | + |
| 26 | +Import the framework: |
| 27 | + |
| 28 | +```swift |
| 29 | +import SwiftHtml |
| 30 | +``` |
| 31 | + |
| 32 | +That's it. |
| 33 | + |
| 34 | + |
| 35 | +## Creating custom tags |
| 36 | + |
| 37 | +You can define your own custom tags by subclassing the `Tag` or `EmptyTag` class. |
| 38 | + |
| 39 | +You can follow the same pattern if you take a look at the core tags. |
| 40 | + |
| 41 | +```swift |
| 42 | +open class Div: Tag { |
| 43 | + |
| 44 | +} |
| 45 | + |
| 46 | +// <div></div> - standard tag |
| 47 | + |
| 48 | +open class Br: EmptyTag { |
| 49 | + |
| 50 | +} |
| 51 | +// <br> - no closing tag |
| 52 | + |
| 53 | +``` |
| 54 | + |
| 55 | +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. |
| 56 | + |
| 57 | +```swift |
| 58 | +open class LastBuildDate: Tag { |
| 59 | + |
| 60 | + open override class func createNode() -> Node { |
| 61 | + Node(type: .standard, name: "lastBuildDate") |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +// <lastBuildDate></lastBuildDate> - standard tag with custom name |
| 66 | +``` |
| 67 | + |
| 68 | +It is also possible to create tags with altered content or default attributes. |
| 69 | + |
| 70 | +```swift |
| 71 | +open class Description: Tag { |
| 72 | + |
| 73 | + public init(_ contents: String) { |
| 74 | + super.init() |
| 75 | + setContents("<![CDATA[" + contents + "]]>") |
| 76 | + } |
| 77 | +} |
| 78 | +// <description><![CDATA[lorem ipsum]]></description> - content wrapped in CDATA |
| 79 | + |
| 80 | +open class Rss: Tag { |
| 81 | + |
| 82 | + public init(@TagBuilder _ builder: () -> [Tag]) { |
| 83 | + super.init(builder()) |
| 84 | + setAttributes([ |
| 85 | + .init(key: "version", value: "2.0"), |
| 86 | + ]) |
| 87 | + } |
| 88 | +} |
| 89 | +// <rss version="2.0">...</rss> - tag with a default attribute |
| 90 | +``` |
| 91 | + |
| 92 | +## Attribute management |
| 93 | + |
| 94 | +You can set, add or delete the attributes of a given tag. |
| 95 | + |
| 96 | +```swift |
| 97 | +Leaf("example") |
| 98 | + // set (override) the current attributes |
| 99 | + .setAttributes([ |
| 100 | + .init(key: "a", value: "foo"), |
| 101 | + .init(key: "b", value: "bar"), |
| 102 | + .init(key: "c", value: "baz"), |
| 103 | + ]) |
| 104 | + // add a new attribute using a key & value |
| 105 | + .attribute("foo", "example") |
| 106 | + // add a new flag attribute (without a value) |
| 107 | + .flagAttribute("bar") |
| 108 | + // delete an attribute by using a key |
| 109 | + .deleteAttribute("b") |
| 110 | + |
| 111 | +// <leaf a="foo" c="baz" foo="example" bar></leaf> |
| 112 | +``` |
| 113 | + |
| 114 | +You can also manage the class atrribute through helper methods. |
| 115 | + |
| 116 | +```swift |
| 117 | +Span("foo") |
| 118 | + // set (override) class values |
| 119 | + .class("a", "b", "c") |
| 120 | + // add new class values |
| 121 | + .class(add: ["d", "e", "f"]) |
| 122 | + // add new class value if the condition is true |
| 123 | + .class(add: "b", true) |
| 124 | + /// remove multiple class values |
| 125 | + .class(remove: ["b", "c", "d"]) |
| 126 | + /// remove a class value if the condition is true |
| 127 | + .class(remove: "e", true) |
| 128 | + |
| 129 | +// <span class="a f"></span> |
| 130 | +``` |
| 131 | + |
| 132 | +You can create your own attribute modifier via an extension. |
| 133 | + |
| 134 | +```swift |
| 135 | +public extension Guid { |
| 136 | + |
| 137 | + func isPermalink(_ value: Bool = true) -> Self { |
| 138 | + attribute("isPermalink", String(value)) |
| 139 | + } |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +There are other built-in type-safe attribute modifiers available on tags. |
| 144 | + |
| 145 | + |
| 146 | +## Composing tags |
| 147 | + |
| 148 | +You can come up with your own `Tag` composition system by introducing a new protocol. |
| 149 | + |
| 150 | +```swift |
| 151 | +protocol TagRepresentable { |
| 152 | + |
| 153 | + func build() -> Tag |
| 154 | +} |
| 155 | + |
| 156 | +struct ListComponent: TagRepresentable { |
| 157 | + |
| 158 | + let items: [String] |
| 159 | + |
| 160 | + init(_ items: [String]) { |
| 161 | + self.items = items |
| 162 | + } |
| 163 | + |
| 164 | + @TagBuilder |
| 165 | + func build() -> Tag { |
| 166 | + Ul { |
| 167 | + items.map { Li($0) } |
| 168 | + } |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +let tag = ListComponent(["a", "b", "c"]).build() |
| 173 | +``` |
| 174 | + |
| 175 | +This way it is also possible to extend the `TagBuilder` to support the new protocol. |
| 176 | + |
| 177 | +```swift |
| 178 | +extension TagBuilder { |
| 179 | + |
| 180 | + static func buildExpression(_ expression: TagRepresentable) -> Tag { |
| 181 | + expression.build() |
| 182 | + } |
| 183 | + |
| 184 | + static func buildExpression(_ expression: TagRepresentable) -> [Tag] { |
| 185 | + [expression.build()] |
| 186 | + } |
| 187 | + |
| 188 | + static func buildExpression(_ expression: [TagRepresentable]) -> [Tag] { |
| 189 | + expression.map { $0.build() } |
| 190 | + } |
| 191 | + |
| 192 | + static func buildExpression(_ expression: [TagRepresentable]) -> Tag { |
| 193 | + GroupTag { |
| 194 | + expression.map { $0.build() } |
| 195 | + } |
| 196 | + } |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +Sometimes you'll need extra parameters for the build function, so you have to call the build method by hand. |
| 201 | + |
| 202 | +In those cases it is recommended to introduce a `render` function instead of using build. |
| 203 | + |
| 204 | +```swift |
| 205 | + |
| 206 | +let tag = WebIndexTemplate(ctx) { |
| 207 | + ListComponent(["a", "b", "c"]) |
| 208 | + .render(req) |
| 209 | +} |
| 210 | +.render(req) |
| 211 | +``` |
| 212 | + |
| 213 | +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. |
| 214 | + |
| 215 | + |
| 216 | +## Credits & references |
| 217 | + |
| 218 | +- [HTML Reference](https://www.w3schools.com/tags/default.asp) |
0 commit comments