Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removing SubscribeSessionFactory and SubscriptionConfiguration #162

Merged
merged 15 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .swiftformat
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
# format options
# Format options
--indent 2
--commas inline

--disable wrapMultilineStatementBraces
--wraparguments before-first
--wrapparameters before-first
--maxwidth 130
--disable wrapSingleLineComments
--voidtype void
--redundanttype inferred
--self remove

# file options
--enable spaceAroundBrackets,spaceAroundComments,spaceAroundGenerics,spaceAroundParens,spaceInsideBraces
--enable spaceInsideBrackets,spaceInsideComments,spaceInsideGenerics,spaceInsideParens,trailingclosures,trailingCommas
--enable wrapConditionalBodies, wrapEnumCases, wrapLoopBodies
--enable redundantLetError,redundantParens,redundantSelf,redundantReturn,redundantBreak
--enable duplicateImports

# File options
--exclude .build

# Swift Version
--swiftversion 5.0
--swiftversion 5.8
2 changes: 2 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
line_length:
warning: 130
ignores_comments: true
disabled_rules:
- identifier_name
Expand All @@ -11,6 +12,7 @@ excluded:
- .bundle
- fastlane
- Tests
- Pods
opt_in_rules:
- force_unwrapping
- overridden_super_call
Expand Down
64 changes: 34 additions & 30 deletions PubNub.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

263 changes: 263 additions & 0 deletions Sources/PubNub/DependencyContainer/DependencyContainer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
//
// DependencyContainer.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

// A protocol that represents a unique key for each dependency. Each type conforming to `DependencyKey`
// represents a distinct dependency.
protocol DependencyKey {
// A value associated with a given `DependencyKey`
associatedtype Value
// Creates a value of the type Value for a given `DependencyKey` if no existing dependency
// is found in the `DependencyContainer`. The `container` parameter is used in case of
// nested dependencies, i.e., when the dependency being created depends on other objects in the `DependencyContainer`.
static func value(from container: DependencyContainer) -> Value
}

// The class that serves as a registry for dependencies. Each dependency is associated with a unique key
// conforming to the `DependencyKey` protocol.
class DependencyContainer {
private var values: [ObjectIdentifier: Any] = [:]

init(instanceID: UUID = UUID(), configuration: PubNubConfiguration) {
self[PubNubConfigurationDependencyKey.self] = configuration
self[PubNubInstanceIDDependencyKey.self] = instanceID
}

subscript<K>(key: K.Type) -> K.Value where K: DependencyKey {
get {
if let existingValue = values[ObjectIdentifier(key)] {
if let existingValue = existingValue as? K.Value {
return existingValue
} else {
preconditionFailure("Cannot resolve value for \(key)")
}
}
let value = key.value(from: self)
values[ObjectIdentifier(key)] = value
return value
} set {
values[ObjectIdentifier(key)] = newValue
}
}

@discardableResult
func register<K: DependencyKey>(value: K.Value?, forKey key: K.Type) -> DependencyContainer {
if let value {
values[ObjectIdentifier(key)] = value
}
return self
}
}

typealias SubscribeEngine = EventEngine<any SubscribeState, Subscribe.Event, Subscribe.Invocation, Subscribe.Dependencies>
typealias PresenceEngine = EventEngine<any PresenceState, Presence.Event, Presence.Invocation, Presence.Dependencies>

extension DependencyContainer {
var configuration: PubNubConfiguration {
self[PubNubConfigurationDependencyKey.self]
}

var instanceID: UUID {
self[PubNubInstanceIDDependencyKey.self]
}

var fileURLSession: URLSessionReplaceable {
self[FileURLSessionDependencyKey.self]
}

var subscriptionSession: SubscriptionSession {
self[SubscriptionSessionDependencyKey.self]
}

var presenceStateContainer: PubNubPresenceStateContainer {
self[PresenceStateContainerDependencyKey.self]
}

var defaultHTTPSession: SessionReplaceable {
resolveSession(
session: self[DefaultHTTPSessionDependencyKey.self],
with: [automaticRetry].compactMap { $0 }
)
}

fileprivate var httpSubscribeSession: SessionReplaceable {
resolveSession(
session: self[HTTPSubscribeSessionDependencyKey.self],
with: [instanceIDOperator].compactMap { $0 }
)
}

fileprivate var httpPresenceSession: SessionReplaceable {
resolveSession(
session: self[HTTPPresenceSessionDependencyKey.self],
with: [instanceIDOperator].compactMap { $0 }
)
}

fileprivate var automaticRetry: RequestOperator? {
configuration.automaticRetry
}

fileprivate var instanceIDOperator: RequestOperator? {
configuration.useInstanceId ? InstanceIdOperator(instanceID: instanceID.uuidString) : nil
}

fileprivate var httpSubscribeSessionQueue: DispatchQueue {
self[HTTPSubscribeSessionQueueDependencyKey.self]
}

fileprivate var subscribeEngine: SubscribeEngine {
self[SubscribeEventEngineDependencyKey.self]
}

fileprivate var presenceEngine: PresenceEngine {
self[PresenceEventEngineDependencyKey.self]
}
}

