Skip to content

Commit 4e8fcf3

Browse files
Fix retain cycle in LiveViewCoordinator (#1455)
* Fix retain cycle in LiveViewCoordinator `LiveViewCoordinator` is initialised from `LiveSessionCoordinator`, which gets passed in as a parameter to `init` and then retained by the view coordinator. This causes a retain cycle. Additionally, this is also retained by the `handleEvent` as `session` was used directly in the closure, which then retained it. This commit fixes both by using a weak reference from the view coordinator to the session coordinator, and accessing the session via the weak `self` reference. * Follow-up robustness fix to avoid rare runtime crash * Follow-up robustness fix, removing `unowned self` * Update Sources/LiveViewNative/Coordinators/LiveViewCoordinator.swift Co-authored-by: Carson Katri <[email protected]> Signed-off-by: Adrian Schönig <[email protected]> * Update CHANGELOG.md --------- Signed-off-by: Adrian Schönig <[email protected]> Co-authored-by: Carson Katri <[email protected]>
1 parent e2c4408 commit 4e8fcf3

File tree

3 files changed

+12
-7
lines changed

3 files changed

+12
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
- Form elements will apply updates from a diff (#1451)
1919
- Updates to change-tracked properties no longer occur on the next RunLoop, fixing modal dismissal on macOS (#1450)
2020
- `+` characters are properly encoded as `%2B` in form events (#1449)
21+
- Fixed retain cycle in `LiveViewCoordinator` (#1455)
2122

2223
## [0.3.0] 2024-08-21
2324

Sources/LiveViewNative/Coordinators/LiveSessionCoordinator.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,9 +421,12 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
421421
var wsEndpoint = URLComponents(url: self.url, resolvingAgainstBaseURL: true)!
422422
wsEndpoint.scheme = self.url.scheme == "https" ? "wss" : "ws"
423423
wsEndpoint.path = "/live/websocket"
424+
let configuration = self.urlSession.configuration
424425
let socket = Socket(
425426
endPoint: wsEndpoint.string!,
426-
transport: { [unowned self] in URLSessionTransport(url: $0, configuration: self.urlSession.configuration) },
427+
transport: {
428+
URLSessionTransport(url: $0, configuration: configuration)
429+
},
427430
paramsClosure: {
428431
[
429432
"_csrf_token": domValues.phxCSRFToken,
@@ -505,12 +508,12 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
505508
}.receive("error") { msg in
506509
logger.debug("[LiveReload] error connecting to channel: \(msg.payload)")
507510
}
508-
self.liveReloadChannel!.on("assets_change") { [unowned self] _ in
511+
self.liveReloadChannel!.on("assets_change") { [weak self] _ in
509512
logger.debug("[LiveReload] assets changed, reloading")
510513
Task {
511514
StylesheetCache.removeAll()
512515
// need to fully reconnect (rather than just re-join channel) because the elixir code reloader only triggers on http reqs
513-
await self.reconnect()
516+
await self?.reconnect()
514517
}
515518
}
516519
}

Sources/LiveViewNative/Coordinators/LiveViewCoordinator.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class LiveViewCoordinator<R: RootRegistry>: ObservableObject {
3232
internalState
3333
}
3434

35-
@_spi(LiveForm) public let session: LiveSessionCoordinator<R>
35+
@_spi(LiveForm) public private(set) weak var session: LiveSessionCoordinator<R>!
3636
var url: URL
3737

3838
private var channel: Channel?
@@ -67,10 +67,11 @@ public class LiveViewCoordinator<R: RootRegistry>: ObservableObject {
6767

6868
self.handleEvent("native_redirect") { [weak self] payload in
6969
guard let self,
70-
let redirect = LiveRedirect(from: payload, relativeTo: self.url)
70+
let redirect = LiveRedirect(from: payload, relativeTo: self.url),
71+
let session = self.session
7172
else { return }
72-
Task { [weak session] in
73-
try await session?.redirect(redirect)
73+
Task {
74+
try await session.redirect(redirect)
7475
}
7576
}
7677
}

0 commit comments

Comments
 (0)