Skip to content

Commit 3c4ddc1

Browse files
authored
Turn StylesheetCache into a thread-safe actor (#1461)
* Turn `StylesheetCache` into a thread-safe actor Fixes #1460 * Update Changelog
1 parent 1e31073 commit 3c4ddc1

File tree

3 files changed

+16
-14
lines changed

3 files changed

+16
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Updates to change-tracked properties no longer occur on the next RunLoop, fixing modal dismissal on macOS (#1450)
2121
- `+` characters are properly encoded as `%2B` in form events (#1449)
2222
- Fixed retain cycle in `LiveViewCoordinator` (#1455)
23+
- Made `StylesheetCache` thread-safe, fixing occasional crashes (#1461)
2324

2425
## [0.3.0] 2024-08-21
2526

Sources/LiveViewNative/Coordinators/LiveSessionCoordinator.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,14 +229,14 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
229229
guard let url = URL(string: try style.attr("url"), relativeTo: url)
230230
else { continue }
231231
group.addTask {
232-
if let cachedStylesheet = StylesheetCache[for: url, registry: R.self] {
232+
if let cachedStylesheet = await StylesheetCache.shared.read(for: url, registry: R.self) {
233233
return cachedStylesheet
234234
} else {
235-
let (data, response) = try await self.urlSession.data(from: url)
235+
let (data, _) = try await self.urlSession.data(from: url)
236236
guard let contents = String(data: data, encoding: .utf8)
237237
else { return Stylesheet<R>(content: [], classes: [:]) }
238238
let stylesheet = try Stylesheet<R>(from: contents, in: .init())
239-
StylesheetCache[for: url, registry: R.self] = stylesheet
239+
await StylesheetCache.shared.write(stylesheet, for: url, registry: R.self)
240240
return stylesheet
241241
}
242242
}
@@ -513,7 +513,7 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
513513
self.liveReloadChannel!.on("assets_change") { [weak self] _ in
514514
logger.debug("[LiveReload] assets changed, reloading")
515515
Task {
516-
StylesheetCache.removeAll()
516+
await StylesheetCache.shared.removeAll()
517517
// need to fully reconnect (rather than just re-join channel) because the elixir code reloader only triggers on http reqs
518518
await self?.reconnect()
519519
}

Sources/LiveViewNative/Stylesheets/Stylesheet.swift

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,20 @@ public struct Stylesheet<R: RootRegistry> {
2727
}
2828
}
2929

30-
enum StylesheetCache {
31-
private static var cache = [URL:Any]()
30+
actor StylesheetCache {
31+
static let shared = StylesheetCache()
3232

33-
static subscript<R: RootRegistry>(for url: URL, registry _: R.Type = R.self) -> Stylesheet<R>? {
34-
get {
35-
Self.cache[url.absoluteURL] as? Stylesheet<R>
36-
}
37-
set {
38-
Self.cache[url.absoluteURL] = newValue as Any
39-
}
33+
private var cache = [URL:Any]()
34+
35+
func read<R: RootRegistry>(for url: URL, registry _: R.Type = R.self) -> Stylesheet<R>? {
36+
cache[url.absoluteURL] as? Stylesheet<R>
37+
}
38+
39+
func write<R: RootRegistry>(_ value: Stylesheet<R>, for url: URL, registry _: R.Type = R.self) {
40+
cache[url.absoluteURL] = value
4041
}
4142

42-
static func removeAll() {
43+
func removeAll() {
4344
cache.removeAll()
4445
}
4546
}

0 commit comments

Comments
 (0)