Skip to content

Releases: swifweb/web

🪚 Improve `@media` rule syntax

04 Dec 13:40
Compare
Choose a tag to compare
@main
public class App: WebApp {
    @AppBuilder public override var body: AppBuilder.Content {
        /// ...
        MainStyle()
    }
}

class MainStyle: Stylesheet {
    @Rules override var rules: Rules.Content {
        MediaRule(.screen.maxWidth(800.px)) {
            Rule(Body.pointer)
                .backgroundColor(.red)
        }
        MediaRule(.screen.maxWidth(1200.px)) {
            Rule(Body.pointer)
                .backgroundColor(.green)
        }
        MediaRule(.screen.aspectRatio(4/3)) {
            Rule(Body.pointer)
                .backgroundColor(.purple)
        }
        MediaRule(!.screen.aspectRatio(4/3)) {
            Rule(Body.pointer)
                .backgroundColor(.black)
        }
    }
}

which represents

@media only screen and (max-width: 800px) {
    background-color: red;
}
@media only screen and (max-width: 1200px) {
    background-color: green;
}
@media only screen and (aspect-ratio: 4/3) {
    background-color: purple;
}
@media not screen and (aspect-ratio: 4/3) {
    background-color: black;
}

🪚 Implement simpler `@State` with `UnitValue`

04 Dec 11:15
Compare
Choose a tag to compare

Normal usage without @State

Div().height(100.px) // static value

Usage with @State

@State var height = 100.px // this way you even can change px to em on the fly

Div().height($height)

New option

@State var height: Double = 100 // this way you can change digit value only

Div().height($height.px)

🪚 Allow to put raw HTML string into `@DOM` block

04 Dec 11:07
Compare
Choose a tag to compare
@DOM override var body: DOM.Content {
    """
    <button>Hello world</button>
    """
}

which represents

<div>
    <button>Hello world</button>
</div>

🌎Localization

31 Jul 17:52
Compare
Choose a tag to compare
🌎Localization Pre-release
Pre-release

Static localization

Classic localization is automatic and depends on user system language

How to use

H1(String(
    .en("Hello"),
    .fr("Bonjour"),
    .ru("Привет"),
    .es("Hola"),
    .zh_Hans("你好"),
    .ja("こんにちは")))

Dynamic localization

If you want to change localized strings on the screen on-the-fly (without page reloading)

You could change current language by calling

Localization.current = .es

If you saved user's language somewhere in cookies or localstorage then you have to set it on app launch

Lifecycle.didFinishLaunching {
    Localization.current = .es
}

How to use

H1(LString(
    .en("Hello"),
    .fr("Bonjour"),
    .ru("Привет"),
    .es("Hola"),
    .zh_Hans("你好"),
    .ja("こんにちは")))

Advanced example

H1(Localization.currentState.map { "Curent language: \($0.rawValue)" })
H2(LString(.en("English string"), .es("Hilo Español")))
Button("change lang").onClick {
	Localization.current = Localization.current.rawValue.contains("en") ? .es : .en
}

🪚 Implement `Img().load(url:)` by @tierracero

21 Feb 14:31
09bc2f8
Compare
Choose a tag to compare

The following method allows to load an image async while showing a default image.
Once the image has loaded in the background it will replace existing img.src

Img()
    .src("/images/placeholder.jpg")
    .loading(.lazy)
    .load("http://google.com/images/cat.jpg")

So the code above will first show placeholder.jpg image, then once cat.jpg is ready to show it will replace placeholder.jpg

🚀 Implement StreamsAPI, FetchAPI, XMLHttpRequest, WebSocketAPI

21 Mar 08:01
Compare
Choose a tag to compare

Fetch

import FetchAPI

Fetch("https://jsonplaceholder.typicode.com/todos/1") {
    switch $0 {
    case .failure:
        break
    case .success(let response):
        print("response.code: \(response.status)")
        print("response.statusText: \(response.statusText)")
        print("response.ok: \(response.ok)")
        print("response.redirected: \(response.redirected)")
        print("response.headers: \(response.headers.dictionary)")
        struct Todo: Decodable {
            let id, userId: Int
            let title: String
            let completed: Bool
        }
        response.json(as: Todo.self) {
            switch $0 {
            case .failure(let error):
                break
            case .success(let todo):
                print("decoded todo: \(todo)")
            }
        }
    }
}

XMLHttpRequest

import XMLHttpRequest

XMLHttpRequest()
    .open(method: "GET", url: "https://jsonplaceholder.typicode.com/todos/1")
    .onAbort {
        print("XHR onAbort")
    }.onLoad {
        print("XHR onLoad")
    }.onError {
        print("XHR onError")
    }.onTimeout {
        print("XHR onTimeout")
    }.onProgress{ progress in
        print("XHR onProgress")
    }.onLoadEnd {
        print("XHR onLoadEnd")
    }.onLoadStart {
        print("XHR onLoadStart")
    }.onReadyStateChange { readyState in
        print("XHR onReadyStateChange")
    }
    .send()

WebSocket

import WebSocket

let webSocket = WebSocket("wss://echo.websocket.org").onOpen {
    print("ws connected")
}.onClose { (closeEvent: CloseEvent) in
    print("ws disconnected code: \(closeEvent.code) reason: \(closeEvent.reason)")
}.onError {
    print("ws error")
}.onMessage { message in
    print("ws message: \(message)")
    switch message.data {
    case .arrayBuffer(let arrayBuffer): break
    case .blob(let blob): break
    case .text(let text): break
    case .unknown(let jsValue): break
    }
}
Dispatch.asyncAfter(2) {
    // send as simple string
    webSocket.send("Hello from SwifWeb")
    // send as Blob
    webSocket.send(Blob("Hello from SwifWeb"))
}

🖥 Implement live preview for VSCode

18 Mar 08:27
Compare
Choose a tag to compare