diff --git a/Package.swift b/Package.swift index bff948f71..95e9b7d63 100644 --- a/Package.swift +++ b/Package.swift @@ -120,6 +120,8 @@ let package = Package( name: "RealtimeTests", dependencies: [ .product(name: "CustomDump", package: "swift-custom-dump"), + .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), + .product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"), "PostgREST", "Realtime", "TestHelpers", diff --git a/Sources/Realtime/PhoenixTransport.swift b/Sources/Realtime/PhoenixTransport.swift index 53d7965fd..79c854005 100644 --- a/Sources/Realtime/PhoenixTransport.swift +++ b/Sources/Realtime/PhoenixTransport.swift @@ -200,8 +200,8 @@ open class URLSessionTransport: NSObject, PhoenixTransport, URLSessionWebSocketD session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil) var request = URLRequest(url: url) - headers.forEach { (key: String, value: Any) in - guard let value = value as? String else { return } + for (key, value) in headers { + guard let value = value as? String else { continue } request.addValue(value, forHTTPHeaderField: key) } diff --git a/Sources/Realtime/V2/RealtimeChannelV2.swift b/Sources/Realtime/V2/RealtimeChannelV2.swift index d9714bf23..a3ab7235a 100644 --- a/Sources/Realtime/V2/RealtimeChannelV2.swift +++ b/Sources/Realtime/V2/RealtimeChannelV2.swift @@ -86,7 +86,7 @@ public final class RealtimeChannelV2: Sendable { let logger: (any SupabaseLogger)? let socket: Socket - private let callbackManager = CallbackManager() + let callbackManager = CallbackManager() private let statusEventEmitter = EventEmitter(initialEvent: .unsubscribed) public private(set) var status: Status { @@ -472,6 +472,24 @@ public final class RealtimeChannelV2: Sendable { } } + /// Listen for postgres changes in a channel. + public func onPostgresChange( + _: AnyAction.Type, + schema: String = "public", + table: String? = nil, + filter: String? = nil, + callback: @escaping @Sendable (AnyAction) -> Void + ) -> Subscription { + _onPostgresChange( + event: .all, + schema: schema, + table: table, + filter: filter + ) { + callback($0) + } + } + /// Listen for postgres changes in a channel. public func onPostgresChange( _: InsertAction.Type, diff --git a/Tests/RealtimeTests/RealtimeChannelTests.swift b/Tests/RealtimeTests/RealtimeChannelTests.swift new file mode 100644 index 000000000..baad47460 --- /dev/null +++ b/Tests/RealtimeTests/RealtimeChannelTests.swift @@ -0,0 +1,102 @@ +// +// RealtimeChannelTests.swift +// Supabase +// +// Created by Guilherme Souza on 09/09/24. +// + +import InlineSnapshotTesting +@testable import Realtime +import XCTest +import XCTestDynamicOverlay + +final class RealtimeChannelTests: XCTestCase { + var sut: RealtimeChannelV2! + + func testOnPostgresChange() { + sut = RealtimeChannelV2( + topic: "topic", + config: RealtimeChannelConfig( + broadcast: BroadcastJoinConfig(), + presence: PresenceJoinConfig(), + isPrivate: false + ), + socket: .mock, + logger: nil + ) + var subscriptions = Set() + sut.onPostgresChange(AnyAction.self) { _ in }.store(in: &subscriptions) + sut.onPostgresChange(InsertAction.self) { _ in }.store(in: &subscriptions) + sut.onPostgresChange(UpdateAction.self) { _ in }.store(in: &subscriptions) + sut.onPostgresChange(DeleteAction.self) { _ in }.store(in: &subscriptions) + + assertInlineSnapshot(of: sut.callbackManager.callbacks, as: .dump) { + """ + ▿ 4 elements + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.all + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 1 + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.insert + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 2 + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.update + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 3 + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.delete + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 4 + + """ + } + } +} + +extension Socket { + static var mock: Socket { + Socket( + broadcastURL: unimplemented(), + status: unimplemented(), + options: unimplemented(), + accessToken: unimplemented(), + apiKey: unimplemented(), + makeRef: unimplemented(), + connect: unimplemented(), + addChannel: unimplemented(), + removeChannel: unimplemented(), + push: unimplemented(), + httpSend: unimplemented() + ) + } +}