Skip to content

Commit

Permalink
Adding the new Listeners API (#153)
Browse files Browse the repository at this point in the history
feat(listeners): adding the new Listeners API
feat(subscribe & presence): enabling EventEngine by default
  • Loading branch information
jguz-pubnub authored Feb 21, 2024
1 parent 649a9e9 commit d5c5083
Show file tree
Hide file tree
Showing 36 changed files with 2,662 additions and 357 deletions.
11 changes: 9 additions & 2 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
---
name: swift
scm: github.com/pubnub/swift
version: "6.3.0"
version: "7.0.0"
schema: 1
changelog:
- date: 2024-02-21
version: 7.0.0
changes:
- type: feature
text: "Adding the new Listeners API."
- type: feature
text: "Enabling EventEngine by default."
- date: 2024-01-22
version: 6.3.0
changes:
Expand Down Expand Up @@ -517,7 +524,7 @@ sdks:
- distribution-type: source
distribution-repository: GitHub release
package-name: PubNub
location: https://github.com/pubnub/swift/archive/refs/tags/6.3.0.zip
location: https://github.com/pubnub/swift/archive/refs/tags/7.0.0.zip
supported-platforms:
supported-operating-systems:
macOS:
Expand Down
222 changes: 156 additions & 66 deletions PubNub.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion PubNubSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'PubNubSwift'
s.version = '6.3.0'
s.version = '7.0.0'
s.homepage = 'https://github.com/pubnub/swift'
s.documentation_url = 'https://www.pubnub.com/docs/swift-native/pubnub-swift-sdk'
s.authors = { 'PubNub, Inc.' => '[email protected]' }
Expand Down
87 changes: 35 additions & 52 deletions Sources/PubNub/EventEngine/Subscribe/Helpers/SubscribeInput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,24 @@ struct SubscribeInput: Equatable {
private let channelEntries: [String: PubNubChannel]
private let groupEntries: [String: PubNubChannel]

typealias InsertingResult = (
newInput: SubscribeInput,
insertedChannels: [PubNubChannel],
insertedGroups: [PubNubChannel]
)
typealias RemovingResult = (
newInput: SubscribeInput,
removedChannels: [PubNubChannel],
removedGroups: [PubNubChannel]
)

init(channels: [PubNubChannel] = [], groups: [PubNubChannel] = []) {
self.channelEntries = channels.reduce(into: [String: PubNubChannel]()) { r, channel in _ = r.insert(channel) }
self.groupEntries = groups.reduce(into: [String: PubNubChannel]()) { r, channel in _ = r.insert(channel) }
self.channelEntries = channels.reduce(into: [String: PubNubChannel]()) { r, channel in
_ = r.insert(channel)
}
self.groupEntries = groups.reduce(into: [String: PubNubChannel]()) { r, channel in
_ = r.insert(channel)
}
}

private init(channels: [String: PubNubChannel], groups: [String: PubNubChannel]) {
Expand Down Expand Up @@ -89,52 +104,42 @@ struct SubscribeInput: Equatable {
func adding(
channels: [PubNubChannel],
and groups: [PubNubChannel]
) -> (
newInput: SubscribeInput,
insertedChannels: [PubNubChannel],
insertedGroups: [PubNubChannel]
) {
) -> SubscribeInput.InsertingResult {
// Gets a copy of current channels and channel groups
var currentChannels = channelEntries
var currentGroups = groupEntries

let insertedChannels = channels.filter { currentChannels.insert($0) }
let insertedGroups = groups.filter { currentGroups.insert($0) }

return (
return InsertingResult(
newInput: SubscribeInput(channels: currentChannels, groups: currentGroups),
insertedChannels: insertedChannels,
insertedGroups: insertedGroups
)
}

func removing(
channels: [String],
and groups: [String]
) -> (
newInput: SubscribeInput,
removedChannels: [PubNubChannel],
removedGroups: [PubNubChannel]
) {
mainChannels: [PubNubChannel],
presenceChannelsOnly: [PubNubChannel],
mainGroups: [PubNubChannel],
presenceGroupsOnly: [PubNubChannel]
) -> SubscribeInput.RemovingResult {
// Gets a copy of current channels and channel groups
var currentChannels = channelEntries
var currentGroups = groupEntries

let removedChannels = channels.compactMap {
if $0.isPresenceChannelName {
return currentChannels.unsubscribePresence($0.trimmingPresenceChannelSuffix)
} else {
return currentChannels.removeValue(forKey: $0)
}
let removedChannels = mainChannels.compactMap {
currentChannels.removeValue(forKey: $0.id)
} + presenceChannelsOnly.compactMap {
currentChannels.unsubscribePresence($0.id)
}

let removedGroups = groups.compactMap {
if $0.isPresenceChannelName {
return currentGroups.unsubscribePresence($0.trimmingPresenceChannelSuffix)
} else {
return currentGroups.removeValue(forKey: $0)
}
let removedGroups = mainGroups.compactMap {
currentGroups.removeValue(forKey: $0.id)
} + presenceGroupsOnly.compactMap {
currentGroups.unsubscribePresence($0.id)
}

return (
return RemovingResult(
newInput: SubscribeInput(channels: currentChannels, groups: currentGroups),
removedChannels: removedChannels,
removedGroups: removedGroups
Expand All @@ -158,28 +163,6 @@ extension Dictionary where Key == String, Value == PubNubChannel {
self[channel.id] = channel
return true
}

func difference(_ dict: [Key:Value]) -> [Key: Value] {
let entriesInSelfAndNotInDict = filter {
dict[$0.0] != self[$0.0]
}
return entriesInSelfAndNotInDict.reduce([Key:Value]()) { (res, entry) -> [Key:Value] in
var res = res
res[entry.0] = entry.1
return res
}
}

func intersection(_ dict: [Key:Value]) -> [Key: Value] {
let entriesInSelfAndInDict = filter {
dict[$0.0] == self[$0.0]
}
return entriesInSelfAndInDict.reduce([Key:Value]()) { (res, entry) -> [Key:Value] in
var res = res
res[entry.0] = entry.1
return res
}
}

// Updates current Dictionary with the new channel value unsubscribed from Presence.
// Returns the updated value if the corresponding entry matching the passed `id:` was found, otherwise `nil`
Expand Down
18 changes: 16 additions & 2 deletions Sources/PubNub/EventEngine/Subscribe/Subscribe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,14 @@ extension Subscribe {
let input: SubscribeInput
let cursor: SubscribeCursor
let error: PubNubError
let connectionStatus = ConnectionStatus.connectionError
let connectionStatus: ConnectionStatus

init(input: SubscribeInput, cursor: SubscribeCursor, error: PubNubError) {
self.input = input
self.cursor = cursor
self.error = error
self.connectionStatus = .connectionError(error)
}
}

struct ReceivingState: SubscribeState {
Expand All @@ -83,7 +90,14 @@ extension Subscribe {
let input: SubscribeInput
let cursor: SubscribeCursor
let error: PubNubError
let connectionStatus = ConnectionStatus.disconnectedUnexpectedly
let connectionStatus: ConnectionStatus

init(input: SubscribeInput, cursor: SubscribeCursor, error: PubNubError) {
self.input = input
self.cursor = cursor
self.error = error
self.connectionStatus = .disconnectedUnexpectedly(error)
}
}

struct UnsubscribedState: SubscribeState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ fileprivate extension SubscribeTransition {
), invocations: [
.regular(.emitStatus(change: Subscribe.ConnectionStatusChange(
oldStatus: state.connectionStatus,
newStatus: .connectionError,
newStatus: .connectionError(error),
error: error
)))
]
Expand Down Expand Up @@ -329,7 +329,7 @@ fileprivate extension SubscribeTransition {
), invocations: [
.regular(.emitStatus(change: Subscribe.ConnectionStatusChange(
oldStatus: state.connectionStatus,
newStatus: .disconnectedUnexpectedly,
newStatus: .disconnectedUnexpectedly(error),
error: error
)))
]
Expand Down
94 changes: 94 additions & 0 deletions Sources/PubNub/Events/New/Entities/EntityCreator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// PubNub+Subscribable.swift
//
// Copyright (c) PubNub Inc.
// All rights reserved.
//
// This source code is licensed under the license found in the
// LICENSE file in the root directory of this source tree.
//

import Foundation

/// Protocol for types capable of creating references for entities to which the user can subscribe,
/// receiving real-time updates.
public protocol EntityCreator {
/// Creates a new channel entity the user can subscribe to.
///
/// This method does not create any entity, either locally or remotely; it merely provides
/// a reference to a channel that can be subscribed to and unsubscribed from
///
/// - Parameters:
/// - name: The unique identifier for the channel.
/// - Returns: A `ChannelRepresentation` object representing the channel.
func channel(_ name: String) -> ChannelRepresentation

/// Creates a new channel group entity the user can subscribe to.
///
/// - Parameters:
/// - name: The unique identifier for the channel group.
/// - Returns: A `ChannelGroupRepresentation` object representing the channel group.
func channelGroup(_ name: String) -> ChannelGroupRepresentation

/// Creates user metadata entity the user can subscribe to.
///
/// This method does not create any entity, either locally or remotely; it merely provides
/// a reference to a channel that can be subscribed to and unsubscribed from
///
/// - Parameters:
/// - name: The unique identifier for the user metadata.
/// - Returns: A `UserMetadataRepresentation` object representing the user metadata.
func userMetadata(_ name: String) -> UserMetadataRepresentation

/// Creates channel metadata entity the user can subscribe to.
///
/// This method does not create any entity, either locally or remotely; it merely provides
/// a reference to a channel that can be subscribed to and unsubscribed from
///
/// - Parameters:
/// - name: The unique identifier for the channel metadata.
/// - Returns: A `ChannelMetadataRepresentation` object representing the channel metadata.
func channelMetadata(_ name: String) -> ChannelMetadataRepresentation
}

public extension EntityCreator {
/// Creates a `SubscriptionSet` object from the collection of `Subscribable` entites.
///
/// Use this function to set up and manage subscriptions for a collection of `Subscribable` entities.
///
/// - Parameters:
/// - queue: The dispatch queue on which the subscription events should be handled
/// - entities: A collection of `Subscribable` entities to subscribe to
/// - options: Additional options for configuring the subscription
/// - Returns: A `SubscriptionSet` instance for managing the specified entities.
func subscription(
queue: DispatchQueue = .main,
entities: any Collection<Subscribable>,
options: SubscriptionOptions = SubscriptionOptions.empty()
) -> SubscriptionSet {
SubscriptionSet(
queue: queue,
entities: entities,
options: options
)
}
}

// This internal protocol is designed for types capable of receiving an intent
// to Subscribe or Unsubscribe and invoking the PubNub service with computed channels
// and channel groups.
protocol SubscribeReceiver: AnyObject {
func registerAdapter(_ adapter: BaseSubscriptionListenerAdapter)
func hasRegisteredAdapter(with uuid: UUID) -> Bool

func internalSubscribe(
with channels: [Subscription],
and groups: [Subscription],
at timetoken: Timetoken?
)
func internalUnsubscribe(
from channels: [Subscription],
and groups: [Subscription],
presenceOnly: Bool
)
}
47 changes: 47 additions & 0 deletions Sources/PubNub/Events/New/Entities/EntitySubscribable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// EntitySubscribable.swift
//
// Copyright (c) PubNub Inc.
// All rights reserved.
//
// This source code is licensed under the license found in the
// LICENSE file in the root directory of this source tree.
//

import Foundation

// MARK: - PubNubChannelRepresentation

/// Represents a channel that can be subscribed to and unsubscribed from using the PubNub service.
public class ChannelRepresentation: Subscribable {
init(name: String, receiver: SubscribeReceiver) {
super.init(name: name, subscriptionType: .channel, receiver: receiver)
}
}

// MARK: - PubNubChannelGroupRepresentation

/// Represents a channel group that can be subscribed to and unsubscribed from using the PubNub service.
public class ChannelGroupRepresentation: Subscribable {
init(name: String, receiver: SubscribeReceiver) {
super.init(name: name, subscriptionType: .channelGroup, receiver: receiver)
}
}

// MARK: - PubNubUserMetadataRepresentation

/// Represents user metadata that can be subscribed to and unsubscribed from using the PubNub service.
public class UserMetadataRepresentation: Subscribable {
init(id: String, receiver: SubscribeReceiver) {
super.init(name: id, subscriptionType: .channel, receiver: receiver)
}
}

// MARK: - PubNubChannelMetadataRepresentation

/// Represents channel metadata that can be subscribed to and unsubscribed from using the PubNub service.
public class ChannelMetadataRepresentation: Subscribable {
init(id: String, receiver: SubscribeReceiver) {
super.init(name: id, subscriptionType: .channel, receiver: receiver)
}
}
Loading

0 comments on commit d5c5083

Please sign in to comment.