Skip to content

Commit a0b3134

Browse files
committed
Add Encodable push_event and support receiveEvent from LiveSessionCoordinator
1 parent 8b9ef32 commit a0b3134

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

Sources/LiveViewNative/Coordinators/LiveSessionCoordinator.swift

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
6161

6262
private var cancellables = Set<AnyCancellable>()
6363

64+
private var mergedEventSubjects: AnyCancellable?
65+
private var eventSubject = PassthroughSubject<(LiveViewCoordinator<R>, (String, Payload)), Never>()
66+
private var eventHandlers = Set<AnyCancellable>()
67+
6468
var isMounted: Bool = false
6569

6670
public convenience init(_ host: some LiveViewHost, config: LiveSessionConfiguration = .init(), customRegistryType: R.Type = R.self) {
@@ -75,6 +79,12 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
7579
self.url = url.appending(path: "").absoluteURL
7680
self.config = config
7781
self.rootCoordinator = .init(session: self, url: self.url)
82+
self.mergedEventSubjects = self.rootCoordinator.eventSubject.compactMap({
83+
[weak self] value in self.map({ ($0.rootCoordinator, value) })
84+
})
85+
.sink(receiveValue: { [weak self] value in
86+
self?.eventSubject.send(value)
87+
})
7888
self.$internalState.sink { state in
7989
if case .connected = state {
8090
Task { [weak self] in
@@ -93,7 +103,21 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
93103
}
94104
}
95105
}.store(in: &cancellables)
96-
$navigationPath.sink { entries in
106+
$navigationPath.sink { [weak self] entries in
107+
// Receive events from all live views.
108+
if let self {
109+
let allCoordinators = ([self.rootCoordinator] + entries.map(\.coordinator))
110+
.reduce(into: [LiveViewCoordinator<R>]()) { result, next in
111+
guard !result.contains(where: { $0 === next }) else { return }
112+
result.append(next)
113+
}
114+
self.mergedEventSubjects = Publishers.MergeMany(allCoordinators.map({ coordinator in
115+
coordinator.eventSubject.map({ (coordinator, $0) })
116+
}))
117+
.sink(receiveValue: { [weak self] value in
118+
self?.eventSubject.send(value)
119+
})
120+
}
97121
guard let entry = entries.last else { return }
98122
Task {
99123
// If the coordinator is not connected to the right URL, update it.
@@ -190,6 +214,34 @@ public class LiveSessionCoordinator<R: RootRegistry>: ObservableObject {
190214
await self.rootCoordinator.reconnect()
191215
}
192216

217+
/// Creates a publisher that can be used to listen for server-sent LiveView events.
218+
///
219+
/// - Parameter event: The event name that is being listened for.
220+
/// - Returns: A publisher that emits event payloads.
221+
///
222+
/// This event will be received from every LiveView handled by this session coordinator.
223+
///
224+
/// See ``LiveViewCoordinator/receiveEvent(_:)`` for more details.
225+
public func receiveEvent(_ event: String) -> some Publisher<(LiveViewCoordinator<R>, Payload), Never> {
226+
eventSubject
227+
.filter { $0.1.0 == event }
228+
.map({ ($0.0, $0.1.1) })
229+
}
230+
231+
/// Permanently registers a handler for a server-sent LiveView event.
232+
///
233+
/// - Parameter event: The event name that is being listened for.
234+
/// - Parameter handler: A closure to invoke when the coordinator receives an event. The event value is provided as the closure's parameter.
235+
///
236+
/// This event handler will be added to every LiveView handled by this session coordinator.
237+
///
238+
/// See ``LiveViewCoordinator/handleEvent(_:handler:)`` for more details.
239+
public func handleEvent(_ event: String, handler: @escaping (LiveViewCoordinator<R>, Payload) -> Void) {
240+
receiveEvent(event)
241+
.sink(receiveValue: handler)
242+
.store(in: &eventHandlers)
243+
}
244+
193245
func fetchDOM(url: URL) async throws -> String {
194246
let data: Data
195247
let resp: URLResponse

Sources/LiveViewNative/Coordinators/LiveViewCoordinator.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public class LiveViewCoordinator<R: RootRegistry>: ObservableObject {
4545
private var currentConnectionToken: ConnectionAttemptToken?
4646
private var currentConnectionTask: Task<Void, Error>?
4747

48-
private var eventSubject = PassthroughSubject<(String, Payload), Never>()
49-
private var eventHandlers = Set<AnyCancellable>()
48+
private(set) internal var eventSubject = PassthroughSubject<(String, Payload), Never>()
49+
private(set) internal var eventHandlers = Set<AnyCancellable>()
5050

5151
init(session: LiveSessionCoordinator<R>, url: URL) {
5252
self.session = session
@@ -83,6 +83,15 @@ public class LiveViewCoordinator<R: RootRegistry>: ObservableObject {
8383
])
8484
}
8585

86+
public func pushEvent(type: String, event: String, value: some Encodable, target: Int? = nil) async throws {
87+
try await pushEvent(
88+
type: type,
89+
event: event,
90+
value: try JSONSerialization.jsonObject(with: JSONEncoder().encode(value)),
91+
target: target
92+
)
93+
}
94+
8695
internal func doPushEvent(_ event: String, payload: Payload) async throws {
8796
guard let channel = channel else {
8897
return

0 commit comments

Comments
 (0)