Skip to content

Commit d5c5083

Browse files
authored
Adding the new Listeners API (#153)
feat(listeners): adding the new Listeners API feat(subscribe & presence): enabling EventEngine by default
1 parent 649a9e9 commit d5c5083

36 files changed

+2662
-357
lines changed

.pubnub.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
---
22
name: swift
33
scm: github.com/pubnub/swift
4-
version: "6.3.0"
4+
version: "7.0.0"
55
schema: 1
66
changelog:
7+
- date: 2024-02-21
8+
version: 7.0.0
9+
changes:
10+
- type: feature
11+
text: "Adding the new Listeners API."
12+
- type: feature
13+
text: "Enabling EventEngine by default."
714
- date: 2024-01-22
815
version: 6.3.0
916
changes:
@@ -517,7 +524,7 @@ sdks:
517524
- distribution-type: source
518525
distribution-repository: GitHub release
519526
package-name: PubNub
520-
location: https://github.com/pubnub/swift/archive/refs/tags/6.3.0.zip
527+
location: https://github.com/pubnub/swift/archive/refs/tags/7.0.0.zip
521528
supported-platforms:
522529
supported-operating-systems:
523530
macOS:

PubNub.xcodeproj/project.pbxproj

Lines changed: 156 additions & 66 deletions
Large diffs are not rendered by default.

PubNubSwift.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'PubNubSwift'
3-
s.version = '6.3.0'
3+
s.version = '7.0.0'
44
s.homepage = 'https://github.com/pubnub/swift'
55
s.documentation_url = 'https://www.pubnub.com/docs/swift-native/pubnub-swift-sdk'
66
s.authors = { 'PubNub, Inc.' => '[email protected]' }

Sources/PubNub/EventEngine/Subscribe/Helpers/SubscribeInput.swift

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,24 @@ struct SubscribeInput: Equatable {
1414
private let channelEntries: [String: PubNubChannel]
1515
private let groupEntries: [String: PubNubChannel]
1616

17+
typealias InsertingResult = (
18+
newInput: SubscribeInput,
19+
insertedChannels: [PubNubChannel],
20+
insertedGroups: [PubNubChannel]
21+
)
22+
typealias RemovingResult = (
23+
newInput: SubscribeInput,
24+
removedChannels: [PubNubChannel],
25+
removedGroups: [PubNubChannel]
26+
)
27+
1728
init(channels: [PubNubChannel] = [], groups: [PubNubChannel] = []) {
18-
self.channelEntries = channels.reduce(into: [String: PubNubChannel]()) { r, channel in _ = r.insert(channel) }
19-
self.groupEntries = groups.reduce(into: [String: PubNubChannel]()) { r, channel in _ = r.insert(channel) }
29+
self.channelEntries = channels.reduce(into: [String: PubNubChannel]()) { r, channel in
30+
_ = r.insert(channel)
31+
}
32+
self.groupEntries = groups.reduce(into: [String: PubNubChannel]()) { r, channel in
33+
_ = r.insert(channel)
34+
}
2035
}
2136

2237
private init(channels: [String: PubNubChannel], groups: [String: PubNubChannel]) {
@@ -89,52 +104,42 @@ struct SubscribeInput: Equatable {
89104
func adding(
90105
channels: [PubNubChannel],
91106
and groups: [PubNubChannel]
92-
) -> (
93-
newInput: SubscribeInput,
94-
insertedChannels: [PubNubChannel],
95-
insertedGroups: [PubNubChannel]
96-
) {
107+
) -> SubscribeInput.InsertingResult {
108+
// Gets a copy of current channels and channel groups
97109
var currentChannels = channelEntries
98110
var currentGroups = groupEntries
99111

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

103-
return (
115+
return InsertingResult(
104116
newInput: SubscribeInput(channels: currentChannels, groups: currentGroups),
105117
insertedChannels: insertedChannels,
106118
insertedGroups: insertedGroups
107119
)
108120
}
109121

110122
func removing(
111-
channels: [String],
112-
and groups: [String]
113-
) -> (
114-
newInput: SubscribeInput,
115-
removedChannels: [PubNubChannel],
116-
removedGroups: [PubNubChannel]
117-
) {
123+
mainChannels: [PubNubChannel],
124+
presenceChannelsOnly: [PubNubChannel],
125+
mainGroups: [PubNubChannel],
126+
presenceGroupsOnly: [PubNubChannel]
127+
) -> SubscribeInput.RemovingResult {
128+
// Gets a copy of current channels and channel groups
118129
var currentChannels = channelEntries
119130
var currentGroups = groupEntries
120131

121-
let removedChannels = channels.compactMap {
122-
if $0.isPresenceChannelName {
123-
return currentChannels.unsubscribePresence($0.trimmingPresenceChannelSuffix)
124-
} else {
125-
return currentChannels.removeValue(forKey: $0)
126-
}
132+
let removedChannels = mainChannels.compactMap {
133+
currentChannels.removeValue(forKey: $0.id)
134+
} + presenceChannelsOnly.compactMap {
135+
currentChannels.unsubscribePresence($0.id)
127136
}
128-
129-
let removedGroups = groups.compactMap {
130-
if $0.isPresenceChannelName {
131-
return currentGroups.unsubscribePresence($0.trimmingPresenceChannelSuffix)
132-
} else {
133-
return currentGroups.removeValue(forKey: $0)
134-
}
137+
let removedGroups = mainGroups.compactMap {
138+
currentGroups.removeValue(forKey: $0.id)
139+
} + presenceGroupsOnly.compactMap {
140+
currentGroups.unsubscribePresence($0.id)
135141
}
136-
137-
return (
142+
return RemovingResult(
138143
newInput: SubscribeInput(channels: currentChannels, groups: currentGroups),
139144
removedChannels: removedChannels,
140145
removedGroups: removedGroups
@@ -158,28 +163,6 @@ extension Dictionary where Key == String, Value == PubNubChannel {
158163
self[channel.id] = channel
159164
return true
160165
}
161-
162-
func difference(_ dict: [Key:Value]) -> [Key: Value] {
163-
let entriesInSelfAndNotInDict = filter {
164-
dict[$0.0] != self[$0.0]
165-
}
166-
return entriesInSelfAndNotInDict.reduce([Key:Value]()) { (res, entry) -> [Key:Value] in
167-
var res = res
168-
res[entry.0] = entry.1
169-
return res
170-
}
171-
}
172-
173-
func intersection(_ dict: [Key:Value]) -> [Key: Value] {
174-
let entriesInSelfAndInDict = filter {
175-
dict[$0.0] == self[$0.0]
176-
}
177-
return entriesInSelfAndInDict.reduce([Key:Value]()) { (res, entry) -> [Key:Value] in
178-
var res = res
179-
res[entry.0] = entry.1
180-
return res
181-
}
182-
}
183166

184167
// Updates current Dictionary with the new channel value unsubscribed from Presence.
185168
// Returns the updated value if the corresponding entry matching the passed `id:` was found, otherwise `nil`

Sources/PubNub/EventEngine/Subscribe/Subscribe.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,14 @@ extension Subscribe {
5656
let input: SubscribeInput
5757
let cursor: SubscribeCursor
5858
let error: PubNubError
59-
let connectionStatus = ConnectionStatus.connectionError
59+
let connectionStatus: ConnectionStatus
60+
61+
init(input: SubscribeInput, cursor: SubscribeCursor, error: PubNubError) {
62+
self.input = input
63+
self.cursor = cursor
64+
self.error = error
65+
self.connectionStatus = .connectionError(error)
66+
}
6067
}
6168

6269
struct ReceivingState: SubscribeState {
@@ -83,7 +90,14 @@ extension Subscribe {
8390
let input: SubscribeInput
8491
let cursor: SubscribeCursor
8592
let error: PubNubError
86-
let connectionStatus = ConnectionStatus.disconnectedUnexpectedly
93+
let connectionStatus: ConnectionStatus
94+
95+
init(input: SubscribeInput, cursor: SubscribeCursor, error: PubNubError) {
96+
self.input = input
97+
self.cursor = cursor
98+
self.error = error
99+
self.connectionStatus = .disconnectedUnexpectedly(error)
100+
}
87101
}
88102

89103
struct UnsubscribedState: SubscribeState {

Sources/PubNub/EventEngine/Subscribe/SubscribeTransition.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ fileprivate extension SubscribeTransition {
258258
), invocations: [
259259
.regular(.emitStatus(change: Subscribe.ConnectionStatusChange(
260260
oldStatus: state.connectionStatus,
261-
newStatus: .connectionError,
261+
newStatus: .connectionError(error),
262262
error: error
263263
)))
264264
]
@@ -329,7 +329,7 @@ fileprivate extension SubscribeTransition {
329329
), invocations: [
330330
.regular(.emitStatus(change: Subscribe.ConnectionStatusChange(
331331
oldStatus: state.connectionStatus,
332-
newStatus: .disconnectedUnexpectedly,
332+
newStatus: .disconnectedUnexpectedly(error),
333333
error: error
334334
)))
335335
]
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//
2+
// PubNub+Subscribable.swift
3+
//
4+
// Copyright (c) PubNub Inc.
5+
// All rights reserved.
6+
//
7+
// This source code is licensed under the license found in the
8+
// LICENSE file in the root directory of this source tree.
9+
//
10+
11+
import Foundation
12+
13+
/// Protocol for types capable of creating references for entities to which the user can subscribe,
14+
/// receiving real-time updates.
15+
public protocol EntityCreator {
16+
/// Creates a new channel entity the user can subscribe to.
17+
///
18+
/// This method does not create any entity, either locally or remotely; it merely provides
19+
/// a reference to a channel that can be subscribed to and unsubscribed from
20+
///
21+
/// - Parameters:
22+
/// - name: The unique identifier for the channel.
23+
/// - Returns: A `ChannelRepresentation` object representing the channel.
24+
func channel(_ name: String) -> ChannelRepresentation
25+
26+
/// Creates a new channel group entity the user can subscribe to.
27+
///
28+
/// - Parameters:
29+
/// - name: The unique identifier for the channel group.
30+
/// - Returns: A `ChannelGroupRepresentation` object representing the channel group.
31+
func channelGroup(_ name: String) -> ChannelGroupRepresentation
32+
33+
/// Creates user metadata entity the user can subscribe to.
34+
///
35+
/// This method does not create any entity, either locally or remotely; it merely provides
36+
/// a reference to a channel that can be subscribed to and unsubscribed from
37+
///
38+
/// - Parameters:
39+
/// - name: The unique identifier for the user metadata.
40+
/// - Returns: A `UserMetadataRepresentation` object representing the user metadata.
41+
func userMetadata(_ name: String) -> UserMetadataRepresentation
42+
43+
/// Creates channel metadata entity the user can subscribe to.
44+
///
45+
/// This method does not create any entity, either locally or remotely; it merely provides
46+
/// a reference to a channel that can be subscribed to and unsubscribed from
47+
///
48+
/// - Parameters:
49+
/// - name: The unique identifier for the channel metadata.
50+
/// - Returns: A `ChannelMetadataRepresentation` object representing the channel metadata.
51+
func channelMetadata(_ name: String) -> ChannelMetadataRepresentation
52+
}
53+
54+
public extension EntityCreator {
55+
/// Creates a `SubscriptionSet` object from the collection of `Subscribable` entites.
56+
///
57+
/// Use this function to set up and manage subscriptions for a collection of `Subscribable` entities.
58+
///
59+
/// - Parameters:
60+
/// - queue: The dispatch queue on which the subscription events should be handled
61+
/// - entities: A collection of `Subscribable` entities to subscribe to
62+
/// - options: Additional options for configuring the subscription
63+
/// - Returns: A `SubscriptionSet` instance for managing the specified entities.
64+
func subscription(
65+
queue: DispatchQueue = .main,
66+
entities: any Collection<Subscribable>,
67+
options: SubscriptionOptions = SubscriptionOptions.empty()
68+
) -> SubscriptionSet {
69+
SubscriptionSet(
70+
queue: queue,
71+
entities: entities,
72+
options: options
73+
)
74+
}
75+
}
76+
77+
// This internal protocol is designed for types capable of receiving an intent
78+
// to Subscribe or Unsubscribe and invoking the PubNub service with computed channels
79+
// and channel groups.
80+
protocol SubscribeReceiver: AnyObject {
81+
func registerAdapter(_ adapter: BaseSubscriptionListenerAdapter)
82+
func hasRegisteredAdapter(with uuid: UUID) -> Bool
83+
84+
func internalSubscribe(
85+
with channels: [Subscription],
86+
and groups: [Subscription],
87+
at timetoken: Timetoken?
88+
)
89+
func internalUnsubscribe(
90+
from channels: [Subscription],
91+
and groups: [Subscription],
92+
presenceOnly: Bool
93+
)
94+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// EntitySubscribable.swift
3+
//
4+
// Copyright (c) PubNub Inc.
5+
// All rights reserved.
6+
//
7+
// This source code is licensed under the license found in the
8+
// LICENSE file in the root directory of this source tree.
9+
//
10+
11+
import Foundation
12+
13+
// MARK: - PubNubChannelRepresentation
14+
15+
/// Represents a channel that can be subscribed to and unsubscribed from using the PubNub service.
16+
public class ChannelRepresentation: Subscribable {
17+
init(name: String, receiver: SubscribeReceiver) {
18+
super.init(name: name, subscriptionType: .channel, receiver: receiver)
19+
}
20+
}
21+
22+
// MARK: - PubNubChannelGroupRepresentation
23+
24+
/// Represents a channel group that can be subscribed to and unsubscribed from using the PubNub service.
25+
public class ChannelGroupRepresentation: Subscribable {
26+
init(name: String, receiver: SubscribeReceiver) {
27+
super.init(name: name, subscriptionType: .channelGroup, receiver: receiver)
28+
}
29+
}
30+
31+
// MARK: - PubNubUserMetadataRepresentation
32+
33+
/// Represents user metadata that can be subscribed to and unsubscribed from using the PubNub service.
34+
public class UserMetadataRepresentation: Subscribable {
35+
init(id: String, receiver: SubscribeReceiver) {
36+
super.init(name: id, subscriptionType: .channel, receiver: receiver)
37+
}
38+
}
39+
40+
// MARK: - PubNubChannelMetadataRepresentation
41+
42+
/// Represents channel metadata that can be subscribed to and unsubscribed from using the PubNub service.
43+
public class ChannelMetadataRepresentation: Subscribable {
44+
init(id: String, receiver: SubscribeReceiver) {
45+
super.init(name: id, subscriptionType: .channel, receiver: receiver)
46+
}
47+
}

0 commit comments

Comments
 (0)