Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed Sep 4, 2024
1 parent cd5419e commit d229008
Show file tree
Hide file tree
Showing 26 changed files with 1,488 additions and 1 deletion.
2 changes: 1 addition & 1 deletion 0292-cross-platform-pt3/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## [Point-Free](https://www.pointfree.co)

> #### This directory contains code from Point-Free Episode: [Cross-Platform Swift: Networkgin](https://www.pointfree.co/episodes/ep292-cross-platform-networking)
> #### This directory contains code from Point-Free Episode: [Cross-Platform Swift: Networking](https://www.pointfree.co/episodes/ep292-cross-platform-networking)
>
> Let’s dial up the complexity of our Wasm application! We’ll introduce some async logic in the form of a network request. We’ll take steps to not only control this dependency, but we’ll do so across both Apple and Wasm platforms, and we’ll isolate its interface from its live implementation to speed up our builds and reduce our app’s size.
8 changes: 8 additions & 0 deletions 0293-cross-platform-pt4/Counter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
1 change: 1 addition & 0 deletions 0293-cross-platform-pt4/Counter/.swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wasm-DEVELOPMENT-SNAPSHOT-2024-07-16-a
61 changes: 61 additions & 0 deletions 0293-cross-platform-pt4/Counter/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// swift-tools-version: 6.0

import PackageDescription

let package = Package(
name: "Counter",
platforms: [.iOS(.v16), .macOS(.v14)],
products: [
.library(
name: "Counter",
targets: ["Counter"]
),
.library(
name: "FactClient",
targets: ["FactClient"]
),
],
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.0.0"),
.package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.0.0"),
.package(url: "https://github.com/pointfreeco/swift-perception", from: "1.0.0"),
.package(url: "https://github.com/swiftwasm/carton", from: "1.0.0"),
.package(url: "https://github.com/swiftwasm/JavaScriptKit", exact: "0.19.2"),
],
targets: [
.executableTarget(
name: "WasmApp",
dependencies: [
"Counter",
"FactClientLive",
.product(name: "SwiftNavigation", package: "swift-navigation"),
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
.product(name: "JavaScriptKit", package: "JavaScriptKit"),
]
),
.target(
name: "Counter",
dependencies: [
"FactClient",
.product(name: "SwiftNavigation", package: "swift-navigation"),
.product(name: "Perception", package: "swift-perception")
]
),
.target(
name: "FactClient",
dependencies: [
.product(name: "Dependencies", package: "swift-dependencies"),
.product(name: "DependenciesMacros", package: "swift-dependencies"),
]
),
.target(
name: "FactClientLive",
dependencies: [
"FactClient",
.product(name: "JavaScriptKit", package: "JavaScriptKit", condition: .when(platforms: [.wasi])),
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit", condition: .when(platforms: [.wasi])),
]
)
],
swiftLanguageVersions: [.v6]
)
71 changes: 71 additions & 0 deletions 0293-cross-platform-pt4/Counter/Sources/Counter/CounterModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Dependencies
import FactClient
import Foundation
import Perception
import SwiftNavigation

@MainActor
@Perceptible
public class CounterModel: HashableObject {
@PerceptionIgnored
@Dependency(FactClient.self) var factClient

public var count = 0 {
didSet {
isTextFocused = !count.isMultiple(of: 3)
}
}
public var alert: AlertState<Never>?
// public var fact: Fact? {
// didSet {
// print("Fact didSet", fact?.value)
// }
// }
public var factIsLoading = false
public var isTextFocused = false
public var text = ""

public struct Fact: Identifiable {
public var value: String
public var id: String { value }
}

public init() {}

public func incrementButtonTapped() {
count += 1
alert = nil
}

public func decrementButtonTapped() {
count -= 1
alert = nil
}

public func factButtonTapped() async {
alert = nil
factIsLoading = true
defer { factIsLoading = false }

do {
try await Task.sleep(for: .seconds(1))

var count = count
let fact = try await factClient.fetch(count)
alert = AlertState {
TextState("Fact")
} actions: {
ButtonState {
TextState("OK")
}
ButtonState {
TextState("Save")
}
} message: {
TextState(fact)
}
} catch {
// TODO: error handling
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Dependencies
import DependenciesMacros

@DependencyClient
public struct FactClient: Sendable {
public var fetch: @Sendable (Int) async throws -> String
}

extension FactClient: TestDependencyKey {
public static let testValue = FactClient()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import FactClient
import Dependencies

#if canImport(JavaScriptKit)
@preconcurrency import JavaScriptKit
#endif
#if canImport(JavaScriptEventLoop)
import JavaScriptEventLoop
#endif

extension FactClient: DependencyKey {
public static let liveValue = FactClient { number in
#if canImport(JavaScriptKit) && canImport(JavaScriptEventLoop)
let response = try await JSPromise(
JSObject.global.fetch!("http://www.numberapi.com/\(number)").object!
)!.value
return try await JSPromise(response.text().object!)!.value.string!
#else
return try await String(
decoding: URLSession.shared
.data(
from: URL(string: "http://www.numberapi.com/\(number)")!
).0,
as: UTF8.self
)
#endif
}
}
93 changes: 93 additions & 0 deletions 0293-cross-platform-pt4/Counter/Sources/WasmApp/App.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import Counter
import IssueReporting
import JavaScriptEventLoop
import JavaScriptKit
import SwiftNavigation

@main
@MainActor
struct App {
static var tokens: Set<ObservationToken> = []

static func main() {
IssueReporters.current = [JavaScriptConsoleWarning()]
JavaScriptEventLoop.installGlobalExecutor()

@UIBindable var model = CounterModel()

let document = JSObject.global.document

var countLabel = document.createElement("span")
_ = document.body.appendChild(countLabel)

var decrementButton = document.createElement("button")
decrementButton.innerText = "-"
decrementButton.onclick = .object(
JSClosure { _ in
model.decrementButtonTapped()
return .undefined
}
)
_ = document.body.appendChild(decrementButton)

var incrementButton = document.createElement("button")
incrementButton.innerText = "+"
incrementButton.onclick = .object(
JSClosure { _ in
model.incrementButtonTapped()
return .undefined
}
)
_ = document.body.appendChild(incrementButton)

var factButton = document.createElement("button")
factButton.innerText = "Get fact"
factButton.onclick = .object(
JSClosure { _ in
Task { await model.factButtonTapped() }
return .undefined
}
)
_ = document.body.appendChild(factButton)

var factLabel = document.createElement("div")
_ = document.body.appendChild(factLabel)

observe {
countLabel.innerText = .string("Count: \(model.count)")

if model.factIsLoading {
factLabel.innerText = "Fact is loading..."
} else {
factLabel.innerText = ""
}
}
.store(in: &tokens)

// alertDialog(item: $model.fact) { _ in
// "Fact"
// } message: { fact in
// fact.value
// }
// .store(in: &tokens)

alertDialog($model.alert)
.store(in: &tokens)
}
}

struct JavaScriptConsoleWarning: IssueReporter {
func reportIssue(
_ message: @autoclosure () -> String?,
fileID: StaticString,
filePath: StaticString,
line: UInt,
column: UInt
) {
#if DEBUG
_ = JSObject.global.console.warn("""
\(fileID):\(line) - \(message() ?? "")
""")
#endif
}
}
Loading

0 comments on commit d229008

Please sign in to comment.