Skip to content

Instantly share code, notes, and snippets.

@pitt500
Last active February 27, 2024 03:01
Show Gist options
  • Save pitt500/4abcc67a2fbc9d868b4a53dc6176efd8 to your computer and use it in GitHub Desktop.
Save pitt500/4abcc67a2fbc9d868b4a53dc6176efd8 to your computer and use it in GitHub Desktop.
Demo explaining Result Builders, check out this link for more context: https://youtu.be/kZ7JPFUVv1w
// Created by Pedro Rojas (aka Pitt)
// Link to learn more about this code: https://youtu.be/kZ7JPFUVv1w
import SwiftUI
import WebKit
@resultBuilder
struct HtmlBuilder {
static func buildBlock() -> [HtmlTag] {
[]
}
static func buildBlock(_ components: HtmlTag...) -> [HtmlTag] {
components
}
static func buildBlock(_ components: [HtmlTag]...) -> [HtmlTag] {
components.flatMap { $0 }
}
static func buildArray(_ components: [[HtmlTag]]) -> [HtmlTag] {
components.flatMap { $0 }
}
static func buildExpression(_ expression: HtmlTag) -> [HtmlTag] {
[expression]
}
static func buildOptional(_ component: [HtmlTag]?) -> [HtmlTag] {
component ?? []
}
static func buildEither(first component: [HtmlTag]) -> [HtmlTag] {
component
}
static func buildEither(second component: [HtmlTag]) -> [HtmlTag] {
component
}
}
// MARK: Html Tags
protocol HtmlTag {
func render() -> String
}
struct Title: HtmlTag {
let title: String
init(_ title: String) {
self.title = title
}
func render() -> String {
"<title>" + title + "</title>"
}
}
struct Body: HtmlTag {
let children: [HtmlTag]
init(@HtmlBuilder _ content: () -> [HtmlTag]) {
self.children = content()
}
func render() -> String {
"<body>" +
children
.map { $0.render() }
.joined() +
"</body>"
}
}
struct Head: HtmlTag {
let children: [HtmlTag]
init(@HtmlBuilder _ content: () -> [HtmlTag]) {
self.children = content()
}
func render() -> String {
"<head>" +
children
.map { $0.render() }
.joined() +
"</head>"
}
}
struct Meta: HtmlTag {
let name: String
let content: String
func render() -> String {
"<meta name=\"\(name)\" content=\"\(content)\">"
}
}
struct P: HtmlTag {
let text: String
init(_ text: String) {
self.text = text
}
func render() -> String {
"<p>" + text + "</p>"
}
}
struct H1: HtmlTag {
let text: String
init(_ text: String) {
self.text = text
}
func render() -> String {
"<h1>" + text + "</h1>"
}
}
struct Table: HtmlTag {
let rows: [HtmlTag]
init(@HtmlBuilder _ content: () -> [HtmlTag]) {
self.rows = content()
}
func render() -> String {
"<table>" +
rows
.map { $0.render() }
.joined() +
"</table>"
}
}
struct Tr: HtmlTag {
let columns: [HtmlTag]
init(@HtmlBuilder _ content: () -> [HtmlTag]) {
self.columns = content()
}
func render() -> String {
"<tr>" +
columns
.map { $0.render() }
.joined() +
"</tr>"
}
}
struct Td<Value: CustomStringConvertible>: HtmlTag {
let data: Value
init(_ data: Value) {
self.data = data
}
func render() -> String {
"<td>" + data.description + "</td>"
}
}
struct Th: HtmlTag {
let title: String
init(_ title: String) {
self.title = title
}
func render() -> String {
"<th>" + title + "</th>"
}
}
struct Img: HtmlTag {
private var attributes: [Attribute] = []
init(url: String) {
self.attributes.append(Attribute(key: "src", value: url))
}
func render() -> String {
return "<img " + buildAttributes() + ">"
}
private func buildAttributes() -> String {
var attributesString = ""
for attribute in attributes {
attributesString.append(attribute.key + "=" + "\"\(attribute.value)\" ")
}
print(attributesString)
return attributesString
}
}
extension Img {
func alt(text: String) -> Self {
var newImage = self
newImage.attributes.append(Attribute(key: "alt", value: text))
return newImage
}
func style(width: Int?, height: Int?) -> Self {
var newImage = self
if let width {
newImage.attributes.append(Attribute(key: "width", value: "\(width)"))
}
if let height {
newImage.attributes.append(Attribute(key: "height", value: "\(height)"))
}
return newImage
}
}
struct Attribute {
let key: String
let value: String
}
// MARK: WebView
struct WebView: UIViewRepresentable {
@HtmlBuilder var content: () -> [HtmlTag]
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
let string = content()
.map { $0.render() }
.joined()
let fullHtml = "<html>" + string + "</html>"
print(fullHtml)
webView.loadHTMLString(
fullHtml,
baseURL: nil
)
}
}
func isValidStringURL(_ string: String) -> Bool {
URL(string: string) != nil
}
let imageUrl = "https://img.pokemondb.net/artwork/large/flareon.jpg"
struct WebView_Previews: PreviewProvider {
static var previews: some View {
WebView {
Head {
Title("My Title")
Meta(
name: "viewport",
content: "width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=1"
)
}
Body {
H1("This is a heading!")
if isValidStringURL(imageUrl) {
P("Flareon")
Img(url: imageUrl)
.alt(text: "This is flareon")
.style(width: 100, height: 100)
} else {
P("No image")
}
P("Moves learnt by level up!")
Table {
Tr {
Th("Lv.")
Th("Move")
Th("Type")
Th("Cat.")
Th("Power")
Th("Acc.")
}
for row in Move.sample {
Tr {
Td(row.level)
Td(row.name)
Td(row.type)
Td(row.category.rawValue)
Td(row.power)
Td(row.accuracy)
}
}
}
}
}
}
}
// Mark: Moveset
enum Category: String {
case special = "Special"
case physical = "Physical"
case status = "Status"
}
struct Move {
let level: Int
let name: String
let type: String
let category: Category
let power: String
let accuracy: String
}
extension Move {
static let sample: [Self] = [
.init(level: 1, name: "Baton Pass", type: "Normal", category: .status, power: "--", accuracy: "--"),
.init(level: 1, name: "Charm", type: "Fairy", category: .status, power: "--", accuracy: "100"),
.init(level: 1, name: "Tackle", type: "Normal", category: .physical, power: "40", accuracy: "100"),
.init(level: 25, name: "Bite", type: "Dark", category: .physical, power: "60", accuracy: "100"),
.init(level: 30, name: "Fire Fang", type: "Fire", category: .physical, power: "65", accuracy: "95"),
.init(level: 30, name: "Fire Spin", type: "Fire", category: .special, power: "35", accuracy: "85"),
.init(level: 45, name: "Scary Face", type: "Normal", category: .status, power: "--", accuracy: "100"),
.init(level: 50, name: "Flare Blitz", type: "Fire", category: .physical, power: "120", accuracy: "100"),
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment