77
88import Foundation
99import SwiftUI
10+ import Combine
1011
1112/// The SwiftUI root view for a Phoenix LiveView.
1213///
@@ -23,7 +24,9 @@ import SwiftUI
2324/// - ``LiveViewModel``
2425public struct LiveView < R: RootRegistry > : View {
2526 @State private var hasAppeared = false
26- @ObservedObject var session : LiveSessionCoordinator < R >
27+
28+ @StateObject var storage : LiveSessionCoordinatorStorage
29+
2730 @State private var hasSetupNavigationControllerDelegate = false
2831
2932 @ObservedObject private var rootCoordinator : LiveViewCoordinator < R >
@@ -32,18 +35,39 @@ public struct LiveView<R: RootRegistry>: View {
3235 ///
3336 /// - Note: Changing coordinators after the `LiveView` is setup and connected is forbidden.
3437 public init ( session: LiveSessionCoordinator < R > ) {
35- self . _session = . init( wrappedValue: session)
38+ self . _storage = . init( wrappedValue: . init ( session: session ) )
3639 self . rootCoordinator = session. rootCoordinator
3740 }
41+
42+ public init ( _ host: some LiveViewHost , configuration: LiveSessionConfiguration = . init( ) ) {
43+ self . init ( session: . init( host. url, config: configuration) )
44+ }
45+
46+ public init ( url: URL , configuration: LiveSessionConfiguration = . init( ) ) {
47+ self . init ( session: . init( url, config: configuration) )
48+ }
49+
50+ final class LiveSessionCoordinatorStorage : ObservableObject {
51+ var session : LiveSessionCoordinator < R >
52+
53+ var cancellable : AnyCancellable ?
54+
55+ init ( session: LiveSessionCoordinator < R > ) {
56+ self . session = session
57+ self . cancellable = session. objectWillChange. sink { [ weak self] _ in
58+ self ? . objectWillChange. send ( )
59+ }
60+ }
61+ }
3862
3963 public var body : some View {
4064 SwiftUI . VStack {
41- switch session. state {
65+ switch storage . session. state {
4266 case . connected:
4367 rootNavEntry
4468 default :
4569 if R . LoadingView. self == Never . self {
46- switch session. state {
70+ switch storage . session. state {
4771 case . connected:
4872 fatalError ( )
4973 case . notConnected:
@@ -80,18 +104,18 @@ public struct LiveView<R: RootRegistry>: View {
80104 }
81105 }
82106 } else {
83- R . loadingView ( for: session. url, state: session. state)
107+ R . loadingView ( for: storage . session. url, state: storage . session. state)
84108 }
85109 }
86110 }
87111 . task {
88- await session. connect ( )
112+ await storage . session. connect ( )
89113 }
90114 }
91115
92116 @ViewBuilder
93117 private var rootNavEntry : some View {
94- switch session. config. navigationMode {
118+ switch storage . session. config. navigationMode {
95119 case . enabled:
96120 navigationStack
97121 case . splitView:
@@ -105,7 +129,7 @@ public struct LiveView<R: RootRegistry>: View {
105129
106130 @ViewBuilder
107131 private var navigationStack : some View {
108- NavigationStack ( path: $session. navigationPath) {
132+ NavigationStack ( path: $storage . session. navigationPath) {
109133 navigationRoot
110134 . navigationDestination ( for: LiveNavigationEntry< R> . self ) { entry in
111135 NavStackEntryView ( entry)
@@ -118,16 +142,16 @@ public struct LiveView<R: RootRegistry>: View {
118142 navigationRoot
119143 } detail: {
120144 NavigationStack ( path: Binding < [ LiveNavigationEntry < R > ] > {
121- Array ( session. navigationPath. dropFirst ( ) )
145+ Array ( storage . session. navigationPath. dropFirst ( ) )
122146 } set: { value in
123147 var result = value
124- if let first = session. navigationPath. first {
148+ if let first = storage . session. navigationPath. first {
125149 result. insert ( first, at: 0 )
126150 }
127- session. navigationPath = result
151+ storage . session. navigationPath = result
128152 } ) {
129153 SwiftUI . Group {
130- if let entry = session. navigationPath. first {
154+ if let entry = storage . session. navigationPath. first {
131155 NavStackEntryView ( entry)
132156 }
133157 }
@@ -143,8 +167,8 @@ public struct LiveView<R: RootRegistry>: View {
143167 private func tabView( _ tabs: [ LiveSessionConfiguration . NavigationMode . Tab ] ) -> some View {
144168 SwiftUI . TabView ( selection: $selectedTab) {
145169 ForEach ( tabs) { tab in
146- NavigationStack ( path: $session. navigationPath) {
147- NavStackEntryView ( . init( url: selectedTab ?? session. url, coordinator: rootCoordinator) )
170+ NavigationStack ( path: $storage . session. navigationPath) {
171+ NavStackEntryView ( . init( url: selectedTab ?? storage . session. url, coordinator: rootCoordinator) )
148172 . navigationDestination ( for: LiveNavigationEntry< R> . self ) { entry in
149173 NavStackEntryView ( entry)
150174 }
@@ -158,13 +182,23 @@ public struct LiveView<R: RootRegistry>: View {
158182 . onChange ( of: selectedTab) { newValue in
159183 guard let newValue else { return }
160184 Task {
161- session. rootCoordinator. url = newValue
162- await session. rootCoordinator. reconnect ( )
185+ storage . session. rootCoordinator. url = newValue
186+ await storage . session. rootCoordinator. reconnect ( )
163187 }
164188 }
165189 }
166190
167191 private var navigationRoot : some View {
168- NavStackEntryView ( . init( url: session. url, coordinator: rootCoordinator) )
192+ NavStackEntryView ( . init( url: storage. session. url, coordinator: rootCoordinator) )
193+ }
194+ }
195+
196+ extension LiveView where R == EmptyRegistry {
197+ public init ( _ host: some LiveViewHost , configuration: LiveSessionConfiguration = . init( ) ) {
198+ self . init ( session: . init( host. url, config: configuration) )
199+ }
200+
201+ public init ( url: URL , configuration: LiveSessionConfiguration = . init( ) ) {
202+ self . init ( session: . init( url, config: configuration) )
169203 }
170204}
0 commit comments