Skip to content

Commit 649a9e9

Browse files
authored
Subscribe & Presence Event Engine (#152)
feat(subscribe & presence): introducing Subscribe & Presence EventEngine
1 parent b47b478 commit 649a9e9

File tree

79 files changed

+10244
-1623
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+10244
-1623
lines changed

.pubnub.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
---
22
name: swift
33
scm: github.com/pubnub/swift
4-
version: "6.2.3"
4+
version: "6.3.0"
55
schema: 1
66
changelog:
7+
- date: 2024-01-22
8+
version: 6.3.0
9+
changes:
10+
- type: feature
11+
text: "Introducing Subscribe & Presence EventEngine."
712
- date: 2023-11-28
813
version: 6.2.3
914
changes:
@@ -512,7 +517,7 @@ sdks:
512517
- distribution-type: source
513518
distribution-repository: GitHub release
514519
package-name: PubNub
515-
location: https://github.com/pubnub/swift/archive/refs/tags/6.2.3.zip
520+
location: https://github.com/pubnub/swift/archive/refs/tags/6.3.0.zip
516521
supported-platforms:
517522
supported-operating-systems:
518523
macOS:

Examples/Sources/MasterDetailTableViewController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ class MasterDetailTableViewController: UITableViewController {
260260
print("Status disconnected")
261261
case .disconnectedUnexpectedly:
262262
print("Status disconnected unexpectedly!")
263+
case .connectionError:
264+
print("Cannot establish initial conection to the remote system")
263265
}
264266
case let .subscriptionChanged(subscribeChange):
265267
switch subscribeChange {

Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ SPEC CHECKSUMS:
1919

2020
PODFILE CHECKSUM: 61a40240486621bb01f596fdd5bc632504940fab
2121

22-
COCOAPODS: 1.12.1
22+
COCOAPODS: 1.14.3

PubNub.xcodeproj/project.pbxproj

Lines changed: 320 additions & 12 deletions
Large diffs are not rendered by default.

PubNubMembership/Sources/Membership+PubNub.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import PubNubUser
1818
public protocol PubNubMembershipInterface {
1919
/// A copy of the configuration object used for this session
2020
var configuration: PubNubConfiguration { get }
21-
2221
/// Session used for performing request/response REST calls
2322
var networkSession: SessionReplaceable { get }
2423

@@ -268,6 +267,7 @@ public extension PubNubMembershipInterface {
268267
(requestConfig.customSession ?? networkSession)
269268
.route(
270269
router,
270+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
271271
responseDecoder: FetchMultipleValueResponseDecoder<PubNubMembership.PartialSpace>(),
272272
responseQueue: requestConfig.responseQueue
273273
) { result in
@@ -320,6 +320,7 @@ public extension PubNubMembershipInterface {
320320
(requestConfig.customSession ?? networkSession)
321321
.route(
322322
router,
323+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
323324
responseDecoder: FetchMultipleValueResponseDecoder<PubNubMembership.PartialUser>(),
324325
responseQueue: requestConfig.responseQueue
325326
) { result in
@@ -365,6 +366,7 @@ public extension PubNubMembershipInterface {
365366
(requestConfig.customSession ?? networkSession)
366367
.route(
367368
router,
369+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
368370
responseDecoder: FetchStatusResponseDecoder(),
369371
responseQueue: requestConfig.responseQueue
370372
) { result in
@@ -401,6 +403,7 @@ public extension PubNubMembershipInterface {
401403
(requestConfig.customSession ?? networkSession)
402404
.route(
403405
router,
406+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
404407
responseDecoder: FetchStatusResponseDecoder(),
405408
responseQueue: requestConfig.responseQueue
406409
) { result in
@@ -463,6 +466,7 @@ public extension PubNubMembershipInterface {
463466
(requestConfig.customSession ?? networkSession)
464467
.route(
465468
router,
469+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
466470
responseDecoder: FetchStatusResponseDecoder(),
467471
responseQueue: requestConfig.responseQueue
468472
) { result in
@@ -499,6 +503,7 @@ public extension PubNubMembershipInterface {
499503
(requestConfig.customSession ?? networkSession)
500504
.route(
501505
router,
506+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
502507
responseDecoder: FetchStatusResponseDecoder(),
503508
responseQueue: requestConfig.responseQueue
504509
) { result in

PubNubSpace/Sources/Space+PubNub.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import PubNub
1616
public protocol PubNubSpaceInterface {
1717
/// A copy of the configuration object used for this session
1818
var configuration: PubNubConfiguration { get }
19-
2019
/// Session used for performing request/response REST calls
2120
var networkSession: SessionReplaceable { get }
2221

@@ -213,6 +212,7 @@ public extension PubNubSpaceInterface {
213212
(requestConfig.customSession ?? networkSession)
214213
.route(
215214
router,
215+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
216216
responseDecoder: FetchMultipleValueResponseDecoder<PubNubSpace>(),
217217
responseQueue: requestConfig.responseQueue
218218
) { result in
@@ -237,6 +237,7 @@ public extension PubNubSpaceInterface {
237237
(requestConfig.customSession ?? networkSession)
238238
.route(
239239
router,
240+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
240241
responseDecoder: FetchSingleValueResponseDecoder<PubNubSpace>(),
241242
responseQueue: requestConfig.responseQueue
242243
) { result in
@@ -273,6 +274,7 @@ public extension PubNubSpaceInterface {
273274
(requestConfig.customSession ?? networkSession)
274275
.route(
275276
router,
277+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
276278
responseDecoder: FetchSingleValueResponseDecoder<PubNubSpace>(),
277279
responseQueue: requestConfig.responseQueue
278280
) { result in
@@ -317,6 +319,7 @@ public extension PubNubSpaceInterface {
317319
(requestConfig.customSession ?? networkSession)
318320
.route(
319321
router,
322+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
320323
responseDecoder: FetchStatusResponseDecoder(),
321324
responseQueue: requestConfig.responseQueue
322325
) { result in

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.2.3'
3+
s.version = '6.3.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]' }

PubNubUser/Sources/User+PubNub.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
//
1010

1111
import Foundation
12-
1312
import PubNub
1413

1514
/// Protocol interface to manage `PubNubUser` entities using closures
1615
public protocol PubNubUserInterface {
1716
/// A copy of the configuration object used for this session
1817
var configuration: PubNubConfiguration { get }
19-
2018
/// Session used for performing request/response REST calls
2119
var networkSession: SessionReplaceable { get }
2220

@@ -221,6 +219,7 @@ public extension PubNubUserInterface {
221219
(requestConfig.customSession ?? networkSession)?
222220
.route(
223221
router,
222+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
224223
responseDecoder: FetchMultipleValueResponseDecoder<PubNubUser>(),
225224
responseQueue: requestConfig.responseQueue
226225
) { result in
@@ -248,6 +247,7 @@ public extension PubNubUserInterface {
248247
(requestConfig.customSession ?? networkSession)
249248
.route(
250249
router,
250+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
251251
responseDecoder: FetchSingleValueResponseDecoder<PubNubUser>(),
252252
responseQueue: requestConfig.responseQueue
253253
) {
@@ -288,6 +288,7 @@ public extension PubNubUserInterface {
288288
(requestConfig.customSession ?? networkSession)
289289
.route(
290290
router,
291+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
291292
responseDecoder: FetchSingleValueResponseDecoder<PubNubUser>(),
292293
responseQueue: requestConfig.responseQueue
293294
) { result in
@@ -336,6 +337,7 @@ public extension PubNubUserInterface {
336337
(requestConfig.customSession ?? networkSession)
337338
.route(
338339
router,
340+
requestOperator: configuration.automaticRetry?.retryOperator(for: .appContext),
339341
responseDecoder: FetchStatusResponseDecoder(),
340342
responseQueue: requestConfig.responseQueue
341343
) { result in

Sources/PubNub/APIs/File+PubNub.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public extension PubNub {
2929
) {
3030
route(
3131
FileManagementRouter(.list(channel: channel, limit: limit, next: next), configuration: configuration),
32+
requestOperator: configuration.automaticRetry?.retryOperator(for: .files),
3233
responseDecoder: FileListResponseDecoder(),
3334
custom: requestConfig
3435
) { result in
@@ -60,6 +61,7 @@ public extension PubNub {
6061
) {
6162
route(
6263
FileManagementRouter(.delete(channel: channel, fileId: fileId, filename: filename), configuration: configuration),
64+
requestOperator: configuration.automaticRetry?.retryOperator(for: .files),
6365
responseDecoder: FileGeneralSuccessResponseDecoder(),
6466
custom: requestConfig
6567
) { result in
@@ -137,6 +139,7 @@ public extension PubNub {
137139
.generateURL(channel: channel, body: .init(name: remoteFilename)),
138140
configuration: configuration
139141
),
142+
requestOperator: configuration.automaticRetry?.retryOperator(for: .files),
140143
responseDecoder: FileGenerateResponseDecoder(),
141144
custom: requestConfig
142145
) { [configuration] result in
@@ -225,9 +228,12 @@ public extension PubNub {
225228
configuration: configuration
226229
)
227230

228-
route(router,
229-
responseDecoder: PublishResponseDecoder(),
230-
custom: request.customRequestConfig) { result in
231+
route(
232+
router,
233+
requestOperator: configuration.automaticRetry?.retryOperator(for: .files),
234+
responseDecoder: PublishResponseDecoder(),
235+
custom: request.customRequestConfig
236+
) { result in
231237
completion?(result.map { $0.payload.timetoken })
232238
}
233239
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// Dispatcher.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: - DispatcherListener
14+
15+
struct DispatcherListener<Event> {
16+
let onAnyInvocationCompleted: (([Event]) -> Void)
17+
}
18+
19+
// MARK: - Dispatcher
20+
21+
protocol Dispatcher<Invocation, Event, Dependencies> {
22+
associatedtype Invocation: AnyEffectInvocation
23+
associatedtype Event
24+
associatedtype Dependencies
25+
26+
func dispatch(
27+
invocations: [EffectInvocation<Invocation>],
28+
with dependencies: EventEngineDependencies<Dependencies>,
29+
notify listener: DispatcherListener<Event>
30+
)
31+
}
32+
33+
// MARK: - EffectDispatcher
34+
35+
class EffectDispatcher<Invocation: AnyEffectInvocation, Event, Dependencies>: Dispatcher {
36+
private let factory: any EffectHandlerFactory<Invocation, Event, Dependencies>
37+
private let effectsCache = EffectsCache<Event>()
38+
39+
init(factory: some EffectHandlerFactory<Invocation, Event, Dependencies>) {
40+
self.factory = factory
41+
}
42+
43+
func hasPendingInvocation(_ invocation: Invocation) -> Bool {
44+
effectsCache.hasPendingEffect(with: invocation.id)
45+
}
46+
47+
func dispatch(
48+
invocations: [EffectInvocation<Invocation>],
49+
with dependencies: EventEngineDependencies<Dependencies>,
50+
notify listener: DispatcherListener<Event>
51+
) {
52+
invocations.forEach {
53+
switch $0 {
54+
case .managed(let invocation):
55+
executeEffect(
56+
effect: factory.effect(for: invocation, with: dependencies),
57+
storageId: invocation.id,
58+
notify: listener
59+
)
60+
case .regular(let invocation):
61+
executeEffect(
62+
effect: factory.effect(for: invocation, with: dependencies),
63+
storageId: UUID().uuidString,
64+
notify: listener
65+
)
66+
case .cancel(let cancelInvocation):
67+
effectsCache.getEffect(with: cancelInvocation.id)?.cancelTask()
68+
effectsCache.removeEffect(id: cancelInvocation.id)
69+
}
70+
}
71+
}
72+
73+
private func executeEffect(
74+
effect: some EffectHandler<Event>,
75+
storageId id: String,
76+
notify listener: DispatcherListener<Event>
77+
) {
78+
effectsCache.put(effect: effect, with: id)
79+
effect.performTask { [weak effectsCache] results in
80+
effectsCache?.removeEffect(id: id)
81+
listener.onAnyInvocationCompleted(results)
82+
}
83+
}
84+
}
85+
86+
// MARK: - EffectsCache
87+
88+
fileprivate class EffectsCache<Event> {
89+
private var managedEffects: Atomic<[String: EffectWrapper<Event>]> = Atomic([:])
90+
91+
func hasPendingEffect(with id: String) -> Bool {
92+
managedEffects.lockedRead { $0[id] } != nil
93+
}
94+
95+
func put(effect: some EffectHandler<Event>, with id: String) {
96+
managedEffects.lockedWrite { $0[id] = EffectWrapper<Event>(id: id, effect: effect) }
97+
}
98+
99+
func getEffect(with id: String) -> (any EffectHandler<Event>)? {
100+
managedEffects.lockedRead() { $0[id] }?.effect
101+
}
102+
103+
func removeEffect(id: String) {
104+
managedEffects.lockedWrite { $0[id] = nil }
105+
}
106+
}
107+
108+
// MARK: - EffectWrapper
109+
110+
fileprivate struct EffectWrapper<Action> {
111+
let id: String
112+
let effect: any EffectHandler<Action>
113+
}

0 commit comments

Comments
 (0)