diff --git a/README.md b/README.md index 7220b99..1f5384b 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,23 @@ allUserId99Changes.subscribe() allUserId99Changes.unsubscribe() allUserId99Changes.off(.all) ``` +### Presence +Presence can be used to share state between clients. + +* Listen to presence `sync` events: + +```swift +let channel = client.channel(.table("channel_id", schema: "someChannel"), options: .init(presenceKey: "user_uuid")) +let presence = Presence(channel: channel) + +presence.onSync { + print("presence sync", presence?.state, presence?.list()) +} + +channel.join() +// ... +``` ## Credits diff --git a/Sources/Realtime/Channel.swift b/Sources/Realtime/Channel.swift index a0ed257..1f07573 100644 --- a/Sources/Realtime/Channel.swift +++ b/Sources/Realtime/Channel.swift @@ -93,12 +93,31 @@ public class Channel { /// Refs of stateChange hooks var stateChangeRefs: [String] + /// Initialize a Channel + /// - parameter topic: Topic of the Channel + /// - parameter options: Optional. Options to configure channel broadcast and presence + /// - parameter socket: Socket that the channel is a part of + public convenience init(topic: ChannelTopic, options: ChannelOptions = ChannelOptions(), socket: RealtimeClient) { + let params = [ + "config": [ + "presence": [ + "key": options.presenceKey ?? "" + ], + "broadcast": [ + "ack": options.broadcastAcknowledge, + "self": options.broadcastSelf + ] + ] + ] + self.init(topic: topic, params: params, socket: socket) + } + /// Initialize a Channel /// /// - parameter topic: Topic of the Channel /// - parameter params: Optional. Parameters to send when joining. /// - parameter socket: Socket that the channel is a part of - init(topic: ChannelTopic, params: [String: Any] = [:], socket: RealtimeClient) { + init(topic: ChannelTopic, params: [String: Any], socket: RealtimeClient) { state = ChannelState.closed self.topic = topic self.params = params diff --git a/Sources/Realtime/Defaults.swift b/Sources/Realtime/Defaults.swift index b006b13..2db13df 100644 --- a/Sources/Realtime/Defaults.swift +++ b/Sources/Realtime/Defaults.swift @@ -198,3 +198,19 @@ public enum ChannelTopic: RawRepresentable, Equatable { } } } + +/// Represents the broadcast and presence options for a channel. +public struct ChannelOptions { + /// Used to track presence payload across clients. Must be unique per client. If `nil`, the server will generate one. + var presenceKey: String? + /// Enables the client to receieve their own`broadcast` messages + var broadcastSelf: Bool + /// Instructs the server to acknoledge the client's `broadcast` messages + var broadcastAcknowledge: Bool + + public init(presenceKey: String? = nil, broadcastSelf: Bool = false, broadcastAcknowledge: Bool = false) { + self.presenceKey = presenceKey + self.broadcastSelf = broadcastSelf + self.broadcastAcknowledge = broadcastAcknowledge + } +} diff --git a/Sources/Realtime/RealtimeClient.swift b/Sources/Realtime/RealtimeClient.swift index 81114f8..55f8f81 100644 --- a/Sources/Realtime/RealtimeClient.swift +++ b/Sources/Realtime/RealtimeClient.swift @@ -583,7 +583,24 @@ public class RealtimeClient: TransportDelegate { // ---------------------------------------------------------------------- // MARK: - Channel Initialization - + // ---------------------------------------------------------------------- + /// Initialize a new Channel + /// + /// Example: + /// + /// let channel = socket.channel("rooms", options: ChannelOptions(presenceKey: "user123")) + /// + /// - parameter topic: Topic of the channel + /// - parameter options: Optional. Options for the channel + /// - return: A new channel + public func channel( + _ topic: ChannelTopic, + options: ChannelOptions = ChannelOptions() + ) -> Channel { + let channel = Channel(topic: topic, options: options, socket: self) + channels.append(channel) + return channel + } // ---------------------------------------------------------------------- /// Initialize a new Channel /// @@ -594,9 +611,10 @@ public class RealtimeClient: TransportDelegate { /// - parameter topic: Topic of the channel /// - parameter params: Optional. Parameters for the channel /// - return: A new channel + @available(*, deprecated, renamed: "channel(_:options:)") public func channel( _ topic: ChannelTopic, - params: [String: Any] = [:] + params: [String: Any] ) -> Channel { let channel = Channel(topic: topic, params: params, socket: self) channels.append(channel)