From 7ec97085f008532fde807568409941badbc1e737 Mon Sep 17 00:00:00 2001 From: jguz-pubnub <102806147+jguz-pubnub@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:00:00 +0200 Subject: [PATCH] Upgrading PubNub Swift SDK (#188) feat(module): replace module name with PubNubSDK (https://github.com/swiftlang/swift/issues/56573) feat(subscribe): add new subscriptionChanged(channels, groups) status, remove connecting and reconnecting cases feat(reconnection-policy): remove .legacyExponential(base, scale, maxDelay) reconnection policy feat(objects): add additional fields to fetch in PubNub.MembershipInclude and PubNub.MemberInclude feat(configuration): set default AutomaticRetry for retrying subscribe requests with exponential reconnection policy feat(listeners): add support for attaching additional listeners to Subscription and SubscriptionSet fix(listeners): ensure that subscriptions are always internally stored as weak references --- .pubnub.yml | 21 +- Documentation/PubNub_8_0_Migration_Guide.md | 21 + Examples/Examples.xcodeproj/project.pbxproj | 10 +- Examples/Sources/AppDelegate.swift | 3 +- .../ConfigDetailTableViewController.swift | 3 +- .../Sources/DetailTableViewController.swift | 8 +- .../Sources/File/FileAPIViewController.swift | 3 +- Examples/Sources/File/LocalFileExample.swift | 3 +- Package.swift | 21 +- PubNub.xcodeproj/project.pbxproj | 224 +++++-- .../xcshareddata/xcschemes/PubNub.xcscheme | 16 +- .../Sources/Membership+PubNub.swift | 3 +- .../Sources/Patcher+PubNubMembership.swift | 3 +- .../Sources/PubNubMembership.swift | 3 +- .../Sources/PubNubMembershipEvent.swift | 3 +- .../SmokeTest+PubNubMembership.swift | 2 +- .../Tests/MembershipTestHelpers.swift | 3 +- .../Tests/Test+PubNubMembership.swift | 6 +- .../Tests/Test+PubNubMembershipEvent.swift | 6 +- .../Test+PubNubMembershipInterface.swift | 6 +- .../Tests/Test+PubNubMembershipPatcher.swift | 6 +- PubNubSpace/Sources/Patcher+PubNubSpace.swift | 3 +- PubNubSpace/Sources/PubNubSpace.swift | 3 +- PubNubSpace/Sources/PubNubSpaceEvent.swift | 3 +- PubNubSpace/Sources/Space+PubNub.swift | 3 +- .../Integration/SmokeTest+PubNubSpace.swift | 6 +- PubNubSpace/Tests/SpaceTestHelpers.swift | 3 +- PubNubSpace/Tests/Test+PubNubSpace.swift | 6 +- PubNubSpace/Tests/Test+PubNubSpaceEvent.swift | 6 +- .../Tests/Test+PubNubSpaceInterface.swift | 6 +- .../Tests/Test+PubNubSpacePatcher.swift | 6 +- PubNubSwift.podspec | 8 +- PubNubUser/Sources/Patcher+PubNubUser.swift | 3 +- PubNubUser/Sources/PubNubUser.swift | 3 +- PubNubUser/Sources/PubNubUserEvent.swift | 3 +- PubNubUser/Sources/User+PubNub.swift | 2 +- .../Integration/SmokeTest+PubNubUser.swift | 6 +- PubNubUser/Tests/Test+PubNubUser.swift | 6 +- PubNubUser/Tests/Test+PubNubUserEvent.swift | 6 +- .../Tests/Test+PubNubUserInterface.swift | 6 +- PubNubUser/Tests/Test+PubNubUserPatcher.swift | 6 +- PubNubUser/Tests/UserTestHelpers.swift | 2 +- README.md | 12 +- Sources/PubNub/APIs/Objects+PubNub.swift | 36 ++ .../DependencyContainer.swift | 72 ++- .../Effects/EmitMessagesEffect.swift | 6 +- .../Subscribe/Effects/EmitStatusEffect.swift | 6 +- .../Effects/SubscribeEffectFactory.swift | 8 +- .../Subscribe/Effects/SubscribeEffects.swift | 13 +- .../EventEngine/Subscribe/Subscribe.swift | 13 +- .../Subscribe/SubscribeTransition.swift | 110 ++-- .../Events/New/Entities/EntityCreator.swift | 21 +- .../New/Entities/EntitySubscribable.swift | 16 +- ...ter.swift => EventListenerInterface.swift} | 90 ++- Sources/PubNub/Events/New/Subscribable.swift | 10 +- Sources/PubNub/Events/New/Subscription.swift | 46 +- .../New/SubscriptionListenersContainer.swift | 48 ++ .../PubNub/Events/New/SubscriptionSet.swift | 51 +- .../PubNub/Extensions/NSLocking+PubNub.swift | 2 +- Sources/PubNub/Helpers/AnyJSONType.swift | 1 - Sources/PubNub/Helpers/Atomic.swift | 2 +- Sources/PubNub/Helpers/Constants.swift | 2 +- Sources/PubNub/Helpers/WeakBox.swift | 12 + .../Dictionary+ObjCRepresentable.swift | 46 ++ Sources/PubNub/KMP/Helpers/Int+NSNumber.swift | 17 + Sources/PubNub/KMP/Helpers/PubNub+URL.swift | 21 + Sources/PubNub/KMP/KMPPubNub+AppContext.swift | 551 ++++++++++++++++++ .../PubNub/KMP/KMPPubNub+ChannelGroups.swift | 100 ++++ Sources/PubNub/KMP/KMPPubNub+Files.swift | 217 +++++++ Sources/PubNub/KMP/KMPPubNub+History.swift | 108 ++++ Sources/PubNub/KMP/KMPPubNub+Listeners.swift | 115 ++++ .../PubNub/KMP/KMPPubNub+MessageActions.swift | 88 +++ Sources/PubNub/KMP/KMPPubNub+Presence.swift | 128 ++++ Sources/PubNub/KMP/KMPPubNub+Publish.swift | 87 +++ Sources/PubNub/KMP/KMPPubNub+Push.swift | 231 ++++++++ Sources/PubNub/KMP/KMPPubNub+Subscribe.swift | 64 ++ Sources/PubNub/KMP/KMPPubNub+Time.swift | 35 ++ Sources/PubNub/KMP/KMPPubNub.swift | 105 ++++ Sources/PubNub/KMP/Wrappers/KMPAnyJSON.swift | 92 +++ .../Wrappers/KMPAppContextEventResult.swift | 345 +++++++++++ Sources/PubNub/KMP/Wrappers/KMPEntity.swift | 71 +++ Sources/PubNub/KMP/Wrappers/KMPError.swift | 46 ++ .../KMP/Wrappers/KMPEventListener.swift | 50 ++ .../KMP/Wrappers/KMPFetchMessagesResult.swift | 60 ++ .../KMP/Wrappers/KMPFileChangeEvent.swift | 93 +++ .../PubNub/KMP/Wrappers/KMPHashedPage.swift | 50 ++ .../KMP/Wrappers/KMPHereNowResult.swift | 54 ++ Sources/PubNub/KMP/Wrappers/KMPMessage.swift | 43 ++ .../KMP/Wrappers/KMPMessageAction.swift | 56 ++ .../KMP/Wrappers/KMPPresenceChange.swift | 78 +++ .../KMP/Wrappers/KMPStatusListener.swift | 69 +++ .../PubNub/KMP/Wrappers/KMPSubscription.swift | 236 ++++++++ .../PubNub/KMP/Wrappers/KMPUploadable.swift | 59 ++ .../Request/Operators/AutomaticRetry.swift | 51 +- .../Routers/ObjectsMembershipsRouter.swift | 4 + Sources/PubNub/PubNub.swift | 50 +- Sources/PubNub/PubNubConfiguration.swift | 2 +- .../Subscription/ConnectionStatus.swift | 54 +- ...entEngineSubscriptionSessionStrategy.swift | 2 +- .../LegacySubscriptionSessionStrategy.swift | 16 +- .../Subscription/SubscriptionSession.swift | 104 ++-- .../PubNubContractTestCase.swift | 2 +- .../PubNubAccessContractTestSteps.swift | 2 +- .../PubNubCryptoModuleContractTestSteps.swift | 2 +- .../PubNubEventEngineContractTestSteps.swift | 2 +- .../PubNubEventEngineTestsHelpers.swift | 2 +- ...ubNubPresenceEngineContractTestSteps.swift | 2 +- ...NubSubscribeEngineContractTestsSteps.swift | 2 +- .../Files/PubNubFilesContractTestSteps.swift | 2 +- .../PubNubHistoryContractTestSteps.swift | 2 +- ...ubNubMessageActionsContractTestSteps.swift | 2 +- ...ectsChannelMetadataContractTestSteps.swift | 2 +- .../Objects/PubNubObjectsContractTests.swift | 2 +- ...ubNubObjectsMembersContractTestSteps.swift | 2 +- ...bObjectsMembershipsContractTestSteps.swift | 2 +- .../Objects/PubNubObjectsTestHelpers.swift | 2 +- ...ObjectsUUIDMetadataContractTestSteps.swift | 2 +- .../PubNubPublishContractTestSteps.swift | 2 +- .../Push/PubNubPushContractTestSteps.swift | 2 +- .../PubNubSubscribeContractTestSteps.swift | 2 +- .../PubNubTimeContractTestSteps.swift | 2 +- .../EventEngine/DispatcherTests.swift | 2 +- .../EventEngine/EventEngineTests.swift | 2 +- .../Helpers/EffectInvocation+Equatable.swift | 2 +- .../Presence/HeartbeatEffectTests.swift | 2 +- .../Presence/LeaveEffectTests.swift | 2 +- .../Presence/PresenceTransitionTests.swift | 2 +- .../Presence/WaitEffectTests.swift | 2 +- .../Subscribe/EmitMessagesTests.swift | 54 +- .../Subscribe/EmitStatusTests.swift | 46 +- .../Subscribe/SubscribeEffectsTests.swift | 2 +- .../Subscribe/SubscribeInputTests.swift | 2 +- .../Subscribe/SubscribeTransitionTests.swift | 342 ++++++----- .../Events/New/SubscriptionSetTests.swift | 67 ++- .../Events/New/SubscriptionTests.swift | 66 ++- .../Events/Old/EventStreamTests.swift | 2 +- .../Events/Old/SessionStreamTests.swift | 102 ---- .../Events/Old/SubscriptionStreamTests.swift | 2 +- .../Events/SessionStreamTests.swift | 2 +- .../SubscribeMessagesGeneratorTests.swift | 2 +- .../Extensions/Bool+PubNubTests.swift | 2 +- .../Extensions/Collection+PubNubTests.swift | 2 +- .../Extensions/Data+PubNubTests.swift | 2 +- .../DateFormatter+PubNubTests.swift | 2 +- .../Extensions/DispatchQueue+PubNub.swift | 2 +- .../Extensions/Error+PubNubTests.swift | 2 +- .../HTTPURLResponse+PubNubTests.swift | 2 +- .../Extensions/Int+PubNubTests.swift | 2 +- .../OperationQueue+PubNubTests.swift | 2 +- .../Extensions/Set+PubNubTests.swift | 2 +- .../Extensions/String+PubNubTests.swift | 2 +- .../Extensions/URL+PubNubTests.swift | 2 +- .../Extensions/URLQueryItem+PubNubTests.swift | 2 +- .../Extensions/URLRequest+PubNubTests.swift | 2 +- .../URLSessionConfiguration+PubNubTests.swift | 2 +- .../Helpers/AnyJSON+CodableTests.swift | 2 +- Tests/PubNubTests/Helpers/AnyJSONTests.swift | 2 +- Tests/PubNubTests/Helpers/AtomicTests.swift | 2 +- .../PubNubTests/Helpers/ConstantsTests.swift | 2 +- Tests/PubNubTests/Helpers/CryptoTests.swift | 3 +- .../Helpers/FlatJSONCodable+Test.swift | 2 +- Tests/PubNubTests/Helpers/PAMTokenTests.swift | 2 +- .../PubNubTests/Helpers/ValidatedTests.swift | 2 +- Tests/PubNubTests/Helpers/WeakBoxTests.swift | 2 +- .../PubNubTests/Helpers/XMLCodingTests.swift | 2 +- ...annelObjectsEndpointIntegrationTests.swift | 2 +- ...ssageActionsEndpointIntegrationTests.swift | 2 +- .../Integration/OnboardingSnippets.swift | 2 +- .../PresenceEndpointIntegrationTests.swift | 2 +- .../Integration/PubNub+Integration.swift | 3 +- .../PublishEndpointIntegrationTests.swift | 2 +- .../Integration/PushIntegrationTests.swift | 4 +- .../SubscriptionIntegrationTests.swift | 52 +- .../UUIDObjectsEndpointIntegrationTests.swift | 2 +- .../Mocking/ImportTestResource.swift | 2 +- .../Mocking/MockRequestOperators.swift | 2 +- .../PubNubTests/Mocking/MockURLSession.swift | 2 +- .../Mocking/SessionStreamAwait.swift | 3 +- Tests/PubNubTests/Mocking/TestLogWriter.swift | 2 +- Tests/PubNubTests/Mocking/TestSetup.swift | 2 +- Tests/PubNubTests/Mocking/XMLEncoder.swift | 2 +- .../Operators/AutomaticRetryTests.swift | 114 +--- .../Operators/RequestIdOperatorTests.swift | 2 +- .../Operators/RequestMutatorTests.swift | 2 +- .../Operators/RequestRetrierTests.swift | 2 +- .../Routers/ChannelGroupEndpointTests.swift | 2 +- .../Routers/FileManagementRouterTests.swift | 2 +- .../Routers/HistoryRouterTests.swift | 2 +- .../Routers/MessageActionsRouterTests.swift | 2 +- .../Routers/ObjectsChannelRouterTests.swift | 2 +- .../ObjectsMembershipsRouterTests.swift | 2 +- .../Routers/ObjectsUUIDRouterTests.swift | 2 +- .../Routers/PresenceRouterTests.swift | 2 +- .../Routers/PublishRouterTests.swift | 2 +- .../Networking/Routers/PushRouterTests.swift | 2 +- .../Routers/SubscribeRouterTests.swift | 93 +-- .../Networking/Routers/TimeRouterTests.swift | 2 +- .../Session+EndpointErrorTests.swift | 2 +- .../Networking/Session+URLErrorTests.swift | 2 +- .../PubNubConfigurationTests.swift | 2 +- Tests/PubNubTests/PubNubTests.swift | 2 +- .../Push/PubNubPushTargetTests.swift | 2 +- .../SubscribeSessionFactoryTests.swift | 2 +- .../SubscriptionSessionTests.swift | 8 +- 204 files changed, 4774 insertions(+), 1006 deletions(-) create mode 100644 Documentation/PubNub_8_0_Migration_Guide.md rename Sources/PubNub/Events/New/{EventEmitter.swift => EventListenerInterface.swift} (61%) create mode 100644 Sources/PubNub/Events/New/SubscriptionListenersContainer.swift create mode 100644 Sources/PubNub/KMP/Helpers/Dictionary+ObjCRepresentable.swift create mode 100644 Sources/PubNub/KMP/Helpers/Int+NSNumber.swift create mode 100644 Sources/PubNub/KMP/Helpers/PubNub+URL.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+AppContext.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+ChannelGroups.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+Files.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+History.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+Listeners.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+MessageActions.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+Presence.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+Publish.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+Push.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+Subscribe.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub+Time.swift create mode 100644 Sources/PubNub/KMP/KMPPubNub.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPAnyJSON.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPAppContextEventResult.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPEntity.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPError.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPEventListener.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPFetchMessagesResult.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPFileChangeEvent.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPHashedPage.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPHereNowResult.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPMessage.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPMessageAction.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPPresenceChange.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPStatusListener.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPSubscription.swift create mode 100644 Sources/PubNub/KMP/Wrappers/KMPUploadable.swift delete mode 100644 Tests/PubNubTests/Events/Old/SessionStreamTests.swift diff --git a/.pubnub.yml b/.pubnub.yml index 20546edb..9262dd04 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,9 +1,26 @@ --- name: swift scm: github.com/pubnub/swift -version: "7.3.3" +version: "8.0.0" schema: 1 changelog: + - date: 2024-09-23 + version: 8.0.0 + changes: + - type: feature + text: "Replace module name with `PubNubSDK` due to compiler error when a public type shares the same name as the module." + - type: feature + text: " add new `subscriptionChanged(channels, groups)` connection status and remove previously deprecated `connecting` and `reconnecting` cases." + - type: feature + text: "Remove previously deprecated `.legacyExponential(base, scale, maxDelay)` reconnection policy." + - type: feature + text: "Added additional fields to fetch in `PubNub.MembershipInclude` and `PubNub.MemberInclude`." + - type: feature + text: "Set default `AutomaticRetry` for retrying subscribe requests with exponential reconnection policy." + - type: feature + text: "Add support for attaching additional listeners to `Subscription` and `SubscriptionSet`." + - type: bug + text: "Ensure that subscriptions are always internally stored as weak references." - date: 2024-09-13 version: 7.3.3 changes: @@ -569,7 +586,7 @@ sdks: - distribution-type: source distribution-repository: GitHub release package-name: PubNub - location: https://github.com/pubnub/swift/archive/refs/tags/7.3.3.zip + location: https://github.com/pubnub/swift/archive/refs/tags/8.0.0.zip supported-platforms: supported-operating-systems: macOS: diff --git a/Documentation/PubNub_8_0_Migration_Guide.md b/Documentation/PubNub_8_0_Migration_Guide.md new file mode 100644 index 00000000..caf80073 --- /dev/null +++ b/Documentation/PubNub_8_0_Migration_Guide.md @@ -0,0 +1,21 @@ +# PubNub 8.0 Migration Guide +PubNub Native Swift SDK v8.0 is the latest major release of the PubNub SDK for iOS, tvOS, macOS and watchOS written in Swift. As a major release, following [Semantic Versioning](https://semver.org/) conventions, 8.0 introduces API-breaking changes. + +This guide is meant to ease the transition to the 8.0 version of the SDK. To read the full set of documentation, please head over to our official [docs page](https://www.pubnub.com/docs/swift-native/pubnub-swift-sdk) + +## Breaking API Changes +___ + +### Module name + +* The module name has been changed to `PubNubSDK` due to a compiler error that occurs when a public type shares the same name as a module. As a result, you will need to replace `import PubNub` with `import PubNubSDK` in your Swift code. Additionally, ensure that `PubNubSDK` is listed in the `Frameworks, Libraries, and Embedded Content` section under the `General` tab in Xcode + +### `ReconnectionPolicy` + +* The `.legacyExponential(base, scale, maxDelay)` enumeration case from `AutomaticRetry.ReconnectionPolicy` is no longer supported. Use `.exponential(minDelay, maxDelay)` instead +* `PubNubConfiguration` uses default `AutomaticRetry` with an exponential reconnection policy to retry Subscribe requests in case of failure. If this behavior doesn’t suit your use case, you can pass custom `AutomaticRetry` object + +### `ConnectionStatus` + +* The following cases of the `ConnectionStatus` enumeration are no longer supported: `.connecting` and `.reconnecting`. +* This version introduces a new `.subscriptionChanged(channels, groups)` connection status, indicating that the SDK has subscribed to new channels or channel groups. This status is triggered each time the channel or channel group mix changes after the initial connection, and it provides all currently subscribed channels and channel groups. Additionally, you can check your current activity status locally by accessing the `isActive` property on `ConnectionStatus` diff --git a/Examples/Examples.xcodeproj/project.pbxproj b/Examples/Examples.xcodeproj/project.pbxproj index 7403ce8c..03937b6a 100644 --- a/Examples/Examples.xcodeproj/project.pbxproj +++ b/Examples/Examples.xcodeproj/project.pbxproj @@ -16,8 +16,8 @@ 359F779F22B7FEF700B6B46F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 359F779E22B7FEF700B6B46F /* Assets.xcassets */; }; 359F77A222B7FEF700B6B46F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 359F77A022B7FEF700B6B46F /* LaunchScreen.storyboard */; }; 35F0258F22BAC6C9007BD7D3 /* ConfigDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35F0258E22BAC6C9007BD7D3 /* ConfigDetailTableViewController.swift */; }; + 3D26A6152C6BB68A007B4539 /* PubNubSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 3D26A6142C6BB68A007B4539 /* PubNubSDK */; }; 3D34160D2BB5CE20008558A0 /* DetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D34160C2BB5CE20008558A0 /* DetailTableViewController.swift */; }; - 792CF27228C4FBB300C83408 /* PubNub in Frameworks */ = {isa = PBXBuildFile; productRef = 792CF27128C4FBB300C83408 /* PubNub */; }; 792CF27428C4FBB300C83408 /* PubNubMembership in Frameworks */ = {isa = PBXBuildFile; productRef = 792CF27328C4FBB300C83408 /* PubNubMembership */; }; 792CF27628C4FBB300C83408 /* PubNubSpace in Frameworks */ = {isa = PBXBuildFile; productRef = 792CF27528C4FBB300C83408 /* PubNubSpace */; }; 792CF27828C4FBB300C83408 /* PubNubUser in Frameworks */ = {isa = PBXBuildFile; productRef = 792CF27728C4FBB300C83408 /* PubNubUser */; }; @@ -70,9 +70,9 @@ buildActionMask = 2147483647; files = ( 792CF27428C4FBB300C83408 /* PubNubMembership in Frameworks */, + 3D26A6152C6BB68A007B4539 /* PubNubSDK in Frameworks */, 792CF27828C4FBB300C83408 /* PubNubUser in Frameworks */, 792CF27628C4FBB300C83408 /* PubNubSpace in Frameworks */, - 792CF27228C4FBB300C83408 /* PubNub in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -176,10 +176,10 @@ ); name = "iOS Example"; packageProductDependencies = ( - 792CF27128C4FBB300C83408 /* PubNub */, 792CF27328C4FBB300C83408 /* PubNubMembership */, 792CF27528C4FBB300C83408 /* PubNubSpace */, 792CF27728C4FBB300C83408 /* PubNubUser */, + 3D26A6142C6BB68A007B4539 /* PubNubSDK */, ); productName = swiftSdkiOS; productReference = 359F779422B7FEF600B6B46F /* iOS Example.app */; @@ -464,9 +464,9 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ - 792CF27128C4FBB300C83408 /* PubNub */ = { + 3D26A6142C6BB68A007B4539 /* PubNubSDK */ = { isa = XCSwiftPackageProductDependency; - productName = PubNub; + productName = PubNubSDK; }; 792CF27328C4FBB300C83408 /* PubNubMembership */ = { isa = XCSwiftPackageProductDependency; diff --git a/Examples/Sources/AppDelegate.swift b/Examples/Sources/AppDelegate.swift index 4801535e..c4820140 100644 --- a/Examples/Sources/AppDelegate.swift +++ b/Examples/Sources/AppDelegate.swift @@ -9,8 +9,7 @@ // import UIKit - -import PubNub +import PubNubSDK @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/Examples/Sources/ConfigDetailTableViewController.swift b/Examples/Sources/ConfigDetailTableViewController.swift index d1ac789e..0f084323 100644 --- a/Examples/Sources/ConfigDetailTableViewController.swift +++ b/Examples/Sources/ConfigDetailTableViewController.swift @@ -9,8 +9,7 @@ // import UIKit - -import PubNub +import PubNubSDK class ConfigDetailTableViewController: UITableViewController { diff --git a/Examples/Sources/DetailTableViewController.swift b/Examples/Sources/DetailTableViewController.swift index 94682640..371deed3 100644 --- a/Examples/Sources/DetailTableViewController.swift +++ b/Examples/Sources/DetailTableViewController.swift @@ -11,7 +11,7 @@ import Foundation import UIKit -import PubNub +import PubNubSDK import PubNubMembership import PubNubSpace import PubNubUser @@ -249,12 +249,10 @@ class DetailTableViewController: UITableViewController { print("The signal is \(signal.payload) and was sent by \(signal.publisher ?? "")") case let .connectionStatusChanged(connectionChange): switch connectionChange { - case .connecting: - print("Status connecting...") case .connected: print("Status connected!") - case .reconnecting: - print("Status reconnecting...") + case .subscriptionChanged: + print("Subscription changed") case .disconnected: print("Status disconnected") case .disconnectedUnexpectedly: diff --git a/Examples/Sources/File/FileAPIViewController.swift b/Examples/Sources/File/FileAPIViewController.swift index 88c06f6c..adf18521 100644 --- a/Examples/Sources/File/FileAPIViewController.swift +++ b/Examples/Sources/File/FileAPIViewController.swift @@ -9,8 +9,7 @@ // import UIKit - -import PubNub +import PubNubSDK enum AlertMessageDirection: String { case upload = "Uploading" diff --git a/Examples/Sources/File/LocalFileExample.swift b/Examples/Sources/File/LocalFileExample.swift index 52930cef..add59ff0 100644 --- a/Examples/Sources/File/LocalFileExample.swift +++ b/Examples/Sources/File/LocalFileExample.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK enum FileError: Error { case fileNameError diff --git a/Package.swift b/Package.swift index a7d0f711..71d2b0a8 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.9 // // Package.swift // @@ -12,18 +12,19 @@ import PackageDescription let package = Package( - name: "PubNub", + name: "PubNubSDK", platforms: [ .iOS(.v12), .macOS(.v10_15), .tvOS(.v12), - .watchOS(.v4) + .watchOS(.v4), + .visionOS(.v1) ], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( - name: "PubNub", - targets: ["PubNub"] + name: "PubNubSDK", + targets: ["PubNubSDK"] ), .library( name: "PubNubUser", @@ -45,28 +46,28 @@ let package = Package( // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( - name: "PubNub", + name: "PubNubSDK", path: "Sources/PubNub", resources: [.copy("PrivacyInfo.xcprivacy")] ), .target( name: "PubNubUser", - dependencies: ["PubNub"], + dependencies: ["PubNubSDK"], path: "PubNubUser/Sources" ), .target( name: "PubNubSpace", - dependencies: ["PubNub"], + dependencies: ["PubNubSDK"], path: "PubNubSpace/Sources" ), .target( name: "PubNubMembership", - dependencies: ["PubNub", "PubNubUser", "PubNubSpace"], + dependencies: ["PubNubSDK", "PubNubUser", "PubNubSpace"], path: "PubNubMembership/Sources" ), .testTarget( name: "PubNubTests", - dependencies: ["PubNub"] + dependencies: ["PubNubSDK"] ) ], swiftLanguageVersions: [.v5] diff --git a/PubNub.xcodeproj/project.pbxproj b/PubNub.xcodeproj/project.pbxproj index 01ee7ccc..4328b207 100644 --- a/PubNub.xcodeproj/project.pbxproj +++ b/PubNub.xcodeproj/project.pbxproj @@ -129,7 +129,7 @@ 35580682230F3A34005CDD92 /* RequestIdOperator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35580681230F3A34005CDD92 /* RequestIdOperator.swift */; }; 35580686230F47EA005CDD92 /* RequestIdOperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35580684230F4771005CDD92 /* RequestIdOperatorTests.swift */; }; 3558069C231303D9005CDD92 /* AutomaticRetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3558069B231303D9005CDD92 /* AutomaticRetryTests.swift */; }; - 355806DB23145749005CDD92 /* PubNub.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNub.framework */; }; + 355806DB23145749005CDD92 /* PubNubSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNubSDK.framework */; }; 3559977B23073D53000BCFD1 /* WeakBoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3559977A23073D53000BCFD1 /* WeakBoxTests.swift */; }; 3559977F23078A7C000BCFD1 /* message_counts_error_invalid_arguments.json in Resources */ = {isa = PBXBuildFile; fileRef = 3559977E230787E7000BCFD1 /* message_counts_error_invalid_arguments.json */; }; 3559978223079070000BCFD1 /* forbidden_Message.json in Resources */ = {isa = PBXBuildFile; fileRef = 3559978023078F85000BCFD1 /* forbidden_Message.json */; }; @@ -365,6 +365,14 @@ 35FE941222EFB70B0051C455 /* unrecognizedEndpointError.json in Resources */ = {isa = PBXBuildFile; fileRef = 35FE941122EFB70B0051C455 /* unrecognizedEndpointError.json */; }; 35FE941422EFB7C10051C455 /* unknownEndpointError.json in Resources */ = {isa = PBXBuildFile; fileRef = 35FE941322EFB7C10051C455 /* unknownEndpointError.json */; }; 35FE941F22F0929A0051C455 /* RequestRetrierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FE941E22F0929A0051C455 /* RequestRetrierTests.swift */; }; + 3D1C4FF62C074BDB0030B3D6 /* KMPHashedPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D1C4FF52C074BDB0030B3D6 /* KMPHashedPage.swift */; }; + 3D2642942C3BE835000154EC /* Dictionary+ObjCRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2642932C3BE835000154EC /* Dictionary+ObjCRepresentable.swift */; }; + 3D339C782BFF826500197342 /* KMPEventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D339C742BFF826500197342 /* KMPEventListener.swift */; }; + 3D339C792BFF826500197342 /* KMPFetchMessagesResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D339C752BFF826500197342 /* KMPFetchMessagesResult.swift */; }; + 3D339C7A2BFF826500197342 /* KMPPresenceChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D339C762BFF826500197342 /* KMPPresenceChange.swift */; }; + 3D339C7C2BFF828B00197342 /* KMPMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D339C7B2BFF828B00197342 /* KMPMessage.swift */; }; + 3D339C7E2BFF82BD00197342 /* KMPMessageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D339C7D2BFF82BD00197342 /* KMPMessageAction.swift */; }; + 3D339C802BFF890700197342 /* KMPFileChangeEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D339C7F2BFF890700197342 /* KMPFileChangeEvent.swift */; }; 3D34D1C42B989B440055A7FA /* subscription_invalid_json.json in Resources */ = {isa = PBXBuildFile; fileRef = 3D34D1C32B989B440055A7FA /* subscription_invalid_json.json */; }; 3D389FE12B35AF4A006928E7 /* TransitionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D389FC32B35AF4A006928E7 /* TransitionProtocol.swift */; }; 3D389FE22B35AF4A006928E7 /* Dispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D389FC42B35AF4A006928E7 /* Dispatcher.swift */; }; @@ -412,9 +420,12 @@ 3D38A02D2B35B087006928E7 /* SubscriptionSessionStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D38A0292B35B087006928E7 /* SubscriptionSessionStrategy.swift */; }; 3D38A02E2B35B087006928E7 /* LegacySubscriptionSessionStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D38A02A2B35B087006928E7 /* LegacySubscriptionSessionStrategy.swift */; }; 3D38A0302B35B208006928E7 /* subscription_handshake_success.json in Resources */ = {isa = PBXBuildFile; fileRef = 3D38A02F2B35B208006928E7 /* subscription_handshake_success.json */; }; + 3D3C38832C47D62700E782E7 /* KMPError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3C38822C47D62700E782E7 /* KMPError.swift */; }; + 3D452B252C05DF6D008987D4 /* KMPHereNowResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D452B242C05DF6D008987D4 /* KMPHereNowResult.swift */; }; 3D4ED42F2B519FC500FE58C7 /* SubscriptionSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4ED42E2B519FC500FE58C7 /* SubscriptionSessionTests.swift */; }; 3D5BE9AE2BCEA5B50091ACA7 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3D34160A2BB5832E008558A0 /* PrivacyInfo.xcprivacy */; }; 3D6265D72ABCA79100FDD5E6 /* CryptorUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6265D62ABCA79100FDD5E6 /* CryptorUtils.swift */; }; + 3D7411A32C171F2B002267B8 /* Int+NSNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D7411A22C171F2B002267B8 /* Int+NSNumber.swift */; }; 3D758DBF2AAA1C49005D2B36 /* CryptoModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D758DBE2AAA1C49005D2B36 /* CryptoModule.swift */; }; 3D758DC82AB06A12005D2B36 /* CryptoInputStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D758DC62AB06A12005D2B36 /* CryptoInputStream.swift */; }; 3D758DC92AB06A12005D2B36 /* CryptoStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D758DC72AB06A12005D2B36 /* CryptoStream.swift */; }; @@ -424,9 +435,24 @@ 3D758DD22AB0A91C005D2B36 /* AESCBCCryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D758DD12AB0A91C005D2B36 /* AESCBCCryptor.swift */; }; 3D758DD52AB48A6A005D2B36 /* CryptorHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D758DD32AB48A6A005D2B36 /* CryptorHeader.swift */; }; 3D758DD62AB48A6A005D2B36 /* CryptorHeaderWithinStreamFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D758DD42AB48A6A005D2B36 /* CryptorHeaderWithinStreamFinder.swift */; }; + 3D8102AA2C009AA5005E3607 /* KMPAppContextEventResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8102A92C009AA5005E3607 /* KMPAppContextEventResult.swift */; }; 3D8BAC102B8C96D70059A5C3 /* DependencyContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8BAC0F2B8C96D70059A5C3 /* DependencyContainer.swift */; }; 3D9134972A1216F7000A5124 /* PubNubPushTargetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9134962A1216F7000A5124 /* PubNubPushTargetTests.swift */; }; + 3DA0C7C92BFDF538000FFE6C /* KMPPubNub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA0C7C82BFDF538000FFE6C /* KMPPubNub.swift */; }; + 3DA0C7D02BFE59AC000FFE6C /* SubscriptionListenersContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA0C7CF2BFE59AC000FFE6C /* SubscriptionListenersContainer.swift */; }; + 3DA24A312C2AA972005B959B /* KMPPubNub+Listeners.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A302C2AA972005B959B /* KMPPubNub+Listeners.swift */; }; + 3DA24A332C2AA9BD005B959B /* KMPPubNub+Subscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A322C2AA9BD005B959B /* KMPPubNub+Subscribe.swift */; }; + 3DA24A352C2AA9F5005B959B /* KMPPubNub+Publish.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A342C2AA9F5005B959B /* KMPPubNub+Publish.swift */; }; + 3DA24A372C2AAA1C005B959B /* KMPPubNub+Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A362C2AAA1C005B959B /* KMPPubNub+Push.swift */; }; + 3DA24A392C2AAA3D005B959B /* KMPPubNub+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A382C2AAA3D005B959B /* KMPPubNub+History.swift */; }; + 3DA24A3B2C2AAA59005B959B /* KMPPubNub+Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A3A2C2AAA59005B959B /* KMPPubNub+Time.swift */; }; + 3DA24A3D2C2AAA8C005B959B /* KMPPubNub+Presence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A3C2C2AAA8C005B959B /* KMPPubNub+Presence.swift */; }; + 3DA24A3F2C2AAAC0005B959B /* KMPPubNub+MessageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A3E2C2AAAC0005B959B /* KMPPubNub+MessageActions.swift */; }; + 3DA24A412C2AAB23005B959B /* KMPPubNub+ChannelGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A402C2AAB23005B959B /* KMPPubNub+ChannelGroups.swift */; }; + 3DA24A432C2AAB54005B959B /* KMPPubNub+AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A422C2AAB54005B959B /* KMPPubNub+AppContext.swift */; }; + 3DA24A452C2AABC0005B959B /* KMPPubNub+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA24A442C2AABC0005B959B /* KMPPubNub+Files.swift */; }; 3DACC7F72AB88F8E00210B14 /* Data+CommonCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DACC7F62AB88F8E00210B14 /* Data+CommonCrypto.swift */; }; + 3DB2C4872C0F4B250060B8CF /* KMPStatusListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB2C4862C0F4B250060B8CF /* KMPStatusListener.swift */; }; 3DB9255C2B7A2B89001B7E90 /* SubscriptionStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925592B7A2B89001B7E90 /* SubscriptionStreamTests.swift */; }; 3DB9255D2B7A2B89001B7E90 /* EventStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB9255A2B7A2B89001B7E90 /* EventStreamTests.swift */; }; 3DB925602B7A2B9B001B7E90 /* SubscriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB9255F2B7A2B9B001B7E90 /* SubscriptionTests.swift */; }; @@ -441,12 +467,16 @@ 3DB925812B7AA75F001B7E90 /* SubscribeMessagePayload+PubNubEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925732B7AA75F001B7E90 /* SubscribeMessagePayload+PubNubEvent.swift */; }; 3DB925822B7AA75F001B7E90 /* Subscribable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925742B7AA75F001B7E90 /* Subscribable.swift */; }; 3DB925832B7AA75F001B7E90 /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925752B7AA75F001B7E90 /* Subscription.swift */; }; - 3DB925842B7AA75F001B7E90 /* EventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925762B7AA75F001B7E90 /* EventEmitter.swift */; }; + 3DB925842B7AA75F001B7E90 /* EventListenerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925762B7AA75F001B7E90 /* EventListenerInterface.swift */; }; 3DB925852B7AA75F001B7E90 /* EntityCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925782B7AA75F001B7E90 /* EntityCreator.swift */; }; 3DB925862B7AA75F001B7E90 /* EntitySubscribable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB925792B7AA75F001B7E90 /* EntitySubscribable.swift */; }; 3DB925872B7AA75F001B7E90 /* SessionStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB9257B2B7AA75F001B7E90 /* SessionStream.swift */; }; 3DBB2C212ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBB2C202ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift */; }; 3DBB2C222ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBB2C202ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift */; }; + 3DBFF2BB2C203EDC00142985 /* KMPUploadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBFF2BA2C203EDC00142985 /* KMPUploadable.swift */; }; + 3DBFF2BF2C22F21200142985 /* KMPEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBFF2BE2C22F21200142985 /* KMPEntity.swift */; }; + 3DBFF2C12C22F27300142985 /* PubNub+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBFF2C02C22F27300142985 /* PubNub+URL.swift */; }; + 3DBFF2C32C2300F500142985 /* KMPSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBFF2C22C2300F500142985 /* KMPSubscription.swift */; }; 3DD1FB992B5A7804005A14E3 /* PubNubPresenceStateContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD1FB982B5A7804005A14E3 /* PubNubPresenceStateContainer.swift */; }; 3DFB01942B0E30EE00146B57 /* subscription_encrypted_message_success.json in Resources */ = {isa = PBXBuildFile; fileRef = 3DFB01932B0E30EE00146B57 /* subscription_encrypted_message_success.json */; }; 4C2A8D84BCD39B07A66FD9B4 /* Pods_PubNubContractTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E7F3D449F2D66FC29674EF6 /* Pods_PubNubContractTests.framework */; }; @@ -472,9 +502,10 @@ 79407BE5271D4CFA0032076C /* PubNubFilesContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79407BD1271D4CFA0032076C /* PubNubFilesContractTestSteps.swift */; }; 79407C00271D519F0032076C /* Features in Resources */ = {isa = PBXBuildFile; fileRef = 79407BFF271D519F0032076C /* Features */; }; 79407C01271D519F0032076C /* Features in Resources */ = {isa = PBXBuildFile; fileRef = 79407BFF271D519F0032076C /* Features */; }; - 7941EEA9270E433F0054D9EF /* PubNub.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNub.framework */; platformFilter = ios; }; + 7941EEA9270E433F0054D9EF /* PubNubSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNubSDK.framework */; platformFilter = ios; }; 7951954E26C955CE001E308C /* PAMToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7951954D26C955CE001E308C /* PAMToken.swift */; }; - 79657AA3271A13F700BACEC5 /* PubNub.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNub.framework */; platformFilter = ios; }; + 79657AA3271A13F700BACEC5 /* PubNubSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNubSDK.framework */; platformFilter = ios; }; + 84AE887E2C0D9CFA009FB148 /* KMPAnyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AE887D2C0D9CFA009FB148 /* KMPAnyJSON.swift */; }; A5115F2529195AF400F6ADA1 /* PubNubObjectsMembersContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5115F2429195AF400F6ADA1 /* PubNubObjectsMembersContractTestSteps.swift */; }; A5115F2629195AF400F6ADA1 /* PubNubObjectsMembersContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5115F2429195AF400F6ADA1 /* PubNubObjectsMembersContractTestSteps.swift */; }; A5115F28291D54F500F6ADA1 /* PubNubObjectsMembershipsContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5115F27291D54F500F6ADA1 /* PubNubObjectsMembershipsContractTestSteps.swift */; }; @@ -491,7 +522,7 @@ D2635DFB22FCCF080097CF64 /* message_counts_success.json in Resources */ = {isa = PBXBuildFile; fileRef = D2635DFA22FCCF080097CF64 /* message_counts_success.json */; }; OBJ_31 /* PubNub.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* PubNub.swift */; }; OBJ_49 /* PubNubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* PubNubTests.swift */; }; - OBJ_51 /* PubNub.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNub.framework */; }; + OBJ_51 /* PubNubSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "PubNub::PubNub::Product" /* PubNubSDK.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -959,6 +990,14 @@ 35FE941122EFB70B0051C455 /* unrecognizedEndpointError.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = unrecognizedEndpointError.json; sourceTree = ""; }; 35FE941322EFB7C10051C455 /* unknownEndpointError.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = unknownEndpointError.json; sourceTree = ""; }; 35FE941E22F0929A0051C455 /* RequestRetrierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestRetrierTests.swift; sourceTree = ""; }; + 3D1C4FF52C074BDB0030B3D6 /* KMPHashedPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPHashedPage.swift; sourceTree = ""; }; + 3D2642932C3BE835000154EC /* Dictionary+ObjCRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+ObjCRepresentable.swift"; sourceTree = ""; }; + 3D339C742BFF826500197342 /* KMPEventListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMPEventListener.swift; sourceTree = ""; }; + 3D339C752BFF826500197342 /* KMPFetchMessagesResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMPFetchMessagesResult.swift; sourceTree = ""; }; + 3D339C762BFF826500197342 /* KMPPresenceChange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMPPresenceChange.swift; sourceTree = ""; }; + 3D339C7B2BFF828B00197342 /* KMPMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPMessage.swift; sourceTree = ""; }; + 3D339C7D2BFF82BD00197342 /* KMPMessageAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPMessageAction.swift; sourceTree = ""; }; + 3D339C7F2BFF890700197342 /* KMPFileChangeEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPFileChangeEvent.swift; sourceTree = ""; }; 3D34160A2BB5832E008558A0 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3D34D1C32B989B440055A7FA /* subscription_invalid_json.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscription_invalid_json.json; sourceTree = ""; }; 3D389FC32B35AF4A006928E7 /* TransitionProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionProtocol.swift; sourceTree = ""; }; @@ -1003,8 +1042,11 @@ 3D38A0292B35B087006928E7 /* SubscriptionSessionStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionSessionStrategy.swift; sourceTree = ""; }; 3D38A02A2B35B087006928E7 /* LegacySubscriptionSessionStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacySubscriptionSessionStrategy.swift; sourceTree = ""; }; 3D38A02F2B35B208006928E7 /* subscription_handshake_success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscription_handshake_success.json; sourceTree = ""; }; + 3D3C38822C47D62700E782E7 /* KMPError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPError.swift; sourceTree = ""; }; + 3D452B242C05DF6D008987D4 /* KMPHereNowResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPHereNowResult.swift; sourceTree = ""; }; 3D4ED42E2B519FC500FE58C7 /* SubscriptionSessionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionSessionTests.swift; sourceTree = ""; }; 3D6265D62ABCA79100FDD5E6 /* CryptorUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptorUtils.swift; sourceTree = ""; }; + 3D7411A22C171F2B002267B8 /* Int+NSNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+NSNumber.swift"; sourceTree = ""; }; 3D758DBE2AAA1C49005D2B36 /* CryptoModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoModule.swift; sourceTree = ""; }; 3D758DC62AB06A12005D2B36 /* CryptoInputStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoInputStream.swift; sourceTree = ""; }; 3D758DC72AB06A12005D2B36 /* CryptoStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoStream.swift; sourceTree = ""; }; @@ -1014,9 +1056,24 @@ 3D758DD12AB0A91C005D2B36 /* AESCBCCryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AESCBCCryptor.swift; sourceTree = ""; }; 3D758DD32AB48A6A005D2B36 /* CryptorHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptorHeader.swift; sourceTree = ""; }; 3D758DD42AB48A6A005D2B36 /* CryptorHeaderWithinStreamFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptorHeaderWithinStreamFinder.swift; sourceTree = ""; }; + 3D8102A92C009AA5005E3607 /* KMPAppContextEventResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPAppContextEventResult.swift; sourceTree = ""; }; 3D8BAC0F2B8C96D70059A5C3 /* DependencyContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyContainer.swift; sourceTree = ""; }; 3D9134962A1216F7000A5124 /* PubNubPushTargetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PubNubPushTargetTests.swift; sourceTree = ""; }; + 3DA0C7C82BFDF538000FFE6C /* KMPPubNub.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMPPubNub.swift; sourceTree = ""; }; + 3DA0C7CF2BFE59AC000FFE6C /* SubscriptionListenersContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionListenersContainer.swift; sourceTree = ""; }; + 3DA24A302C2AA972005B959B /* KMPPubNub+Listeners.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+Listeners.swift"; sourceTree = ""; }; + 3DA24A322C2AA9BD005B959B /* KMPPubNub+Subscribe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+Subscribe.swift"; sourceTree = ""; }; + 3DA24A342C2AA9F5005B959B /* KMPPubNub+Publish.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+Publish.swift"; sourceTree = ""; }; + 3DA24A362C2AAA1C005B959B /* KMPPubNub+Push.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+Push.swift"; sourceTree = ""; }; + 3DA24A382C2AAA3D005B959B /* KMPPubNub+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+History.swift"; sourceTree = ""; }; + 3DA24A3A2C2AAA59005B959B /* KMPPubNub+Time.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+Time.swift"; sourceTree = ""; }; + 3DA24A3C2C2AAA8C005B959B /* KMPPubNub+Presence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+Presence.swift"; sourceTree = ""; }; + 3DA24A3E2C2AAAC0005B959B /* KMPPubNub+MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+MessageActions.swift"; sourceTree = ""; }; + 3DA24A402C2AAB23005B959B /* KMPPubNub+ChannelGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+ChannelGroups.swift"; sourceTree = ""; }; + 3DA24A422C2AAB54005B959B /* KMPPubNub+AppContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+AppContext.swift"; sourceTree = ""; }; + 3DA24A442C2AABC0005B959B /* KMPPubNub+Files.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KMPPubNub+Files.swift"; sourceTree = ""; }; 3DACC7F62AB88F8E00210B14 /* Data+CommonCrypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+CommonCrypto.swift"; sourceTree = ""; }; + 3DB2C4862C0F4B250060B8CF /* KMPStatusListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPStatusListener.swift; sourceTree = ""; }; 3DB925592B7A2B89001B7E90 /* SubscriptionStreamTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionStreamTests.swift; sourceTree = ""; }; 3DB9255A2B7A2B89001B7E90 /* EventStreamTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventStreamTests.swift; sourceTree = ""; }; 3DB9255F2B7A2B9B001B7E90 /* SubscriptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionTests.swift; sourceTree = ""; }; @@ -1031,11 +1088,15 @@ 3DB925732B7AA75F001B7E90 /* SubscribeMessagePayload+PubNubEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SubscribeMessagePayload+PubNubEvent.swift"; sourceTree = ""; }; 3DB925742B7AA75F001B7E90 /* Subscribable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subscribable.swift; sourceTree = ""; }; 3DB925752B7AA75F001B7E90 /* Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subscription.swift; sourceTree = ""; }; - 3DB925762B7AA75F001B7E90 /* EventEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventEmitter.swift; sourceTree = ""; }; + 3DB925762B7AA75F001B7E90 /* EventListenerInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventListenerInterface.swift; sourceTree = ""; }; 3DB925782B7AA75F001B7E90 /* EntityCreator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntityCreator.swift; sourceTree = ""; }; 3DB925792B7AA75F001B7E90 /* EntitySubscribable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntitySubscribable.swift; sourceTree = ""; }; 3DB9257B2B7AA75F001B7E90 /* SessionStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionStream.swift; sourceTree = ""; }; 3DBB2C202ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PubNubCryptoModuleContractTestSteps.swift; sourceTree = ""; }; + 3DBFF2BA2C203EDC00142985 /* KMPUploadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPUploadable.swift; sourceTree = ""; }; + 3DBFF2BE2C22F21200142985 /* KMPEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPEntity.swift; sourceTree = ""; }; + 3DBFF2C02C22F27300142985 /* PubNub+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PubNub+URL.swift"; sourceTree = ""; }; + 3DBFF2C22C2300F500142985 /* KMPSubscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPSubscription.swift; sourceTree = ""; }; 3DD1FB982B5A7804005A14E3 /* PubNubPresenceStateContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PubNubPresenceStateContainer.swift; sourceTree = ""; }; 3DE632651BA8B2E27ACFC4AD /* Pods-PubNubContractTestsBeta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PubNubContractTestsBeta.release.xcconfig"; path = "Target Support Files/Pods-PubNubContractTestsBeta/Pods-PubNubContractTestsBeta.release.xcconfig"; sourceTree = ""; }; 3DFB01932B0E30EE00146B57 /* subscription_encrypted_message_success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscription_encrypted_message_success.json; sourceTree = ""; }; @@ -1058,6 +1119,7 @@ 7941EF47270E46B40054D9EF /* PubNubContractTests_Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = PubNubContractTests_Info.plist; path = PubNub.xcodeproj/PubNubContractTests_Info.plist; sourceTree = SOURCE_ROOT; }; 7951954D26C955CE001E308C /* PAMToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PAMToken.swift; sourceTree = ""; }; 79657AAB271A13F700BACEC5 /* PubNubContractTestsBeta.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PubNubContractTestsBeta.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 84AE887D2C0D9CFA009FB148 /* KMPAnyJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPAnyJSON.swift; sourceTree = ""; }; 979DD162B86D78BE9B72DEEF /* Pods-PubNubContractTestsBeta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PubNubContractTestsBeta.debug.xcconfig"; path = "Target Support Files/Pods-PubNubContractTestsBeta/Pods-PubNubContractTestsBeta.debug.xcconfig"; sourceTree = ""; }; A5115F2429195AF400F6ADA1 /* PubNubObjectsMembersContractTestSteps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PubNubObjectsMembersContractTestSteps.swift; sourceTree = ""; }; A5115F27291D54F500F6ADA1 /* PubNubObjectsMembershipsContractTestSteps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PubNubObjectsMembershipsContractTestSteps.swift; sourceTree = ""; }; @@ -1076,7 +1138,7 @@ OBJ_24 /* PubNubSwift.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = PubNubSwift.podspec; sourceTree = ""; }; OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; OBJ_9 /* PubNub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PubNub.h; sourceTree = ""; }; - "PubNub::PubNub::Product" /* PubNub.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PubNub.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + "PubNub::PubNub::Product" /* PubNubSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PubNubSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; "PubNub::PubNubTests::Product" /* PubNubTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = PubNubTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -1085,7 +1147,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 0; files = ( - 355806DB23145749005CDD92 /* PubNub.framework in Frameworks */, + 355806DB23145749005CDD92 /* PubNubSDK.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1162,7 +1224,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 0; files = ( - 7941EEA9270E433F0054D9EF /* PubNub.framework in Frameworks */, + 7941EEA9270E433F0054D9EF /* PubNubSDK.framework in Frameworks */, 4C2A8D84BCD39B07A66FD9B4 /* Pods_PubNubContractTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1171,7 +1233,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 0; files = ( - 79657AA3271A13F700BACEC5 /* PubNub.framework in Frameworks */, + 79657AA3271A13F700BACEC5 /* PubNubSDK.framework in Frameworks */, 0162B986DE5A8773D6F8C8A0 /* Pods_PubNubContractTestsBeta.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1187,7 +1249,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 0; files = ( - OBJ_51 /* PubNub.framework in Frameworks */, + OBJ_51 /* PubNubSDK.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1999,6 +2061,28 @@ path = EndpointError; sourceTree = ""; }; + 3D339C772BFF826500197342 /* Wrappers */ = { + isa = PBXGroup; + children = ( + 84AE887D2C0D9CFA009FB148 /* KMPAnyJSON.swift */, + 3D339C742BFF826500197342 /* KMPEventListener.swift */, + 3DB2C4862C0F4B250060B8CF /* KMPStatusListener.swift */, + 3D339C7B2BFF828B00197342 /* KMPMessage.swift */, + 3D339C7D2BFF82BD00197342 /* KMPMessageAction.swift */, + 3D339C762BFF826500197342 /* KMPPresenceChange.swift */, + 3D339C752BFF826500197342 /* KMPFetchMessagesResult.swift */, + 3D339C7F2BFF890700197342 /* KMPFileChangeEvent.swift */, + 3D8102A92C009AA5005E3607 /* KMPAppContextEventResult.swift */, + 3D452B242C05DF6D008987D4 /* KMPHereNowResult.swift */, + 3D1C4FF52C074BDB0030B3D6 /* KMPHashedPage.swift */, + 3DBFF2BA2C203EDC00142985 /* KMPUploadable.swift */, + 3DBFF2BE2C22F21200142985 /* KMPEntity.swift */, + 3DBFF2C22C2300F500142985 /* KMPSubscription.swift */, + 3D3C38822C47D62700E782E7 /* KMPError.swift */, + ); + path = Wrappers; + sourceTree = ""; + }; 3D389FC12B35AF4A006928E7 /* EventEngine */ = { isa = PBXGroup; children = ( @@ -2157,6 +2241,16 @@ path = CryptorModule; sourceTree = ""; }; + 3D7411A12C171F20002267B8 /* Helpers */ = { + isa = PBXGroup; + children = ( + 3D7411A22C171F2B002267B8 /* Int+NSNumber.swift */, + 3DBFF2C02C22F27300142985 /* PubNub+URL.swift */, + 3D2642932C3BE835000154EC /* Dictionary+ObjCRepresentable.swift */, + ); + path = Helpers; + sourceTree = ""; + }; 3D758DC42AB06977005D2B36 /* Miscellaneous */ = { isa = PBXGroup; children = ( @@ -2204,6 +2298,27 @@ path = Push; sourceTree = ""; }; + 3DA0C7C52BFDF508000FFE6C /* KMP */ = { + isa = PBXGroup; + children = ( + 3DA0C7C82BFDF538000FFE6C /* KMPPubNub.swift */, + 3DA24A302C2AA972005B959B /* KMPPubNub+Listeners.swift */, + 3DA24A322C2AA9BD005B959B /* KMPPubNub+Subscribe.swift */, + 3DA24A342C2AA9F5005B959B /* KMPPubNub+Publish.swift */, + 3DA24A362C2AAA1C005B959B /* KMPPubNub+Push.swift */, + 3DA24A382C2AAA3D005B959B /* KMPPubNub+History.swift */, + 3DA24A3A2C2AAA59005B959B /* KMPPubNub+Time.swift */, + 3DA24A3C2C2AAA8C005B959B /* KMPPubNub+Presence.swift */, + 3DA24A3E2C2AAAC0005B959B /* KMPPubNub+MessageActions.swift */, + 3DA24A402C2AAB23005B959B /* KMPPubNub+ChannelGroups.swift */, + 3DA24A422C2AAB54005B959B /* KMPPubNub+AppContext.swift */, + 3DA24A442C2AABC0005B959B /* KMPPubNub+Files.swift */, + 3D339C772BFF826500197342 /* Wrappers */, + 3D7411A12C171F20002267B8 /* Helpers */, + ); + path = KMP; + sourceTree = ""; + }; 3DB925572B7A2B89001B7E90 /* Old */ = { isa = PBXGroup; children = ( @@ -2254,10 +2369,11 @@ isa = PBXGroup; children = ( 3DB925712B7AA75F001B7E90 /* PubNubEvent.swift */, - 3DB925762B7AA75F001B7E90 /* EventEmitter.swift */, + 3DB925762B7AA75F001B7E90 /* EventListenerInterface.swift */, 3DB925742B7AA75F001B7E90 /* Subscribable.swift */, 3DB925752B7AA75F001B7E90 /* Subscription.swift */, 3DB925702B7AA75F001B7E90 /* SubscriptionSet.swift */, + 3DA0C7CF2BFE59AC000FFE6C /* SubscriptionListenersContainer.swift */, 3DB925772B7AA75F001B7E90 /* Entities */, 3DB925722B7AA75F001B7E90 /* Extensions */, ); @@ -2470,7 +2586,7 @@ isa = PBXGroup; children = ( "PubNub::PubNubTests::Product" /* PubNubTests.xctest */, - "PubNub::PubNub::Product" /* PubNub.framework */, + "PubNub::PubNub::Product" /* PubNubSDK.framework */, 3558073723145749005CDD92 /* PubNubIntTests.xctest */, 7941EF40270E433F0054D9EF /* PubNubContractTests.xctest */, 79657AAB271A13F700BACEC5 /* PubNubContractTestsBeta.xctest */, @@ -2521,6 +2637,7 @@ 35F0259222BBE68B007BD7D3 /* Extensions */, 3556E36E24802392004FDC25 /* Property Wrappers */, 355BE9F922C28C29000EC334 /* Helpers */, + 3DA0C7C52BFDF508000FFE6C /* KMP */, 35EA73F722B199EA00D97BF0 /* Supporting Files */, ); name = Sources; @@ -2779,9 +2896,9 @@ productReference = 79657AAB271A13F700BACEC5 /* PubNubContractTestsBeta.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - "PubNub::PubNub" /* PubNub */ = { + "PubNub::PubNub" /* PubNubSDK */ = { isa = PBXNativeTarget; - buildConfigurationList = OBJ_27 /* Build configuration list for PBXNativeTarget "PubNub" */; + buildConfigurationList = OBJ_27 /* Build configuration list for PBXNativeTarget "PubNubSDK" */; buildPhases = ( 3D5BE9AD2BCEA5AC0091ACA7 /* Resources */, OBJ_30 /* Sources */, @@ -2791,9 +2908,9 @@ ); dependencies = ( ); - name = PubNub; + name = PubNubSDK; productName = PubNub; - productReference = "PubNub::PubNub::Product" /* PubNub.framework */; + productReference = "PubNub::PubNub::Product" /* PubNubSDK.framework */; productType = "com.apple.product-type.framework"; }; "PubNub::PubNubTests" /* PubNubTests */ = { @@ -2864,7 +2981,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - "PubNub::PubNub" /* PubNub */, + "PubNub::PubNub" /* PubNubSDK */, "PubNub::PubNubTests" /* PubNubTests */, 7941EE6B270E433F0054D9EF /* PubNubContractTests */, 79657A93271A13F700BACEC5 /* PubNubContractTestsBeta */, @@ -3436,18 +3553,24 @@ 35D0615F2304830600FDB2F9 /* GenericServicePayloadResponse.swift in Sources */, 3D758DD62AB48A6A005D2B36 /* CryptorHeaderWithinStreamFinder.swift in Sources */, 3D758DBF2AAA1C49005D2B36 /* CryptoModule.swift in Sources */, + 3D339C792BFF826500197342 /* KMPFetchMessagesResult.swift in Sources */, 3D389FEC2B35AF4A006928E7 /* SubscribeRequest.swift in Sources */, + 3DBFF2BF2C22F21200142985 /* KMPEntity.swift in Sources */, 3D389FE72B35AF4A006928E7 /* EmitStatusEffect.swift in Sources */, 3D758DD02AB0A8C6005D2B36 /* CryptorVector.swift in Sources */, + 3DBFF2C12C22F27300142985 /* PubNub+URL.swift in Sources */, 3D6265D72ABCA79100FDD5E6 /* CryptorUtils.swift in Sources */, + 3DA24A332C2AA9BD005B959B /* KMPPubNub+Subscribe.swift in Sources */, 35D8D4C522EB4600001B07D9 /* AnyJSON.swift in Sources */, 35AC16332487179400A66030 /* PubNubPage.swift in Sources */, + 3DA0C7D02BFE59AC000FFE6C /* SubscriptionListenersContainer.swift in Sources */, 3DB925802B7AA75F001B7E90 /* PubNubEvent.swift in Sources */, 35AC162F2486C9A400A66030 /* PubNubMessageAction.swift in Sources */, 3585033B22CD545400A11D9A /* URLRequest+PubNub.swift in Sources */, 35C6B6E622F51A060054F242 /* AnyJSONType.swift in Sources */, 3585033422CD139400A11D9A /* String+PubNub.swift in Sources */, 3D389FE12B35AF4A006928E7 /* TransitionProtocol.swift in Sources */, + 3D2642942C3BE835000154EC /* Dictionary+ObjCRepresentable.swift in Sources */, 354ADA8C22D923F20093EFFB /* Replaceables+PubNub.swift in Sources */, 3D389FF62B35AF4A006928E7 /* PresenceLeaveRequest.swift in Sources */, 35CF549D248D73500099FE81 /* SubscribeObjectPayload.swift in Sources */, @@ -3457,6 +3580,8 @@ 35CDFEAF22E7664D00F3B9F2 /* URLQueryItem+PubNub.swift in Sources */, 3D389FF12B35AF4A006928E7 /* HeartbeatEffect.swift in Sources */, 35304F8A22FE5425006A02CA /* Validated.swift in Sources */, + 3DA24A452C2AABC0005B959B /* KMPPubNub+Files.swift in Sources */, + 3DA24A3F2C2AAAC0005B959B /* KMPPubNub+MessageActions.swift in Sources */, 35EE358822E247B200E3F081 /* URLSessionConfiguration+PubNub.swift in Sources */, 3D389FF22B35AF4A006928E7 /* LeaveEffect.swift in Sources */, 35A6C7A822FBCC8B00E97CC5 /* PushRouter.swift in Sources */, @@ -3465,13 +3590,18 @@ 3DB925812B7AA75F001B7E90 /* SubscribeMessagePayload+PubNubEvent.swift in Sources */, 35A66A7E22F861BA00AC67A9 /* SubscriptionSession.swift in Sources */, 3DB925872B7AA75F001B7E90 /* SessionStream.swift in Sources */, + 3D7411A32C171F2B002267B8 /* Int+NSNumber.swift in Sources */, + 3D339C7C2BFF828B00197342 /* KMPMessage.swift in Sources */, 3D389FE42B35AF4A006928E7 /* EventEngine.swift in Sources */, 35C6B6E322F515760054F242 /* SubscribeRouter.swift in Sources */, 3DB9257C2B7AA75F001B7E90 /* EventStream.swift in Sources */, 3DB925832B7AA75F001B7E90 /* Subscription.swift in Sources */, + 3D8102AA2C009AA5005E3607 /* KMPAppContextEventResult.swift in Sources */, + 3D452B252C05DF6D008987D4 /* KMPHereNowResult.swift in Sources */, 35C6B6DD22F501780054F242 /* Encodable+PubNub.swift in Sources */, 35580682230F3A34005CDD92 /* RequestIdOperator.swift in Sources */, 35A66A8322F861BA00AC67A9 /* PubNubMessage.swift in Sources */, + 3DBFF2BB2C203EDC00142985 /* KMPUploadable.swift in Sources */, 35A66A8022F861BA00AC67A9 /* AutomaticRetry.swift in Sources */, 357D1C1B22CB04C4003625BA /* Bool+PubNub.swift in Sources */, 35CF548C248971BF0099FE81 /* ObjectsUUIDRouter.swift in Sources */, @@ -3488,19 +3618,23 @@ 35CF54922489912F0099FE81 /* PubNubUUIDMetadata.swift in Sources */, 35B6FBAF22F226F4005EE490 /* NSNumber+PubNub.swift in Sources */, 3D38A02E2B35B087006928E7 /* LegacySubscriptionSessionStrategy.swift in Sources */, + 3DA24A352C2AA9F5005B959B /* KMPPubNub+Publish.swift in Sources */, 357024BF283C07C900567EE8 /* Objects+PubNub.swift in Sources */, 35B0ACE3252BE36D00537A18 /* File+PubNub.swift in Sources */, 3D758DD52AB48A6A005D2B36 /* CryptorHeader.swift in Sources */, 35CF549C248ABE8B0099FE81 /* PubNubObjectMetadataPatcher.swift in Sources */, + 3DA24A412C2AAB23005B959B /* KMPPubNub+ChannelGroups.swift in Sources */, 35C829DC23147AC000F59D3C /* SubscriptionState.swift in Sources */, 35E71C3C2490678E0032A991 /* PubNubPresence.swift in Sources */, 35D8D4CD22EB90F1001B07D9 /* Int+PubNub.swift in Sources */, 35B3824A233AAB8C0028803F /* JSONCodable.swift in Sources */, 35599792230A3F11000BCFD1 /* Thread+PubNub.swift in Sources */, + 3DB2C4872C0F4B250060B8CF /* KMPStatusListener.swift in Sources */, 3D389FEF2B35AF4A006928E7 /* Presence.swift in Sources */, 3DB925862B7AA75F001B7E90 /* EntitySubscribable.swift in Sources */, 3D389FF82B35AF4A006928E7 /* PresenceInput.swift in Sources */, 3DB9257D2B7AA75F001B7E90 /* SubscriptionStream.swift in Sources */, + 3DA24A392C2AAA3D005B959B /* KMPPubNub+History.swift in Sources */, 3567434822E1E4F700BF2639 /* Collection+PubNub.swift in Sources */, 354FC4C122D04D3600318932 /* DispatchQueue+PubNub.swift in Sources */, 359512102301DCAB00C9D3AE /* Crypto.swift in Sources */, @@ -3508,7 +3642,9 @@ 3585A02423C63EE900FDA860 /* CBORSerialization.swift in Sources */, 359152A122BA9AA30048842D /* PubNubConfiguration.swift in Sources */, 3D389FE62B35AF4A006928E7 /* EmitMessagesEffect.swift in Sources */, + 3DA24A312C2AA972005B959B /* KMPPubNub+Listeners.swift in Sources */, 3D38A02C2B35B087006928E7 /* LegacySubscriptionSessionStrategy+Presence.swift in Sources */, + 3DA24A3D2C2AAA8C005B959B /* KMPPubNub+Presence.swift in Sources */, 3DB925852B7AA75F001B7E90 /* EntityCreator.swift in Sources */, 358C641F238C5FCA009CE354 /* FCMWebpushPayload.swift in Sources */, 352DBFEA237CCB9D00A0106E /* EndpointResponse.swift in Sources */, @@ -3519,7 +3655,8 @@ 3D389FE92B35AF4A006928E7 /* SubscribeEffectFactory.swift in Sources */, 355F213722DECFCD004DEFBF /* Typealias+PubNub.swift in Sources */, 3585A02623C63F3900FDA860 /* DecodingError+PubNub.swift in Sources */, - 3DB925842B7AA75F001B7E90 /* EventEmitter.swift in Sources */, + 3D339C802BFF890700197342 /* KMPFileChangeEvent.swift in Sources */, + 3DB925842B7AA75F001B7E90 /* EventListenerInterface.swift in Sources */, 3557CE0723886434004BBACC /* PubNubAPNSPayload.swift in Sources */, 3D389FED2B35AF4A006928E7 /* Subscribe.swift in Sources */, 3D389FE22B35AF4A006928E7 /* Dispatcher.swift in Sources */, @@ -3531,20 +3668,26 @@ 35F0259922BBFA85007BD7D3 /* HTTPSession.swift in Sources */, 353F78C42527934500FFB72C /* InputStream+PubNub.swift in Sources */, 3D38A02D2B35B087006928E7 /* SubscriptionSessionStrategy.swift in Sources */, + 84AE887E2C0D9CFA009FB148 /* KMPAnyJSON.swift in Sources */, + 3DBFF2C32C2300F500142985 /* KMPSubscription.swift in Sources */, 353E8CE423C68F01003FBFF5 /* Float32+PubNub.swift in Sources */, 352DBFE9237C937F00A0106E /* HTTPSessionDelegate.swift in Sources */, 35CF548E248971CD0099FE81 /* ObjectsChannelRouter.swift in Sources */, 35A6C7BE22FCCBF900E97CC5 /* HistoryRouter.swift in Sources */, 35A66A9022F913B200AC67A9 /* ConnectionStatus.swift in Sources */, + 3D3C38832C47D62700E782E7 /* KMPError.swift in Sources */, 3585033722CD4A1A00A11D9A /* RequestOperator.swift in Sources */, 354ADA8822D909A30093EFFB /* Convertibles+PubNub.swift in Sources */, + 3DA24A432C2AAB54005B959B /* KMPPubNub+AppContext.swift in Sources */, 3556E3762485936B004FDC25 /* SubscribePresencePayload.swift in Sources */, 3D389FF42B35AF4A006928E7 /* PresenceEffectFactory.swift in Sources */, + 3D339C7E2BFF82BD00197342 /* KMPMessageAction.swift in Sources */, 3D38A02B2B35B087006928E7 /* EventEngineSubscriptionSessionStrategy.swift in Sources */, 350EFBDC22C951F700FA33AA /* Request.swift in Sources */, 3D758DD22AB0A91C005D2B36 /* AESCBCCryptor.swift in Sources */, 35A66A7F22F861BA00AC67A9 /* WeakBox.swift in Sources */, 357CA288251D3C9100BC40D3 /* MultipartInputStream.swift in Sources */, + 3DA24A372C2AAA1C005B959B /* KMPPubNub+Push.swift in Sources */, 35CF54942489918E0099FE81 /* PubNubChannelMetadata.swift in Sources */, OBJ_31 /* PubNub.swift in Sources */, 358C641A2388BF76009CE354 /* PubNubFCMPayload.swift in Sources */, @@ -3554,18 +3697,23 @@ 35A6C78F22FB4F0500E97CC5 /* ChannelGroupsRouter.swift in Sources */, 354ADA9022DA81650093EFFB /* DateFormatter+PubNub.swift in Sources */, 35DB0C4B2874768C001E1F76 /* FlatJSONCodable.swift in Sources */, + 3DA24A3B2C2AAA59005B959B /* KMPPubNub+Time.swift in Sources */, 35CF54962489B3760099FE81 /* PubNubMembershipMetadata.swift in Sources */, 3556E370248023B2004FDC25 /* BoundedValue.swift in Sources */, + 3D1C4FF62C074BDB0030B3D6 /* KMPHashedPage.swift in Sources */, 35270C0323AC124800501388 /* CBORDecoder.swift in Sources */, 3D389FE82B35AF4A006928E7 /* SubscribeEffects.swift in Sources */, 3559978C230A02B7000BCFD1 /* PubNubLogger.swift in Sources */, 35AE6A3224FD6CEE00BBFA37 /* FileManagementRouter.swift in Sources */, + 3D339C782BFF826500197342 /* KMPEventListener.swift in Sources */, 3D389FF32B35AF4A006928E7 /* WaitEffect.swift in Sources */, + 3D339C7A2BFF826500197342 /* KMPPresenceChange.swift in Sources */, 3D8BAC102B8C96D70059A5C3 /* DependencyContainer.swift in Sources */, 35089A0B22E56F1F002BCC94 /* Constants.swift in Sources */, 358C6421238C6787009CE354 /* PubNubPushMessage.swift in Sources */, 3DD1FB992B5A7804005A14E3 /* PubNubPresenceStateContainer.swift in Sources */, 3D758DC82AB06A12005D2B36 /* CryptoInputStream.swift in Sources */, + 3DA0C7C92BFDF538000FFE6C /* KMPPubNub.swift in Sources */, 35E4604F234B8B9D005D04AE /* ErrorDescription.swift in Sources */, 35089A0922E3C08D002BCC94 /* Error+PubNub.swift in Sources */, 3534D4E422C57659008E89FA /* PublishRouter.swift in Sources */, @@ -3663,7 +3811,7 @@ /* Begin PBXTargetDependency section */ 3558069E23145749005CDD92 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = "PubNub::PubNub" /* PubNub */; + target = "PubNub::PubNub" /* PubNubSDK */; targetProxy = 3558069F23145749005CDD92 /* PBXContainerItemProxy */; }; 358B8917284D206B00DB0F3D /* PBXTargetDependency */ = { @@ -3683,17 +3831,17 @@ }; 358B8962284D22B100DB0F3D /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = "PubNub::PubNub" /* PubNub */; + target = "PubNub::PubNub" /* PubNubSDK */; targetProxy = 358B8961284D22B100DB0F3D /* PBXContainerItemProxy */; }; 358B8966284D22D800DB0F3D /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = "PubNub::PubNub" /* PubNub */; + target = "PubNub::PubNub" /* PubNubSDK */; targetProxy = 358B8965284D22D800DB0F3D /* PBXContainerItemProxy */; }; 358B8968284D22E200DB0F3D /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = "PubNub::PubNub" /* PubNub */; + target = "PubNub::PubNub" /* PubNubSDK */; targetProxy = 358B8967284D22E200DB0F3D /* PBXContainerItemProxy */; }; 358B896A284D22E200DB0F3D /* PBXTargetDependency */ = { @@ -3724,18 +3872,18 @@ 7941EE6C270E433F0054D9EF /* PBXTargetDependency */ = { isa = PBXTargetDependency; platformFilter = ios; - target = "PubNub::PubNub" /* PubNub */; + target = "PubNub::PubNub" /* PubNubSDK */; targetProxy = 7941EE6D270E433F0054D9EF /* PBXContainerItemProxy */; }; 79657A94271A13F700BACEC5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; platformFilter = ios; - target = "PubNub::PubNub" /* PubNub */; + target = "PubNub::PubNub" /* PubNubSDK */; targetProxy = 79657A95271A13F700BACEC5 /* PBXContainerItemProxy */; }; OBJ_52 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = "PubNub::PubNub" /* PubNub */; + target = "PubNub::PubNub" /* PubNubSDK */; targetProxy = 35EA73F422B1916100D97BF0 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -3847,7 +3995,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -3898,7 +4046,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -4006,7 +4154,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -4059,7 +4207,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -4180,7 +4328,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -4232,7 +4380,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -4712,7 +4860,7 @@ "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14"; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -4725,9 +4873,10 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - TARGET_NAME = PubNub; + TARGET_NAME = PubNubSDK; TVOS_DEPLOYMENT_TARGET = 12.0; WATCHOS_DEPLOYMENT_TARGET = 4.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; @@ -4754,7 +4903,7 @@ "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.3; + MARKETING_VERSION = 8.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14"; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -4767,9 +4916,10 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - TARGET_NAME = PubNub; + TARGET_NAME = PubNubSDK; TVOS_DEPLOYMENT_TARGET = 12.0; WATCHOS_DEPLOYMENT_TARGET = 4.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; @@ -5087,7 +5237,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - OBJ_27 /* Build configuration list for PBXNativeTarget "PubNub" */ = { + OBJ_27 /* Build configuration list for PBXNativeTarget "PubNubSDK" */ = { isa = XCConfigurationList; buildConfigurations = ( OBJ_28 /* Debug */, diff --git a/PubNub.xcodeproj/xcshareddata/xcschemes/PubNub.xcscheme b/PubNub.xcodeproj/xcshareddata/xcschemes/PubNub.xcscheme index 43cdf6d9..ddd6763d 100644 --- a/PubNub.xcodeproj/xcshareddata/xcschemes/PubNub.xcscheme +++ b/PubNub.xcodeproj/xcshareddata/xcschemes/PubNub.xcscheme @@ -15,8 +15,8 @@ @@ -34,8 +34,8 @@ @@ -80,8 +80,8 @@ @@ -96,8 +96,8 @@ diff --git a/PubNubMembership/Sources/Membership+PubNub.swift b/PubNubMembership/Sources/Membership+PubNub.swift index b3005f74..31aece05 100644 --- a/PubNubMembership/Sources/Membership+PubNub.swift +++ b/PubNubMembership/Sources/Membership+PubNub.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK import PubNubSpace import PubNubUser diff --git a/PubNubMembership/Sources/Patcher+PubNubMembership.swift b/PubNubMembership/Sources/Patcher+PubNubMembership.swift index 969e2112..52a9e44d 100644 --- a/PubNubMembership/Sources/Patcher+PubNubMembership.swift +++ b/PubNubMembership/Sources/Patcher+PubNubMembership.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK import PubNubSpace import PubNubUser diff --git a/PubNubMembership/Sources/PubNubMembership.swift b/PubNubMembership/Sources/PubNubMembership.swift index 5eb25066..c30b78be 100644 --- a/PubNubMembership/Sources/PubNubMembership.swift +++ b/PubNubMembership/Sources/PubNubMembership.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK import PubNubSpace import PubNubUser diff --git a/PubNubMembership/Sources/PubNubMembershipEvent.swift b/PubNubMembership/Sources/PubNubMembershipEvent.swift index 911d9cd7..90de0d8b 100644 --- a/PubNubMembership/Sources/PubNubMembershipEvent.swift +++ b/PubNubMembership/Sources/PubNubMembershipEvent.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK /// All the changes that can be received for Membership entities public enum PubNubMembershipEvent { diff --git a/PubNubMembership/Tests/Integration/SmokeTest+PubNubMembership.swift b/PubNubMembership/Tests/Integration/SmokeTest+PubNubMembership.swift index 001e6007..49a16b4c 100644 --- a/PubNubMembership/Tests/Integration/SmokeTest+PubNubMembership.swift +++ b/PubNubMembership/Tests/Integration/SmokeTest+PubNubMembership.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import PubNubMembership import PubNubSpace import PubNubUser diff --git a/PubNubMembership/Tests/MembershipTestHelpers.swift b/PubNubMembership/Tests/MembershipTestHelpers.swift index cca7ab77..4abf7f7a 100644 --- a/PubNubMembership/Tests/MembershipTestHelpers.swift +++ b/PubNubMembership/Tests/MembershipTestHelpers.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK struct MembershipCustom: FlatJSONCodable, Hashable { var value: String? diff --git a/PubNubMembership/Tests/Test+PubNubMembership.swift b/PubNubMembership/Tests/Test+PubNubMembership.swift index 26c572a0..7ce70d63 100644 --- a/PubNubMembership/Tests/Test+PubNubMembership.swift +++ b/PubNubMembership/Tests/Test+PubNubMembership.swift @@ -8,13 +8,13 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubMembership +import PubNubSDK import PubNubSpace import PubNubUser - import XCTest +@testable import PubNubMembership + class PubNubMembershipModelTests: XCTestCase { let testMembership = PubNubMembership( user: PubNubUser(id: "TestUserId"), diff --git a/PubNubMembership/Tests/Test+PubNubMembershipEvent.swift b/PubNubMembership/Tests/Test+PubNubMembershipEvent.swift index 55113b8e..11f1162f 100644 --- a/PubNubMembership/Tests/Test+PubNubMembershipEvent.swift +++ b/PubNubMembership/Tests/Test+PubNubMembershipEvent.swift @@ -8,13 +8,13 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubMembership +import PubNubSDK import PubNubSpace import PubNubUser - import XCTest +@testable import PubNubMembership + class PubNubMembershipEventTests: XCTestCase { let testMembership = PubNubMembership( user: PubNubUser(id: "TestUserId"), diff --git a/PubNubMembership/Tests/Test+PubNubMembershipInterface.swift b/PubNubMembership/Tests/Test+PubNubMembershipInterface.swift index c21c469f..9722e067 100644 --- a/PubNubMembership/Tests/Test+PubNubMembershipInterface.swift +++ b/PubNubMembership/Tests/Test+PubNubMembershipInterface.swift @@ -8,13 +8,13 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubMembership +import PubNubSDK import PubNubSpace import PubNubUser - import XCTest +@testable import PubNubMembership + // swiftlint:disable:next type_body_length class PubNubMembershipInterfaceTests: XCTestCase { let testMembership = PubNubMembership( diff --git a/PubNubMembership/Tests/Test+PubNubMembershipPatcher.swift b/PubNubMembership/Tests/Test+PubNubMembershipPatcher.swift index 9ad76ae0..8a506743 100644 --- a/PubNubMembership/Tests/Test+PubNubMembershipPatcher.swift +++ b/PubNubMembership/Tests/Test+PubNubMembershipPatcher.swift @@ -8,13 +8,13 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubMembership +import PubNubSDK import PubNubSpace import PubNubUser - import XCTest +@testable import PubNubMembership + class PubNubMembershipPatcherTests: XCTestCase { var testMembership = PubNubMembership( user: PubNubUser(id: "TestUserId"), diff --git a/PubNubSpace/Sources/Patcher+PubNubSpace.swift b/PubNubSpace/Sources/Patcher+PubNubSpace.swift index 2e203fb8..f72448d4 100644 --- a/PubNubSpace/Sources/Patcher+PubNubSpace.swift +++ b/PubNubSpace/Sources/Patcher+PubNubSpace.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK public extension PubNubSpace { /// Object that can be used to apply an update to another Space diff --git a/PubNubSpace/Sources/PubNubSpace.swift b/PubNubSpace/Sources/PubNubSpace.swift index e7572003..d88107dd 100644 --- a/PubNubSpace/Sources/PubNubSpace.swift +++ b/PubNubSpace/Sources/PubNubSpace.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK /// A concrete representation of a Space entity in PubNub public struct PubNubSpace { diff --git a/PubNubSpace/Sources/PubNubSpaceEvent.swift b/PubNubSpace/Sources/PubNubSpaceEvent.swift index d3b731c9..8e1ca31d 100644 --- a/PubNubSpace/Sources/PubNubSpaceEvent.swift +++ b/PubNubSpace/Sources/PubNubSpaceEvent.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK public enum PubNubSpaceEvent { /// The changeset for the Space entity that changed diff --git a/PubNubSpace/Sources/Space+PubNub.swift b/PubNubSpace/Sources/Space+PubNub.swift index 7eed572e..13669b12 100644 --- a/PubNubSpace/Sources/Space+PubNub.swift +++ b/PubNubSpace/Sources/Space+PubNub.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK /// Protocol interface to manage `PubNubSpace` entities using closures public protocol PubNubSpaceInterface { diff --git a/PubNubSpace/Tests/Integration/SmokeTest+PubNubSpace.swift b/PubNubSpace/Tests/Integration/SmokeTest+PubNubSpace.swift index 3d561ec7..7bd3ebff 100644 --- a/PubNubSpace/Tests/Integration/SmokeTest+PubNubSpace.swift +++ b/PubNubSpace/Tests/Integration/SmokeTest+PubNubSpace.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubSpace - +import PubNubSDK import XCTest +@testable import PubNubSpace + class PubNubSpaceInterfaceITests: XCTestCase { let testSpace = PubNubSpace( id: "TestSpaceId", diff --git a/PubNubSpace/Tests/SpaceTestHelpers.swift b/PubNubSpace/Tests/SpaceTestHelpers.swift index 6052aa96..2cfb8f86 100644 --- a/PubNubSpace/Tests/SpaceTestHelpers.swift +++ b/PubNubSpace/Tests/SpaceTestHelpers.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK struct SpaceCustom: FlatJSONCodable, Hashable { var value: String? diff --git a/PubNubSpace/Tests/Test+PubNubSpace.swift b/PubNubSpace/Tests/Test+PubNubSpace.swift index 49d1dccd..22001903 100644 --- a/PubNubSpace/Tests/Test+PubNubSpace.swift +++ b/PubNubSpace/Tests/Test+PubNubSpace.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubSpace - +import PubNubSDK import XCTest +@testable import PubNubSpace + class PubNubSpaceModelTests: XCTestCase { let testSpace = PubNubSpace( id: "TestSpaceId", diff --git a/PubNubSpace/Tests/Test+PubNubSpaceEvent.swift b/PubNubSpace/Tests/Test+PubNubSpaceEvent.swift index 7d8b03fd..7ef68742 100644 --- a/PubNubSpace/Tests/Test+PubNubSpaceEvent.swift +++ b/PubNubSpace/Tests/Test+PubNubSpaceEvent.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubSpace - +import PubNubSDK import XCTest +@testable import PubNubSpace + class PubNubSpaceEventTests: XCTestCase { let testSpace = PubNubSpace( id: "TestSpaceId", diff --git a/PubNubSpace/Tests/Test+PubNubSpaceInterface.swift b/PubNubSpace/Tests/Test+PubNubSpaceInterface.swift index b4464b07..b007fd1b 100644 --- a/PubNubSpace/Tests/Test+PubNubSpaceInterface.swift +++ b/PubNubSpace/Tests/Test+PubNubSpaceInterface.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubSpace - +import PubNubSDK import XCTest +@testable import PubNubSpace + class PubNubSpaceInterfaceTests: XCTestCase { let testSpace = PubNubSpace( id: "TestSpaceId", diff --git a/PubNubSpace/Tests/Test+PubNubSpacePatcher.swift b/PubNubSpace/Tests/Test+PubNubSpacePatcher.swift index 0660125e..0f0aec46 100644 --- a/PubNubSpace/Tests/Test+PubNubSpacePatcher.swift +++ b/PubNubSpace/Tests/Test+PubNubSpacePatcher.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubSpace - +import PubNubSDK import XCTest +@testable import PubNubSpace + class PubNubSpacePatcherTests: XCTestCase { var testSpace = PubNubSpace( id: "TestSpaceId", diff --git a/PubNubSwift.podspec b/PubNubSwift.podspec index 9b7321d6..4e3391b0 100644 --- a/PubNubSwift.podspec +++ b/PubNubSwift.podspec @@ -1,13 +1,13 @@ Pod::Spec.new do |s| s.name = 'PubNubSwift' - s.version = '7.3.3' + s.version = '8.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.' => 'support@pubnub.com' } s.social_media_url = 'https://twitter.com/pubnub' s.license = { :type => 'PubNub Software Development Kit License', :file => 'LICENSE' } s.source = { :git => 'https://github.com/pubnub/swift.git', :tag => s.version } - s.summary = 'PubNub Swift-based SDK for iOS, macOS, tvOS, & watchOS' + s.summary = 'PubNub Swift-based SDK for iOS, macOS, tvOS, watchOS & visionOS' s.description = <<-DESC The PubNub Real-Time Network. Build real-time apps quickly and scale them globally. @@ -19,9 +19,9 @@ The PubNub Real-Time Network. Build real-time apps quickly and scale them global s.tvos.deployment_target = '12.0' s.watchos.deployment_target = '4.0' - s.swift_version = '5.0' + s.swift_version = '5.9' - s.module_name = 'PubNub' + s.module_name = 'PubNubSDK' s.source_files = 'Sources/**/*.swift' s.resource_bundles = { "PubNubSwift" => ["Sources/PubNub/PrivacyInfo.xcprivacy"]} diff --git a/PubNubUser/Sources/Patcher+PubNubUser.swift b/PubNubUser/Sources/Patcher+PubNubUser.swift index 2c8fa32d..6d2cbbca 100644 --- a/PubNubUser/Sources/Patcher+PubNubUser.swift +++ b/PubNubUser/Sources/Patcher+PubNubUser.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK public extension PubNubUser { /// Object that can be used to apply an update to another User diff --git a/PubNubUser/Sources/PubNubUser.swift b/PubNubUser/Sources/PubNubUser.swift index 40bceade..7054f7a3 100644 --- a/PubNubUser/Sources/PubNubUser.swift +++ b/PubNubUser/Sources/PubNubUser.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK /// A concrete representation of a User entity in PubNub public struct PubNubUser { diff --git a/PubNubUser/Sources/PubNubUserEvent.swift b/PubNubUser/Sources/PubNubUserEvent.swift index 871ce3f1..445b03ec 100644 --- a/PubNubUser/Sources/PubNubUserEvent.swift +++ b/PubNubUser/Sources/PubNubUserEvent.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK /// All the changes that can be received for User entities public enum PubNubUserEvent { diff --git a/PubNubUser/Sources/User+PubNub.swift b/PubNubUser/Sources/User+PubNub.swift index e14f8762..bb6da9ba 100644 --- a/PubNubUser/Sources/User+PubNub.swift +++ b/PubNubUser/Sources/User+PubNub.swift @@ -9,7 +9,7 @@ // import Foundation -import PubNub +import PubNubSDK /// Protocol interface to manage `PubNubUser` entities using closures public protocol PubNubUserInterface { diff --git a/PubNubUser/Tests/Integration/SmokeTest+PubNubUser.swift b/PubNubUser/Tests/Integration/SmokeTest+PubNubUser.swift index c45e0768..cd6dae07 100644 --- a/PubNubUser/Tests/Integration/SmokeTest+PubNubUser.swift +++ b/PubNubUser/Tests/Integration/SmokeTest+PubNubUser.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubUser - +import PubNubSDK import XCTest +@testable import PubNubUser + class PubNubUserInterfaceITests: XCTestCase { let testUser = PubNubUser( id: "TestUserId", diff --git a/PubNubUser/Tests/Test+PubNubUser.swift b/PubNubUser/Tests/Test+PubNubUser.swift index 33fd6dab..340ee4cc 100644 --- a/PubNubUser/Tests/Test+PubNubUser.swift +++ b/PubNubUser/Tests/Test+PubNubUser.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubUser - +import PubNubSDK import XCTest +@testable import PubNubUser + class PubNubUserModelTests: XCTestCase { let testUser = PubNubUser( id: "TestUserId", diff --git a/PubNubUser/Tests/Test+PubNubUserEvent.swift b/PubNubUser/Tests/Test+PubNubUserEvent.swift index 12d33c22..70801260 100644 --- a/PubNubUser/Tests/Test+PubNubUserEvent.swift +++ b/PubNubUser/Tests/Test+PubNubUserEvent.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubUser - +import PubNubSDK import XCTest +@testable import PubNubUser + class PubNubUserEventTests: XCTestCase { let testUser = PubNubUser( id: "TestUserId", diff --git a/PubNubUser/Tests/Test+PubNubUserInterface.swift b/PubNubUser/Tests/Test+PubNubUserInterface.swift index 34857e23..c6161327 100644 --- a/PubNubUser/Tests/Test+PubNubUserInterface.swift +++ b/PubNubUser/Tests/Test+PubNubUserInterface.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubUser - +import PubNubSDK import XCTest +@testable import PubNubUser + class PubNubUserInterfaceTests: XCTestCase { let testUser = PubNubUser( id: "TestUserId", diff --git a/PubNubUser/Tests/Test+PubNubUserPatcher.swift b/PubNubUser/Tests/Test+PubNubUserPatcher.swift index 24081c11..150efe21 100644 --- a/PubNubUser/Tests/Test+PubNubUserPatcher.swift +++ b/PubNubUser/Tests/Test+PubNubUserPatcher.swift @@ -8,11 +8,11 @@ // LICENSE file in the root directory of this source tree. // -import PubNub -@testable import PubNubUser - +import PubNubSDK import XCTest +@testable import PubNubUser + class PubNubUserPatcherTests: XCTestCase { var testUser = PubNubUser( id: "TestUserId", diff --git a/PubNubUser/Tests/UserTestHelpers.swift b/PubNubUser/Tests/UserTestHelpers.swift index e84e81c6..b8d0d931 100644 --- a/PubNubUser/Tests/UserTestHelpers.swift +++ b/PubNubUser/Tests/UserTestHelpers.swift @@ -9,7 +9,7 @@ // import Foundation -import PubNub +import PubNubSDK struct UserCustom: FlatJSONCodable, Hashable { var value: String? diff --git a/README.md b/README.md index 32123bb4..e7ab97cc 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ PubNub takes care of the infrastructure and APIs needed for the realtime communi ## Requirements -* iOS 12.0+ / macOS 10.15+ / Mac Catalyst 13.0+ / tvOS 12.0+ / watchOS 4.0+ +* iOS 12.0+ / macOS 10.15+ / tvOS 12.0+ / watchOS 4.0+ / visionOS 1.0+ * Xcode 15+ * Swift 5+ @@ -42,7 +42,7 @@ You have several options to set up your project. We provide instructions here fo 1. Create or open your project inside of Xcode 1. Navigate to File > Swift Packages > Add Package Dependency 1. Search for PubNub and select the swift package owned by pubnub, and hit the Next button -1. Use the `Up to Next Major Version` rule spanning from `7.0.0` < `8.0.0`, and hit the Next button +1. Use the `Up to Next Major Version` rule spanning from `8.0.0` < `9.0.0`, and hit the Next button For more information see Apple's guide on [Adding Package Dependencies to Your App](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app) @@ -53,7 +53,7 @@ For more information see Apple's guide on [Adding Package Dependencies to Your A use_frameworks! target 'YOUR_TARGET_NAME' do - pod 'PubNubSwift', '~> 7.0' + pod 'PubNubSwift', '~> 8.0' end ``` @@ -72,7 +72,7 @@ Officially supported: Carthage 0.39.1 and up. Add the following to `Cartfile`: ```ruby -github "pubnub/swift" ~> 7.0 +github "pubnub/swift" ~> 8.0 ``` Then in the directory containing your `Cartfile`, execute the following: @@ -87,7 +87,7 @@ carthage update --use-xcframeworks ```swift import UIKit - import PubNub // <- Here is our PubNub module import. + import PubNubSDK // <- Here is our PubNub module import. ``` 1. Create and configure a PubNub object: @@ -150,7 +150,7 @@ pubnub.subscribe(to: ["my_channel"]) * [Build your first realtime Swift app with PubNub](https://www.pubnub.com/docs/platform/quickstarts/swift) * [API reference for Swift](https://www.pubnub.com/docs/swift-native/pubnub-swift-sdk) -* [PubNub Swift SDK 3.x Migration Guide](https://github.com/pubnub/swift/blob/master/Documentation/PubNub_3_0_Migration_Guide.md) +* [PubNub Swift SDK 8.x Migration Guide](https://github.com/pubnub/swift/blob/master/Documentation/PubNub_8_0_Migration_Guide.md) ## Support diff --git a/Sources/PubNub/APIs/Objects+PubNub.swift b/Sources/PubNub/APIs/Objects+PubNub.swift index c336e058..78a3cf02 100644 --- a/Sources/PubNub/APIs/Objects+PubNub.swift +++ b/Sources/PubNub/APIs/Objects+PubNub.swift @@ -58,6 +58,12 @@ public extension PubNub { public var customFields: Bool /// The `PubNubChannelMetadata` instance of the Membership public var channelFields: Bool + /// The `status` field of the `Membership` object + public var statusField: Bool + /// The `type` field of the `PubNubChannelMetadata` instance in Membership + public var channelTypeField: Bool + /// The `status` field of the `PubNubChannelMetadata` instance in Membership + public var channelStatusField: Bool /// The `custom` dictionary of the `PubNubChannelMetadata` for the Membership object public var channelCustomFields: Bool /// The `totalCount` of how many Objects are available @@ -67,17 +73,26 @@ public extension PubNub { /// - Parameters: /// - customFields: The `custom` dictionary for the Object /// - channelFields: The `PubNubChannelMetadata` instance of the Membership + /// - statusField: The `status` field of the Membership /// - channelCustomFields: The `custom` dictionary of the `PubNubChannelMetadata` for the Membership object + /// - channelTypeField: The `type` field of the `PubNubChannelMetadata` for the Membership object + /// - channelStatusField: The `status` field of the `PubNubChannelMetadata` for the Membership object /// - totalCount: The `totalCount` of how many Objects are available public init( customFields: Bool = true, channelFields: Bool = false, + statusField: Bool = false, channelCustomFields: Bool = false, + channelTypeField: Bool = false, + channelStatusField: Bool = false, totalCount: Bool = false ) { self.customFields = customFields self.channelFields = channelFields + self.statusField = statusField self.channelCustomFields = channelCustomFields + self.channelTypeField = channelTypeField + self.channelStatusField = channelStatusField self.totalCount = totalCount } @@ -86,7 +101,10 @@ public extension PubNub { if customFields { includes.append(.custom) } if channelFields { includes.append(.channel) } + if statusField { includes.append(.status) } if channelCustomFields { includes.append(.channelCustom) } + if channelTypeField { includes.append(.channelType) } + if channelStatusField { includes.append(.channelStatus) } return includes.isEmpty ? nil : includes } @@ -97,6 +115,12 @@ public extension PubNub { public var customFields: Bool /// The `PubNubUUIDMetadata` instance of the Membership public var uuidFields: Bool + /// The `status` field of the `Membership` object + public var statusField: Bool + /// The `type` field of the `PubNubUUIDMetadata` instance in Membership + public var uuidTypeField: Bool + /// The `status` field of the `PubNubUUIDMetadata` instance in Membership + public var uuidStatusField: Bool /// The `custom` dictionary of the `PubNubUUIDMetadata` for the Membership object public var uuidCustomFields: Bool /// The `totalCount` of how many Objects are available @@ -106,17 +130,26 @@ public extension PubNub { /// - Parameters: /// - customFields: The `custom` dictionary for the Object /// - uuidFields: The `PubNubUUIDMetadata` instance of the Membership + /// - statusField: The `status` field of the Membership /// - uuidCustomFields: The `custom` dictionary of the `PubNubUUIDMetadata` for the Membership object + /// - uuidTypeField: The `type` field of the `PubNubUUIDMetadata` for the Membership object + /// - uuidStatusField: The `status` field of the `PubNubUUIDMetadata` for the Membership object /// - totalCount: The `totalCount` of how many Objects are available public init( customFields: Bool = true, uuidFields: Bool = false, + statusField: Bool = false, uuidCustomFields: Bool = false, + uuidTypeField: Bool = false, + uuidStatusField: Bool = false, totalCount: Bool = false ) { self.customFields = customFields self.uuidFields = uuidFields + self.statusField = statusField self.uuidCustomFields = uuidCustomFields + self.uuidTypeField = uuidTypeField + self.uuidStatusField = uuidStatusField self.totalCount = totalCount } @@ -125,7 +158,10 @@ public extension PubNub { if customFields { includes.append(.custom) } if uuidFields { includes.append(.uuid) } + if statusField { includes.append(.status) } if uuidCustomFields { includes.append(.uuidCustom) } + if uuidTypeField { includes.append(.uuidType) } + if uuidStatusField { includes.append(.uuidStatus) } return includes.isEmpty ? nil : includes } diff --git a/Sources/PubNub/DependencyContainer/DependencyContainer.swift b/Sources/PubNub/DependencyContainer/DependencyContainer.swift index 53566bf1..edb30e41 100644 --- a/Sources/PubNub/DependencyContainer/DependencyContainer.swift +++ b/Sources/PubNub/DependencyContainer/DependencyContainer.swift @@ -19,6 +19,17 @@ protocol DependencyKey { // 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 + + // Called when a value is resolved in the dependency container. + // + // This method is invoked when a specific `Value` type is resolved within the + // dependency container. It can be used for custom logic or configuration + // after the dependency has been resolved. + static func onValueResolved(value: Value, in container: DependencyContainer) +} + +extension DependencyKey { + static func onValueResolved(value: Value, in container: DependencyContainer) {} } // The class that serves as a registry for dependencies. Each dependency is associated with a unique key @@ -60,6 +71,7 @@ class DependencyContainer { } if underlyingKey.scope == .transient { if let value = underlyingKey.key.value(from: self) as? K.Value { + key.onValueResolved(value: value, in: self) return value } else { preconditionFailure("Cannot create value for key \(key)") @@ -72,9 +84,11 @@ class DependencyContainer { } if let value = underlyingKey.key.value(from: self) as? K.Value { if Mirror(reflecting: value).displayStyle == .class && underlyingKey.scope == .weak { - resolvedValues[ObjectIdentifier(key)] = WeakWrapper(value as AnyObject) + self.resolvedValues[ObjectIdentifier(key)] = WeakWrapper(value as AnyObject) + key.onValueResolved(value: value, in: self) } else { - resolvedValues[ObjectIdentifier(key)] = ValueWrapper(value) + self.resolvedValues[ObjectIdentifier(key)] = ValueWrapper(value) + key.onValueResolved(value: value, in: self) } return value } @@ -127,24 +141,15 @@ extension DependencyContainer { } var defaultHTTPSession: SessionReplaceable { - resolveSession( - session: self[DefaultHTTPSessionDependencyKey.self], - with: [automaticRetry].compactMap { $0 } - ) + self[DefaultHTTPSessionDependencyKey.self] } - fileprivate var httpSubscribeSession: SessionReplaceable { - resolveSession( - session: self[HTTPSubscribeSessionDependencyKey.self], - with: [instanceIDOperator].compactMap { $0 } - ) + var httpSubscribeSession: SessionReplaceable { + self[HTTPSubscribeSessionDependencyKey.self] } - fileprivate var httpPresenceSession: SessionReplaceable { - resolveSession( - session: self[HTTPPresenceSessionDependencyKey.self], - with: [instanceIDOperator].compactMap { $0 } - ) + var httpPresenceSession: SessionReplaceable { + self[HTTPPresenceSessionDependencyKey.self] } fileprivate var automaticRetry: RequestOperator? { @@ -168,16 +173,6 @@ extension DependencyContainer { } } -private extension DependencyContainer { - func resolveSession(session: SessionReplaceable, with operators: [RequestOperator]) -> SessionReplaceable { - session.defaultRequestOperator == nil ? session.usingDefault(requestOperator: MultiplexRequestOperator( - operators: operators - )) : session.usingDefault(requestOperator: session.defaultRequestOperator?.merge( - operators: operators - )) - } -} - // - MARK: PubNubConfiguration struct PubNubConfigurationDependencyKey: DependencyKey { @@ -194,10 +189,25 @@ struct PubNubInstanceIDDependencyKey: DependencyKey { // MARK: - HTTPSessions +extension DependencyKey where Value == SessionReplaceable { + @discardableResult + static func updateSession(session: SessionReplaceable, with operators: [RequestOperator]) -> SessionReplaceable { + session.defaultRequestOperator == nil ? session.usingDefault(requestOperator: MultiplexRequestOperator( + operators: operators + )) : session.usingDefault(requestOperator: session.defaultRequestOperator?.merge( + operators: operators + )) + } +} + struct DefaultHTTPSessionDependencyKey: DependencyKey { static func value(from container: DependencyContainer) -> SessionReplaceable { HTTPSession(configuration: .pubnub) } + + static func onValueResolved(value: SessionReplaceable, in container: DependencyContainer) { + updateSession(session: value, with: [container.automaticRetry].compactMap { $0 }) + } } struct HTTPSubscribeSessionDependencyKey: DependencyKey { @@ -208,6 +218,10 @@ struct HTTPSubscribeSessionDependencyKey: DependencyKey { sessionStream: SessionListener(queue: container.httpSubscribeSessionQueue) ) } + + static func onValueResolved(value: SessionReplaceable, in container: DependencyContainer) { + updateSession(session: value, with: [container.instanceIDOperator].compactMap { $0 }) + } } struct HTTPSubscribeSessionQueueDependencyKey: DependencyKey { @@ -217,13 +231,16 @@ struct HTTPSubscribeSessionQueueDependencyKey: DependencyKey { } struct HTTPPresenceSessionDependencyKey: DependencyKey { - static func value(from container: DependencyContainer) -> HTTPSession { + static func value(from container: DependencyContainer) -> SessionReplaceable { HTTPSession( configuration: .pubnub, sessionQueue: container.httpSubscribeSessionQueue, sessionStream: SessionListener(queue: container.httpSubscribeSessionQueue) ) } + static func onValueResolved(value: SessionReplaceable, in container: DependencyContainer) { + updateSession(session: value, with: [container.instanceIDOperator].compactMap { $0 }) + } } // MARK: - FileURLSession @@ -310,6 +327,7 @@ struct SubscriptionSessionDependencyKey: DependencyKey { // Provides a standard interface for objects that wrap or encapsulate other objects in a dependency container context. protocol Wrappable { associatedtype T + var value: T? { get } } diff --git a/Sources/PubNub/EventEngine/Subscribe/Effects/EmitMessagesEffect.swift b/Sources/PubNub/EventEngine/Subscribe/Effects/EmitMessagesEffect.swift index a64a1faa..88ce36fd 100644 --- a/Sources/PubNub/EventEngine/Subscribe/Effects/EmitMessagesEffect.swift +++ b/Sources/PubNub/EventEngine/Subscribe/Effects/EmitMessagesEffect.swift @@ -37,13 +37,13 @@ class MessageCache { struct EmitMessagesEffect: EffectHandler { let messages: [SubscribeMessagePayload] let cursor: SubscribeCursor - let listeners: [BaseSubscriptionListener] + let subscriptions: WeakSet let messageCache: MessageCache func performTask(completionBlock: @escaping ([Subscribe.Event]) -> Void) { // Attempt to detect missed messages due to queue overflow if messages.count >= 100 { - listeners.forEach { + subscriptions.forEach { $0.emit(subscribe: .errorReceived( PubNubError( .messageCountExceededMaximum, @@ -67,7 +67,7 @@ struct EmitMessagesEffect: EffectHandler { return false } - listeners.forEach { + subscriptions.forEach { $0.emit(batch: filteredMessages) } diff --git a/Sources/PubNub/EventEngine/Subscribe/Effects/EmitStatusEffect.swift b/Sources/PubNub/EventEngine/Subscribe/Effects/EmitStatusEffect.swift index bcb2df5d..da0d113d 100644 --- a/Sources/PubNub/EventEngine/Subscribe/Effects/EmitStatusEffect.swift +++ b/Sources/PubNub/EventEngine/Subscribe/Effects/EmitStatusEffect.swift @@ -11,15 +11,15 @@ import Foundation struct EmitStatusEffect: EffectHandler { let statusChange: Subscribe.ConnectionStatusChange - let listeners: [BaseSubscriptionListener] + let subscriptions: WeakSet func performTask(completionBlock: @escaping ([Subscribe.Event]) -> Void) { if let error = statusChange.error { - listeners.forEach { + subscriptions.forEach { $0.emit(subscribe: .errorReceived(error)) } } - listeners.forEach { + subscriptions.forEach { $0.emit(subscribe: .connectionChanged(statusChange.newStatus)) } completionBlock([]) diff --git a/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift b/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift index 76e07b80..ca0a7d9c 100644 --- a/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift +++ b/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift @@ -43,7 +43,7 @@ class SubscribeEffectFactory: EffectHandlerFactory { timetoken: 0, session: session, sessionResponseQueue: sessionResponseQueue - ), listeners: dependencies.value.listeners + ), listeners: dependencies.value.subscriptions ) case let .receiveMessages(channels, groups, cursor): return ReceivingEffect( @@ -56,19 +56,19 @@ class SubscribeEffectFactory: EffectHandlerFactory { region: cursor.region, session: session, sessionResponseQueue: sessionResponseQueue - ), listeners: dependencies.value.listeners + ), listeners: dependencies.value.subscriptions ) case let .emitMessages(messages, cursor): return EmitMessagesEffect( messages: messages, cursor: cursor, - listeners: dependencies.value.listeners, + subscriptions: dependencies.value.subscriptions, messageCache: messageCache ) case let .emitStatus(statusChange): return EmitStatusEffect( statusChange: statusChange, - listeners: dependencies.value.listeners + subscriptions: dependencies.value.subscriptions ) } } diff --git a/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffects.swift b/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffects.swift index 02035957..109b0935 100644 --- a/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffects.swift +++ b/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffects.swift @@ -15,7 +15,7 @@ import Foundation class HandshakeEffect: EffectHandler { private let subscribeEffect: SubscribeEffect - init(request: SubscribeRequest, listeners: [BaseSubscriptionListener]) { + init(request: SubscribeRequest, listeners: WeakSet) { self.subscribeEffect = SubscribeEffect( request: request, listeners: listeners, @@ -25,7 +25,6 @@ class HandshakeEffect: EffectHandler { } func performTask(completionBlock: @escaping ([Subscribe.Event]) -> Void) { - subscribeEffect.listeners.forEach { $0.emit(subscribe: .connectionChanged(.connecting)) } subscribeEffect.performTask(completionBlock: completionBlock) } @@ -43,7 +42,7 @@ class HandshakeEffect: EffectHandler { class ReceivingEffect: EffectHandler { private let subscribeEffect: SubscribeEffect - init(request: SubscribeRequest, listeners: [BaseSubscriptionListener]) { + init(request: SubscribeRequest, listeners: WeakSet) { self.subscribeEffect = SubscribeEffect( request: request, listeners: listeners, @@ -69,18 +68,18 @@ class ReceivingEffect: EffectHandler { private class SubscribeEffect: EffectHandler { let request: SubscribeRequest - let listeners: [BaseSubscriptionListener] + let subscriptions: WeakSet let onResponseReceived: (SubscribeResponse) -> Subscribe.Event let onErrorReceived: (PubNubError) -> Subscribe.Event init( request: SubscribeRequest, - listeners: [BaseSubscriptionListener], + listeners: WeakSet, onResponseReceived: @escaping ((SubscribeResponse) -> Subscribe.Event), onErrorReceived: @escaping ((PubNubError) -> Subscribe.Event) ) { self.request = request - self.listeners = listeners + self.subscriptions = listeners self.onResponseReceived = onResponseReceived self.onErrorReceived = onErrorReceived } @@ -90,7 +89,7 @@ private class SubscribeEffect: EffectHandler { guard let selfRef = self else { return } switch $0 { case .success(let response): - selfRef.listeners.forEach { + selfRef.subscriptions.forEach { $0.emit(subscribe: .responseReceived( SubscribeResponseHeader( channels: selfRef.request.channels.map { PubNubChannel(channel: $0) }, diff --git a/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift b/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift index e3063dd5..18b32545 100644 --- a/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift +++ b/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift @@ -35,7 +35,7 @@ extension Subscribe { struct HandshakingState: SubscribeState { let input: SubscribeInput let cursor: SubscribeCursor - let connectionStatus = ConnectionStatus.connecting + let connectionStatus = ConnectionStatus.disconnected } struct HandshakeStoppedState: SubscribeState { @@ -61,7 +61,7 @@ extension Subscribe { struct ReceivingState: SubscribeState { let input: SubscribeInput let cursor: SubscribeCursor - let connectionStatus = ConnectionStatus.connected + let connectionStatus: ConnectionStatus } struct ReceiveStoppedState: SubscribeState { @@ -119,11 +119,14 @@ extension Subscribe { extension Subscribe { struct Dependencies { let configuration: PubNubConfiguration - let listeners: [BaseSubscriptionListener] + let subscriptions: WeakSet - init(configuration: PubNubConfiguration, listeners: [BaseSubscriptionListener] = []) { + init( + configuration: PubNubConfiguration, + listeners: WeakSet = WeakSet([]) + ) { self.configuration = configuration - self.listeners = listeners + self.subscriptions = listeners } } } diff --git a/Sources/PubNub/EventEngine/Subscribe/SubscribeTransition.swift b/Sources/PubNub/EventEngine/Subscribe/SubscribeTransition.swift index ef076916..e57e5726 100644 --- a/Sources/PubNub/EventEngine/Subscribe/SubscribeTransition.swift +++ b/Sources/PubNub/EventEngine/Subscribe/SubscribeTransition.swift @@ -30,7 +30,7 @@ class SubscribeTransition: TransitionProtocol { case .subscriptionRestored: return true case .unsubscribeAll: - return true + return !(state is Subscribe.UnsubscribedState) case .disconnect: return !( state is Subscribe.HandshakeStoppedState || state is Subscribe.ReceiveStoppedState || @@ -140,25 +140,64 @@ fileprivate extension SubscribeTransition { if newInput.isEmpty { return setUnsubscribedState(from: state) - } else { - switch state { - case is Subscribe.HandshakingState: - return TransitionResult(state: Subscribe.HandshakingState(input: newInput, cursor: cursor)) - case is Subscribe.HandshakeStoppedState: - return TransitionResult(state: Subscribe.HandshakeStoppedState(input: newInput, cursor: cursor)) - case is Subscribe.HandshakeFailedState: - return TransitionResult(state: Subscribe.HandshakingState(input: newInput, cursor: cursor)) - case is Subscribe.ReceivingState: - return TransitionResult(state: Subscribe.ReceivingState(input: newInput, cursor: cursor)) - case is Subscribe.ReceiveStoppedState: - return TransitionResult(state: Subscribe.ReceiveStoppedState(input: newInput, cursor: cursor)) - case is Subscribe.ReceiveFailedState: - return TransitionResult(state: Subscribe.HandshakingState(input: newInput, cursor: cursor)) - case is Subscribe.UnsubscribedState: - return TransitionResult(state: Subscribe.HandshakingState(input: newInput, cursor: cursor)) - default: - return TransitionResult(state: state) - } + } + + let invocations: [EffectInvocation] = state is Subscribe.ReceivingState ? [ + .regular(.emitStatus(change: Subscribe.ConnectionStatusChange( + oldStatus: state.connectionStatus, + newStatus: .subscriptionChanged( + channels: newInput.subscribedChannelNames, + groups: newInput.subscribedGroupNames + ), + error: nil + ))) + ] : [] + + switch state { + case is Subscribe.HandshakingState: + return TransitionResult( + state: Subscribe.HandshakingState(input: newInput, cursor: cursor), + invocations: invocations + ) + case is Subscribe.HandshakeStoppedState: + return TransitionResult( + state: Subscribe.HandshakeStoppedState(input: newInput, cursor: cursor), + invocations: invocations + ) + case is Subscribe.HandshakeFailedState: + return TransitionResult( + state: Subscribe.HandshakingState(input: newInput, cursor: cursor), + invocations: invocations + ) + case is Subscribe.ReceivingState: + let newStatus: ConnectionStatus = .subscriptionChanged( + channels: newInput.subscribedChannelNames, + groups: newInput.subscribedGroupNames + ) + return TransitionResult( + state: Subscribe.ReceivingState(input: newInput, cursor: cursor, connectionStatus: newStatus), + invocations: invocations + ) + case is Subscribe.ReceiveStoppedState: + return TransitionResult( + state: Subscribe.ReceiveStoppedState(input: newInput, cursor: cursor), + invocations: invocations + ) + case is Subscribe.ReceiveFailedState: + return TransitionResult( + state: Subscribe.HandshakingState(input: newInput, cursor: cursor), + invocations: invocations + ) + case is Subscribe.UnsubscribedState: + return TransitionResult( + state: Subscribe.HandshakingState(input: newInput, cursor: cursor), + invocations: invocations + ) + default: + return TransitionResult( + state: state, + invocations: invocations + ) } } } @@ -202,27 +241,30 @@ fileprivate extension SubscribeTransition { messages: [SubscribeMessagePayload] = [] ) -> TransitionResult { let emitMessagesInvocation = EffectInvocation.managed( - Subscribe.Invocation.emitMessages(events: messages, forCursor: cursor) - ) - let emitStatusInvocation = EffectInvocation.regular( - Subscribe.Invocation.emitStatus(change: Subscribe.ConnectionStatusChange( - oldStatus: state.connectionStatus, - newStatus: .connected, - error: nil - )) + Subscribe.Invocation.emitMessages( + events: messages, + forCursor: cursor + ) ) if state is Subscribe.HandshakingState { - return TransitionResult( - state: Subscribe.ReceivingState(input: state.input, cursor: cursor), - invocations: [messages.isEmpty ? nil : emitMessagesInvocation, emitStatusInvocation].compactMap { $0 } + let emitStatusInvocation = EffectInvocation.regular( + Subscribe.Invocation.emitStatus(change: Subscribe.ConnectionStatusChange( + oldStatus: state.connectionStatus, + newStatus: .connected, + error: nil + )) ) - } else { return TransitionResult( - state: Subscribe.ReceivingState(input: state.input, cursor: cursor), - invocations: [messages.isEmpty ? nil : emitMessagesInvocation].compactMap { $0 } + state: Subscribe.ReceivingState(input: state.input, cursor: cursor, connectionStatus: .connected), + invocations: [messages.isEmpty ? nil : emitMessagesInvocation, emitStatusInvocation].compactMap { $0 } ) } + + return TransitionResult( + state: Subscribe.ReceivingState(input: state.input, cursor: cursor, connectionStatus: state.connectionStatus), + invocations: [messages.isEmpty ? nil : emitMessagesInvocation].compactMap { $0 } + ) } } diff --git a/Sources/PubNub/Events/New/Entities/EntityCreator.swift b/Sources/PubNub/Events/New/Entities/EntityCreator.swift index ed05acd6..4224c19e 100644 --- a/Sources/PubNub/Events/New/Entities/EntityCreator.swift +++ b/Sources/PubNub/Events/New/Entities/EntityCreator.swift @@ -12,7 +12,7 @@ import Foundation /// Protocol for types capable of creating references for entities to which the user can subscribe, /// receiving real-time updates. -public protocol EntityCreator { +public protocol EntityCreator: AnyObject { /// Creates a new channel entity the user can subscribe to. /// /// This method does not create any entity, either locally or remotely; it merely provides @@ -73,22 +73,3 @@ public extension EntityCreator { ) } } - -// 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 - ) -} diff --git a/Sources/PubNub/Events/New/Entities/EntitySubscribable.swift b/Sources/PubNub/Events/New/Entities/EntitySubscribable.swift index 0367ae04..aedff181 100644 --- a/Sources/PubNub/Events/New/Entities/EntitySubscribable.swift +++ b/Sources/PubNub/Events/New/Entities/EntitySubscribable.swift @@ -14,8 +14,8 @@ import Foundation /// 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) + init(name: String, pubnub: PubNub) { + super.init(name: name, subscriptionType: .channel, pubnub: pubnub) } } @@ -23,8 +23,8 @@ public class ChannelRepresentation: Subscribable { /// 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) + init(name: String, pubnub: PubNub) { + super.init(name: name, subscriptionType: .channelGroup, pubnub: pubnub) } } @@ -32,8 +32,8 @@ public class ChannelGroupRepresentation: Subscribable { /// 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) + init(id: String, pubnub: PubNub) { + super.init(name: id, subscriptionType: .channel, pubnub: pubnub) } } @@ -41,7 +41,7 @@ public class UserMetadataRepresentation: Subscribable { /// 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) + init(id: String, pubnub: PubNub) { + super.init(name: id, subscriptionType: .channel, pubnub: pubnub) } } diff --git a/Sources/PubNub/Events/New/EventEmitter.swift b/Sources/PubNub/Events/New/EventListenerInterface.swift similarity index 61% rename from Sources/PubNub/Events/New/EventEmitter.swift rename to Sources/PubNub/Events/New/EventListenerInterface.swift index f8ce4534..fe9f61d7 100644 --- a/Sources/PubNub/Events/New/EventEmitter.swift +++ b/Sources/PubNub/Events/New/EventListenerInterface.swift @@ -13,17 +13,38 @@ import Foundation // MARK: - StatusEmitter /// A protocol for types that emit PubNub status events from the Subscribe loop. -public protocol StatusEmitter: AnyObject { +public protocol StatusListenerInterface: AnyObject { + /// An underlying queue to dispatch events + var queue: DispatchQueue { get } + /// A unique emitter's identifier + var uuid: UUID { get } /// A closure to be called when the connection status changes. var onConnectionStateChange: ((ConnectionStatus) -> Void)? { get set } } -// MARK: - EventEmitter +/// Defines additional status listener that can be attached to `Subscription` or `SubscriptionSet` +public class StatusListener: StatusListenerInterface { + public let uuid: UUID + public let queue: DispatchQueue + public var onConnectionStateChange: ((ConnectionStatus) -> Void)? + + public init( + uuid: UUID = UUID(), + queue: DispatchQueue = .main, + onConnectionStateChange: @escaping ((ConnectionStatus) -> Void) + ) { + self.uuid = uuid + self.queue = queue + self.onConnectionStateChange = onConnectionStateChange + } +} -/// A protocol for types that emit PubNub events. +// MARK: - EventListenerInterface + +/// A protocol for types that emit PubNub events from the Subscribe loop. /// /// Utilize closures to receive notifications when specific types of PubNub events occur. -public protocol EventEmitter: AnyObject { +public protocol EventListenerInterface: AnyObject { /// An underlying queue to dispatch events var queue: DispatchQueue { get } /// A unique emitter's identifier @@ -46,15 +67,45 @@ public protocol EventEmitter: AnyObject { var onAppContext: ((PubNubAppContextEvent) -> Void)? { get set } } -/// A protocol representing a type that can be utilized to dispose of a conforming object. -public protocol SubscriptionDisposable { - /// Determines whether current emitter is disposed - var isDisposed: Bool { get } - /// Stops listening to incoming events and disposes current emitter - func dispose() +/// Defines additional event listener that can be attached to `Subscription` or `SubscriptionSet` +public class EventListener: EventListenerInterface { + public let queue: DispatchQueue + public let uuid: UUID + public var onEvent: ((PubNubEvent) -> Void)? + public var onEvents: (([PubNubEvent]) -> Void)? + public var onMessage: ((PubNubMessage) -> Void)? + public var onSignal: ((PubNubMessage) -> Void)? + public var onPresence: ((PubNubPresenceChange) -> Void)? + public var onMessageAction: ((PubNubMessageActionEvent) -> Void)? + public var onFileEvent: ((PubNubFileChangeEvent) -> Void)? + public var onAppContext: ((PubNubAppContextEvent) -> Void)? + + public init( + queue: DispatchQueue = .main, + uuid: UUID = UUID(), + onEvent: ((PubNubEvent) -> Void)? = nil, + onEvents: (([PubNubEvent]) -> Void)? = nil, + onMessage: ((PubNubMessage) -> Void)? = nil, + onSignal: ((PubNubMessage) -> Void)? = nil, + onPresence: ((PubNubPresenceChange) -> Void)? = nil, + onMessageAction: ((PubNubMessageActionEvent) -> Void)? = nil, + onFileEvent: ((PubNubFileChangeEvent) -> Void)? = nil, + onAppContext: ((PubNubAppContextEvent) -> Void)? = nil + ) { + self.queue = queue + self.uuid = uuid + self.onEvent = onEvent + self.onEvents = onEvents + self.onMessage = onMessage + self.onSignal = onSignal + self.onPresence = onPresence + self.onMessageAction = onMessageAction + self.onFileEvent = onFileEvent + self.onAppContext = onAppContext + } } -extension EventEmitter { +extension EventListenerInterface { func emit(events: [PubNubEvent]) { queue.async { [weak self] in if !events.isEmpty { @@ -84,7 +135,7 @@ extension EventEmitter { } } -extension EventEmitter { +extension EventListenerInterface { func clearCallbacks() { onEvent = nil onEvents = nil @@ -97,6 +148,21 @@ extension EventEmitter { } } +/// Provides functionalities to add and remove additional listeners. +public protocol EventListenerHandler { + func addEventListener(_ listener: EventListener) + func removeEventListener(_ listener: EventListener) + func removeAllListeners() +} + +/// A protocol representing a type that can be utilized to dispose of a conforming object. +public protocol SubscriptionDisposable { + /// Determines whether current emitter is disposed + var isDisposed: Bool { get } + /// Stops listening to incoming events and disposes current emitter + func dispose() +} + // `SubscribeMessagesReceiver` is an internal protocol defining a receiver for subscription messages. // Types that conform to this protocol are responsible for handling and processing these payloads // into concrete events for the user. diff --git a/Sources/PubNub/Events/New/Subscribable.swift b/Sources/PubNub/Events/New/Subscribable.swift index d7d49887..f18e652e 100644 --- a/Sources/PubNub/Events/New/Subscribable.swift +++ b/Sources/PubNub/Events/New/Subscribable.swift @@ -35,14 +35,14 @@ public class Subscribable: Subscriber { /// An entity name public let name: String /// The PubNub client associated with this channel. - weak var receiver: SubscribeReceiver? + weak var pubnub: PubNub? /// An underlying subscription type let subscriptionType: SubscribableType - init(name: String, subscriptionType: SubscribableType, receiver: SubscribeReceiver) { + init(name: String, subscriptionType: SubscribableType, pubnub: PubNub) { self.name = name self.subscriptionType = subscriptionType - self.receiver = receiver + self.pubnub = pubnub } } @@ -87,9 +87,9 @@ public extension Subscriber where Self: Subscribable { /// A typealias representing an interface for PubNub subscriptions. /// -/// This alias combines the conformance of `EventEmitter` and `SubscribeCapable`. +/// This alias combines the conformance of `EventListenerInterface` and `SubscribeCapable`. /// Thus, objects conforming to this type can both emit PubNub events and perform subscription-related actions. -public typealias SubscriptionInterface = EventEmitter & SubscriptionDisposable & SubscribeCapable +public typealias SubscriptionInterface = EventListenerInterface & SubscriptionDisposable & SubscribeCapable /// A class representing subscription options for PubNub subscriptions. /// diff --git a/Sources/PubNub/Events/New/Subscription.swift b/Sources/PubNub/Events/New/Subscription.swift index 8dd34ae5..2dde94ce 100644 --- a/Sources/PubNub/Events/New/Subscription.swift +++ b/Sources/PubNub/Events/New/Subscription.swift @@ -13,8 +13,9 @@ import Foundation /// A final class representing a PubNub subscription. /// /// Use this class to create and manage subscriptions for a specific `Subscribable` entity. -/// It conforms to `EventEmitter`, allowing the handling of subscription-related events. -public final class Subscription: EventEmitter, SubscriptionDisposable { +/// Utilize closures inherited from `EventListenerInterface` for the handling of subscription-related events. +/// You can also create an additional `EventListener` and register it by calling `addEventListener(_:)`. +public final class Subscription: EventListenerInterface, SubscriptionDisposable, EventListenerHandler { /// Initializes a `Subscription` object. /// /// - Parameters: @@ -42,6 +43,8 @@ public final class Subscription: EventEmitter, SubscriptionDisposable { public private(set) var isDisposed = false // Stores the timetoken the user subscribed with private(set) var timetoken: Timetoken? + // Stores additional listeners + private let listenersContainer: SubscriptionListenersContainer = .init() public var onEvent: ((PubNubEvent) -> Void)? public var onEvents: (([PubNubEvent]) -> Void)? @@ -59,8 +62,8 @@ public final class Subscription: EventEmitter, SubscriptionDisposable { queue: queue ) - internal var receiver: SubscribeReceiver? { - entity.receiver + internal var pubnub: PubNub? { + entity.pubnub } internal var subscriptionType: SubscribableType { @@ -91,8 +94,8 @@ public final class Subscription: EventEmitter, SubscriptionDisposable { entity: entity, options: options ) - if receiver?.hasRegisteredAdapter(with: uuid) ?? false { - receiver?.registerAdapter(clonedSubscription.adapter) + if pubnub?.hasRegisteredAdapter(with: uuid) ?? false { + pubnub?.registerAdapter(clonedSubscription.adapter) } return clonedSubscription } @@ -104,9 +107,25 @@ public final class Subscription: EventEmitter, SubscriptionDisposable { public func dispose() { clearCallbacks() unsubscribe() + removeAllListeners() isDisposed = true } + /// Adds additional subscription listener + public func addEventListener(_ listener: EventListener) { + listenersContainer.storeEventListener(listener) + } + + /// Removes subscription listener + public func removeEventListener(_ listener: EventListener) { + listenersContainer.removeEventListener(listener) + } + + /// Removes all event listeners + public func removeAllListeners() { + listenersContainer.removeAllEventListeners() + } + deinit { dispose() } @@ -117,14 +136,13 @@ extension Subscription: SubscribeCapable { /// /// - Parameter timetoken: The timetoken to use for subscribing. If `nil`, the `0` value is used. public func subscribe(with timetoken: Timetoken?) { - guard let receiver = receiver, !isDisposed else { + guard let pubnub = pubnub, !isDisposed else { return } let channels = subscriptionType == .channel ? [self] : [] let channelGroups = subscriptionType == .channelGroup ? [self] : [] - receiver.registerAdapter(adapter) - receiver.internalSubscribe(with: channels, and: channelGroups, at: timetoken) + pubnub.internalSubscribe(with: channels, and: channelGroups, at: timetoken) } /// Unsubscribes from the associated entity, ending the PubNub subscription. @@ -134,13 +152,13 @@ extension Subscription: SubscribeCapable { /// and the entity will be deregistered from the Subscribe loop. After unsubscribing, the subscription interface /// can be restarted if needed. public func unsubscribe() { - guard let receiver = receiver, !isDisposed else { + guard let pubnub = pubnub, !isDisposed else { return } let channels = subscriptionType == .channel ? [self] : [] let groups = subscriptionType == .channelGroup ? [self] : [] - receiver.internalUnsubscribe(from: channels, and: groups, presenceOnly: false) + pubnub.internalUnsubscribe(from: channels, and: groups, presenceOnly: false) } } @@ -154,7 +172,7 @@ extension Subscription: Hashable { } } -// MARK: - SubscribeMessagePayloadReceiver +// MARK: - SubscribeMessagesReceiver extension Subscription: SubscribeMessagesReceiver { var subscriptionTopology: [SubscribableType: [String]] { @@ -163,7 +181,11 @@ extension Subscription: SubscribeMessagesReceiver { @discardableResult func onPayloadsReceived(payloads: [SubscribeMessagePayload]) -> [PubNubEvent] { let events = payloads.compactMap { event(from: $0) } + // Emit events to the current Subscription's closures emit(events: events) + // Emits events to the underlying attached listeners + listenersContainer.eventListeners.forEach { $0.emit(events: events) } + // Returns events that were emitted return events } diff --git a/Sources/PubNub/Events/New/SubscriptionListenersContainer.swift b/Sources/PubNub/Events/New/SubscriptionListenersContainer.swift new file mode 100644 index 00000000..e3618fd9 --- /dev/null +++ b/Sources/PubNub/Events/New/SubscriptionListenersContainer.swift @@ -0,0 +1,48 @@ +// +// WeakStatusListenerBox.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 + +class SubscriptionListenersContainer { + private let eventListenersCache: Atomic<[UUID: EventListener]> = Atomic([:]) + private let statusListenersCache: Atomic<[UUID: StatusListener]> = Atomic([:]) + + var eventListeners: [EventListener] { + eventListenersCache.lockedRead { $0.values.compactMap { $0 } } + } + + var statusListeners: [StatusListener] { + statusListenersCache.lockedRead { $0.values.compactMap { $0 } } + } + + func storeEventListener(_ eventListener: EventListener) { + eventListenersCache.lockedWrite { $0[eventListener.uuid] = eventListener } + } + + func storeStatusListener(_ statusListener: StatusListener) { + statusListenersCache.lockedWrite { $0[statusListener.uuid] = statusListener } + } + + func removeEventListener(_ listener: EventListener) { + eventListenersCache.lockedWrite { $0[listener.uuid] = nil } + } + + func removeStatusListener(_ listener: StatusListener) { + statusListenersCache.lockedWrite { $0[listener.uuid] = nil } + } + + func removeAllEventListeners() { + eventListenersCache.lockedWrite { $0.removeAll() } + } + + func removeAllStatusListeners() { + statusListenersCache.lockedWrite { $0.removeAll() } + } +} diff --git a/Sources/PubNub/Events/New/SubscriptionSet.swift b/Sources/PubNub/Events/New/SubscriptionSet.swift index f93d2c5c..3f8c2ff6 100644 --- a/Sources/PubNub/Events/New/SubscriptionSet.swift +++ b/Sources/PubNub/Events/New/SubscriptionSet.swift @@ -13,7 +13,9 @@ import Foundation /// A final class representing a set of `Subscription`. /// /// Use this class to manage multiple `Subscription` concurrently. -public final class SubscriptionSet: EventEmitter, SubscriptionDisposable { +/// Utilize closures inherited from `EventListenerInterface` for the handling of subscription-related events. +/// You can also create an additional `EventListener` and register it by calling `addEventListener(_:)`. +public final class SubscriptionSet: EventListenerInterface, SubscriptionDisposable, EventListenerHandler { public var onEvent: ((PubNubEvent) -> Void)? public var onEvents: (([PubNubEvent]) -> Void)? public var onMessage: ((PubNubMessage) -> Void)? @@ -32,6 +34,8 @@ public final class SubscriptionSet: EventEmitter, SubscriptionDisposable { public private(set) var isDisposed = false // Internally holds a collection of child subscriptions private(set) var currentSubscriptions: Set + // Stores additional listeners + private var listenersContainer: SubscriptionListenersContainer = .init() // Internally intercepts messages from the Subscribe loop // and forwards them to the current `SubscriptionSet` @@ -54,13 +58,7 @@ public final class SubscriptionSet: EventEmitter, SubscriptionDisposable { ) { self.queue = queue self.options = SubscriptionOptions.empty() + options - self.currentSubscriptions = Set(entities.map { - Subscription( - queue: queue, - entity: $0, - options: options - ) - }) + self.currentSubscriptions = Set(entities.map { Subscription(queue: queue, entity: $0, options: options) }) } /// Initializes `SubscriptionSet` object with the specified parameters. @@ -125,8 +123,8 @@ public final class SubscriptionSet: EventEmitter, SubscriptionDisposable { subscriptions: currentSubscriptions.map { $0.clone() }, options: options ) - if let receiver = currentSubscriptions.first?.receiver, receiver.hasRegisteredAdapter(with: uuid) { - receiver.registerAdapter(clonedSubscriptionSet.adapter) + if let pubnub = currentSubscriptions.first?.pubnub, pubnub.hasRegisteredAdapter(with: uuid) { + pubnub.registerAdapter(clonedSubscriptionSet.adapter) } return clonedSubscriptionSet } @@ -138,9 +136,25 @@ public final class SubscriptionSet: EventEmitter, SubscriptionDisposable { public func dispose() { clearCallbacks() currentSubscriptions.forEach { $0.dispose() } + removeAllListeners() isDisposed = true } + /// Adds additional subscription listener + public func addEventListener(_ listener: EventListener) { + listenersContainer.storeEventListener(listener) + } + + /// Removes subscription listener + public func removeEventListener(_ listener: EventListener) { + listenersContainer.removeEventListener(listener) + } + + /// Removes all event listeners + public func removeAllListeners() { + listenersContainer.removeAllEventListeners() + } + deinit { dispose() } @@ -155,11 +169,10 @@ extension SubscriptionSet: SubscribeCapable { /// /// - Parameter timetoken: The timetoken to use for the subscriptions public func subscribe(with timetoken: Timetoken?) { - guard let receiver = currentSubscriptions.first?.receiver, !isDisposed else { + guard let pubnub = currentSubscriptions.first?.pubnub, !isDisposed else { return } - receiver.registerAdapter(adapter) - currentSubscriptions.forEach { receiver.registerAdapter($0.adapter) } + pubnub.registerAdapter(adapter) let channels = currentSubscriptions.filter { $0.subscriptionType == .channel @@ -169,7 +182,7 @@ extension SubscriptionSet: SubscribeCapable { $0.subscriptionType == .channelGroup }.allObjects - receiver.internalSubscribe( + pubnub.internalSubscribe( with: channels, and: groups, at: timetoken @@ -183,10 +196,11 @@ extension SubscriptionSet: SubscribeCapable { /// Use this method to gracefully end all subscriptions and stop receiving messages for all /// associated entities. After unsubscribing, the subscription set can be restarted if needed. public func unsubscribe() { - guard let receiver = currentSubscriptions.first?.receiver, !isDisposed else { + guard let pubnub = currentSubscriptions.first?.pubnub, !isDisposed else { return } - receiver.internalUnsubscribe( + pubnub.subscription.remove(adapter) + pubnub.internalUnsubscribe( from: currentSubscriptions.filter { $0.subscriptionType == .channel }, and: currentSubscriptions.filter { $0.subscriptionType == .channelGroup }, presenceOnly: false @@ -214,7 +228,7 @@ extension SubscriptionSet: SubscribeMessagesReceiver { // 1. Gets a subscription from the associated list of child subscriptions // 2. Checks which payloads the currently iterated child subscription can map to events // 3. Checks the events result received in the previous step against SubscriptionSet's options - // 4. Emits filtered events from SubscriptionSet + // 4. Emits filtered events from SubscriptionSet and to additional listeners attached @discardableResult func onPayloadsReceived(payloads: [SubscribeMessagePayload]) -> [PubNubEvent] { currentSubscriptions.reduce(into: [PubNubEvent]()) { accumulatedRes, childSubscription in let events = payloads.compactMap { payload in @@ -223,7 +237,10 @@ extension SubscriptionSet: SubscribeMessagesReceiver { options.filterCriteriaSatisfied(event: $0) } accumulatedRes.append(contentsOf: events) + // Emits events to the current SubscriptionSet's closures emit(events: events) + // Emits events to the underlying attached listeners + listenersContainer.eventListeners.forEach { $0.emit(events: events) } } } } diff --git a/Sources/PubNub/Extensions/NSLocking+PubNub.swift b/Sources/PubNub/Extensions/NSLocking+PubNub.swift index 9dc63b3c..acc6bf35 100644 --- a/Sources/PubNub/Extensions/NSLocking+PubNub.swift +++ b/Sources/PubNub/Extensions/NSLocking+PubNub.swift @@ -28,7 +28,7 @@ extension NSLocking { @discardableResult @inline(__always) - func synchronize(_ closure: () throws -> T) throws -> T { + func synchronize(_ closure: () throws -> T) rethrows -> T { lock() defer { unlock() } let result = try closure() diff --git a/Sources/PubNub/Helpers/AnyJSONType.swift b/Sources/PubNub/Helpers/AnyJSONType.swift index 29278533..c11de9fe 100644 --- a/Sources/PubNub/Helpers/AnyJSONType.swift +++ b/Sources/PubNub/Helpers/AnyJSONType.swift @@ -19,7 +19,6 @@ public enum AnyJSONType { case null case array([AnyJSONType]) case dictionary([String: AnyJSONType]) - case codable(Codable) case unknown(Any) } diff --git a/Sources/PubNub/Helpers/Atomic.swift b/Sources/PubNub/Helpers/Atomic.swift index 21967bb9..cb0d20fd 100644 --- a/Sources/PubNub/Helpers/Atomic.swift +++ b/Sources/PubNub/Helpers/Atomic.swift @@ -70,7 +70,7 @@ final class Atomic { /// Thread-safe execution of a closure that can throw exceptions @inline(__always) - func lockedTry(_ closure: (inout Locked) throws -> Value) throws -> Value { + func lockedTry(_ closure: (inout Locked) throws -> Value) rethrows -> Value { return try lock.synchronize { try closure(&self.value) } } } diff --git a/Sources/PubNub/Helpers/Constants.swift b/Sources/PubNub/Helpers/Constants.swift index 93efe562..547ad41a 100644 --- a/Sources/PubNub/Helpers/Constants.swift +++ b/Sources/PubNub/Helpers/Constants.swift @@ -57,7 +57,7 @@ public enum Constant { static let pubnubSwiftSDKName: String = "PubNubSwift" - static let pubnubSwiftSDKVersion: String = "7.3.3" + static let pubnubSwiftSDKVersion: String = "8.0.0" static let appBundleId: String = { if let info = Bundle.main.infoDictionary, diff --git a/Sources/PubNub/Helpers/WeakBox.swift b/Sources/PubNub/Helpers/WeakBox.swift index 21f3f120..94862f19 100644 --- a/Sources/PubNub/Helpers/WeakBox.swift +++ b/Sources/PubNub/Helpers/WeakBox.swift @@ -77,3 +77,15 @@ extension WeakSet: Collection { elements.lockedRead { $0.index(after: index) } } } + +extension WeakSet where Element == BaseSubscriptionListener { + func forEach(_ body: (BaseSubscriptionListener) throws -> Void) rethrows { + try elements.lockedTry { + try $0.compactMap { + $0.underlying + }.forEach { + try body($0) + } + } + } +} diff --git a/Sources/PubNub/KMP/Helpers/Dictionary+ObjCRepresentable.swift b/Sources/PubNub/KMP/Helpers/Dictionary+ObjCRepresentable.swift new file mode 100644 index 00000000..29015ba9 --- /dev/null +++ b/Sources/PubNub/KMP/Helpers/Dictionary+ObjCRepresentable.swift @@ -0,0 +1,46 @@ +// +// Dictionary+ObjCRepresentable.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 + +extension AnyJSONType { + var objCRepresentable: Any? { + switch self { + case .string(let string): + return string + case .integer(let number): + return number + case .double(let number): + return number + case .boolean(let booleanLiteral): + return booleanLiteral + case .array(let array): + return array.map { $0.objCRepresentable } + case .dictionary(let dictionary): + return dictionary.mapValues { $0.objCRepresentable } + case .codable(let codableValue): + return codableValue + case .unknown(let unknownValue): + return unknownValue + case .null: + return NSNull() + } + } +} + +extension Dictionary { + func asObjCRepresentable() -> [String: Any] { + compactMapValues { + $0.scalarValue.underlying.objCRepresentable + }.compactMapValues { + $0 + } + } +} diff --git a/Sources/PubNub/KMP/Helpers/Int+NSNumber.swift b/Sources/PubNub/KMP/Helpers/Int+NSNumber.swift new file mode 100644 index 00000000..eae957bc --- /dev/null +++ b/Sources/PubNub/KMP/Helpers/Int+NSNumber.swift @@ -0,0 +1,17 @@ +// +// Int+NSNumber.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 + +extension Int { + var asNumber: NSNumber { + NSNumber(value: self) + } +} diff --git a/Sources/PubNub/KMP/Helpers/PubNub+URL.swift b/Sources/PubNub/KMP/Helpers/PubNub+URL.swift new file mode 100644 index 00000000..9448aa10 --- /dev/null +++ b/Sources/PubNub/KMP/Helpers/PubNub+URL.swift @@ -0,0 +1,21 @@ +// +// PubNub+URL.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 + +extension PubNub { + func generateFileDownloadURL(for file: PubNubFile) -> URL? { + try? generateFileDownloadURL( + channel: file.channel, + fileId: file.fileId, + filename: file.filename + ) + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+AppContext.swift b/Sources/PubNub/KMP/KMPPubNub+AppContext.swift new file mode 100644 index 00000000..32d5f761 --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+AppContext.swift @@ -0,0 +1,551 @@ +// +// KMPPubNub+AppContext.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +extension KMPPubNub { + private func objectSortProperties(from properties: [KMPObjectSortProperty]) -> [PubNub.ObjectSortField] { + properties.compactMap { + if let property = PubNub.ObjectSortProperty(rawValue: $0.key) { + return PubNub.ObjectSortField(property: property, ascending: $0.direction == "asc") + } else { + return nil + } + } + } + + private func convertPage(from page: KMPHashedPage?) -> PubNubHashedPage? { + guard let page = page else { + return nil + } + return PubNub.Page( + start: page.start, + end: page.end, + totalCount: page.totalCount?.intValue + ) + } + + private func convertDictionaryToScalars(_ dictionary: [String: Any]?) -> [String: JSONCodableScalar]? { + dictionary?.compactMapValues { item -> JSONCodableScalar? in + if let number = item as? NSNumber { + if let intValue = number as? Int { + return intValue + } else if let doubleValue = number as? Double { + return doubleValue + } else if let boolValue = number as? Bool { + return boolValue + } else { + return nil + } + } else { + return item as? JSONCodableScalar + } + } + } + + // swiftlint:disable todo + // TODO: Swift SDK allows to sort by the status field, it's not present in KMP + + private func mapToMembershipSortFields(from array: [String]) -> [PubNub.MembershipSortField] { + array.compactMap { + switch $0 { + case "channel.id", "uuid.id": + return PubNub.MembershipSortField(property: .object(.id)) + case "channel.name", "uuid.name": + return PubNub.MembershipSortField(property: .object(.name)) + case "channel.updated", "uuid.updated": + return PubNub.MembershipSortField(property: .object(.updated)) + case "updated": + return PubNub.MembershipSortField(property: .updated) + default: + return nil + } + } + } + + // swiftlint:enable todo +} + +@objc +public extension KMPPubNub { + func getAllChannelMetadata( + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [KMPObjectSortProperty], + includeCount: Bool, + includeCustom: Bool, + onSuccess: @escaping (([KMPChannelMetadata], NSNumber?, KMPHashedPage) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.allChannelMetadata( + include: PubNub.IncludeFields(custom: includeCustom, totalCount: includeCount), + filter: filter, + sort: objectSortProperties(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.channels.map { KMPChannelMetadata(metadata: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getChannelMetadata( + channel: String, + includeCustom: Bool, + onSuccess: @escaping ((KMPChannelMetadata) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.fetch(channel: channel, include: includeCustom) { + switch $0 { + case .success(let metadata): + onSuccess(KMPChannelMetadata(metadata: metadata)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func setChannelMetadata( + channel: String, + name: String?, + description: String?, + custom: KMPAnyJSON?, + includeCustom: Bool, + type: String?, + status: String?, + onSuccess: @escaping ((KMPChannelMetadata) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.set( + channel: PubNubChannelMetadataBase( + metadataId: channel, + name: name, + type: type, + status: status, + channelDescription: description, + custom: convertDictionaryToScalars(custom?.asMap()) + ), + include: includeCustom + ) { + switch $0 { + case .success(let metadata): + onSuccess(KMPChannelMetadata(metadata: metadata)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func removeChannelMetadata( + channel: String, + onSuccess: @escaping ((String) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.remove(channel: channel) { + switch $0 { + case .success(let channel): + onSuccess(channel) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getAllUUIDMetadata( + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [KMPObjectSortProperty], + includeCount: Bool, + includeCustom: Bool, + onSuccess: @escaping (([KMPUUIDMetadata], NSNumber?, KMPHashedPage) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.allUUIDMetadata( + include: PubNub.IncludeFields(custom: includeCustom, totalCount: includeCount), + filter: filter, + sort: objectSortProperties(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.uuids.map { KMPUUIDMetadata(metadata: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getUUIDMetadata( + uuid: String?, + includeCustom: Bool, + onSuccess: @escaping ((KMPUUIDMetadata) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.fetch(uuid: uuid, include: includeCustom) { + switch $0 { + case .success(let metadata): + onSuccess(KMPUUIDMetadata(metadata: metadata)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func setUUIDMetadata( + uuid: String?, + name: String?, + externalId: String?, + profileUrl: String?, + email: String?, + custom: KMPAnyJSON?, + includeCustom: Bool, + type: String?, + status: String?, + onSuccess: @escaping ((KMPUUIDMetadata) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.set( + uuid: PubNubUUIDMetadataBase( + metadataId: uuid ?? pubnub.configuration.userId, + name: name, + type: type, + status: status, + externalId: externalId, + profileURL: profileUrl, + email: email, + custom: convertDictionaryToScalars(custom?.asMap()) + ), + include: includeCustom + ) { + switch $0 { + case .success(let metadata): + onSuccess(KMPUUIDMetadata(metadata: metadata)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func removeUUIDMetadata( + uuid: String?, + onSuccess: @escaping ((String) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.remove(uuid: uuid) { + switch $0 { + case .success(let result): + onSuccess(result) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getMemberships( + uuid: String?, + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [String], + includeCount: Bool, + includeCustom: Bool, + includeChannelFields: Bool, + includeChannelCustomFields: Bool, + includeChannelType: Bool, + onSuccess: @escaping (([KMPMembershipMetadata], NSNumber?, KMPHashedPage?) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.fetchMemberships( + uuid: uuid, + include: .init( + customFields: includeCustom, + channelFields: includeChannelFields, + channelCustomFields: includeChannelCustomFields, + channelTypeField: includeChannelType, + totalCount: includeCount + ), + filter: filter, + sort: mapToMembershipSortFields(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.memberships.map { KMPMembershipMetadata(from: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func setMemberships( + channels: [KMPChannelMetadata], + uuid: String?, + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [String], + includeCount: Bool, + includeCustom: Bool, + includeChannelFields: Bool, + includeChannelCustomFields: Bool, + includeChannelType: Bool, + onSuccess: @escaping (([KMPMembershipMetadata], NSNumber?, KMPHashedPage?) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.setMemberships( + uuid: uuid, + channels: channels.map { + PubNubMembershipMetadataBase( + uuidMetadataId: uuid ?? pubnub.configuration.userId, + channelMetadataId: $0.id, + status: $0.status, + custom: convertDictionaryToScalars($0.custom) + ) + }, + include: .init( + customFields: includeCustom, + channelFields: includeChannelFields, + channelCustomFields: includeChannelCustomFields, + channelTypeField: includeChannelType, + totalCount: includeCount + ), + filter: filter, + sort: mapToMembershipSortFields(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.memberships.map { KMPMembershipMetadata(from: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func removeMemberships( + channels: [String], + uuid: String?, + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [String], + includeCount: Bool, + includeCustom: Bool, + includeChannelFields: Bool, + includeChannelCustomFields: Bool, + includeChannelType: Bool, + onSuccess: @escaping (([KMPMembershipMetadata], NSNumber?, KMPHashedPage?) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.removeMemberships( + uuid: uuid, + channels: channels.map { + PubNubMembershipMetadataBase( + uuidMetadataId: uuid ?? pubnub.configuration.userId, + channelMetadataId: $0 + ) + }, + include: .init( + customFields: includeCustom, + channelFields: includeChannelFields, + channelCustomFields: includeChannelCustomFields, + channelTypeField: includeChannelType, + totalCount: includeCount + ), + filter: filter, + sort: mapToMembershipSortFields(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.memberships.map { KMPMembershipMetadata(from: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getChannelMembers( + channel: String, + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [String], + includeCount: Bool, + includeCustom: Bool, + includeUUIDFields: Bool, + includeUUIDCustomFields: Bool, + includeUUIDType: Bool, + onSuccess: @escaping (([KMPMembershipMetadata], NSNumber?, KMPHashedPage?) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.fetchMembers( + channel: channel, + include: .init( + customFields: includeCustom, + uuidFields: includeUUIDFields, + uuidCustomFields: includeUUIDCustomFields, + uuidTypeField: includeUUIDType, + totalCount: includeCount + ), + filter: filter, + sort: mapToMembershipSortFields(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.memberships.map { KMPMembershipMetadata(from: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func setChannelMembers( + channel: String, + uuids: [KMPUUIDMetadata], + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [String], + includeCount: Bool, + includeCustom: Bool, + includeUUIDFields: Bool, + includeUUIDCustomFields: Bool, + includeUUIDType: Bool, + onSuccess: @escaping (([KMPMembershipMetadata], NSNumber?, KMPHashedPage?) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.setMembers( + channel: channel, + uuids: uuids.map { + PubNubMembershipMetadataBase( + uuidMetadataId: $0.id, + channelMetadataId: channel, + status: $0.status, + custom: convertDictionaryToScalars($0.custom) + ) + }, + include: .init( + customFields: includeCustom, + uuidFields: includeUUIDFields, + uuidCustomFields: includeUUIDCustomFields, + uuidTypeField: includeUUIDType, + totalCount: includeCount + ), + filter: filter, + sort: mapToMembershipSortFields(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.memberships.map { KMPMembershipMetadata(from: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func removeChannelMembers( + channel: String, + uuids: [String], + limit: NSNumber?, + page: KMPHashedPage?, + filter: String?, + sort: [String], + includeCount: Bool, + includeCustom: Bool, + includeUUIDFields: Bool, + includeUUIDCustomFields: Bool, + includeUUIDType: Bool, + onSuccess: @escaping (([KMPMembershipMetadata], NSNumber?, KMPHashedPage?) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.removeMembers( + channel: channel, + uuids: uuids.map { + PubNubMembershipMetadataBase( + uuidMetadataId: $0, + channelMetadataId: channel + ) + }, + include: .init( + customFields: includeCustom, + uuidFields: includeUUIDFields, + uuidCustomFields: includeUUIDCustomFields, + uuidTypeField: includeUUIDType, + totalCount: includeCount + ), + filter: filter, + sort: mapToMembershipSortFields(from: sort), + limit: limit?.intValue, + page: convertPage(from: page) + ) { + switch $0 { + case .success(let res): + onSuccess( + res.memberships.map { KMPMembershipMetadata(from: $0) }, + res.next?.totalCount?.asNumber, + KMPHashedPage(page: res.next) + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + // swiftlint:disable:next file_length +} diff --git a/Sources/PubNub/KMP/KMPPubNub+ChannelGroups.swift b/Sources/PubNub/KMP/KMPPubNub+ChannelGroups.swift new file mode 100644 index 00000000..ae68113d --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+ChannelGroups.swift @@ -0,0 +1,100 @@ +// +// KMPPubNub+ChannelGroups.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public extension KMPPubNub { + func addChannels( + to channelGroup: String, + channels: [String], + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping ((Error)) -> Void + ) { + pubnub.add( + channels: channels, + to: channelGroup + ) { + switch $0 { + case .success(let res): + onSuccess(res.channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func listChannels( + for channelGroup: String, + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.listChannels(for: channelGroup) { + switch $0 { + case .success(let res): + onSuccess(res.channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func remove( + channels: [String], + from channelGroup: String, + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping ((Error)) -> Void + ) { + pubnub.remove(channels: channels, from: channelGroup) { + switch $0 { + case .success(let res): + onSuccess(res.channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func listChannelGroups( + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping ((Error)) -> Void + ) { + pubnub.listChannelGroups { + switch $0 { + case .success(let channelGroups): + onSuccess(channelGroups) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func delete( + channelGroup: String, + onSuccess: @escaping ((String) -> Void), + onFailure: @escaping ((Error)) -> Void + ) { + pubnub.remove(channelGroup: channelGroup) { + switch $0 { + case .success(let channelGroup): + onSuccess(channelGroup) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+Files.swift b/Sources/PubNub/KMP/KMPPubNub+Files.swift new file mode 100644 index 00000000..06c836ad --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+Files.swift @@ -0,0 +1,217 @@ +// +// KMPPubNub+Files.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +extension KMPPubNub { + var defaultFileDownloadPath: URL { + FileManager.default.temporaryDirectory.appendingPathComponent("pubnub-chat-sdk") + } + + func convertUploadContent(from content: KMPUploadable) -> PubNub.FileUploadContent? { + switch content { + case let content as KMPDataUploadContent: + return .data(content.data, contentType: content.contentType) + case let content as KMPFileUploadContent: + return .file(url: content.fileURL) + case let content as KMPInputStreamUploadContent: + return .stream(content.stream, contentType: content.contentType, contentLength: content.contentLength) + default: + return nil + } + } +} + +@objc +public extension KMPPubNub { + func listFiles( + channel: String, + limit: NSNumber?, + next: KMPHashedPage?, + onSuccess: @escaping (([KMPFile], String?) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.listFiles( + channel: channel, + limit: limit?.uintValue ?? 100, + next: next?.end + ) { [weak pubnub] in + switch $0 { + case .success(let res): + onSuccess(res.files.map { KMPFile(from: $0, url: pubnub?.generateFileDownloadURL(for: $0)) }, next?.end) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getFileUrl( + channel: String, + fileName: String, + fileId: String, + onSuccess: @escaping ((String) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + do { + onSuccess( + try pubnub.generateFileDownloadURL( + channel: channel, + fileId: fileId, + filename: fileName + ).absoluteString + ) + } catch { + onFailure(KMPError(underlying: error)) + } + } + + func deleteFile( + channel: String, + fileName: String, + fileId: String, + onSuccess: @escaping (() -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.remove(fileId: fileId, filename: fileName, channel: channel) { + switch $0 { + case .success: + onSuccess() + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + // swiftlint:disable todo + // TODO: Missing contentType and fileSize from KMP which are required in Swift SDK + + func publishFileMessage( + channel: String, + fileName: String, + fileId: String, + message: Any?, + meta: Any?, + ttl: NSNumber?, + shouldStore: NSNumber?, + onSuccess: @escaping ((Timetoken) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + let messageCodable: AnyJSON? = if let message { + AnyJSON(message) + } else { + nil + } + let metaCodable: AnyJSON? = if let meta { + AnyJSON(meta) + } else { + nil + } + pubnub.publish( + file: PubNubFileBase( + channel: channel, + fileId: fileId, + filename: fileName, + size: 0, + contentType: nil + ), + request: PubNub.PublishFileRequest( + additionalMessage: messageCodable, + store: shouldStore?.boolValue, + ttl: ttl?.intValue, + meta: metaCodable + ) + ) { + switch $0 { + case .success(let timetoken): + onSuccess(timetoken) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + // swiftlint:enable todo + + func downloadFile( + channel: String, + fileName: String, + fileId: String, + onSuccess: @escaping ((KMPFile) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + let fileBase = PubNubLocalFileBase( + channel: channel, + fileId: fileId, + fileURL: defaultFileDownloadPath.appendingPathComponent(fileName) + ) + pubnub.download(file: fileBase, toFileURL: fileBase.fileURL) { + switch $0 { + case .success(let res): + onSuccess(KMPFile(from: res.file, url: res.file.fileURL)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func sendFile( + channel: String, + fileName: String, + content: KMPUploadable, + message: Any?, + meta: Any?, + ttl: NSNumber?, + shouldStore: NSNumber?, + onSuccess: @escaping ((KMPFile, Timetoken) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + guard let fileContent = convertUploadContent(from: content) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Cannot create expected PubNub.FileUploadContent"] + ) + )) + return + } + + let additionalMessage: AnyJSON? = if let message { AnyJSON(message) } else { nil } + let meta: AnyJSON? = if let meta { AnyJSON(meta) } else { nil } + + pubnub.send( + fileContent, + channel: channel, + remoteFilename: fileName, + publishRequest: PubNub.PublishFileRequest( + additionalMessage: additionalMessage, + store: shouldStore?.boolValue, + ttl: ttl?.intValue, + meta: meta + ) + ) { [weak pubnub] in + switch $0 { + case .success(let res): + onSuccess( + KMPFile(from: res.file, url: pubnub?.generateFileDownloadURL(for: res.file)), + res.publishedAt + ) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+History.swift b/Sources/PubNub/KMP/KMPPubNub+History.swift new file mode 100644 index 00000000..3e471fe7 --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+History.swift @@ -0,0 +1,108 @@ +// +// KMPPubNub+History.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public extension KMPPubNub { + func fetchMessages( + from channels: [String], + includeUUID: Bool, + includeMeta: Bool, + includeMessageActions: Bool, + includeMessageType: Bool, + page: KMPBoundedPage?, + onSuccess: @escaping ((KMPFetchMessagesResult)) -> Void, + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.fetchMessageHistory( + for: channels, + includeActions: includeMessageActions, + includeMeta: includeMeta, + includeUUID: includeUUID, + includeMessageType: includeMessageType, + page: PubNubBoundedPageBase( + start: page?.start?.uint64Value, + end: page?.end?.uint64Value, + limit: page?.limit?.intValue + ) + ) { + switch $0 { + case .success(let response): + onSuccess(KMPFetchMessagesResult( + messages: response.messagesByChannel.mapValues { $0.map { KMPMessage(message: $0) } }, + page: KMPBoundedPage(page: response.next) + )) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + // swiftlint:disable todo + // TODO: Deleting history from more than one channel isn't supported in Swift SDK + + func deleteMessages( + from channels: [String], + start: NSNumber?, + end: NSNumber?, + onSuccess: @escaping (() -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + guard let channel = channels.first else { + onFailure(PubNubError( + .invalidArguments, + additional: ["Empty channel list for deleteMessages"] + )) + return + } + pubnub.deleteMessageHistory( + from: channel, + start: start?.uint64Value, + end: end?.uint64Value + ) { + switch $0 { + case .success: + onSuccess() + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + // swiftlint:enable todo + + func messageCounts( + for channels: [String], + channelsTimetokens: [Timetoken], + onSuccess: @escaping (([String: Timetoken]) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + let keys = Set(channels) + let count = min(keys.count, channelsTimetokens.count) + let dictionary = Dictionary(uniqueKeysWithValues: zip(keys.prefix(count), channelsTimetokens.prefix(count))) + + pubnub.messageCounts(channels: dictionary) { + switch $0 { + case .success(let response): + onSuccess(response.mapValues { UInt64($0) }) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+Listeners.swift b/Sources/PubNub/KMP/KMPPubNub+Listeners.swift new file mode 100644 index 00000000..38b54bfc --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+Listeners.swift @@ -0,0 +1,115 @@ +// +// KMPPubNub+Listeners.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +extension KMPPubNub { + func createEventListener(from listener: KMPEventListener) -> EventListener { + let swiftListener = EventListener( + uuid: listener.uuid, + onMessage: { listener.onMessage?(KMPMessage(message: $0)) }, + onSignal: { listener.onSignal?(KMPMessage(message: $0)) }, + onPresence: { listener.onPresence?(KMPPresenceChange.from(change: $0)) }, + onMessageAction: { listener.onMessageAction?(KMPMessageAction(action: $0)) }, + onFileEvent: { [weak pubnub] in listener.onFile?(KMPFileChangeEvent.from(event: $0, with: pubnub)) }, + onAppContext: { listener.onAppContext?(KMPAppContextEventResult.from(event: $0)) } + ) + + // Establishes an association to infer which Swift listener + // corresponds to which KMP listener + listener.underlying = swiftListener + + return swiftListener + } + + func createStatusListener(from listener: KMPStatusListener) -> StatusListener { + let swiftStatusListener = StatusListener(onConnectionStateChange: { [weak pubnub] newStatus in + guard let pubnub = pubnub else { + return + } + + let category: KMPConnectionStatusCategory + let errorIfAny: Error? + + var affectedChannels: [String] = pubnub.subscribedChannels + var affectedGroups: [String] = pubnub.subscribedChannelGroups + + switch newStatus { + case .connected: + category = .connected + errorIfAny = nil + case .disconnected: + category = .disconnected + errorIfAny = nil + case let .disconnectedUnexpectedly(error): + category = .disconnectedUnexpectedly + errorIfAny = error + case let .subscriptionChanged(channels, groups): + category = .subscriptionChanged + errorIfAny = nil + affectedChannels = channels + affectedGroups = groups + case let .connectionError(error): + category = .connectionError + errorIfAny = error + } + + listener.onStatusChange?( + KMPConnectionStatus( + category: category, + error: errorIfAny, + currentTimetoken: NSNumber(value: pubnub.previousTimetoken ?? 0), + affectedChannels: Set(affectedChannels), + affectedChannelGroups: Set(affectedGroups) + ) + ) + }) + + // Establishes an association to infer which Swift listener + // corresponds to which KMP listener + listener.underlying = swiftStatusListener + + return swiftStatusListener + } +} + +@objc +public extension KMPPubNub { + func addStatusListener(listener: KMPStatusListener) { + pubnub.addStatusListener(createStatusListener(from: listener)) + } + + func removeStatusListener(listener: KMPStatusListener) { + if let underlying = listener.underlying { + pubnub.removeStatusListener(underlying) + } + } + + func addEventListener(listener: KMPEventListener) { + pubnub.addEventListener(createEventListener(from: listener)) + } + + func removeEventListener(listener: KMPEventListener) { + if let underlying = listener.underlying { + pubnub.removeEventListener(underlying) + } + } + + func removeAllListeners() { + pubnub.removeAllListeners() + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+MessageActions.swift b/Sources/PubNub/KMP/KMPPubNub+MessageActions.swift new file mode 100644 index 00000000..35ec3b9f --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+MessageActions.swift @@ -0,0 +1,88 @@ +// +// KMPPubNub+MessageActions.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public extension KMPPubNub { + func addMessageAction( + channel: String, + actionType: String, + actionValue: String, + messageTimetoken: Timetoken, + onSuccess: @escaping ((KMPMessageAction) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.addMessageAction( + channel: channel, + type: actionType, + value: actionValue, + messageTimetoken: messageTimetoken + ) { + switch $0 { + case .success(let action): + onSuccess(KMPMessageAction(action: action)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func removeMessageAction( + channel: String, + messageTimetoken: Timetoken, + actionTimetoken: Timetoken, + onSuccess: @escaping (() -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.removeMessageActions( + channel: channel, + message: messageTimetoken, + action: actionTimetoken + ) { + switch $0 { + case .success: + onSuccess() + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getMessageActions( + from channel: String, + page: KMPBoundedPage, + onSuccess: @escaping (([KMPMessageAction], KMPBoundedPage?) -> Void), + onFailure: @escaping ((Error)) -> Void + ) { + pubnub.fetchMessageActions( + channel: channel, + page: PubNubBoundedPageBase( + start: page.start?.uint64Value, + end: page.end?.uint64Value, + limit: page.limit?.intValue + ) + ) { + switch $0 { + case .success(let res): + onSuccess(res.actions.map { KMPMessageAction(action: $0) }, KMPBoundedPage(page: res.next)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+Presence.swift b/Sources/PubNub/KMP/KMPPubNub+Presence.swift new file mode 100644 index 00000000..ed613244 --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+Presence.swift @@ -0,0 +1,128 @@ +// +// KMPPubNub+MessageActions.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public extension KMPPubNub { + func hereNow( + channels: [String], + channelGroups: [String], + includeState: Bool, + includeUUIDs: Bool, + onSuccess: @escaping ((KMPHereNowResult) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.hereNow( + on: channels, + and: channelGroups, + includeUUIDs: includeUUIDs, + includeState: includeState + ) { + switch $0 { + case let .success(map): + onSuccess( + KMPHereNowResult( + totalChannels: map.count, + totalOccupancy: map.values.reduce(0, { accResult, channel in accResult + channel.occupancy }), + channels: map.mapValues { value in + KMPHereNowChannelData( + channelName: value.channel, + occupancy: value.occupancy, + occupants: value.occupants.map { + let stateValue: KMPAnyJSON? = if let state = value.occupantsState[$0]?.rawValue { + KMPAnyJSON(state) + } else { + nil + } + return KMPHereNowOccupantData( + uuid: $0, + state: stateValue + ) + } + ) + } + ) + ) + case let .failure(error): + onFailure(KMPError(underlying: error)) + } + } + } + + func whereNow( + uuid: String, + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping (Error) -> Void + ) { + pubnub.whereNow(for: uuid) { + switch $0 { + case .success(let map): + onSuccess(map[uuid] ?? []) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + func getPresenceState( + channels: [String], + channelGroups: [String], + uuid: String, + onSuccess: @escaping (([String: Any]) -> Void), + onFailure: @escaping (Error) -> Void + ) { + pubnub.getPresenceState( + for: uuid, + on: channels, + and: channelGroups + ) { + switch $0 { + case .success(let response): + onSuccess(response.stateByChannel.mapValues { $0.rawValue }) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + // swiftlint:disable todo + // TODO: It's not possible to set Presence state other than [String: JSONCodableScalar] in Swift SDK + + func setPresenceState( + channels: [String], + channelGroups: [String], + state: KMPAnyJSON, + onSuccess: @escaping ((KMPAnyJSON) -> Void), + onFailure: @escaping (Error) -> Void + ) { + pubnub.setPresence( + state: [:], + on: channels, + and: channelGroups + ) { + switch $0 { + case .success(let codable): + onSuccess(KMPAnyJSON(codable.rawValue)) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + // swiftlint:enable todo +} diff --git a/Sources/PubNub/KMP/KMPPubNub+Publish.swift b/Sources/PubNub/KMP/KMPPubNub+Publish.swift new file mode 100644 index 00000000..0809a141 --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+Publish.swift @@ -0,0 +1,87 @@ +// +// KMPPubNub+Publish.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +// MARK: - Publish + +extension KMPPubNub { + private func asOptionalCodable(_ object: Any?) -> JSONCodable? { + if let object { + return asCodable(object) + } else { + return nil + } + } + + private func asCodable(_ object: Any) -> JSONCodable { + if let codableValue = object as? JSONCodable { + return codableValue + } else { + return AnyJSON(object) + } + } +} + +@objc +public extension KMPPubNub { + func publish( + channel: String, + message: Any, + meta: Any?, + shouldStore: NSNumber?, + ttl: NSNumber?, + onSuccess: @escaping ((Timetoken) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.publish( + channel: channel, + message: asCodable(message), + shouldStore: shouldStore?.boolValue, + storeTTL: shouldStore?.intValue, + meta: asOptionalCodable(meta) + ) { + switch $0 { + case .success(let timetoken): + onSuccess(timetoken) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } +} + +// MARK: - Signal + +@objc +public extension KMPPubNub { + func signal( + channel: String, + message: Any, + onSuccess: @escaping ((Timetoken) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.signal(channel: channel, message: asCodable(message)) { + switch $0 { + case .success(let timetoken): + onSuccess(timetoken) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+Push.swift b/Sources/PubNub/KMP/KMPPubNub+Push.swift new file mode 100644 index 00000000..3a7d1078 --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+Push.swift @@ -0,0 +1,231 @@ +// +// KMPPubNub+Push.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +extension KMPPubNub { + func pushService(from rawString: String) -> PubNub.PushService? { + switch rawString { + case "gcm": + return .fcm + case "apns", "apns2": + return .apns + case "mpns": + return .mpns + default: + return nil + } + } +} + +@objc +public extension KMPPubNub { + func addChannelsToPushNotifications( + channels: [String], + deviceId: Data, + pushType: String, + topic: String, + environment: String, + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + guard let pushService = pushService(from: pushType) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid pushType parameter"] + ) + )) + return + } + guard let environment = PubNub.PushEnvironment(rawValue: environment) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid environment parameter"] + ) + )) + return + } + + if !topic.isEmpty { + pubnub.addAPNSDevicesOnChannels(channels, device: deviceId, on: topic, environment: environment) { + switch $0 { + case .success(let channels): + onSuccess(channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } else { + pubnub.addPushChannelRegistrations(channels, for: deviceId, of: pushService) { + switch $0 { + case .success(let channels): + onSuccess(channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + } + + func listPushChannels( + deviceId: Data, + pushType: String, + topic: String, + environment: String, + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + guard let pushService = pushService(from: pushType) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid pushType parameter"] + ) + )) + return + } + guard let environment = PubNub.PushEnvironment(rawValue: environment) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid environment parameter"] + ) + )) + return + } + + if !topic.isEmpty { + pubnub.listAPNSPushChannelRegistrations(for: deviceId, on: topic, environment: environment) { + switch $0 { + case .success(let channels): + onSuccess(channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } else { + pubnub.listPushChannelRegistrations(for: deviceId, of: pushService) { + switch $0 { + case .success(let channels): + onSuccess(channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + } + + func removeChannelsFromPush( + channels: [String], + deviceId: Data, + pushType: String, + topic: String, + environment: String, + onSuccess: @escaping (([String]) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + guard let pushService = pushService(from: pushType) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid pushType parameter"] + ) + )) + return + } + guard let environment = PubNub.PushEnvironment(rawValue: environment) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid environment parameter"] + ) + )) + return + } + + if !topic.isEmpty { + pubnub.removeAPNSDevicesOnChannels(channels, device: deviceId, on: topic, environment: environment) { + switch $0 { + case .success(let channels): + onSuccess(channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } else { + pubnub.removePushChannelRegistrations(channels, for: deviceId, of: pushService) { + switch $0 { + case .success(let channels): + onSuccess(channels) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + + } + + func removeAllChannelsFromPush( + pushType: String, + deviceId: Data, + topic: String, + environment: String, + onSuccess: @escaping (() -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + guard let pushService = pushService(from: pushType) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid pushType parameter"] + ) + )) + return + } + guard let environment = PubNub.PushEnvironment(rawValue: environment) else { + onFailure(KMPError( + underlying: PubNubError( + .invalidArguments, + additional: ["Invalid environment parameter"] + ) + )) + return + } + + if !topic.isEmpty { + pubnub.removeAllAPNSPushDevice(for: deviceId, on: topic, environment: environment) { + switch $0 { + case .success: + onSuccess() + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } else { + pubnub.removeAllPushChannelRegistrations(for: deviceId, of: pushService) { + switch $0 { + case .success: + onSuccess() + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+Subscribe.swift b/Sources/PubNub/KMP/KMPPubNub+Subscribe.swift new file mode 100644 index 00000000..f16fde6f --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+Subscribe.swift @@ -0,0 +1,64 @@ +// +// KMPPubNub+Subscribe.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +// MARK: - Subscribed channels & channel groups + +@objc +public extension KMPPubNub { + var subscribedChannels: [String] { + pubnub.subscribedChannels + } + + var subscribedChannelGroups: [String] { + pubnub.subscribedChannelGroups + } +} + +// MARK: - Subscribe & Unsubscribe + +@objc +public extension KMPPubNub { + func subscribe( + channels: [String], + channelGroups: [String], + withPresence: Bool, + timetoken: Timetoken + ) { + pubnub.subscribe( + to: channels, + and: channelGroups, + at: timetoken, + withPresence: withPresence + ) + } + + func unsubscribe( + from channels: [String], + channelGroups: [String] + ) { + pubnub.unsubscribe( + from: channels, + and: channelGroups + ) + } + + func unsubscribeAll() { + pubnub.unsubscribeAll() + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub+Time.swift b/Sources/PubNub/KMP/KMPPubNub+Time.swift new file mode 100644 index 00000000..7458e23c --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub+Time.swift @@ -0,0 +1,35 @@ +// +// KMPPubNub+Time.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public extension KMPPubNub { + func time( + onSuccess: @escaping ((Timetoken) -> Void), + onFailure: @escaping ((Error) -> Void) + ) { + pubnub.time { + switch $0 { + case .success(let timetoken): + onSuccess(timetoken) + case .failure(let error): + onFailure(KMPError(underlying: error)) + } + } + } +} diff --git a/Sources/PubNub/KMP/KMPPubNub.swift b/Sources/PubNub/KMP/KMPPubNub.swift new file mode 100644 index 00000000..f884773a --- /dev/null +++ b/Sources/PubNub/KMP/KMPPubNub.swift @@ -0,0 +1,105 @@ +// +// KMPPubNub.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes.. + +import Foundation + +@objc +public class KMPPubNub: NSObject { + let pubnub: PubNub + + @objc + public let configObjC: KMPPubNubConfiguration + + public init(pubnub: PubNub) { + self.pubnub = pubnub + self.configObjC = KMPPubNubConfiguration(configuration: pubnub.configuration) + super.init() + } + + @objc + public init(user: String, subKey: String, pubKey: String) { + self.pubnub = PubNub( + configuration: PubNubConfiguration( + publishKey: pubKey, + subscribeKey: subKey, + userId: user + ) + ) + self.configObjC = KMPPubNubConfiguration(configuration: self.pubnub.configuration) + super.init() + } +} + +// MARK: - Token + +@objc +public extension KMPPubNub { + func set(token: String) { + pubnub.set(token: token) + } +} + +// MARK: - Disconnect + +@objc +public extension KMPPubNub { + func disconnect() { + pubnub.disconnect() + } +} + +// MARK: - Entities + +@objc +public extension KMPPubNub { + func channel(with name: String) -> KMPChannelEntity { + KMPChannelEntity(channel: pubnub.channel(name)) + } + + func channelGroup(with name: String) -> KMPChannelGroupEntity { + KMPChannelGroupEntity(channelGroup: pubnub.channelGroup(name)) + } + + func userMetadata(with id: String) -> KMPUserMetadataEntity { + KMPUserMetadataEntity(userMetadata: pubnub.userMetadata(id)) + } + + func channelMetadata(with id: String) -> KMPChannelMetadataEntity { + KMPChannelMetadataEntity(channelMetadata: pubnub.channelMetadata(id)) + } +} + +// MARK: - Configuration + +@objc +public class KMPPubNubConfiguration: NSObject { + let configuration: PubNubConfiguration + + public init(configuration: PubNubConfiguration) { + self.configuration = configuration + } + + @objc + public var userId: String { + configuration.userId + } + + @objc + public var authKey: String? { + configuration.authKey + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPAnyJSON.swift b/Sources/PubNub/KMP/Wrappers/KMPAnyJSON.swift new file mode 100644 index 00000000..9df611c5 --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPAnyJSON.swift @@ -0,0 +1,92 @@ +// +// AnyJSONObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPAnyJSON: NSObject { + public let value: AnyJSON + + @objc + public init(_ value: Any?) { + if let anyJSON = value as? AnyJSON { + self.value = anyJSON + } else if value == nil { + self.value = AnyJSON(AnyJSONType.null) + } else { + self.value = AnyJSON(value as Any) + } + } + + @objc + public func asString() -> String? { + value.stringOptional + } + + @objc + public func asMap() -> [String: Any]? { + value.dictionaryOptional + } + + @objc + public func asList() -> [Any]? { + value.arrayOptional + } + + @objc + public func isNull() -> Bool { + value.isNil + } + + @objc + public func asInt() -> NSNumber? { + if let intValue = value.intOptional { + NSNumber(value: intValue) + } else { + nil + } + } + + @objc + public func asDouble() -> NSNumber? { + if let doubleValue = value.doubleOptional { + NSNumber(value: doubleValue) + } else { + nil + } + } + + @objc + public func asBool() -> NSNumber? { + if let boolValue = value.boolOptional { + NSNumber(value: boolValue) + } else { + nil + } + } + + @objc + public func asNumber() -> NSNumber? { + if let doubleValue = value.doubleOptional { + NSNumber(value: doubleValue) + } else if let intValue = value.intOptional { + NSNumber(value: intValue) + } else { + nil + } + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPAppContextEventResult.swift b/Sources/PubNub/KMP/Wrappers/KMPAppContextEventResult.swift new file mode 100644 index 00000000..e92cc4cf --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPAppContextEventResult.swift @@ -0,0 +1,345 @@ +// +// PubNubObjectEventResult.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +// MARK: - KMPAppContextEventResult + +@objc +public class KMPAppContextEventResult: NSObject { + @objc public let channel: String + @objc public let subscription: String? + @objc public let timetoken: NSNumber? + @objc public let userMetadata: KMPAnyJSON? + @objc public let publisher: String? + @objc public let source: String + @objc public let version: String + @objc public let event: String + @objc public let type: String + + // swiftlint:disable todo + // TODO: These parameters are not retrieved from Swift SDK + + init( + channel: String = "", + subscription: String? = nil, + timetoken: NSNumber? = nil, + userMetadata: AnyJSON? = nil, + publisher: String? = nil, + source: String = "", + version: String = "", + event: String = "", + type: String = "" + ) { + self.channel = channel + self.source = source + self.version = version + self.event = event + self.type = type + self.subscription = subscription + self.timetoken = timetoken + self.userMetadata = if let metadata = userMetadata { KMPAnyJSON(metadata.codableValue) } else { nil } + self.publisher = publisher + } + + // swiftlint:enable todo +} + +// MARK: - KMPSetUUIDMetadataResult + +@objc +public class KMPSetUUIDMetadataResult: KMPAppContextEventResult { + @objc public let metadata: KMPUUIDMetadata + + init(metadata: KMPUUIDMetadata) { + self.metadata = metadata + super.init() + } +} + +// MARK: - KMPRemoveUUIDMetadataResult + +@objc +public class KMPRemoveUUIDMetadataResult: KMPAppContextEventResult { + @objc public let uuid: String + + init(uuid: String) { + self.uuid = uuid + super.init() + } +} + +// MARK: - KMPSetChannelMetadataResult + +@objc +public class KMPSetChannelMetadataResult: KMPAppContextEventResult { + @objc public let metadata: KMPChannelMetadata + + init(metadata: KMPChannelMetadata) { + self.metadata = metadata + super.init(channel: metadata.id) + } +} + +// MARK: - PubNubRemoveChannelMetadataResultObjC + +@objc +public class KMPRemoveChannelMetadataResult: KMPAppContextEventResult { + @objc public let channelMetadataId: String + + init(channelMetadataId: String) { + self.channelMetadataId = channelMetadataId + super.init(channel: channelMetadataId) + } +} + +// MARK: - KMPSetMembershipResult + +@objc +public class KMPSetMembershipResult: KMPAppContextEventResult { + @objc public let metadata: KMPMembershipMetadata + + init(metadata: KMPMembershipMetadata) { + self.metadata = metadata + super.init(channel: metadata.channelMetadataId) + } +} + +// MARK: - KMPRemoveMembershipResult + +@objc +public class KMPRemoveMembershipResult: KMPAppContextEventResult { + @objc public let channelId: String + @objc public let uuid: String + + init(channelId: String, uuid: String) { + self.channelId = channelId + self.uuid = uuid + super.init(channel: channelId) + } +} + +// MARK: - KMPAppContextEventResult (Factory Method) + +extension KMPAppContextEventResult { + static func from(event: PubNubAppContextEvent) -> KMPAppContextEventResult { + switch event { + case .userMetadataSet(let changeset): + return KMPSetUUIDMetadataResult(metadata: KMPUUIDMetadata(changeset: changeset)) + case .userMetadataRemoved(let metadataId): + return KMPRemoveUUIDMetadataResult(uuid: metadataId) + case .channelMetadataSet(let changeset): + return KMPSetChannelMetadataResult(metadata: KMPChannelMetadata(changeset: changeset)) + case .channelMetadataRemoved(let metadataId): + return KMPRemoveChannelMetadataResult(channelMetadataId: metadataId) + case .membershipMetadataSet(let metadata): + return KMPSetMembershipResult(metadata: KMPMembershipMetadata(from: metadata)) + case .membershipMetadataRemoved(let metadata): + return KMPRemoveMembershipResult(channelId: metadata.channelMetadataId, uuid: metadata.uuidMetadataId) + } + } +} + +// MARK: - KMPChannelMetadata + +@objc +public class KMPChannelMetadata: NSObject { + @objc public var id: String + @objc public var name: String? + @objc public var descr: String? + @objc public var custom: [String: Any]? + @objc public var updated: String? + @objc public var eTag: String? + @objc public var type: String? + @objc public var status: String? + + @objc public var hasName: Bool = false + @objc public var hasDescr: Bool = false + @objc public var hasCustom: Bool = false + @objc public var hasType: Bool = false + @objc public var hasStatus: Bool = false + + init(changeset: PubNubChannelMetadataChangeset) { + self.id = changeset.metadataId + self.updated = DateFormatter.iso8601.string(from: changeset.updated) + self.eTag = changeset.eTag + + for change in changeset.changes { + switch change { + case let .stringOptional(keyPath, value): + switch keyPath { + case \.name: + self.name = value + self.hasName = true + case \.type: + self.type = value + self.hasType = true + case \.status: + self.status = value + self.hasStatus = true + case \.channelDescription: + self.descr = value + self.hasDescr = true + default: + break + } + case .customOptional(_, let value): + if let value { + self.custom = value.asObjCRepresentable() + self.hasCustom = true + } else { + self.custom = nil + self.hasCustom = true + } + } + } + } + + init(metadata: PubNubChannelMetadata) { + self.id = metadata.metadataId + self.name = metadata.name + self.descr = metadata.channelDescription + self.custom = metadata.custom?.asObjCRepresentable() + self.updated = if let date = metadata.updated { DateFormatter.iso8601.string(from: date) } else { nil } + self.eTag = metadata.eTag + self.type = metadata.type + self.status = metadata.status + } + + @objc + public init(id: String, custom: KMPAnyJSON, status: String?) { + self.id = id + self.custom = custom.asMap() + self.status = status + } +} + +// MARK: - KMPUUIDMetadata + +@objc +public class KMPUUIDMetadata: NSObject { + @objc public var id: String + @objc public var name: String? + @objc public var externalId: String? + @objc public var profileUrl: String? + @objc public var email: String? + @objc public var custom: [String: Any]? + @objc public var updated: String? + @objc public var eTag: String? + @objc public var type: String? + @objc public var status: String? + + @objc public var hasName: Bool = false + @objc public var hasExternalId: Bool = false + @objc public var hasProfileUrl: Bool = false + @objc public var hasEmail: Bool = false + @objc public var hasCustom: Bool = false + @objc public var hasType: Bool = false + @objc public var hasStatus: Bool = false + + @objc + public init( + id: String, + custom: KMPAnyJSON?, + status: String? + ) { + self.id = id + self.custom = custom?.asMap() + self.status = status + } + + // swiftlint:disable:next cyclomatic_complexity + init(changeset: PubNubUUIDMetadataChangeset) { + self.id = changeset.metadataId + self.updated = DateFormatter.iso8601.string(from: changeset.updated) + self.eTag = changeset.eTag + + for change in changeset.changes { + switch change { + case let .stringOptional(keyPath, value): + switch keyPath { + case \.name: + self.name = value + self.hasName = true + case \.type: + self.type = value + self.hasType = true + case \.status: + self.status = value + self.hasStatus = true + case \.externalId: + self.externalId = value + self.hasExternalId = true + case \.profileURL: + self.profileUrl = value + self.hasProfileUrl = true + case \.email: + self.email = value + self.hasEmail = true + default: + break + } + case .customOptional(_, let value): + if let value { + self.custom = value.asObjCRepresentable() + self.hasCustom = true + } else { + self.custom = nil + self.hasCustom = true + } + } + } + } + + init(metadata: PubNubUUIDMetadata) { + self.id = metadata.metadataId + self.name = metadata.name + self.externalId = metadata.externalId + self.profileUrl = metadata.profileURL + self.email = metadata.email + self.custom = metadata.custom?.asObjCRepresentable() + self.updated = if let date = metadata.updated { DateFormatter.iso8601.string(from: date) } else { nil } + self.eTag = metadata.eTag + self.type = metadata.type + self.status = metadata.status + } +} + +// MARK: - KMPMembershipMetadata + +@objc +public class KMPMembershipMetadata: NSObject { + @objc public var uuidMetadataId: String + @objc public var channelMetadataId: String + @objc public var status: String? + @objc public var uuid: KMPUUIDMetadata? + @objc public var channel: KMPChannelMetadata? + @objc public var updated: String? + @objc public var eTag: String? + @objc public var custom: [String: Any]? + + init(from: PubNubMembershipMetadata) { + self.uuidMetadataId = from.uuidMetadataId + self.channelMetadataId = from.channelMetadataId + self.status = from.status + self.uuid = if let uuid = from.uuid { KMPUUIDMetadata(metadata: uuid) } else { nil } + self.channel = if let channel = from.channel { KMPChannelMetadata(metadata: channel) } else { nil } + self.updated = if let date = from.updated { DateFormatter.iso8601.string(from: date) } else { nil } + self.eTag = from.eTag + self.custom = from.custom?.asObjCRepresentable() + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPEntity.swift b/Sources/PubNub/KMP/Wrappers/KMPEntity.swift new file mode 100644 index 00000000..9c11208a --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPEntity.swift @@ -0,0 +1,71 @@ +// +// PubNubEntityRepresentableObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc public class KMPEntity: NSObject { + let entity: Subscribable + + init(entity: Subscribable) { + self.entity = entity + } + + @objc + public var name: String { + entity.name + } +} + +@objc +public class KMPChannelEntity: KMPEntity { + let channel: ChannelRepresentation + + init(channel: ChannelRepresentation) { + self.channel = channel + super.init(entity: channel) + } +} + +@objc +public class KMPChannelGroupEntity: KMPEntity { + let channelGroup: ChannelGroupRepresentation + + init(channelGroup: ChannelGroupRepresentation) { + self.channelGroup = channelGroup + super.init(entity: channelGroup) + } +} + +@objc +public class KMPUserMetadataEntity: KMPEntity { + let userMetadata: UserMetadataRepresentation + + init(userMetadata: UserMetadataRepresentation) { + self.userMetadata = userMetadata + super.init(entity: userMetadata) + } +} + +@objc +public class KMPChannelMetadataEntity: KMPEntity { + let channelMetadata: ChannelMetadataRepresentation + + init(channelMetadata: ChannelMetadataRepresentation) { + self.channelMetadata = channelMetadata + super.init(entity: channelMetadata) + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPError.swift b/Sources/PubNub/KMP/Wrappers/KMPError.swift new file mode 100644 index 00000000..1792ca05 --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPError.swift @@ -0,0 +1,46 @@ +// +// KMPPubNubError.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPError: NSError { + let underlying: Error + + init(underlying: Error) { + self.underlying = underlying + super.init(domain: "pubnub", code: (underlying as? PubNubError)?.reason.rawValue ?? 0) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + public override var localizedDescription: String { + underlying.localizedDescription + } + + @objc + public var statusCode: Int { + if let response = (underlying as? PubNubError)?.affected.findFirst(by: PubNubError.AffectedValue.response) { + return response.statusCode + } else { + return 0 + } + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPEventListener.swift b/Sources/PubNub/KMP/Wrappers/KMPEventListener.swift new file mode 100644 index 00000000..c85125b0 --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPEventListener.swift @@ -0,0 +1,50 @@ +// +// PubNubEventListenerObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPEventListener: NSObject { + @objc public let uuid: UUID + @objc public var onMessage: ((KMPMessage) -> Void)? + @objc public var onPresence: (([KMPPresenceChange]) -> Void)? + @objc public var onSignal: ((KMPMessage) -> Void)? + @objc public var onMessageAction: ((KMPMessageAction) -> Void)? + @objc public var onAppContext: ((KMPAppContextEventResult) -> Void)? + @objc public var onFile: ((KMPFileChangeEvent) -> Void)? + + // Stores a reference to the Swift listener that acts as a proxy + // and forwards all calls to this (KMPEventListener) instance + weak var underlying: EventListener? + + @objc public init( + onMessage: ((KMPMessage) -> Void)?, + onPresence: (([KMPPresenceChange]) -> Void)?, + onSignal: ((KMPMessage) -> Void)?, + onMessageAction: ((KMPMessageAction) -> Void)?, + onAppContext: ((KMPAppContextEventResult) -> Void)?, + onFile: ((KMPFileChangeEvent) -> Void)? + ) { + self.uuid = UUID() + self.onMessage = onMessage + self.onPresence = onPresence + self.onSignal = onSignal + self.onMessageAction = onMessageAction + self.onAppContext = onAppContext + self.onFile = onFile + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPFetchMessagesResult.swift b/Sources/PubNub/KMP/Wrappers/KMPFetchMessagesResult.swift new file mode 100644 index 00000000..32417c4c --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPFetchMessagesResult.swift @@ -0,0 +1,60 @@ +// +// PubNubFetchMessagesResultObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPFetchMessagesResult: NSObject { + @objc public let messages: [String: [KMPMessage]] + @objc public let page: KMPBoundedPage? + + init(messages: [String: [KMPMessage]], page: KMPBoundedPage?) { + self.messages = messages + self.page = page + } +} + +@objc +public class KMPBoundedPage: NSObject { + @objc public let start: NSNumber? + @objc public let end: NSNumber? + @objc public let limit: NSNumber? + + @objc public init(start: NSNumber?, end: NSNumber?, limit: NSNumber?) { + self.start = start + self.end = end + self.limit = limit + } + + init(page: PubNubBoundedPage?) { + if let start = page?.start { + self.start = NSNumber(value: start) + } else { + self.start = nil + } + if let end = page?.end { + self.end = NSNumber(value: end) + } else { + self.end = nil + } + if let limit = page?.limit { + self.limit = NSNumber(value: limit) + } else { + self.limit = nil + } + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPFileChangeEvent.swift b/Sources/PubNub/KMP/Wrappers/KMPFileChangeEvent.swift new file mode 100644 index 00000000..123687d9 --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPFileChangeEvent.swift @@ -0,0 +1,93 @@ +// +// PubNubFileChangeEventObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPFileChangeEvent: NSObject { + @objc public let channel: String + @objc public let timetoken: NSNumber? + @objc public let publisher: String? + @objc public let message: KMPAnyJSON? + @objc public let metadata: KMPAnyJSON? + @objc public let subscription: String? + @objc public let file: KMPFile + + static func from(event: PubNubFileChangeEvent, with pubnub: PubNub?) -> KMPFileChangeEvent { + switch event { + case .uploaded(let fileEvent): + return KMPFileChangeEvent( + channel: fileEvent.file.channel, + timetoken: NSNumber(value: fileEvent.timetoken), + publisher: fileEvent.publisher, + message: fileEvent.additionalMessage?.codableValue, + metadata: fileEvent.metadata?.codableValue, + subscription: fileEvent.channelGroup, + file: KMPFile( + from: fileEvent.file, + url: pubnub?.generateFileDownloadURL(for: fileEvent.file) + ) + ) + } + } + + private init( + channel: String, + timetoken: NSNumber?, + publisher: String?, + message: AnyJSON?, + metadata: AnyJSON?, + subscription: String?, + file: KMPFile + ) { + self.channel = channel + self.timetoken = timetoken + self.publisher = publisher + self.message = if let message = message { KMPAnyJSON(message) } else { nil } + self.metadata = if let metadata = metadata { KMPAnyJSON(metadata) } else { nil } + self.subscription = subscription + self.file = file + } +} + +@objc +public class KMPFile: NSObject { + @objc public let id: String + @objc public let name: String + @objc public let url: URL? + @objc public let size: Int64 + @objc public let contentType: String? + @objc public let createdDate: Date? + + @objc + public var createdDateStringValue: String? { + if let createdDate { + return DateFormatter.iso8601.string(from: createdDate) + } else { + return nil + } + } + + init(from: PubNubFile, url: URL?) { + self.id = from.channel + self.name = from.filename + self.size = from.size + self.contentType = from.contentType + self.createdDate = from.createdDate + self.url = url + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPHashedPage.swift b/Sources/PubNub/KMP/Wrappers/KMPHashedPage.swift new file mode 100644 index 00000000..82c12e4b --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPHashedPage.swift @@ -0,0 +1,50 @@ +// +// PubNubPageObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPHashedPage: NSObject { + @objc public let start: String? + @objc public let end: String? + @objc public let totalCount: NSNumber? + + @objc + public init(start: String?, end: String?, totalCount: NSNumber?) { + self.start = start + self.end = end + self.totalCount = totalCount + } + + init(page: PubNubHashedPage?) { + self.start = page?.start + self.end = page?.end + self.totalCount = if let count = page?.totalCount { NSNumber(value: count) } else { nil } + } +} + +@objc +public class KMPObjectSortProperty: NSObject { + @objc public let key: String + @objc public let direction: String + + @objc + public init(key: String, direction: String) { + self.key = key + self.direction = direction + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPHereNowResult.swift b/Sources/PubNub/KMP/Wrappers/KMPHereNowResult.swift new file mode 100644 index 00000000..3a0636db --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPHereNowResult.swift @@ -0,0 +1,54 @@ +// +// PubNubHereNowResultObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPHereNowResult: NSObject { + @objc public let totalChannels: Int + @objc public let totalOccupancy: Int + @objc public let channels: [String: KMPHereNowChannelData] + + init(totalChannels: Int, totalOccupancy: Int, channels: [String: KMPHereNowChannelData]) { + self.totalChannels = totalChannels + self.totalOccupancy = totalOccupancy + self.channels = channels + } +} + +@objc +public class KMPHereNowChannelData: NSObject { + @objc public let channelName: String + @objc public let occupancy: Int + @objc public let occupants: [KMPHereNowOccupantData] + + init(channelName: String, occupancy: Int, occupants: [KMPHereNowOccupantData]) { + self.channelName = channelName + self.occupancy = occupancy + self.occupants = occupants + } +} + +@objc public class KMPHereNowOccupantData: NSObject { + @objc public let uuid: String + @objc public let state: KMPAnyJSON? + + init(uuid: String, state: KMPAnyJSON?) { + self.uuid = uuid + self.state = state + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPMessage.swift b/Sources/PubNub/KMP/Wrappers/KMPMessage.swift new file mode 100644 index 00000000..2bbc2984 --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPMessage.swift @@ -0,0 +1,43 @@ +// +// PubNubMessageObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPMessage: NSObject { + init(message: PubNubMessage) { + self.payload = KMPAnyJSON(message.payload.codableValue) + self.actions = message.actions.map { KMPMessageAction(action: $0) } + self.publisher = message.publisher + self.channel = message.channel + self.subscription = message.subscription + self.published = message.published + self.metadata = if let value = message.metadata { KMPAnyJSON(value.codableValue) } else { nil } + self.messageType = message.messageType.rawValue + self.error = message.error + } + + @objc public let payload: KMPAnyJSON + @objc public let actions: [KMPMessageAction] + @objc public let publisher: String? + @objc public let channel: String + @objc public let subscription: String? + @objc public let published: Timetoken + @objc public let metadata: KMPAnyJSON? + @objc public let messageType: Int + @objc public let error: Error? +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPMessageAction.swift b/Sources/PubNub/KMP/Wrappers/KMPMessageAction.swift new file mode 100644 index 00000000..a31aacd4 --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPMessageAction.swift @@ -0,0 +1,56 @@ +// +// PubNubMessageActionObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPMessageAction: NSObject { + @objc public let event: String + @objc public let actionType: String + @objc public let actionValue: String + @objc public let actionTimetoken: Timetoken + @objc public let messageTimetoken: Timetoken + @objc public let publisher: String + @objc public let channel: String + @objc public let subscription: String? + @objc public let published: NSNumber? + + convenience init(action: PubNubMessageActionEvent) { + switch action { + case .added(let action): + self.init(event: "added", action: action) + case .removed(let action): + self.init(event: "removed", action: action) + } + } + + convenience init(action: PubNubMessageAction) { + self.init(event: "", action: action) + } + + private init(event: String, action: PubNubMessageAction) { + self.event = event + self.actionType = action.actionType + self.actionValue = action.actionValue + self.actionTimetoken = action.actionTimetoken + self.messageTimetoken = action.messageTimetoken + self.publisher = action.publisher + self.channel = action.channel + self.subscription = action.subscription + self.published = if let tt = action.published { NSNumber(value: tt) } else { nil } + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPPresenceChange.swift b/Sources/PubNub/KMP/Wrappers/KMPPresenceChange.swift new file mode 100644 index 00000000..6a8cf93f --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPPresenceChange.swift @@ -0,0 +1,78 @@ +// +// PubNubPresenceChangeObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPPresenceChange: NSObject { + @objc public let event: String? + @objc public let uuid: String? + @objc public let occupancy: NSNumber? + @objc public let state: KMPAnyJSON? + @objc public let channel: String? + @objc public let subscription: String? + @objc public let timetoken: NSNumber? + @objc public let join: [String]? + @objc public let leave: [String]? + @objc public let timeout: [String]? + @objc public let refreshHereNow: NSNumber? + @objc public let userMetadata: KMPAnyJSON? + + static func from(change: PubNubPresenceChange) -> [KMPPresenceChange] { + change.actions.map { KMPPresenceChange(change: change, action: $0) } + } + + private init(change: PubNubPresenceChange, action: PubNubPresenceChangeAction) { + occupancy = NSNumber(value: change.occupancy) + channel = change.channel + subscription = change.subscription + timetoken = NSNumber(value: change.timetoken) + refreshHereNow = NSNumber(value: change.refreshHereNow) + userMetadata = if let value = change.metadata?.rawValue { KMPAnyJSON(value) } else { nil } + + switch action { + case .join(let uuids): + event = "join" + join = uuids + uuid = uuids.first + state = nil + leave = nil + timeout = nil + case .leave(let uuids): + event = "leave" + leave = uuids + uuid = uuids.first + state = nil + join = nil + timeout = nil + case let .stateChange(affectedUUID, newState): + event = "state-change" + state = KMPAnyJSON(newState.codableValue) + uuid = affectedUUID + join = nil + leave = nil + timeout = nil + case .timeout(let uuids): + event = "timeout" + timeout = uuids + uuid = uuids.first + state = nil + join = nil + leave = nil + } + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPStatusListener.swift b/Sources/PubNub/KMP/Wrappers/KMPStatusListener.swift new file mode 100644 index 00000000..5fb0168f --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPStatusListener.swift @@ -0,0 +1,69 @@ +// +// PubNubStatusListenerObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPStatusListener: NSObject { + @objc public let uuid: UUID + @objc public var onStatusChange: ((KMPConnectionStatus) -> Void)? + + // Stores a reference to the Swift listener that acts as a proxy + // and forwards all calls to this (KMPStatusListener) instance + weak var underlying: StatusListener? + + @objc + public init(onStatusChange: ((KMPConnectionStatus) -> Void)? = nil) { + self.uuid = UUID() + self.onStatusChange = onStatusChange + } +} + +@objc +public class KMPConnectionStatus: NSObject { + @objc public let category: KMPConnectionStatusCategory + @objc public let error: Error? + @objc public let currentTimetoken: NSNumber? + @objc public let affectedChannels: Set + @objc public let affectedChannelGroups: Set + + init( + category: KMPConnectionStatusCategory, + error: Error?, + currentTimetoken: NSNumber?, + affectedChannels: Set, + affectedChannelGroups: Set + ) { + self.category = category + self.error = error + self.currentTimetoken = currentTimetoken + self.affectedChannels = affectedChannels + self.affectedChannelGroups = affectedChannelGroups + } +} + +@objc +public enum KMPConnectionStatusCategory: Int { + case connected + case subscriptionChanged + case disconnectedUnexpectedly + case disconnected + case connectionError + case heartbeatFailed + case heartbeatSuccess + case malformedResponseCategory +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPSubscription.swift b/Sources/PubNub/KMP/Wrappers/KMPSubscription.swift new file mode 100644 index 00000000..9ffd6e55 --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPSubscription.swift @@ -0,0 +1,236 @@ +// +// PubNubSubscriptionObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPSubscription: NSObject { + let subscription: Subscription + + @objc public var onMessage: ((KMPMessage) -> Void)? + @objc public var onPresence: (([KMPPresenceChange]) -> Void)? + @objc public var onSignal: ((KMPMessage) -> Void)? + @objc public var onMessageAction: ((KMPMessageAction) -> Void)? + @objc public var onAppContext: ((KMPAppContextEventResult) -> Void)? + @objc public var onFile: ((KMPFileChangeEvent) -> Void)? + + @objc + public init(entity: KMPEntity, receivePresenceEvents: Bool) { + self.subscription = Subscription( + entity: entity.entity, + options: receivePresenceEvents ? ReceivePresenceEvents() : .empty() + ) + } + + @objc + public init(entity: KMPEntity) { + self.subscription = Subscription(entity: entity.entity) + } + + @objc + public func dispose() { + subscription.dispose() + } + + @objc + public func addListener(_ listener: KMPEventListener) { + let eventListener = EventListener( + uuid: listener.uuid + ) + eventListener.onMessage = { + listener.onMessage?(KMPMessage(message: $0)) + } + eventListener.onSignal = { + listener.onSignal?(KMPMessage(message: $0)) + } + eventListener.onPresence = { + listener.onPresence?(KMPPresenceChange.from(change: $0)) + } + eventListener.onMessageAction = { + listener.onMessageAction?(KMPMessageAction(action: $0)) + } + eventListener.onFileEvent = { [weak self] in + listener.onFile?(KMPFileChangeEvent.from(event: $0, with: self?.subscription.entity.pubnub)) + } + eventListener.onAppContext = { + listener.onAppContext?(KMPAppContextEventResult.from(event: $0)) + } + + subscription.addEventListener(eventListener) + } + + @objc + public func removeListener(_ listener: KMPEventListener) { + if let underlying = listener.underlying { + subscription.removeEventListener(underlying) + } + } + + @objc + public func removeAllListeners() { + subscription.removeAllListeners() + } + + @objc + public func subscribe(with timetoken: Timetoken) { + subscription.subscribe(with: timetoken) + } + + @objc + public func unsubscribe() { + subscription.unsubscribe() + } + + @objc + public func append(subscription: KMPSubscription) -> KMPSubscriptionSet { + let underlyingSubscription = Subscription( + entity: subscription.subscription.entity + ) + + underlyingSubscription.onMessage = { + subscription.onMessage?(KMPMessage(message: $0)) + } + underlyingSubscription.onSignal = { + subscription.onSignal?(KMPMessage(message: $0)) + } + underlyingSubscription.onPresence = { + subscription.onPresence?(KMPPresenceChange.from(change: $0)) + } + underlyingSubscription.onMessageAction = { + subscription.onMessageAction?(KMPMessageAction(action: $0)) + } + underlyingSubscription.onFileEvent = { [weak underlyingSubscription] in + subscription.onFile?(KMPFileChangeEvent.from(event: $0, with: underlyingSubscription?.pubnub)) + } + underlyingSubscription.onAppContext = { + subscription.onAppContext?(KMPAppContextEventResult.from(event: $0)) + } + + return KMPSubscriptionSet( + subscriptionSet: SubscriptionSet(subscriptions: [ + self.subscription, + underlyingSubscription + ]) + ) + } +} + +@objc +public class KMPSubscriptionSet: NSObject { + private let subscriptionSet: SubscriptionSet + + @objc public var onMessage: ((KMPMessage) -> Void)? + @objc public var onPresence: (([KMPPresenceChange]) -> Void)? + @objc public var onSignal: ((KMPMessage) -> Void)? + @objc public var onMessageAction: ((KMPMessageAction) -> Void)? + @objc public var onAppContext: ((KMPAppContextEventResult) -> Void)? + @objc public var onFile: ((KMPFileChangeEvent) -> Void)? + + init(subscriptionSet: SubscriptionSet) { + self.subscriptionSet = subscriptionSet + } + + @objc + public init(subscriptions: [KMPSubscription]) { + self.subscriptionSet = SubscriptionSet(subscriptions: subscriptions.map { $0.subscription }) + } + + @objc + public func dispose() { + subscriptionSet.dispose() + } + + @objc + public func addListener(_ listener: KMPEventListener) { + let pubnub = subscriptionSet.currentSubscriptions.first?.entity.pubnub + let eventListener = EventListener(uuid: listener.uuid) + + eventListener.onMessage = { + listener.onMessage?(KMPMessage(message: $0)) + } + eventListener.onSignal = { + listener.onSignal?(KMPMessage(message: $0)) + } + eventListener.onPresence = { + listener.onPresence?(KMPPresenceChange.from(change: $0)) + } + eventListener.onMessageAction = { + listener.onMessageAction?(KMPMessageAction(action: $0)) + } + eventListener.onFileEvent = { + listener.onFile?(KMPFileChangeEvent.from(event: $0, with: pubnub)) + } + eventListener.onAppContext = { + listener.onAppContext?(KMPAppContextEventResult.from(event: $0)) + } + + subscriptionSet.addEventListener(eventListener) + } + + @objc + public func removeListener(_ listener: KMPEventListener) { + if let underlying = listener.underlying { + subscriptionSet.removeEventListener(underlying) + } + } + + @objc + public func removeAllListeners() { + subscriptionSet.removeAllListeners() + } + + @objc + public func subscribe(with timetoken: Timetoken) { + subscriptionSet.subscribe(with: timetoken) + } + + @objc + public func unsubscribe() { + subscriptionSet.unsubscribe() + } + + @objc + public func append(subscription: KMPSubscription) { + let underlyingSubscription = Subscription(entity: subscription.subscription.entity) + + underlyingSubscription.onMessage = { + subscription.onMessage?(KMPMessage(message: $0)) + } + underlyingSubscription.onSignal = { + subscription.onSignal?(KMPMessage(message: $0)) + } + underlyingSubscription.onPresence = { + subscription.onPresence?(KMPPresenceChange.from(change: $0)) + } + underlyingSubscription.onMessageAction = { + subscription.onMessageAction?(KMPMessageAction(action: $0)) + } + underlyingSubscription.onFileEvent = { [weak underlyingSubscription] in + subscription.onFile?(KMPFileChangeEvent.from(event: $0, with: underlyingSubscription?.pubnub)) + } + underlyingSubscription.onAppContext = { + subscription.onAppContext?(KMPAppContextEventResult.from(event: $0)) + } + + subscriptionSet.add(subscription: underlyingSubscription) + } + + @objc + public func remove(subscription: KMPSubscription) { + subscriptionSet.remove(subscription: subscription.subscription) + } +} diff --git a/Sources/PubNub/KMP/Wrappers/KMPUploadable.swift b/Sources/PubNub/KMP/Wrappers/KMPUploadable.swift new file mode 100644 index 00000000..af1c92ea --- /dev/null +++ b/Sources/PubNub/KMP/Wrappers/KMPUploadable.swift @@ -0,0 +1,59 @@ +// +// PubNubUploadableObjC.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. +// +// IMPORTANT NOTE FOR DEVELOPERS USING THIS SDK +// +// All public symbols in this file are intended to allow interoperation with Kotlin Multiplatform for other PubNub frameworks. +// While these symbols are public, they are intended strictly for internal usage. +// +// External developers should refrain from directly using these symbols in their code, as their implementation details +// may change in future versions of the framework, potentially leading to breaking changes. + +import Foundation + +@objc +public class KMPUploadable: NSObject { + +} + +@objc +public class KMPDataUploadContent: KMPUploadable { + @objc public let data: Data + @objc public let contentType: String? + + @objc + public init(data: Data, contentType: String?) { + self.data = data + self.contentType = contentType + } +} + +@objc +public class KMPFileUploadContent: KMPUploadable { + @objc public let fileURL: URL + + @objc + public init(fileURL: URL) { + self.fileURL = fileURL + } +} + +@objc +public class KMPInputStreamUploadContent: KMPUploadable { + @objc public let stream: InputStream + @objc public let contentType: String? + @objc public let contentLength: Int + + @objc + public init(stream: InputStream, contentType: String?, contentLength: Int) { + self.stream = stream + self.contentType = contentType + self.contentLength = contentLength + } +} diff --git a/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift b/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift index 95269659..b783b8a7 100644 --- a/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift +++ b/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift @@ -28,11 +28,13 @@ public struct AutomaticRetry: RequestOperator, Hashable { ) // The minimum value allowed between retries static let minDelay: UInt = 2 + // The maximum value allowed between retries + static let maxDelay: UInt = 150 /// Provides the action taken when a retry is to be performed public enum ReconnectionPolicy: Hashable, Equatable { /// Exponential backoff with base/scale factor of 2, and a 150s max delay - public static let defaultExponential: ReconnectionPolicy = .legacyExponential(base: 2, scale: 2, maxDelay: 300) + public static let defaultExponential: ReconnectionPolicy = .exponential(minDelay: minDelay, maxDelay: maxDelay) /// Linear reconnect every 3 seconds public static let defaultLinear: ReconnectionPolicy = .linear(delay: Double(3)) @@ -40,9 +42,6 @@ public struct AutomaticRetry: RequestOperator, Hashable { case exponential(minDelay: UInt, maxDelay: UInt) /// Attempt to reconnect every X seconds case linear(delay: Double) - /// Reconnect with an exponential backoff - @available(*, deprecated, message: "Use exponential(minDelay:maxDelay:) instead") - case legacyExponential(base: UInt, scale: Double, maxDelay: UInt) func delay(for retryAttempt: Int) -> TimeInterval { /// Generates a random interval that's added to the final value @@ -50,8 +49,6 @@ public struct AutomaticRetry: RequestOperator, Hashable { let randomDelay = Double.random(in: 0...1) switch self { - case let .legacyExponential(base, scale, maxDelay): - return legacyExponentialBackoffDelay(for: base, scale: scale, maxDelay: maxDelay, current: retryAttempt) + randomDelay case let .exponential(minDelay, maxDelay): return min(Double(maxDelay), Double(minDelay) * pow(2, Double(retryAttempt))) + randomDelay case let .linear(delay): @@ -59,8 +56,13 @@ public struct AutomaticRetry: RequestOperator, Hashable { } } - func legacyExponentialBackoffDelay(for base: UInt, scale: Double, maxDelay: UInt, current retryCount: Int) -> Double { - max(min(pow(Double(base), Double(retryCount)) * scale, Double(maxDelay)), Double(AutomaticRetry.minDelay)) + func maximumRetryLimit() -> Int { + switch self { + case .linear: + return 10 + case .exponential: + return 6 + } } } @@ -122,6 +124,7 @@ public struct AutomaticRetry: RequestOperator, Hashable { retryableHTTPStatusCodes: Set = [500, 429], retryableURLErrorCodes: Set = AutomaticRetry.defaultRetryableURLErrorCodes, excluded endpoints: [AutomaticRetry.Endpoint] = [ + .presence, .messageSend, .files, .messageStorage, @@ -131,18 +134,11 @@ public struct AutomaticRetry: RequestOperator, Hashable { .messageActions ] ) { - self.retryLimit = Self.validate( - value: UInt(retryLimit), - using: retryLimit < 10, - replaceOnFailure: UInt(10), - warningMessage: "The `retryLimit` must be less than or equal 10" - ) - switch policy { case let .exponential(minDelay, maxDelay): let validatedMinDelay = Self.validate( value: minDelay, - using: minDelay > Self.minDelay, + using: minDelay >= Self.minDelay, replaceOnFailure: Self.minDelay, warningMessage: "The `minDelay` must be a minimum of \(Self.minDelay)" ) @@ -163,24 +159,15 @@ public struct AutomaticRetry: RequestOperator, Hashable { replaceOnFailure: Double(Self.minDelay), warningMessage: "The `linear.delay` must be greater than or equal \(Self.minDelay)." )) - case let .legacyExponential(base, scale, maxDelay): - self.policy = .legacyExponential( - base: Self.validate( - value: base, - using: base >= 2, - replaceOnFailure: 2, - warningMessage: "The `exponential.base` must be a minimum of 2." - ), - scale: Self.validate( - value: scale, - using: scale > 0, - replaceOnFailure: 0, - warningMessage: "The `exponential.scale` must be a positive value." - ), - maxDelay: maxDelay - ) } + self.retryLimit = Self.validate( + value: UInt(retryLimit), + using: retryLimit <= policy.maximumRetryLimit(), + replaceOnFailure: UInt(policy.maximumRetryLimit()), + warningMessage: "The `retryLimit` for \(policy) must be less than or equal \(policy.maximumRetryLimit())" + ) + self.retryableHTTPStatusCodes = retryableHTTPStatusCodes self.retryableURLErrorCodes = retryableURLErrorCodes self.excluded = endpoints diff --git a/Sources/PubNub/Networking/Routers/ObjectsMembershipsRouter.swift b/Sources/PubNub/Networking/Routers/ObjectsMembershipsRouter.swift index 0351dca4..4bce6646 100644 --- a/Sources/PubNub/Networking/Routers/ObjectsMembershipsRouter.swift +++ b/Sources/PubNub/Networking/Routers/ObjectsMembershipsRouter.swift @@ -69,8 +69,12 @@ public struct ObjectsMembershipsRouter: HTTPRouter { case custom case channel case channelCustom = "channel.custom" + case channelType = "channel.type" + case channelStatus = "channel.status" case uuid case uuidCustom = "uuid.custom" + case uuidType = "uuid.type" + case uuidStatus = "uuid.status" case status static func merge(_ other: [Include]?) -> [String] { diff --git a/Sources/PubNub/PubNub.swift b/Sources/PubNub/PubNub.swift index c25e35bb..300f07f4 100644 --- a/Sources/PubNub/PubNub.swift +++ b/Sources/PubNub/PubNub.swift @@ -349,7 +349,8 @@ public extension PubNub { to: channels, and: channelGroups, at: SubscribeCursor(timetoken: timetoken), - withPresence: withPresence + withPresence: withPresence, + using: self ) } @@ -422,7 +423,7 @@ public extension PubNub { } } -extension PubNub: SubscribeReceiver { +extension PubNub { func registerAdapter(_ adapter: BaseSubscriptionListenerAdapter) { subscription.registerAdapter(adapter) } @@ -460,19 +461,19 @@ extension PubNub: SubscribeReceiver { extension PubNub: EntityCreator { public func channel(_ name: String) -> ChannelRepresentation { - subscription.channel(name) + ChannelRepresentation(name: name, pubnub: self) } public func channelGroup(_ name: String) -> ChannelGroupRepresentation { - subscription.channelGroup(name) + ChannelGroupRepresentation(name: name, pubnub: self) } public func userMetadata(_ name: String) -> UserMetadataRepresentation { - subscription.userMetadata(name) + UserMetadataRepresentation(id: name, pubnub: self) } public func channelMetadata(_ name: String) -> ChannelMetadataRepresentation { - subscription.channelMetadata(name) + ChannelMetadataRepresentation(id: name, pubnub: self) } } @@ -1416,12 +1417,12 @@ public extension PubNub { // MARK: - Global EventEmitter -/// An extension to the PubNub class, making it conform to the `EventEmitter` protocol and serving +/// An extension to the PubNub class, making it conform to the `EventListenerInterface` protocol and serving /// as a global emitter for all entities. /// /// This extension enables `PubNub` instances to act as event emitters, allowing them to dispatch /// various types of events for all registered entities in the Subscribe loop. -extension PubNub: EventEmitter { +extension PubNub: EventListenerInterface { public var queue: DispatchQueue { subscription.queue } @@ -1471,13 +1472,42 @@ extension PubNub: EventEmitter { } } -/// An extension to the `PubNub` class, making it conform to the `StatusEmitter` protocol and serving +/// An extension to the `PubNub` class, making it conform to the `StatusListenerInterface` protocol and serving /// as a global listener for connection changes and possible errors along the way. -extension PubNub: StatusEmitter { +extension PubNub: StatusListenerInterface { public var onConnectionStateChange: ((ConnectionStatus) -> Void)? { get { subscription.onConnectionStateChange } set { subscription.onConnectionStateChange = newValue } } + + /// Adds additional status listeners + public func addStatusListener(_ listener: StatusListener) { + subscription.addStatusListener(listener) + } + + /// Removes status listener + public func removeStatusListener(_ listener: StatusListener) { + subscription.removeStatusListener(listener) + } + + /// Removes all status listeners + public func removeAllStatusListeners() { + subscription.removeAllStatusListeners() + } +} + +extension PubNub: EventListenerHandler { + public func addEventListener(_ listener: EventListener) { + subscription.addEventListener(listener) + } + + public func removeEventListener(_ listener: EventListener) { + subscription.removeEventListener(listener) + } + + public func removeAllListeners() { + subscription.removeAllListeners() + } } // swiftlint:disable:this file_length diff --git a/Sources/PubNub/PubNubConfiguration.swift b/Sources/PubNub/PubNubConfiguration.swift index c17db4cb..380ac600 100644 --- a/Sources/PubNub/PubNubConfiguration.swift +++ b/Sources/PubNub/PubNubConfiguration.swift @@ -85,7 +85,7 @@ public struct PubNubConfiguration: Hashable { origin: String = "ps.pndsn.com", useInstanceId: Bool = false, useRequestId: Bool = false, - automaticRetry: AutomaticRetry? = nil, + automaticRetry: AutomaticRetry? = .default, urlSessionConfiguration: URLSessionConfiguration = .pubnub, durationUntilTimeout: UInt = 300, heartbeatInterval: UInt = 0, diff --git a/Sources/PubNub/Subscription/ConnectionStatus.swift b/Sources/PubNub/Subscription/ConnectionStatus.swift index 4ddb5d56..ebbe05c2 100644 --- a/Sources/PubNub/Subscription/ConnectionStatus.swift +++ b/Sources/PubNub/Subscription/ConnectionStatus.swift @@ -12,25 +12,22 @@ import Foundation /// Status of a connection to a remote system public enum ConnectionStatus: Equatable { - /// Attempting to connect to a remote system - @available(*, deprecated, message: "This case will be removed in future versions") - case connecting /// Successfully connected to a remote system case connected /// Explicit disconnect from a remote system case disconnected - /// Attempting to reconnect to a remote system - @available(*, deprecated, message: "This case will be removed in future versions") - case reconnecting /// Unexpected disconnect from a remote system case disconnectedUnexpectedly(PubNubError) /// Unable to establish initial connection. Applies if `enableEventEngine` in `PubNubConfiguration` is true. case connectionError(PubNubError) + /// Indicates that the SDK has subscribed to new channels or channel groups. + /// This status is triggered each time the channel or channel group mix changes after the initial connection, and it provides all currently subscribed channels and channel groups + case subscriptionChanged(channels: [String], groups: [String]) /// If the connection is connected or attempting to connect public var isActive: Bool { switch self { - case .connecting, .connected, .reconnecting: + case .connected, .subscriptionChanged: return true default: return false @@ -41,37 +38,52 @@ public enum ConnectionStatus: Equatable { public var isConnected: Bool { if case .connected = self { return true + } else if case .subscriptionChanged = self { + return true } else { return false } } + public static func == (lhs: ConnectionStatus, rhs: ConnectionStatus) -> Bool { + switch (lhs, rhs) { + case (.connected, .connected): + return true + case (.disconnected, .disconnected): + return true + case let (.disconnectedUnexpectedly(lhsError), .disconnectedUnexpectedly(rhsError)): + return lhsError == rhsError + case let (.connectionError(lhsError), .connectionError(rhsError)): + return lhsError == rhsError + case let (.subscriptionChanged(lhsChannels, lhsGroups), .subscriptionChanged(rhsChannels, rhsGroups)): + return Set(lhsChannels) == Set(rhsChannels) && Set(lhsGroups) == Set(rhsGroups) + default: + return false + } + } + // swiftlint:disable:next cyclomatic_complexity func canTransition(to state: ConnectionStatus) -> Bool { switch (self, state) { - case (.connecting, .connected): - return true - case (.connecting, .disconnected): + case (.disconnected, .connected): return true - case (.connecting, .disconnectedUnexpectedly): + case (.disconnected, .connectionError): return true - case (.connecting, .connectionError): + case (.disconnected, .disconnectedUnexpectedly): return true - case (.connected, .disconnected): - return true - case (.reconnecting, .connected): + case (.disconnectedUnexpectedly, .connected): return true - case (.reconnecting, .disconnected): + case (.disconnectedUnexpectedly, .disconnected): return true - case (.reconnecting, .disconnectedUnexpectedly): + case (.connected, .subscriptionChanged): return true - case (.reconnecting, .connectionError): + case (.connected, .disconnected): return true - case (.disconnected, .connecting): + case (.connected, .disconnectedUnexpectedly): return true - case (.disconnectedUnexpectedly, .connecting): + case (.subscriptionChanged, .disconnectedUnexpectedly): return true - case (.connectionError, .connecting): + case (.subscriptionChanged, .disconnected): return true default: return false diff --git a/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift b/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift index abe084d5..89ccd1dc 100644 --- a/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift +++ b/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift @@ -76,7 +76,7 @@ class EventEngineSubscriptionSessionStrategy: SubscriptionSessionStrategy { subscribeEngine.dependencies = EventEngineDependencies( value: Subscribe.Dependencies( configuration: configuration, - listeners: listeners.allObjects + listeners: listeners ) ) } diff --git a/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift b/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift index a33384f0..a55b86b2 100644 --- a/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift +++ b/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift @@ -73,7 +73,6 @@ class LegacySubscriptionSessionStrategy: SubscriptionSessionStrategy { presenceSession: SessionReplaceable ) { self.configuration = configuration - var mutableSession = subscribeSession filterExpression = configuration.filterExpression nonSubscribeSession = presenceSession @@ -82,21 +81,9 @@ class LegacySubscriptionSessionStrategy: SubscriptionSessionStrategy { sessionStream = SessionListener(queue: responseQueue) // Add listener to session + var mutableSession = subscribeSession mutableSession.sessionStream = sessionStream longPollingSession = mutableSession - - sessionStream.didRetryRequest = { [weak self] _ in - self?.connectionStatus = .reconnecting - } - - sessionStream.sessionDidReceiveChallenge = { [weak self] _, _ in - if self?.connectionStatus == .reconnecting { - // Delay time for server to process connection after TLS handshake - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.05) { - self?.connectionStatus = .connected - } - } - } } deinit { @@ -130,7 +117,6 @@ class LegacySubscriptionSessionStrategy: SubscriptionSessionStrategy { /// - parameter timetoken: The timetoken to subscribe with func reconnect(at cursor: SubscribeCursor? = nil) { if !connectionStatus.isActive { - connectionStatus = .connecting // Start subscribe loop performSubscribeLoop(at: cursor) // Start presence heartbeat diff --git a/Sources/PubNub/Subscription/SubscriptionSession.swift b/Sources/PubNub/Subscription/SubscriptionSession.swift index 4f1789c3..9d2ef441 100644 --- a/Sources/PubNub/Subscription/SubscriptionSession.swift +++ b/Sources/PubNub/Subscription/SubscriptionSession.swift @@ -10,13 +10,15 @@ import Foundation -class SubscriptionSession: EventEmitter, StatusEmitter { +class SubscriptionSession: EventListenerInterface, StatusListenerInterface { // An underlying queue to dispatch events let queue: DispatchQueue // A unique identifier for subscription session var uuid: UUID { strategy.uuid } // The `Timetoken` used for the last successful subscription request var previousTokenResponse: SubscribeCursor? { strategy.previousTokenResponse } + // Additional listeners for global subscriptions + private let listenersContainer: SubscriptionListenersContainer = .init() // PSV2 feature to subscribe with a custom filter expression. var filterExpression: String? { @@ -59,6 +61,11 @@ class SubscriptionSession: EventEmitter, StatusEmitter { statusListener.didReceiveStatus = { [weak self] statusChange in if case .success(let newStatus) = statusChange { self?.onConnectionStateChange?(newStatus) + self?.listenersContainer.statusListeners.forEach { listener in + listener.queue.async { [weak listener] in + listener?.onConnectionStateChange?(newStatus) + } + } } } return statusListener @@ -106,16 +113,17 @@ class SubscriptionSession: EventEmitter, StatusEmitter { to channels: [String], and groups: [String] = [], at cursor: SubscribeCursor? = nil, - withPresence: Bool = false + withPresence: Bool = false, + using pubnub: PubNub ) { - let channelSubscriptions = channels.compactMap { - channel($0).subscription( + let channelSubscriptions = Set(channels).compactMap { + pubnub.channel($0).subscription( queue: queue, options: withPresence ? ReceivePresenceEvents() : SubscriptionOptions.empty() ) } - let channelGroupSubscriptions = groups.compactMap { - channelGroup($0).subscription( + let channelGroupSubscriptions = Set(groups).compactMap { + pubnub.channelGroup($0).subscription( queue: queue, options: withPresence ? ReceivePresenceEvents() : SubscriptionOptions.empty() ) @@ -203,9 +211,7 @@ class SubscriptionSession: EventEmitter, StatusEmitter { } } -// MARK: - SubscribeIntentReceiver - -extension SubscriptionSession: SubscribeReceiver { +extension SubscriptionSession { func hasRegisteredAdapter(with uuid: UUID) -> Bool { strategy.listeners.contains { $0?.uuid == uuid } } @@ -316,7 +322,8 @@ extension SubscriptionSession: SubscribeReceiver { ) } - // Returns an array of subscriptions that subscribe to at least one name in common with the given Subscription + // Returns an array of subscriptions, excluding the given subscription and the global listener, + // that subscribe to at least one name in common with the given subscription func matchingSubscriptions(for subscription: Subscription, presenceOnly: Bool) -> [SubscribeMessagesReceiver] { let allSubscriptions = strategy.listeners.compactMap { $0 as? BaseSubscriptionListenerAdapter @@ -324,15 +331,14 @@ extension SubscriptionSession: SubscribeReceiver { let namesToFind = subscription.subscriptionNames.filter { presenceOnly ? $0.isPresenceChannelName : true } - - return allSubscriptions.filter { - $0.uuid != subscription.uuid && $0.uuid != globalEventsListener.uuid - }.compactMap { - $0.receiver - }.filter { - ($0.subscriptionTopology[subscription.subscriptionType] ?? [String]()).contains { - namesToFind.contains($0) + return allSubscriptions.compactMap { + if $0.uuid != subscription.uuid && $0.uuid != globalEventsListener.uuid { + return $0.receiver + } else { + return nil } + }.filter { + !(Set($0.subscriptionTopology[subscription.subscriptionType] ?? []).isDisjoint(with: namesToFind)) } } @@ -359,14 +365,7 @@ extension SubscriptionSession: SubscribeReceiver { } let channels = presenceItemsOnly ? [] : Set(subscriptions.filter { - matchingSubscriptions( - for: $0, - presenceOnly: false - ).isEmpty && - matchingSubscriptions( - for: $0, - presenceOnly: true - ).isEmpty + matchingSubscriptions(for: $0, presenceOnly: false).isEmpty && matchingSubscriptions(for: $0, presenceOnly: true).isEmpty }.flatMap { $0.subscriptionNames }).symmetricDifference(presenceItems.map { @@ -382,26 +381,6 @@ extension SubscriptionSession: SubscribeReceiver { } } -// MARK: - EntityCreator - -extension SubscriptionSession: EntityCreator { - public func channel(_ name: String) -> ChannelRepresentation { - ChannelRepresentation(name: name, receiver: self) - } - - public func channelGroup(_ name: String) -> ChannelGroupRepresentation { - ChannelGroupRepresentation(name: name, receiver: self) - } - - public func userMetadata(_ name: String) -> UserMetadataRepresentation { - UserMetadataRepresentation(id: name, receiver: self) - } - - public func channelMetadata(_ name: String) -> ChannelMetadataRepresentation { - ChannelMetadataRepresentation(id: name, receiver: self) - } -} - // MARK: - EventStreamEmitter extension SubscriptionSession: EventStreamEmitter { @@ -452,10 +431,43 @@ extension SubscriptionSession: SubscribeMessagesReceiver { } func onPayloadsReceived(payloads: [SubscribeMessagePayload]) -> [PubNubEvent] { + // Translates payloads into PubNub Subscibe Loop events let events = payloads.map { $0.asPubNubEvent() } + // Emits events from the SubscriptionSession emit(events: events) + // Emits events to the underlying attached listeners + listenersContainer.eventListeners.forEach { $0.emit(events: events) } + // Returns events that were processed return events } +} + +extension SubscriptionSession: EventListenerHandler { + func addEventListener(_ listener: EventListener) { + listenersContainer.storeEventListener(listener) + } + + func removeEventListener(_ listener: EventListener) { + listenersContainer.removeEventListener(listener) + } + + func removeAllListeners() { + listenersContainer.removeAllEventListeners() + } +} + +extension SubscriptionSession { + func addStatusListener(_ listener: StatusListener) { + listenersContainer.storeStatusListener(listener) + } + + func removeStatusListener(_ listener: StatusListener) { + listenersContainer.removeStatusListener(listener) + } + + func removeAllStatusListeners() { + listenersContainer.removeAllStatusListeners() + } // swiftlint:disable:next file_length } diff --git a/Tests/PubNubContractTest/PubNubContractTestCase.swift b/Tests/PubNubContractTest/PubNubContractTestCase.swift index f0883b3d..bec9fba2 100644 --- a/Tests/PubNubContractTest/PubNubContractTestCase.swift +++ b/Tests/PubNubContractTest/PubNubContractTestCase.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK // Origin which should be used to reach mock server for contract testing. let mockServerAddress = "localhost:8090" diff --git a/Tests/PubNubContractTest/Steps/Access/PubNubAccessContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Access/PubNubAccessContractTestSteps.swift index ce4035b1..7d14a3b5 100644 --- a/Tests/PubNubContractTest/Steps/Access/PubNubAccessContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Access/PubNubAccessContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK import XCTest public class PubNubAccessContractTestSteps: PubNubContractTestCase { diff --git a/Tests/PubNubContractTest/Steps/CryptorModule/PubNubCryptoModuleContractTestSteps.swift b/Tests/PubNubContractTest/Steps/CryptorModule/PubNubCryptoModuleContractTestSteps.swift index 7d45ad30..c15f2bc6 100644 --- a/Tests/PubNubContractTest/Steps/CryptorModule/PubNubCryptoModuleContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/CryptorModule/PubNubCryptoModuleContractTestSteps.swift @@ -13,7 +13,7 @@ import Foundation import XCTest import CommonCrypto -@testable import PubNub +@testable import PubNubSDK public class PubNubCryptoModuleContractTestSteps: PubNubContractTestCase { var cryptorKind: String! diff --git a/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift b/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift index 05ead904..62e038e1 100644 --- a/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift @@ -11,7 +11,7 @@ import Foundation import Cucumberish -@testable import PubNub +@testable import PubNubSDK class PubNubEventEngineContractTestsSteps: PubNubContractTestCase { func extractExpectedResults(from: [AnyHashable: Any]?) -> (events: [String], invocations: [String]) { diff --git a/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineTestsHelpers.swift b/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineTestsHelpers.swift index 321266aa..838231b5 100644 --- a/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineTestsHelpers.swift +++ b/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineTestsHelpers.swift @@ -10,7 +10,7 @@ import Foundation -@testable import PubNub +@testable import PubNubSDK protocol ContractTestIdentifiable { var contractTestIdentifier: String { get } diff --git a/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift b/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift index 490ef513..a28e73a8 100644 --- a/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift @@ -11,7 +11,7 @@ import Cucumberish import Foundation -@testable import PubNub +@testable import PubNubSDK extension Presence.Invocation: ContractTestIdentifiable { var contractTestIdentifier: String { diff --git a/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift b/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift index 8e219622..5b65bb57 100644 --- a/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift +++ b/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift @@ -11,7 +11,7 @@ import Cucumberish import Foundation -@testable import PubNub +@testable import PubNubSDK extension Subscribe.Invocation: ContractTestIdentifiable { var contractTestIdentifier: String { diff --git a/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift index 0f907a32..8de81dfe 100644 --- a/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK public class PubNubFilesContractTestSteps: PubNubContractTestCase { override public func setup() { diff --git a/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift b/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift index 3c1cc2e3..e0721ee4 100644 --- a/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK public class PubNubHistoryContractTestSteps: PubNubContractTestCase { override public func setup() { diff --git a/Tests/PubNubContractTest/Steps/Message Actions/PubNubMessageActionsContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Message Actions/PubNubMessageActionsContractTestSteps.swift index 41ea0f10..deffd1cf 100644 --- a/Tests/PubNubContractTest/Steps/Message Actions/PubNubMessageActionsContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Message Actions/PubNubMessageActionsContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK public class PubNubMessageActionsContractTestSteps: PubNubContractTestCase { override public func setup() { diff --git a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsChannelMetadataContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsChannelMetadataContractTestSteps.swift index f23e0565..552992d7 100644 --- a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsChannelMetadataContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsChannelMetadataContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK import XCTest public class PubNubObjectsChannelMetadataContractTestSteps: PubNubObjectsContractTests { diff --git a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsContractTests.swift b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsContractTests.swift index f79e6697..831b413d 100644 --- a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsContractTests.swift +++ b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsContractTests.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK @objc public class PubNubObjectsContractTests: PubNubContractTestCase { public static var membershipMetadata: [String: PubNubTestMembershipForAction?] = [:] diff --git a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembersContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembersContractTestSteps.swift index 81099775..9f774825 100644 --- a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembersContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembersContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK import XCTest public class PubNubObjectsMembersContractTestSteps: PubNubObjectsContractTests { diff --git a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembershipsContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembershipsContractTestSteps.swift index f9e793ef..882fa2a6 100644 --- a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembershipsContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsMembershipsContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK import XCTest public class PubNubObjectsMembershipsContractTestSteps: PubNubObjectsContractTests { diff --git a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsTestHelpers.swift b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsTestHelpers.swift index 9b5a3249..adbd75e6 100644 --- a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsTestHelpers.swift +++ b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsTestHelpers.swift @@ -9,7 +9,7 @@ // import Foundation -import PubNub +import PubNubSDK extension PubNubContractTestCase { /// Use entity name to compose path on JSON with it's representation. diff --git a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsUUIDMetadataContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsUUIDMetadataContractTestSteps.swift index 5ce0fbee..cbce935b 100644 --- a/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsUUIDMetadataContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Objects/PubNubObjectsUUIDMetadataContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK import XCTest public class PubNubObjectsUUIDMetadataContractTestSteps: PubNubObjectsContractTests { diff --git a/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift index 115876d3..633171a9 100644 --- a/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK public class PubNubPublishContractTestSteps: PubNubContractTestCase { override public func setup() { diff --git a/Tests/PubNubContractTest/Steps/Push/PubNubPushContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Push/PubNubPushContractTestSteps.swift index e5d1709c..61c7af80 100644 --- a/Tests/PubNubContractTest/Steps/Push/PubNubPushContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Push/PubNubPushContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK public class PubNubPushContractTestSteps: PubNubContractTestCase { override public func setup() { diff --git a/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift index 6f019fb2..0713ebe9 100644 --- a/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK public class PubNubSubscribeContractTestSteps: PubNubContractTestCase { fileprivate var cryptoModule: CryptoModule? diff --git a/Tests/PubNubContractTest/Steps/Utilities/PubNubTimeContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Utilities/PubNubTimeContractTestSteps.swift index ebcd2ce3..1fccee13 100644 --- a/Tests/PubNubContractTest/Steps/Utilities/PubNubTimeContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Utilities/PubNubTimeContractTestSteps.swift @@ -10,7 +10,7 @@ import Cucumberish import Foundation -import PubNub +import PubNubSDK public class PubNubTimeContractTestSteps: PubNubContractTestCase { override public func setup() { diff --git a/Tests/PubNubTests/EventEngine/DispatcherTests.swift b/Tests/PubNubTests/EventEngine/DispatcherTests.swift index c1989c5d..a595347f 100644 --- a/Tests/PubNubTests/EventEngine/DispatcherTests.swift +++ b/Tests/PubNubTests/EventEngine/DispatcherTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK class DispatcherTests: XCTestCase { func testDispatcher_FinishingAnyInvocationNotifiesListener() { diff --git a/Tests/PubNubTests/EventEngine/EventEngineTests.swift b/Tests/PubNubTests/EventEngine/EventEngineTests.swift index fb5311f1..ba626a55 100644 --- a/Tests/PubNubTests/EventEngine/EventEngineTests.swift +++ b/Tests/PubNubTests/EventEngine/EventEngineTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK fileprivate var initialState: ExampleState { ExampleState( diff --git a/Tests/PubNubTests/EventEngine/Helpers/EffectInvocation+Equatable.swift b/Tests/PubNubTests/EventEngine/Helpers/EffectInvocation+Equatable.swift index d62cad9b..6a32c585 100644 --- a/Tests/PubNubTests/EventEngine/Helpers/EffectInvocation+Equatable.swift +++ b/Tests/PubNubTests/EventEngine/Helpers/EffectInvocation+Equatable.swift @@ -10,7 +10,7 @@ import Foundation -@testable import PubNub +@testable import PubNubSDK extension EffectInvocation: Equatable where Invocation: Equatable { public static func ==(lhs: EffectInvocation, rhs: EffectInvocation) -> Bool { diff --git a/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift b/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift index 4d78ef8f..af51cdc3 100644 --- a/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK class HeartbeatEffectTests: XCTestCase { private var mockUrlSession: MockURLSession! diff --git a/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift b/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift index 00d6cefc..332bb9b3 100644 --- a/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK class LeaveEffectTests: XCTestCase { private var mockUrlSession: MockURLSession! diff --git a/Tests/PubNubTests/EventEngine/Presence/PresenceTransitionTests.swift b/Tests/PubNubTests/EventEngine/Presence/PresenceTransitionTests.swift index fbf3b8f1..209540a2 100644 --- a/Tests/PubNubTests/EventEngine/Presence/PresenceTransitionTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/PresenceTransitionTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK extension Presence.Invocation: Equatable { public static func ==(lhs: Presence.Invocation, rhs: Presence.Invocation) -> Bool { diff --git a/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift b/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift index 92a54d30..d43d8e5a 100644 --- a/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK class WaitEffectTests: XCTestCase { private var mockUrlSession: MockURLSession! diff --git a/Tests/PubNubTests/EventEngine/Subscribe/EmitMessagesTests.swift b/Tests/PubNubTests/EventEngine/Subscribe/EmitMessagesTests.swift index 9ff29869..1374e5f7 100644 --- a/Tests/PubNubTests/EventEngine/Subscribe/EmitMessagesTests.swift +++ b/Tests/PubNubTests/EventEngine/Subscribe/EmitMessagesTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK fileprivate class MockListener: BaseSubscriptionListener { var onEmitMessagesCalled: ([SubscribeMessagePayload]) -> Void = { _ in } @@ -26,22 +26,22 @@ fileprivate class MockListener: BaseSubscriptionListener { } class EmitMessagesTests: XCTestCase { - private var listeners: [MockListener] = [] + private var subscriptions: [MockListener] = [] override func setUp() { - listeners = (0...2).map { _ in MockListener() } + subscriptions = (0...2).map { _ in MockListener() } super.setUp() } override func tearDown() { - listeners = [] + subscriptions = [] super.tearDown() } func testListener_WithMessage() { let expectation = XCTestExpectation(description: "Emit Messages") expectation.assertForOverFulfill = true - expectation.expectedFulfillmentCount = listeners.count + expectation.expectedFulfillmentCount = subscriptions.count let messages = [ testMessage, @@ -54,11 +54,11 @@ class EmitMessagesTests: XCTestCase { let effect = EmitMessagesEffect( messages: messages, cursor: SubscribeCursor(timetoken: 12345, region: 11), - listeners: listeners, + subscriptions: WeakSet(subscriptions), messageCache: MessageCache() ) - listeners.forEach { + subscriptions.forEach { $0.onEmitMessagesCalled = { receivedMessages in XCTAssertTrue(receivedMessages.map { $0.messageType } == messages.map { $0.messageType }) expectation.fulfill() @@ -75,21 +75,22 @@ class EmitMessagesTests: XCTestCase { func testListener_MessageCountExceededMaximum() { let expectation = XCTestExpectation(description: "Emit Messages") expectation.assertForOverFulfill = true - expectation.expectedFulfillmentCount = listeners.count + expectation.expectedFulfillmentCount = subscriptions.count + let generatedMessages = (1...100).map { + generateMessage( + with: .message, + payload: AnyJSON("Hello, it's message number \($0)") + ) + } let effect = EmitMessagesEffect( - messages: (1...100).map { - generateMessage( - with: .message, - payload: AnyJSON("Hello, it's message number \($0)") - ) - }, + messages: generatedMessages, cursor: SubscribeCursor(timetoken: 12345, region: 11), - listeners: listeners, + subscriptions: WeakSet(subscriptions), messageCache: MessageCache() ) - listeners.forEach() { + subscriptions.forEach() { $0.onEmitSubscribeEventCalled = { event in if case let .errorReceived(error) = event { XCTAssertTrue(error.reason == .messageCountExceededMaximum) @@ -108,21 +109,22 @@ class EmitMessagesTests: XCTestCase { func testEffect_SkipsDuplicatedMessages() { let expectation = XCTestExpectation(description: "Emit Messages") expectation.assertForOverFulfill = true - expectation.expectedFulfillmentCount = listeners.count + expectation.expectedFulfillmentCount = subscriptions.count + let generatedMessages = (1...50).map { _ in + generateMessage( + with: .message, + payload: AnyJSON("Hello, it's a message") + ) + } let effect = EmitMessagesEffect( - messages: (1...50).map { _ in - generateMessage( - with: .message, - payload: AnyJSON("Hello, it's a message") - ) - }, + messages: generatedMessages, cursor: SubscribeCursor(timetoken: 12345, region: 11), - listeners: listeners, + subscriptions: WeakSet(subscriptions), messageCache: MessageCache() ) - listeners.forEach { + subscriptions.forEach { $0.onEmitMessagesCalled = { messages in XCTAssertTrue(messages.count == 1) XCTAssertTrue(messages[0].payload == "Hello, it's a message") @@ -156,7 +158,7 @@ class EmitMessagesTests: XCTestCase { let effect = EmitMessagesEffect( messages: newMessages, cursor: SubscribeCursor(timetoken: 12345, region: 11), - listeners: listeners, + subscriptions: WeakSet(subscriptions), messageCache: cache ) diff --git a/Tests/PubNubTests/EventEngine/Subscribe/EmitStatusTests.swift b/Tests/PubNubTests/EventEngine/Subscribe/EmitStatusTests.swift index 07b4b0c5..78dfc1b9 100644 --- a/Tests/PubNubTests/EventEngine/Subscribe/EmitStatusTests.swift +++ b/Tests/PubNubTests/EventEngine/Subscribe/EmitStatusTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK fileprivate class MockListener: BaseSubscriptionListener { var onEmitSubscribeEventCalled: ((PubNubSubscribeEvent) -> Void) = { _ in } @@ -22,32 +22,34 @@ fileprivate class MockListener: BaseSubscriptionListener { } class EmitStatusTests: XCTestCase { - private var listeners: [MockListener] = [] + private var subscriptions: [MockListener] = [] override func setUp() { - listeners = (0...2).map { _ in MockListener() } + subscriptions = (0...2).map { _ in MockListener() } super.setUp() } override func tearDown() { - listeners = [] + subscriptions = [] super.tearDown() } func testEmitStatus_FromDisconnectedToConnected() { let expectation = XCTestExpectation(description: "Emit Status Effect") - expectation.expectedFulfillmentCount = listeners.count + expectation.expectedFulfillmentCount = subscriptions.count expectation.assertForOverFulfill = true + let testedStatusChange = Subscribe.ConnectionStatusChange( + oldStatus: .disconnected, + newStatus: .connected, + error: nil + ) let effect = EmitStatusEffect( - statusChange: Subscribe.ConnectionStatusChange( - oldStatus: .disconnected, - newStatus: .connected, - error: nil - ), - listeners: listeners + statusChange: testedStatusChange, + subscriptions: WeakSet(subscriptions) ) - listeners.forEach { + + subscriptions.forEach { $0.onEmitSubscribeEventCalled = { event in if case let .connectionChanged(status) = event { XCTAssertEqual(status, .connected) @@ -67,22 +69,24 @@ class EmitStatusTests: XCTestCase { func testEmitStatus_WithError() { let expectation = XCTestExpectation(description: "Emit Status Effect") - expectation.expectedFulfillmentCount = listeners.count + expectation.expectedFulfillmentCount = subscriptions.count expectation.assertForOverFulfill = true let errorExpectation = XCTestExpectation(description: "Emit Status Effect - Error Listener") - errorExpectation.expectedFulfillmentCount = listeners.count + errorExpectation.expectedFulfillmentCount = subscriptions.count errorExpectation.assertForOverFulfill = true + let testedStatusChange = Subscribe.ConnectionStatusChange( + oldStatus: .disconnected, + newStatus: .connected, + error: PubNubError(.unknown) + ) let effect = EmitStatusEffect( - statusChange: Subscribe.ConnectionStatusChange( - oldStatus: .disconnected, - newStatus: .connected, - error: PubNubError(.unknown) - ), - listeners: listeners + statusChange: testedStatusChange, + subscriptions: WeakSet(subscriptions) ) - listeners.forEach { + + subscriptions.forEach { $0.onEmitSubscribeEventCalled = { event in if case let .connectionChanged(status) = event { XCTAssertEqual(status, .connected) diff --git a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift index 18a7edea..9649cfbf 100644 --- a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift +++ b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK class SubscribeEffectsTests: XCTestCase { private var mockUrlSession: MockURLSession! diff --git a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeInputTests.swift b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeInputTests.swift index 1f196567..42951785 100644 --- a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeInputTests.swift +++ b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeInputTests.swift @@ -10,7 +10,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK class SubscribeInputTests: XCTestCase { func test_ChannelsWithoutPresence() { diff --git a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeTransitionTests.swift b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeTransitionTests.swift index 86b5e664..d4e38875 100644 --- a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeTransitionTests.swift +++ b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeTransitionTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest -@testable import PubNub +@testable import PubNubSDK extension SubscribeState { func isEqual(to otherState: some SubscribeState) -> Bool { @@ -77,25 +77,25 @@ class SubscribeTransitionTests: XCTestCase { groups: ["g1", "g1-pnpres", "g2", "g2", "g2-pnpres", "g3"] ) ) - let expectedInvocations: [EffectInvocation] = [ - .managed(.handshakeRequest( - channels: ["c1", "c1-pnpres", "c2"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] - )) - ] - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: false) ] - let expectedState = Subscribe.HandshakingState(input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor(timetoken: 0)!) + let expectedInvocations: [EffectInvocation] = [ + .managed(.handshakeRequest( + channels: ["c1", "c1-pnpres", "c2"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] + )) + ] + let expectedState = Subscribe.HandshakingState( + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 0)! + ) XCTAssertTrue(results.state.isEqual(to: expectedState)) XCTAssertTrue(results.invocations.elementsEqual(expectedInvocations)) @@ -113,25 +113,26 @@ class SubscribeTransitionTests: XCTestCase { groups: ["g1", "g1-pnpres", "g2", "g2", "g2-pnpres", "g3"] ) ) - let expectedInvocations: [EffectInvocation] = [ - .managed(.handshakeRequest( - channels: ["c1", "c1-pnpres", "c2"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] - )) - ] - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: false) ] - let expectedState = Subscribe.HandshakingState(input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor(timetoken: 0, region: 0)) + let expectedInvocations: [EffectInvocation] = [ + .managed(.handshakeRequest( + channels: ["c1", "c1-pnpres", "c2"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] + )) + ] + + let expectedState = Subscribe.HandshakingState( + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 0, region: 0) + ) XCTAssertTrue(results.state.isEqual(to: expectedState)) XCTAssertTrue(results.invocations.elementsEqual(expectedInvocations)) @@ -154,10 +155,11 @@ class SubscribeTransitionTests: XCTestCase { PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: false) ] - let expectedState = Subscribe.HandshakeStoppedState(input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor(timetoken: 0, region: 0)) + + let expectedState = Subscribe.HandshakeStoppedState( + input: SubscribeInput(channels: expectedChannels,groups: expectedGroups), + cursor: SubscribeCursor(timetoken: 0, region: 0) + ) XCTAssertTrue(results.state.isEqual(to: expectedState)) XCTAssertTrue(results.invocations.isEmpty) @@ -171,13 +173,6 @@ class SubscribeTransitionTests: XCTestCase { groups: ["g1", "g1-pnpres", "g2", "g2", "g2-pnpres", "g3"] ) ) - let expectedInvocations: [EffectInvocation] = [ - .cancel(.handshakeRequest), - .managed(.handshakeRequest( - channels: ["c1", "c1-pnpres", "c2"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] - )) - ] let expectedChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: false) @@ -187,48 +182,70 @@ class SubscribeTransitionTests: XCTestCase { PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: false) ] - let expectedState = Subscribe.HandshakingState(input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor(timetoken: 0, region: 0)) + let expectedInvocations: [EffectInvocation] = [ + .cancel(.handshakeRequest), + .managed(.handshakeRequest( + channels: ["c1", "c1-pnpres", "c2"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] + )) + ] + + let expectedState = Subscribe.HandshakingState( + input: SubscribeInput(channels: expectedChannels, groups: expectedGroups), + cursor: SubscribeCursor(timetoken: 0, region: 0) + ) XCTAssertTrue(results.state.isEqual(to: expectedState)) XCTAssertTrue(results.invocations.elementsEqual(expectedInvocations)) } func test_SubscriptionChangedForReceivingState() throws { + let status: ConnectionStatus = .subscriptionChanged( + channels: input.subscribedChannelNames, + groups: input.subscribedGroupNames + ) let results = transition.transition( - from: Subscribe.ReceivingState(input: input, cursor: SubscribeCursor(timetoken: 5001000, region: 22)), + from: Subscribe.ReceivingState( + input: input, cursor: SubscribeCursor(timetoken: 5001000, region: 22), + connectionStatus: status + ), event: .subscriptionChanged( channels: ["c1", "c1", "c1-pnpres", "c2"], groups: ["g1", "g1-pnpres", "g2", "g2", "g2-pnpres", "g3"] ) ) + let expChannels = [ + PubNubChannel(id: "c1", withPresence: true), + PubNubChannel(id: "c2", withPresence: false) + ] + let expGroups = [ + PubNubChannel(id: "g1", withPresence: true), + PubNubChannel(id: "g2", withPresence: true), + PubNubChannel(id: "g3", withPresence: false) + ] + + let expectedNewStatus: ConnectionStatus = .subscriptionChanged( + channels: expChannels.map { $0.id }, + groups: expGroups.map { $0.id } + ) let expectedInvocations: [EffectInvocation] = [ .cancel(.receiveMessages), + .regular(.emitStatus(change: Subscribe.ConnectionStatusChange( + oldStatus: status, + newStatus: expectedNewStatus, + error: nil + ))), .managed(.receiveMessages( channels: ["c1", "c1-pnpres", "c2"], groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"], cursor: SubscribeCursor(timetoken: 5001000, region: 22) )) ] - let expectedChannels = [ - PubNubChannel(id: "c1", withPresence: true), - PubNubChannel(id: "c2", withPresence: false) - ] - let expectedGroups = [ - PubNubChannel(id: "g1", withPresence: true), - PubNubChannel(id: "g2", withPresence: true), - PubNubChannel(id: "g3", withPresence: false) - ] + let expectedState = Subscribe.ReceivingState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor( - timetoken: 5001000, - region: 22 - ) + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 5001000, region: 22), + connectionStatus: .subscriptionChanged(channels: expChannels.map { $0.id }, groups: expGroups.map { $0.id }) ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -247,27 +264,24 @@ class SubscribeTransitionTests: XCTestCase { groups: ["g1", "g1-pnpres", "g2", "g2", "g2-pnpres", "g3"] ) ) - - let expectedInvocations: [EffectInvocation] = [ - .managed(.handshakeRequest( - channels: ["c1", "c1-pnpres", "c2"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] - )) - ] - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: false) ] + let expectedInvocations: [EffectInvocation] = [ + .managed(.handshakeRequest( + channels: ["c1", "c1-pnpres", "c2"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3"] + )) + ] let expectedState = Subscribe.HandshakingState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor(timetoken: 500100900, region: 11) + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 500100900, region: 11) ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -282,23 +296,18 @@ class SubscribeTransitionTests: XCTestCase { groups: ["g1", "g1-pnpres", "g2", "g2", "g2-pnpres", "g3"] ) ) - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: false) ] let expectedState = Subscribe.ReceiveStoppedState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor( - timetoken: 500100900, - region: 11 - ) + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 500100900, region: 11) ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -308,40 +317,58 @@ class SubscribeTransitionTests: XCTestCase { // MARK: - Subscription Restored func test_SubscriptionRestoredForReceivingState() throws { + let status: ConnectionStatus = .subscriptionChanged( + channels: input.subscribedChannelNames, + groups: input.subscribedGroupNames + ) let results = transition.transition( - from: Subscribe.ReceivingState(input: input, cursor: SubscribeCursor(timetoken: 1500100900, region: 41)), + from: Subscribe.ReceivingState( + input: input, cursor: SubscribeCursor(timetoken: 1500100900, region: 41), + connectionStatus: status + ), event: .subscriptionRestored( channels: ["c1", "c1-pnpres", "c2", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4", "g4"], cursor: SubscribeCursor(timetoken: 100, region: 55) ) ) - let expectedInvocations: [EffectInvocation] = [ - .cancel(.receiveMessages), - .managed(.receiveMessages( - channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"], - cursor: SubscribeCursor(timetoken: 100, region: 55) - )) - ] - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: true), PubNubChannel(id: "c3", withPresence: true), PubNubChannel(id: "c4", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: true), PubNubChannel(id: "g4", withPresence: false) ] + let expectedNewStatus: ConnectionStatus = .subscriptionChanged( + channels: expChannels.map { $0.id }, + groups: expGroups.map { $0.id } + ) + let expectedInvocations: [EffectInvocation] = [ + .cancel(.receiveMessages), + .regular(.emitStatus(change: Subscribe.ConnectionStatusChange( + oldStatus: status, + newStatus: expectedNewStatus, + error: nil + ))), + .managed(.receiveMessages( + channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"], + cursor: SubscribeCursor(timetoken: 100, region: 55) + )) + ] + let expectedState = Subscribe.ReceivingState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), - cursor: SubscribeCursor(timetoken: 100, region: 55) + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 100, region: 55), + connectionStatus: .subscriptionChanged( + channels: expChannels.map { $0.id }, + groups: expGroups.map { $0.id } + ) ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -361,29 +388,28 @@ class SubscribeTransitionTests: XCTestCase { cursor: SubscribeCursor(timetoken: 100, region: 55) ) ) - let expectedInvocations: [EffectInvocation] = [ - .managed(.handshakeRequest( - channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"] - )) - ] - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: true), PubNubChannel(id: "c3", withPresence: true), PubNubChannel(id: "c4", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: true), PubNubChannel(id: "g4", withPresence: false) ] + + let expectedInvocations: [EffectInvocation] = [ + .managed(.handshakeRequest( + channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"] + )) + ] let expectedState = Subscribe.HandshakingState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor(timetoken: 100, region: 55) + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 100, region: 55) ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -402,23 +428,20 @@ class SubscribeTransitionTests: XCTestCase { cursor: SubscribeCursor(timetoken: 100, region: 55) ) ) - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: true), PubNubChannel(id: "c3", withPresence: true), PubNubChannel(id: "c4", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: true), PubNubChannel(id: "g4", withPresence: false) ] let expectedState = Subscribe.ReceiveStoppedState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), + input: SubscribeInput(channels: expChannels, groups: expGroups), cursor: SubscribeCursor(timetoken: 100, region: 55) ) @@ -435,30 +458,27 @@ class SubscribeTransitionTests: XCTestCase { cursor: SubscribeCursor(timetoken: 100, region: 55) ) ) - let expectedInvocations: [EffectInvocation] = [ - .cancel(.handshakeRequest), - .managed(.handshakeRequest( - channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"] - )) - ] - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: true), PubNubChannel(id: "c3", withPresence: true), PubNubChannel(id: "c4", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: true), PubNubChannel(id: "g4", withPresence: false) ] + let expectedInvocations: [EffectInvocation] = [ + .cancel(.handshakeRequest), + .managed(.handshakeRequest( + channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"] + )) + ] let expectedState = Subscribe.HandshakingState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), + input: SubscribeInput(channels: expChannels, groups: expGroups), cursor: SubscribeCursor(timetoken: 100, region: 55) ) @@ -479,29 +499,28 @@ class SubscribeTransitionTests: XCTestCase { cursor: SubscribeCursor(timetoken: 100, region: 55) ) ) - let expectedInvocations: [EffectInvocation] = [ - .managed(.handshakeRequest( - channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], - groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"] - )) - ] - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: true), PubNubChannel(id: "c3", withPresence: true), PubNubChannel(id: "c4", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: true), PubNubChannel(id: "g4", withPresence: false) ] + let expectedInvocations: [EffectInvocation] = [ + .managed(.handshakeRequest( + channels: ["c1", "c1-pnpres", "c2", "c2-pnpres", "c3", "c3-pnpres", "c4"], + groups: ["g1", "g1-pnpres", "g2", "g2-pnpres", "g3", "g3-pnpres", "g4"] + )) + ] + let expectedState = Subscribe.HandshakingState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), cursor: SubscribeCursor(timetoken: 100, region: 55) + input: SubscribeInput(channels: expChannels, groups: expGroups), + cursor: SubscribeCursor(timetoken: 100, region: 55) ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -517,23 +536,20 @@ class SubscribeTransitionTests: XCTestCase { cursor: SubscribeCursor(timetoken: 100, region: 55) ) ) - let expectedChannels = [ + let expChannels = [ PubNubChannel(id: "c1", withPresence: true), PubNubChannel(id: "c2", withPresence: true), PubNubChannel(id: "c3", withPresence: true), PubNubChannel(id: "c4", withPresence: false) ] - let expectedGroups = [ + let expGroups = [ PubNubChannel(id: "g1", withPresence: true), PubNubChannel(id: "g2", withPresence: true), PubNubChannel(id: "g3", withPresence: true), PubNubChannel(id: "g4", withPresence: false) ] let expectedState = Subscribe.HandshakeStoppedState( - input: SubscribeInput( - channels: expectedChannels, - groups: expectedGroups - ), + input: SubscribeInput(channels: expChannels, groups: expGroups), cursor: SubscribeCursor(timetoken: 100, region: 55) ) @@ -555,7 +571,7 @@ class SubscribeTransitionTests: XCTestCase { let expectedInvocations: [EffectInvocation] = [ .cancel(.handshakeRequest), .regular(.emitStatus(change: Subscribe.ConnectionStatusChange( - oldStatus: .connecting, + oldStatus: .disconnected, newStatus: .connected, error: nil ))), @@ -566,7 +582,8 @@ class SubscribeTransitionTests: XCTestCase { ] let expectedState = Subscribe.ReceivingState( input: input, - cursor: SubscribeCursor(timetoken: 1500100900, region: 41) + cursor: SubscribeCursor(timetoken: 1500100900, region: 41), + connectionStatus: .connected ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -583,7 +600,7 @@ class SubscribeTransitionTests: XCTestCase { let expectedInvocations: [EffectInvocation] = [ .cancel(.handshakeRequest), .regular(.emitStatus(change: Subscribe.ConnectionStatusChange( - oldStatus: .connecting, + oldStatus: .disconnected, newStatus: .connectionError(PubNubError(.unknown)), error: PubNubError(.unknown)) )) @@ -604,7 +621,8 @@ class SubscribeTransitionTests: XCTestCase { let results = transition.transition( from: Subscribe.ReceivingState( input: input, - cursor: SubscribeCursor(timetoken: 18001000, region: 123) + cursor: SubscribeCursor(timetoken: 18001000, region: 123), + connectionStatus: .connected ), event: .receiveSuccess( cursor: SubscribeCursor(timetoken: 18002000, region: 123), @@ -625,7 +643,8 @@ class SubscribeTransitionTests: XCTestCase { ] let expectedState = Subscribe.ReceivingState( input: input, - cursor: SubscribeCursor(timetoken: 18002000, region: 123) + cursor: SubscribeCursor(timetoken: 18002000, region: 123), + connectionStatus: .connected ) XCTAssertTrue(results.state.isEqual(to: expectedState)) @@ -638,7 +657,8 @@ class SubscribeTransitionTests: XCTestCase { let results = transition.transition( from: Subscribe.ReceivingState( input: input, - cursor: SubscribeCursor(timetoken: 100500900, region: 11) + cursor: SubscribeCursor(timetoken: 100500900, region: 11), + connectionStatus: .connected ), event: .receiveFailure(error: PubNubError(.unknown)) ) @@ -762,7 +782,7 @@ class SubscribeTransitionTests: XCTestCase { let expectedInvocations: [EffectInvocation] = [ .cancel(.handshakeRequest), .regular(.emitStatus(change: Subscribe.ConnectionStatusChange( - oldStatus: .connecting, + oldStatus: .disconnected, newStatus: .disconnected, error: nil ))) @@ -780,7 +800,8 @@ class SubscribeTransitionTests: XCTestCase { let results = transition.transition( from: Subscribe.ReceivingState( input: input, - cursor: SubscribeCursor(timetoken: 123, region: 456) + cursor: SubscribeCursor(timetoken: 123, region: 456), + connectionStatus: .connected ), event: .disconnect ) @@ -811,14 +832,13 @@ class SubscribeTransitionTests: XCTestCase { let expectedInvocations: [EffectInvocation] = [ .cancel(.handshakeRequest), .regular(.emitStatus(change: Subscribe.ConnectionStatusChange( - oldStatus: .connecting, + oldStatus: .disconnected, newStatus: .disconnected, error: nil ))) ] - let expectedState = Subscribe.UnsubscribedState() - XCTAssertTrue(results.state.isEqual(to: expectedState)) + XCTAssertTrue(results.state.isEqual(to: Subscribe.UnsubscribedState())) XCTAssertTrue(results.invocations.elementsEqual(expectedInvocations)) } @@ -865,7 +885,8 @@ class SubscribeTransitionTests: XCTestCase { let results = transition.transition( from: Subscribe.ReceivingState( input: input, - cursor: SubscribeCursor(timetoken: 123, region: 456) + cursor: SubscribeCursor(timetoken: 123, region: 456), + connectionStatus: .connected ), event: .unsubscribeAll ) @@ -877,9 +898,8 @@ class SubscribeTransitionTests: XCTestCase { error: nil ))) ] - let expectedState = Subscribe.UnsubscribedState() - XCTAssertTrue(results.state.isEqual(to: expectedState)) + XCTAssertTrue(results.state.isEqual(to: Subscribe.UnsubscribedState())) XCTAssertTrue(results.invocations.elementsEqual(expectedInvocations)) } @@ -899,9 +919,8 @@ class SubscribeTransitionTests: XCTestCase { error: nil ))) ] - let expectedState = Subscribe.UnsubscribedState() - XCTAssertTrue(results.state.isEqual(to: expectedState)) + XCTAssertTrue(results.state.isEqual(to: Subscribe.UnsubscribedState())) XCTAssertTrue(results.invocations.elementsEqual(expectedInvocations)) } @@ -920,9 +939,8 @@ class SubscribeTransitionTests: XCTestCase { error: nil ))) ] - let expectedState = Subscribe.UnsubscribedState() - XCTAssertTrue(results.state.isEqual(to: expectedState)) + XCTAssertTrue(results.state.isEqual(to: Subscribe.UnsubscribedState())) XCTAssertTrue(results.invocations.elementsEqual(expectedInvocations)) } } diff --git a/Tests/PubNubTests/Events/New/SubscriptionSetTests.swift b/Tests/PubNubTests/Events/New/SubscriptionSetTests.swift index 5756b976..66223e41 100644 --- a/Tests/PubNubTests/Events/New/SubscriptionSetTests.swift +++ b/Tests/PubNubTests/Events/New/SubscriptionSetTests.swift @@ -9,7 +9,7 @@ // import XCTest -@testable import PubNub +@testable import PubNubSDK class SubscriptionSetTests: XCTestCase { private let pubnub = PubNub( @@ -125,4 +125,69 @@ class SubscriptionSetTests: XCTestCase { expectedTopology[.channelGroup]!.sorted(by: <) ) } + + func testSubscriptionSet_WithListeners() { + let messagesExpectation = XCTestExpectation(description: "Message") + messagesExpectation.assertForOverFulfill = true + messagesExpectation.expectedFulfillmentCount = 1 + + let signalExpectation = XCTestExpectation(description: "Signal") + signalExpectation.assertForOverFulfill = true + signalExpectation.expectedFulfillmentCount = 1 + + let messageAction = XCTestExpectation(description: "Message Action") + messageAction.assertForOverFulfill = true + messageAction.expectedFulfillmentCount = 1 + + let presenceChangeExpectation = XCTestExpectation(description: "Presence") + presenceChangeExpectation.assertForOverFulfill = true + presenceChangeExpectation.expectedFulfillmentCount = 1 + + let appContextExpectation = XCTestExpectation(description: "App Context") + appContextExpectation.assertForOverFulfill = true + appContextExpectation.expectedFulfillmentCount = 1 + + let fileExpectation = XCTestExpectation(description: "File") + fileExpectation.assertForOverFulfill = true + fileExpectation.expectedFulfillmentCount = 1 + + let channel = pubnub.channel("test-channel") + let channel2 = pubnub.channel("test-channel2") + let subscriptionSet = pubnub.subscription(entities: [channel, channel2]) + + let listener = EventListener( + onMessage: { _ in + messagesExpectation.fulfill() + }, + onSignal: { _ in + signalExpectation.fulfill() + }, + onPresence: { _ in + presenceChangeExpectation.fulfill() + }, + onMessageAction: { _ in + messageAction.fulfill() + }, + onFileEvent: { _ in + fileExpectation.fulfill() + }, + onAppContext: { _ in + appContextExpectation.fulfill() + } + ) + + subscriptionSet.addEventListener(listener) + subscriptionSet.onPayloadsReceived(payloads: [ + mockMessagePayload(channel: channel.name), mockSignalPayload(channel: channel2.name), + mockPresenceChangePayload(channel: channel.name), mockAppContextPayload(channel: channel2.name), + mockFilePayload(channel: channel.name), mockMessageActionPayload(channel: channel2.name) + ]) + + let allExpectations = [ + messagesExpectation, signalExpectation, presenceChangeExpectation, + messageAction, fileExpectation, appContextExpectation + ] + + wait(for: allExpectations, timeout: 1.0) + } } diff --git a/Tests/PubNubTests/Events/New/SubscriptionTests.swift b/Tests/PubNubTests/Events/New/SubscriptionTests.swift index 895d198f..2a538772 100644 --- a/Tests/PubNubTests/Events/New/SubscriptionTests.swift +++ b/Tests/PubNubTests/Events/New/SubscriptionTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class SubscriptionTests: XCTestCase { @@ -236,4 +236,68 @@ class SubscriptionTests: XCTestCase { XCTAssertEqual(subscription.subscriptionType, .channelGroup) XCTAssertEqual(subscription.subscriptionTopology, [.channelGroup: ["g", "g-pnpres"]]) } + + func testSubscription_WithListeners() { + let messagesExpectation = XCTestExpectation(description: "Message") + messagesExpectation.assertForOverFulfill = true + messagesExpectation.expectedFulfillmentCount = 1 + + let signalExpectation = XCTestExpectation(description: "Signal") + signalExpectation.assertForOverFulfill = true + signalExpectation.expectedFulfillmentCount = 1 + + let messageAction = XCTestExpectation(description: "Message Action") + messageAction.assertForOverFulfill = true + messageAction.expectedFulfillmentCount = 1 + + let presenceChangeExpectation = XCTestExpectation(description: "Presence") + presenceChangeExpectation.assertForOverFulfill = true + presenceChangeExpectation.expectedFulfillmentCount = 1 + + let appContextExpectation = XCTestExpectation(description: "App Context") + appContextExpectation.assertForOverFulfill = true + appContextExpectation.expectedFulfillmentCount = 1 + + let fileExpectation = XCTestExpectation(description: "File") + fileExpectation.assertForOverFulfill = true + fileExpectation.expectedFulfillmentCount = 1 + + let channel = pubnub.channel("test-channel") + let subscription = channel.subscription() + + let listener = EventListener( + onMessage: { _ in + messagesExpectation.fulfill() + }, + onSignal: { _ in + signalExpectation.fulfill() + }, + onPresence: { _ in + presenceChangeExpectation.fulfill() + }, + onMessageAction: { _ in + messageAction.fulfill() + }, + onFileEvent: { _ in + fileExpectation.fulfill() + }, + onAppContext: { _ in + appContextExpectation.fulfill() + } + ) + + subscription.addEventListener(listener) + subscription.onPayloadsReceived(payloads: [ + mockMessagePayload(channel: channel.name), mockSignalPayload(channel: channel.name), + mockPresenceChangePayload(channel: channel.name), mockAppContextPayload(channel: channel.name), + mockFilePayload(channel: channel.name), mockMessageActionPayload(channel: channel.name) + ]) + + let allExpectations = [ + messagesExpectation, signalExpectation, presenceChangeExpectation, + messageAction, fileExpectation, appContextExpectation + ] + + wait(for: allExpectations, timeout: 1.0) + } } diff --git a/Tests/PubNubTests/Events/Old/EventStreamTests.swift b/Tests/PubNubTests/Events/Old/EventStreamTests.swift index af6ef7ba..c78c4636 100644 --- a/Tests/PubNubTests/Events/Old/EventStreamTests.swift +++ b/Tests/PubNubTests/Events/Old/EventStreamTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class EventStreamTests: XCTestCase { diff --git a/Tests/PubNubTests/Events/Old/SessionStreamTests.swift b/Tests/PubNubTests/Events/Old/SessionStreamTests.swift deleted file mode 100644 index 138913f8..00000000 --- a/Tests/PubNubTests/Events/Old/SessionStreamTests.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// SessionStreamTests.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. -// - -@testable import PubNub -import XCTest - -class SessionStreamTests: XCTestCase { - var pubnub: PubNub! - let config = PubNubConfiguration(publishKey: "FakeTestString", subscribeKey: "FakeTestString", userId: UUID().uuidString) - - // swiftlint:disable:next function_body_length - func testSessionStream_Closure() { - var expectations = [XCTestExpectation]() - - let closureStream = SessionListener(queue: DispatchQueue(label: "Session Listener", - qos: .userInitiated, - attributes: .concurrent)) - let multiplexStream = MultiplexSessionStream([closureStream]) - - guard let sessions = try? MockURLSession.mockSession(for: ["time_success"], - with: multiplexStream) - else { - return XCTFail("Could not create mock url session") - } - - let sessionMultiplex = sessions.session?.sessionStream as? MultiplexSessionStream - let sessionListener = sessionMultiplex?.streams.first as? SessionListener - - XCTAssertEqual(sessionMultiplex, multiplexStream) - XCTAssertEqual(sessionListener, closureStream) - - let sessionExpector = SessionExpector(session: closureStream) - - sessionExpector.expectDidCreateURLRequest { request, urlRequest in - XCTAssertEqual(request.urlRequest, urlRequest) - } - - sessionExpector.expectDidCreateTask { request, task in - let mockTask = sessions.mockSession.tasks.first - XCTAssertEqual(request.urlRequest, mockTask?.originalRequest) - XCTAssertEqual(task.taskIdentifier, mockTask?.taskIdentifier) - } - - sessionExpector.expectDidResumeTask { request, task in - let mockTask = sessions.mockSession.tasks.first - XCTAssertEqual(request.urlRequest, mockTask?.originalRequest) - XCTAssertEqual(task.taskIdentifier, mockTask?.taskIdentifier) - } - - sessionExpector.expectDidCompleteTask { request, task in - let mockTask = sessions.mockSession.tasks.first - XCTAssertEqual(request.urlRequest, mockTask?.originalRequest) - XCTAssertEqual(task.taskIdentifier, mockTask?.taskIdentifier) - } - - sessionExpector.expectDidResumeRequest { request in - XCTAssertEqual(request.urlRequest, sessions.mockSession.tasks.first?.originalRequest) - } - - sessionExpector.expectDidFinishRequest { request in - XCTAssertEqual(request.urlRequest, sessions.mockSession.tasks.first?.originalRequest) - } - - sessionExpector.expectDidReceiveURLSessionData { urlSession, task, data in - let mockTask = sessions.mockSession.tasks.first as? MockURLSessionDataTask - XCTAssertEqual(urlSession.sessionDescription, sessions.mockSession.sessionDescription) - XCTAssertEqual(task.taskIdentifier, mockTask?.taskIdentifier) - XCTAssertEqual(data, mockTask?.mockData) - } - - sessionExpector.expectDidCompleteURLSessionTask { urlSession, task, error in - XCTAssertEqual(urlSession.sessionDescription, sessions.mockSession.sessionDescription) - XCTAssertEqual(task.taskIdentifier, sessions.mockSession.tasks.first?.taskIdentifier) - XCTAssertNil(error) - } - - let totalExpectation = expectation(description: "Time Response Received") - pubnub = PubNub(configuration: config, session: sessions.session) - pubnub.time { result in - switch result { - case let .success(timetoken): - XCTAssertEqual(timetoken, 15_643_405_135_132_358) - case let .failure(error): - XCTFail("Time request failed with error: \(error.localizedDescription)") - } - totalExpectation.fulfill() - } - expectations.append(totalExpectation) - - XCTAssertEqual(sessionExpector.expectations.count, 8) - expectations.append(contentsOf: sessionExpector.expectations) - - wait(for: expectations, timeout: 1.0) - } -} diff --git a/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift b/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift index 6e4d00e9..4ad1ba0e 100644 --- a/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift +++ b/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class SubscriptionListenerTests: XCTestCase { diff --git a/Tests/PubNubTests/Events/SessionStreamTests.swift b/Tests/PubNubTests/Events/SessionStreamTests.swift index 138913f8..25ecd158 100644 --- a/Tests/PubNubTests/Events/SessionStreamTests.swift +++ b/Tests/PubNubTests/Events/SessionStreamTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class SessionStreamTests: XCTestCase { diff --git a/Tests/PubNubTests/Events/SubscribeMessagesGeneratorTests.swift b/Tests/PubNubTests/Events/SubscribeMessagesGeneratorTests.swift index 1936d84f..a4f12ffe 100644 --- a/Tests/PubNubTests/Events/SubscribeMessagesGeneratorTests.swift +++ b/Tests/PubNubTests/Events/SubscribeMessagesGeneratorTests.swift @@ -9,7 +9,7 @@ // import Foundation -@testable import PubNub +@testable import PubNubSDK func mockMessagePayload( channel: String = "channel", diff --git a/Tests/PubNubTests/Extensions/Bool+PubNubTests.swift b/Tests/PubNubTests/Extensions/Bool+PubNubTests.swift index 74a0face..07f35d9f 100644 --- a/Tests/PubNubTests/Extensions/Bool+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/Bool+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class BoolPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/Collection+PubNubTests.swift b/Tests/PubNubTests/Extensions/Collection+PubNubTests.swift index 57ca0b80..d12e4f5d 100644 --- a/Tests/PubNubTests/Extensions/Collection+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/Collection+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class CollectionPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/Data+PubNubTests.swift b/Tests/PubNubTests/Extensions/Data+PubNubTests.swift index b88e169d..aef8fa43 100644 --- a/Tests/PubNubTests/Extensions/Data+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/Data+PubNubTests.swift @@ -10,7 +10,7 @@ import Foundation -@testable import PubNub +@testable import PubNubSDK import XCTest final class DataPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/DateFormatter+PubNubTests.swift b/Tests/PubNubTests/Extensions/DateFormatter+PubNubTests.swift index d9a07a8b..92c50469 100644 --- a/Tests/PubNubTests/Extensions/DateFormatter+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/DateFormatter+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class DateFormatterPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/DispatchQueue+PubNub.swift b/Tests/PubNubTests/Extensions/DispatchQueue+PubNub.swift index 0cd7fdf2..f5be01dd 100644 --- a/Tests/PubNubTests/Extensions/DispatchQueue+PubNub.swift +++ b/Tests/PubNubTests/Extensions/DispatchQueue+PubNub.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class DispatchQueuePubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/Error+PubNubTests.swift b/Tests/PubNubTests/Extensions/Error+PubNubTests.swift index a1ca43d2..cb59f27b 100644 --- a/Tests/PubNubTests/Extensions/Error+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/Error+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class ErrorPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/HTTPURLResponse+PubNubTests.swift b/Tests/PubNubTests/Extensions/HTTPURLResponse+PubNubTests.swift index 3cecb377..02ed8b5c 100644 --- a/Tests/PubNubTests/Extensions/HTTPURLResponse+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/HTTPURLResponse+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class HTTPURLResponsePubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/Int+PubNubTests.swift b/Tests/PubNubTests/Extensions/Int+PubNubTests.swift index b0adce54..8287c323 100644 --- a/Tests/PubNubTests/Extensions/Int+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/Int+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class TimetokenTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/OperationQueue+PubNubTests.swift b/Tests/PubNubTests/Extensions/OperationQueue+PubNubTests.swift index 6fc87b1a..a64a4d39 100644 --- a/Tests/PubNubTests/Extensions/OperationQueue+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/OperationQueue+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class OperationQueuePubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/Set+PubNubTests.swift b/Tests/PubNubTests/Extensions/Set+PubNubTests.swift index 130fbe4d..87de458c 100644 --- a/Tests/PubNubTests/Extensions/Set+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/Set+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class SetPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/String+PubNubTests.swift b/Tests/PubNubTests/Extensions/String+PubNubTests.swift index 9620ee4c..b5e3a5fe 100644 --- a/Tests/PubNubTests/Extensions/String+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/String+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class StringPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/URL+PubNubTests.swift b/Tests/PubNubTests/Extensions/URL+PubNubTests.swift index 5f09679b..a395dac8 100644 --- a/Tests/PubNubTests/Extensions/URL+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/URL+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class URLPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/URLQueryItem+PubNubTests.swift b/Tests/PubNubTests/Extensions/URLQueryItem+PubNubTests.swift index 48d0abb7..2d739c18 100644 --- a/Tests/PubNubTests/Extensions/URLQueryItem+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/URLQueryItem+PubNubTests.swift @@ -9,7 +9,7 @@ // // -@testable import PubNub +@testable import PubNubSDK import XCTest final class URLQueryItemPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/URLRequest+PubNubTests.swift b/Tests/PubNubTests/Extensions/URLRequest+PubNubTests.swift index 1d5afd4b..33c88580 100644 --- a/Tests/PubNubTests/Extensions/URLRequest+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/URLRequest+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class URLRequestPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Extensions/URLSessionConfiguration+PubNubTests.swift b/Tests/PubNubTests/Extensions/URLSessionConfiguration+PubNubTests.swift index 16cd9396..82ff6ea9 100644 --- a/Tests/PubNubTests/Extensions/URLSessionConfiguration+PubNubTests.swift +++ b/Tests/PubNubTests/Extensions/URLSessionConfiguration+PubNubTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest final class URLSessionConfigurationPubNubTests: XCTestCase { diff --git a/Tests/PubNubTests/Helpers/AnyJSON+CodableTests.swift b/Tests/PubNubTests/Helpers/AnyJSON+CodableTests.swift index 3695f073..31d01e3d 100644 --- a/Tests/PubNubTests/Helpers/AnyJSON+CodableTests.swift +++ b/Tests/PubNubTests/Helpers/AnyJSON+CodableTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest // swiftlint:disable:next type_body_length diff --git a/Tests/PubNubTests/Helpers/AnyJSONTests.swift b/Tests/PubNubTests/Helpers/AnyJSONTests.swift index e26f8b56..30b5ba0f 100644 --- a/Tests/PubNubTests/Helpers/AnyJSONTests.swift +++ b/Tests/PubNubTests/Helpers/AnyJSONTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest struct SomeCodable: Codable, Equatable { diff --git a/Tests/PubNubTests/Helpers/AtomicTests.swift b/Tests/PubNubTests/Helpers/AtomicTests.swift index 5284a550..4d55e10b 100644 --- a/Tests/PubNubTests/Helpers/AtomicTests.swift +++ b/Tests/PubNubTests/Helpers/AtomicTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class AtomicTests: XCTestCase { diff --git a/Tests/PubNubTests/Helpers/ConstantsTests.swift b/Tests/PubNubTests/Helpers/ConstantsTests.swift index 414274df..8b5b8ab6 100644 --- a/Tests/PubNubTests/Helpers/ConstantsTests.swift +++ b/Tests/PubNubTests/Helpers/ConstantsTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest #if os(iOS) let osName = "iOS" diff --git a/Tests/PubNubTests/Helpers/CryptoTests.swift b/Tests/PubNubTests/Helpers/CryptoTests.swift index 868bd921..dc17115c 100644 --- a/Tests/PubNubTests/Helpers/CryptoTests.swift +++ b/Tests/PubNubTests/Helpers/CryptoTests.swift @@ -9,9 +9,10 @@ // import CommonCrypto -@testable import PubNub import XCTest +@testable import PubNubSDK + class CryptoTests: XCTestCase { func testEncryptDecrypt_Data() { let cryptoModule = CryptoModule.legacyCryptoModule(with: "SomeTestString") diff --git a/Tests/PubNubTests/Helpers/FlatJSONCodable+Test.swift b/Tests/PubNubTests/Helpers/FlatJSONCodable+Test.swift index 8166ffe0..a651e4f7 100644 --- a/Tests/PubNubTests/Helpers/FlatJSONCodable+Test.swift +++ b/Tests/PubNubTests/Helpers/FlatJSONCodable+Test.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class FlatJSONCodableTests: XCTestCase { diff --git a/Tests/PubNubTests/Helpers/PAMTokenTests.swift b/Tests/PubNubTests/Helpers/PAMTokenTests.swift index e11bb583..07e3ba64 100644 --- a/Tests/PubNubTests/Helpers/PAMTokenTests.swift +++ b/Tests/PubNubTests/Helpers/PAMTokenTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest // swiftlint:disable line_length diff --git a/Tests/PubNubTests/Helpers/ValidatedTests.swift b/Tests/PubNubTests/Helpers/ValidatedTests.swift index 3cc6eb85..d13962e9 100644 --- a/Tests/PubNubTests/Helpers/ValidatedTests.swift +++ b/Tests/PubNubTests/Helpers/ValidatedTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class ValidatedTests: XCTestCase { diff --git a/Tests/PubNubTests/Helpers/WeakBoxTests.swift b/Tests/PubNubTests/Helpers/WeakBoxTests.swift index bbef3d3c..116eed30 100644 --- a/Tests/PubNubTests/Helpers/WeakBoxTests.swift +++ b/Tests/PubNubTests/Helpers/WeakBoxTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class WeakBoxTests: XCTestCase { diff --git a/Tests/PubNubTests/Helpers/XMLCodingTests.swift b/Tests/PubNubTests/Helpers/XMLCodingTests.swift index 5c00cb66..da3bcd42 100644 --- a/Tests/PubNubTests/Helpers/XMLCodingTests.swift +++ b/Tests/PubNubTests/Helpers/XMLCodingTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class XMLCodingTests: XCTestCase { diff --git a/Tests/PubNubTests/Integration/ChannelObjectsEndpointIntegrationTests.swift b/Tests/PubNubTests/Integration/ChannelObjectsEndpointIntegrationTests.swift index f11a76b2..3bbfc6f5 100644 --- a/Tests/PubNubTests/Integration/ChannelObjectsEndpointIntegrationTests.swift +++ b/Tests/PubNubTests/Integration/ChannelObjectsEndpointIntegrationTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import XCTest class ChannelObjectsEndpointIntegrationTests: XCTestCase { diff --git a/Tests/PubNubTests/Integration/MessageActionsEndpointIntegrationTests.swift b/Tests/PubNubTests/Integration/MessageActionsEndpointIntegrationTests.swift index 16b4bcbc..3fb0eb5e 100644 --- a/Tests/PubNubTests/Integration/MessageActionsEndpointIntegrationTests.swift +++ b/Tests/PubNubTests/Integration/MessageActionsEndpointIntegrationTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import XCTest class MessageActionsEndpointIntegrationTests: XCTestCase { diff --git a/Tests/PubNubTests/Integration/OnboardingSnippets.swift b/Tests/PubNubTests/Integration/OnboardingSnippets.swift index 47a51444..d0ea16f5 100644 --- a/Tests/PubNubTests/Integration/OnboardingSnippets.swift +++ b/Tests/PubNubTests/Integration/OnboardingSnippets.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import XCTest class OnboardingSnippets: XCTestCase { diff --git a/Tests/PubNubTests/Integration/PresenceEndpointIntegrationTests.swift b/Tests/PubNubTests/Integration/PresenceEndpointIntegrationTests.swift index 535e0a23..26443dbd 100644 --- a/Tests/PubNubTests/Integration/PresenceEndpointIntegrationTests.swift +++ b/Tests/PubNubTests/Integration/PresenceEndpointIntegrationTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import XCTest class PresenceEndpointIntegrationTests: XCTestCase { diff --git a/Tests/PubNubTests/Integration/PubNub+Integration.swift b/Tests/PubNubTests/Integration/PubNub+Integration.swift index f626306c..e42ca2d0 100644 --- a/Tests/PubNubTests/Integration/PubNub+Integration.swift +++ b/Tests/PubNubTests/Integration/PubNub+Integration.swift @@ -9,8 +9,7 @@ // import Foundation - -import PubNub +import PubNubSDK extension PubNub { func publishWithMessageAction( diff --git a/Tests/PubNubTests/Integration/PublishEndpointIntegrationTests.swift b/Tests/PubNubTests/Integration/PublishEndpointIntegrationTests.swift index 5fc0c138..ee3e092e 100644 --- a/Tests/PubNubTests/Integration/PublishEndpointIntegrationTests.swift +++ b/Tests/PubNubTests/Integration/PublishEndpointIntegrationTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import XCTest class PublishEndpointIntegrationTests: XCTestCase { diff --git a/Tests/PubNubTests/Integration/PushIntegrationTests.swift b/Tests/PubNubTests/Integration/PushIntegrationTests.swift index aeb957d7..d383a154 100644 --- a/Tests/PubNubTests/Integration/PushIntegrationTests.swift +++ b/Tests/PubNubTests/Integration/PushIntegrationTests.swift @@ -7,9 +7,9 @@ // This source code is licensed under the license found in the // LICENSE file in the root directory of this source tree. // -import XCTest -import PubNub +import XCTest +import PubNubSDK class PushIntegrationTests: XCTestCase { let testsBundle = Bundle(for: PushIntegrationTests.self) diff --git a/Tests/PubNubTests/Integration/SubscriptionIntegrationTests.swift b/Tests/PubNubTests/Integration/SubscriptionIntegrationTests.swift index 9892df70..c6d74e8e 100644 --- a/Tests/PubNubTests/Integration/SubscriptionIntegrationTests.swift +++ b/Tests/PubNubTests/Integration/SubscriptionIntegrationTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import XCTest class SubscriptionIntegrationTests: XCTestCase { @@ -32,8 +32,6 @@ class SubscriptionIntegrationTests: XCTestCase { for config in [configuration, eeConfiguration] { XCTContext.runActivity(named: "Testing configuration with enableEventEngine=\(config.enableEventEngine)") { _ in let subscribeExpect = expectation(description: "Subscribe Expectation") - let connectingExpect = expectation(description: "Connecting Expectation") - let disconnectedExpect = expectation(description: "Disconnected Expectation") disconnectedExpect.assertForOverFulfill = true disconnectedExpect.expectedFulfillmentCount = 1 @@ -46,8 +44,6 @@ class SubscriptionIntegrationTests: XCTestCase { switch event { case let .connectionStatusChanged(status): switch status { - case .connecting: - connectingExpect.fulfill() case .disconnectedUnexpectedly: disconnectedExpect.fulfill() case .connectionError: @@ -66,7 +62,7 @@ class SubscriptionIntegrationTests: XCTestCase { pubnub.subscribe(to: [testChannel]) defer { pubnub.disconnect() } - wait(for: [subscribeExpect, connectingExpect, disconnectedExpect], timeout: 10.0) + wait(for: [subscribeExpect, disconnectedExpect], timeout: 10.0) } } } @@ -82,7 +78,8 @@ class SubscriptionIntegrationTests: XCTestCase { let configWithEventEngineEnabled = PubNubConfiguration( publishKey: configurationFromBundle.publishKey, subscribeKey: configurationFromBundle.subscribeKey, - userId: configurationFromBundle.userId + userId: configurationFromBundle.userId, + enableEventEngine: true ) for config in [configurationFromBundle, configWithEventEngineEnabled] { @@ -163,7 +160,8 @@ class SubscriptionIntegrationTests: XCTestCase { let configWithEventEngineEnabled = PubNubConfiguration( publishKey: configurationFromBundle.publishKey, subscribeKey: configurationFromBundle.subscribeKey, - userId: configurationFromBundle.userId + userId: configurationFromBundle.userId, + enableEventEngine: true ) for config in [configurationFromBundle, configWithEventEngineEnabled] { @@ -208,7 +206,6 @@ class SubscriptionIntegrationTests: XCTestCase { firstSubscription = nil secondSubscription = nil pubnub.unsubscribe(from: [self.testChannel]) - subscriptionSet?.unsubscribe() subscriptionSet = nil default: break @@ -242,7 +239,8 @@ class SubscriptionIntegrationTests: XCTestCase { let configWithEventEngineEnabled = PubNubConfiguration( publishKey: configurationFromBundle.publishKey, subscribeKey: configurationFromBundle.subscribeKey, - userId: configurationFromBundle.userId + userId: configurationFromBundle.userId, + enableEventEngine: true ) for config in [configurationFromBundle, configWithEventEngineEnabled] { @@ -253,7 +251,7 @@ class SubscriptionIntegrationTests: XCTestCase { let statusExpect = XCTestExpectation(description: "StatusExpect") statusExpect.assertForOverFulfill = true - statusExpect.expectedFulfillmentCount = 3 + statusExpect.expectedFulfillmentCount = 2 let pubnub = PubNub(configuration: config) var statusCounter = 0 @@ -263,13 +261,11 @@ class SubscriptionIntegrationTests: XCTestCase { messageExpect.fulfill() pubnub.unsubscribe(from: [self.testChannel]) } - pubnub.onConnectionStateChange = { [unowned pubnub] change in + pubnub.onConnectionStateChange = { [unowned pubnub, unowned self] change in if statusCounter == 0 { - XCTAssertTrue(change == .connecting) - } else if statusCounter == 1 { XCTAssertTrue(change == .connected) pubnub.publish(channel: self.testChannel, message: "This is a message", completion: nil) - } else if statusCounter == 2 { + } else if statusCounter == 1 { XCTAssertTrue(change == .disconnected) } else { XCTFail("Unexpected condition") @@ -299,7 +295,9 @@ class SubscriptionIntegrationTests: XCTestCase { subscribeKey: PubNubConfiguration(from: testsBundle).subscribeKey, userId: PubNubConfiguration(from: testsBundle).userId )) - let timetoken = Timetoken(Int(Date().timeIntervalSince1970 * 10000000)) + let timetoken = Timetoken( + Int(Date().timeIntervalSince1970 * 10000000) + ) pubnub.publish(channel: testChannel, message: "Message", completion: { [unowned pubnub, unowned self] _ in pubnub.publish(channel: self.testChannel, message: "Second message", completion: { _ in @@ -328,7 +326,7 @@ class SubscriptionIntegrationTests: XCTestCase { func test_SimultaneousSubscriptionsToTheSameChannel() { let expectation = XCTestExpectation(description: "Test Simultaneous Subscriptions") expectation.assertForOverFulfill = true - expectation.expectedFulfillmentCount = 2 + expectation.expectedFulfillmentCount = 1 let pubnub = PubNub(configuration: PubNubConfiguration( publishKey: PubNubConfiguration(from: testsBundle).publishKey, @@ -336,10 +334,10 @@ class SubscriptionIntegrationTests: XCTestCase { userId: PubNubConfiguration(from: testsBundle).userId )) + let channelName = "channel" + pubnub.onConnectionStateChange = { newStatus in switch newStatus { - case .connecting: - expectation.fulfill() case .connected: expectation.fulfill() default: @@ -347,17 +345,17 @@ class SubscriptionIntegrationTests: XCTestCase { } } - pubnub.subscribe(to: ["channel"]) - pubnub.subscribe(to: ["channel"]) + pubnub.subscribe(to: [channelName]) + pubnub.subscribe(to: [channelName]) - XCTAssertEqual(pubnub.subscribedChannels, ["channel"]) + XCTAssertEqual(pubnub.subscribedChannels, [channelName]) wait(for: [expectation], timeout: 5.0) } func test_SimultaneousSubscriptionsToTheSameChannelWithTimetoken() { let expectation = XCTestExpectation(description: "Test Simultaneous Subscriptions With Timetoken") expectation.assertForOverFulfill = true - expectation.expectedFulfillmentCount = 3 + expectation.expectedFulfillmentCount = 1 let pubnub = PubNub(configuration: PubNubConfiguration( publishKey: PubNubConfiguration(from: testsBundle).publishKey, @@ -365,10 +363,10 @@ class SubscriptionIntegrationTests: XCTestCase { userId: PubNubConfiguration(from: testsBundle).userId )) + let channelName = "channel" + pubnub.onConnectionStateChange = { newStatus in switch newStatus { - case .connecting: - expectation.fulfill() case .connected: expectation.fulfill() default: @@ -376,8 +374,8 @@ class SubscriptionIntegrationTests: XCTestCase { } } - pubnub.subscribe(to: ["channel"]) - pubnub.subscribe(to: ["channel"], at: Timetoken(Int(Date().timeIntervalSince1970 * 10000000))) + pubnub.subscribe(to: [channelName]) + pubnub.subscribe(to: [channelName], at: Timetoken(Int(Date().timeIntervalSince1970 * 10000000))) wait(for: [expectation], timeout: 5.0) } diff --git a/Tests/PubNubTests/Integration/UUIDObjectsEndpointIntegrationTests.swift b/Tests/PubNubTests/Integration/UUIDObjectsEndpointIntegrationTests.swift index 94561e05..5adc27ef 100644 --- a/Tests/PubNubTests/Integration/UUIDObjectsEndpointIntegrationTests.swift +++ b/Tests/PubNubTests/Integration/UUIDObjectsEndpointIntegrationTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -import PubNub +import PubNubSDK import XCTest class UUIDObjectsEndpointIntegrationTests: XCTestCase { diff --git a/Tests/PubNubTests/Mocking/ImportTestResource.swift b/Tests/PubNubTests/Mocking/ImportTestResource.swift index afe0d48c..bf3f91d2 100644 --- a/Tests/PubNubTests/Mocking/ImportTestResource.swift +++ b/Tests/PubNubTests/Mocking/ImportTestResource.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest enum ImportTestResourceError: Error, LocalizedError { diff --git a/Tests/PubNubTests/Mocking/MockRequestOperators.swift b/Tests/PubNubTests/Mocking/MockRequestOperators.swift index 7fa2fb21..be33a248 100644 --- a/Tests/PubNubTests/Mocking/MockRequestOperators.swift +++ b/Tests/PubNubTests/Mocking/MockRequestOperators.swift @@ -8,8 +8,8 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub import XCTest +@testable import PubNubSDK struct DefaultOperator: RequestOperator, Equatable { let uuid = UUID() diff --git a/Tests/PubNubTests/Mocking/MockURLSession.swift b/Tests/PubNubTests/Mocking/MockURLSession.swift index 72b9b500..f8960aff 100644 --- a/Tests/PubNubTests/Mocking/MockURLSession.swift +++ b/Tests/PubNubTests/Mocking/MockURLSession.swift @@ -9,7 +9,7 @@ // import Foundation -@testable import PubNub +@testable import PubNubSDK class MockURLSessionDataTask: URLSessionDataTask { weak var mockSession: MockURLSession? diff --git a/Tests/PubNubTests/Mocking/SessionStreamAwait.swift b/Tests/PubNubTests/Mocking/SessionStreamAwait.swift index cf08415a..dd2d29bf 100644 --- a/Tests/PubNubTests/Mocking/SessionStreamAwait.swift +++ b/Tests/PubNubTests/Mocking/SessionStreamAwait.swift @@ -9,9 +9,10 @@ // import Foundation -@testable import PubNub import XCTest +@testable import PubNubSDK + final class SessionExpector { public var expectations = [XCTestExpectation]() public var sessionListener: SessionListener diff --git a/Tests/PubNubTests/Mocking/TestLogWriter.swift b/Tests/PubNubTests/Mocking/TestLogWriter.swift index 33595e4d..7ea76920 100644 --- a/Tests/PubNubTests/Mocking/TestLogWriter.swift +++ b/Tests/PubNubTests/Mocking/TestLogWriter.swift @@ -9,7 +9,7 @@ // import Foundation -@testable import PubNub +@testable import PubNubSDK class TestSyncLogWriter: LogWriter { var executor: LogExecutable = LogExecutionType.sync(lock: NSRecursiveLock()) diff --git a/Tests/PubNubTests/Mocking/TestSetup.swift b/Tests/PubNubTests/Mocking/TestSetup.swift index a00b1b6d..af1a2920 100644 --- a/Tests/PubNubTests/Mocking/TestSetup.swift +++ b/Tests/PubNubTests/Mocking/TestSetup.swift @@ -9,7 +9,7 @@ // import Foundation -import PubNub +import PubNubSDK /// Anything that needs to be executed once can be placed inside the init class TestSetup: NSObject { diff --git a/Tests/PubNubTests/Mocking/XMLEncoder.swift b/Tests/PubNubTests/Mocking/XMLEncoder.swift index cec9ca8d..519041e3 100644 --- a/Tests/PubNubTests/Mocking/XMLEncoder.swift +++ b/Tests/PubNubTests/Mocking/XMLEncoder.swift @@ -10,7 +10,7 @@ import Foundation -@testable import PubNub +@testable import PubNubSDK /// `XMLEncoder` facilitates the encoding of `Encodable` values into XML. class XMLEncoder { diff --git a/Tests/PubNubTests/Networking/Operators/AutomaticRetryTests.swift b/Tests/PubNubTests/Networking/Operators/AutomaticRetryTests.swift index dd1d8577..e5c4059f 100644 --- a/Tests/PubNubTests/Networking/Operators/AutomaticRetryTests.swift +++ b/Tests/PubNubTests/Networking/Operators/AutomaticRetryTests.swift @@ -8,7 +8,7 @@ // LICENSE file in the root directory of this source tree. // -@testable import PubNub +@testable import PubNubSDK import XCTest class AutomaticRetryTests: XCTestCase { @@ -26,10 +26,9 @@ class AutomaticRetryTests: XCTestCase { func testReconnectionPolicy_DefaultExponentialPolicy() { switch defaultExpoentialPolicy { - case let .legacyExponential(base, scale, max): - XCTAssertEqual(base, 2) - XCTAssertEqual(scale, 2) - XCTAssertEqual(max, 300) + case let .exponential(minDelay, maxDelay): + XCTAssertEqual(minDelay, 2) + XCTAssertEqual(maxDelay, 150) default: XCTFail("Default Exponential Policy should only match to linear case") } @@ -44,59 +43,6 @@ class AutomaticRetryTests: XCTestCase { XCTAssertEqual(testPolicy, policy) } - func testEquatable_Init_Exponential_InvalidBase() { - let invalidBasePolicy = AutomaticRetry.ReconnectionPolicy.legacyExponential( - base: 0, - scale: 3.0, - maxDelay: 1 - ) - let validBasePolicy = AutomaticRetry.ReconnectionPolicy.legacyExponential( - base: 2, scale: 3.0, maxDelay: 1 - ) - let testPolicy = AutomaticRetry( - retryLimit: 2, policy: invalidBasePolicy, retryableHTTPStatusCodes: [], retryableURLErrorCodes: [] - ) - - XCTAssertNotEqual(testPolicy.policy, invalidBasePolicy) - XCTAssertEqual(testPolicy.policy, validBasePolicy) - } - - func testEquatable_Init_Exponential_InvalidScale() { - let invalidBasePolicy = AutomaticRetry.ReconnectionPolicy.legacyExponential( - base: 2, scale: -1.0, maxDelay: 1 - ) - let validBasePolicy = AutomaticRetry.ReconnectionPolicy.legacyExponential( - base: 2, scale: 0.0, maxDelay: 1 - ) - let testPolicy = AutomaticRetry( - retryLimit: 2, - policy: invalidBasePolicy, - retryableHTTPStatusCodes: [], - retryableURLErrorCodes: [] - ) - - XCTAssertNotEqual(testPolicy.policy, invalidBasePolicy) - XCTAssertEqual(testPolicy.policy, validBasePolicy) - } - - func testEquatable_Init_Exponential_InvalidBaseAndScale() { - let invalidBasePolicy = AutomaticRetry.ReconnectionPolicy.legacyExponential( - base: 0, scale: -1.0, maxDelay: 1 - ) - let validBasePolicy = AutomaticRetry.ReconnectionPolicy.legacyExponential( - base: 2, scale: 0.0, maxDelay: 1 - ) - let testPolicy = AutomaticRetry( - retryLimit: 2, - policy: invalidBasePolicy, - retryableHTTPStatusCodes: [], - retryableURLErrorCodes: [] - ) - - XCTAssertNotEqual(testPolicy.policy, invalidBasePolicy) - XCTAssertEqual(testPolicy.policy, validBasePolicy) - } - func testEquatable_Init_Linear_InvalidDelay() { let invalidBasePolicy = AutomaticRetry.ReconnectionPolicy.linear(delay: -1.0) let validBasePolicy = AutomaticRetry.ReconnectionPolicy.linear(delay: 2.0) @@ -162,6 +108,28 @@ class AutomaticRetryTests: XCTestCase { XCTAssertTrue(testPolicy.shouldRetry(response: testResponse, error: PubNubError(.unknown))) } + + func testShouldRetry_True_TooManyRequestsStatusCode() { + guard let url = URL(string: "http://example.com") else { + return XCTFail("Could not create URL") + } + + let testStatusCode = 429 + let testPolicy = AutomaticRetry( + retryLimit: 2, + policy: .linear(delay: 2.0), + retryableHTTPStatusCodes: [testStatusCode], + retryableURLErrorCodes: [] + ) + let testResponse = HTTPURLResponse( + url: url, + statusCode: testStatusCode, + httpVersion: nil, + headerFields: [:] + ) + + XCTAssertTrue(testPolicy.shouldRetry(response: testResponse, error: PubNubError(.unknown))) + } func testShouldRetry_True_ErrorCodeMatch() { let testURLErrorCode = URLError.Code.timedOut @@ -187,36 +155,6 @@ class AutomaticRetryTests: XCTestCase { XCTAssertFalse(testPolicy.shouldRetry(response: nil, error: testError)) } - - // MARK: - legacyExponential(base:scale:maxDelay:) - - func testLegacyExponentialBackoffDelay_DefaultScale() { - let maxRetryCount = 5 - let scale = 2.0 - let base: UInt = 3 - let maxDelay = UInt.max - let delayForRetry = [2.0...3.0, 6.0...7.0, 18.0...19.0, 54.0...55.0, 162.0...163.0] - - for count in 0..