private extension DependencyContainer {
func resolveSession(session: SessionReplaceable, with operators: [RequestOperator?]) -> SessionReplaceable {
session.defaultRequestOperator == nil ? session.usingDefault(requestOperator: MultiplexRequestOperator(
operators: operators.compactMap { $0 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a private extension and can be sure of what we are passing, do we really need to map values again? We did it once when called resolveSession.
Can we leave compactMap only in resolveSession and just pass arrays from defaultHTTPSession, httpSubscribeSession, and httpPresenceSession?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, you're right. I will send you the updated version shortly.

)) : session.usingDefault(requestOperator: session.defaultRequestOperator?.merge(
operators: operators.compactMap { $0 })
)
}
}

// - MARK: PubNubConfiguration

struct PubNubConfigurationDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> PubNubConfiguration {
container.configuration
}
}

struct PubNubInstanceIDDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> UUID {
container.instanceID
}
}

// MARK: - HTTPSessions

struct DefaultHTTPSessionDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> SessionReplaceable {
HTTPSession(configuration: .pubnub)
}
}

struct HTTPSubscribeSessionDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> SessionReplaceable {
HTTPSession(
configuration: .subscription,
sessionQueue: container.httpSubscribeSessionQueue,
sessionStream: SessionListener(queue: container.httpSubscribeSessionQueue)
)
}
}

struct HTTPSubscribeSessionQueueDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> DispatchQueue {
DispatchQueue(label: "Subscribe Response Queue")
}
}

struct HTTPPresenceSessionDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> HTTPSession {
HTTPSession(
configuration: .pubnub,
sessionQueue: container.httpSubscribeSessionQueue,
sessionStream: SessionListener(queue: container.httpSubscribeSessionQueue)
)
}
}

// MARK: - FileURLSession

struct FileURLSessionDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> URLSessionReplaceable {
URLSession(
configuration: .pubnubBackground,
delegate: FileSessionManager(),
delegateQueue: .main
)
}
}

// MARK: - PresenceStateContainer

struct PresenceStateContainerDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> PubNubPresenceStateContainer {
PubNubPresenceStateContainer.shared
}
}

// MARK: SubscribeEventEngine

struct SubscribeEventEngineDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> SubscribeEngine {
let effectHandlerFactory = SubscribeEffectFactory(
session: container.httpSubscribeSession,
presenceStateContainer: container.presenceStateContainer
)
let subscribeEngine = SubscribeEngine(
state: Subscribe.UnsubscribedState(),
transition: SubscribeTransition(),
dispatcher: EffectDispatcher(factory: effectHandlerFactory),
dependencies: EventEngineDependencies(value: Subscribe.Dependencies(configuration: container.configuration))
)
return subscribeEngine
}
}

// MARK: PresenceEventEngine

struct PresenceEventEngineDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> PresenceEngine {
let effectHandlerFactory = PresenceEffectFactory(
session: container.httpPresenceSession,
presenceStateContainer: container.presenceStateContainer
)
let presenceEngine = PresenceEngine(
state: Presence.HeartbeatInactive(),
transition: PresenceTransition(configuration: container.configuration),
dispatcher: EffectDispatcher(factory: effectHandlerFactory),
dependencies: EventEngineDependencies(value: Presence.Dependencies(configuration: container.configuration))
)
return presenceEngine
}
}

// MARK: - SubscriptionSession

struct SubscriptionSessionDependencyKey: DependencyKey {
static func value(from container: DependencyContainer) -> SubscriptionSession {
if container.configuration.enableEventEngine {
return SubscriptionSession(
strategy: EventEngineSubscriptionSessionStrategy(
configuration: container.configuration,
subscribeEngine: container.subscribeEngine,
presenceEngine: container.presenceEngine,
presenceStateContainer: container.presenceStateContainer
)
)
} else {
return SubscriptionSession(
strategy: LegacySubscriptionSessionStrategy(
configuration: container.configuration,
network: container.httpSubscribeSession,
presenceSession: container.httpPresenceSession
)
)
}
}
}
47 changes: 0 additions & 47 deletions Sources/PubNub/EventEngine/Core/EventEngineFactory.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import Foundation

class WaitEffect: EffectHandler {
private let timerEffect: TimerEffect?
init(configuration: SubscriptionConfiguration) {

init(configuration: PubNubConfiguration) {
if configuration.heartbeatInterval > 0 {
self.timerEffect = TimerEffect(interval: TimeInterval(configuration.heartbeatInterval))
} else {
self.timerEffect = nil
}
}

func performTask(completionBlock: @escaping ([Presence.Event]) -> Void) {
guard let timerEffect = timerEffect else {
completionBlock([]); return
Expand All @@ -29,7 +29,7 @@ class WaitEffect: EffectHandler {
completionBlock([.timesUp])
})
}

func cancelTask() {
timerEffect?.cancelTask()
}
Expand Down
Loading
Loading