Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed Sep 10, 2024
1 parent d229008 commit f9865e4
Show file tree
Hide file tree
Showing 27 changed files with 1,630 additions and 5 deletions.
4 changes: 0 additions & 4 deletions 0293-cross-platform-pt4/ModernUIKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
2A154CF02C5D56C7009FC7D9 /* Epoxy in Frameworks */ = {isa = PBXBuildFile; productRef = 2A154CEF2C5D56C7009FC7D9 /* Epoxy */; };
2A154CF22C5D56E1009FC7D9 /* CounterFeatureEpoxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A154CF12C5D56E1009FC7D9 /* CounterFeatureEpoxy.swift */; };
2A154CF42C5D607D009FC7D9 /* Counter in Resources */ = {isa = PBXBuildFile; fileRef = 2A154CF32C5D607D009FC7D9 /* Counter */; };
2A154CF82C5D620A009FC7D9 /* .swift-version in Resources */ = {isa = PBXBuildFile; fileRef = 2A154CF72C5D6206009FC7D9 /* .swift-version */; };
2A1CEB272BFD271600753A66 /* Perception in Frameworks */ = {isa = PBXBuildFile; productRef = 2A1CEB262BFD271600753A66 /* Perception */; };
2A6230292BFEA5C600930179 /* AppFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6230282BFEA5C600930179 /* AppFeature.swift */; };
4B54C2252BFD0D1900E95174 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C2242BFD0D1900E95174 /* App.swift */; };
Expand All @@ -27,7 +26,6 @@
/* Begin PBXFileReference section */
2A154CF12C5D56E1009FC7D9 /* CounterFeatureEpoxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterFeatureEpoxy.swift; sourceTree = "<group>"; };
2A154CF32C5D607D009FC7D9 /* Counter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Counter; sourceTree = SOURCE_ROOT; };
2A154CF72C5D6206009FC7D9 /* .swift-version */ = {isa = PBXFileReference; lastKnownFileType = text; path = ".swift-version"; sourceTree = "<group>"; };
2A1CEB242BFD1A3800753A66 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
2A6230282BFEA5C600930179 /* AppFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFeature.swift; sourceTree = "<group>"; };
4B54C2212BFD0D1900E95174 /* ModernUIKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ModernUIKit.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -74,7 +72,6 @@
4B54C2232BFD0D1900E95174 /* ModernUIKit */ = {
isa = PBXGroup;
children = (
2A154CF72C5D6206009FC7D9 /* .swift-version */,
2A154CF32C5D607D009FC7D9 /* Counter */,
2A1CEB242BFD1A3800753A66 /* Info.plist */,
4B54C2242BFD0D1900E95174 /* App.swift */,
Expand Down Expand Up @@ -167,7 +164,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2A154CF82C5D620A009FC7D9 /* .swift-version in Resources */,
2A154CF42C5D607D009FC7D9 /* Counter in Resources */,
4B54C22C2BFD0D1A00E95174 /* Preview Assets.xcassets in Resources */,
4B54C2292BFD0D1A00E95174 /* Assets.xcassets in Resources */,
Expand Down
2 changes: 1 addition & 1 deletion 0293-cross-platform-pt4/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: Navigation](https://www.pointfree.co/episodes/ep293-cross-platform-navigation)
> #### This directory contains code from Point-Free Episode: [Cross-Platform Swift: Navigation](https://www.pointfree.co/episodes/ep293-cross-platform-swift-navigation)
>
> We will introduce navigation APIs to our Wasm application, starting simply with an alert before ramping things up with a `dialog` tag that can be fully configurable from a value type that represents its state and actions.
8 changes: 8 additions & 0 deletions 0294-cross-platform-pt5/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 0294-cross-platform-pt5/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 0294-cross-platform-pt5/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]
)
70 changes: 70 additions & 0 deletions 0294-cross-platform-pt5/Counter/Sources/Counter/CounterModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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 factIsLoading = false
public var isTextFocused = false {
didSet {
print("isTextFocused", isTextFocused)
}
}
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
}
}
121 changes: 121 additions & 0 deletions 0294-cross-platform-pt5/Counter/Sources/WasmApp/App.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
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 counter = document.createElement("input")
counter.type = "number"
_ = document.body.appendChild(counter)
counter
.bind($model.count.toString, to: \.value, event: \.onchange)
.store(in: &tokens)

var textField = document.createElement("input")
textField.type = "text"
_ = document.body.appendChild(textField)
textField.bind($model.text, to: \.value, event: \.onkeyup)
.store(in: &tokens)

textField.bind(focus: $model.isTextFocused)
.store(in: &tokens)

// enum Focus {
// case counter
// case textField
// }
// var focus: Focus?
// counter.bind(focus: $model.focus, equals: .counter)
// textField.bind(focus: $model.focus, equals: .textField)

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($model.alert)
.store(in: &tokens)
}
}

extension Int {
fileprivate var toString: String {
get {
String(self)
}
set {
self = Int(newValue) ?? 0
}
}
}

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 f9865e4

Please sign in to comment.