From 11e9af27f4772ce6509a35a2118209c26c5d4dac Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Wed, 30 Nov 2022 06:47:46 +0100
Subject: [PATCH 01/19] Migrate to Swift (close #732)

PR #733
---
 .github/workflows/build.yml                   |   66 +-
 .github/workflows/snyk.yml                    |    2 +-
 .scripts/copy_public_headers.sh               |   46 -
 Package.swift                                 |   32 +-
 README.md                                     |    6 +-
 .../Configurations/TestEmitterConfiguration.m |   87 -
 .../Configurations/TestMultipleInstances.m    |  104 -
 .../Configurations/TestRemoteConfiguration.m  |  387 --
 .../Configurations/TestTrackerConfiguration.m |  405 --
 .../Configurations/TestTrackerController.m    |   86 -
 .../Global Contexts/TestGlobalContexts.m      |  244 --
 .../Global Contexts/TestSchemaRuleset.m       |  109 -
 .../Legacy Tests/LegacyTestEmitter.m          |  404 --
 .../Legacy Tests/LegacyTestEvent.m            |  388 --
 .../Legacy Tests/LegacyTestSubject.m          |  130 -
 .../Legacy Tests/LegacyTestTracker.m          |  212 -
 Snowplow iOSTests/TestDataPersistence.m       |  102 -
 Snowplow iOSTests/TestEvents.m                |  557 ---
 Snowplow iOSTests/TestGeneratedJsons.m        |  396 --
 Snowplow iOSTests/TestLifecycleState.m        |  102 -
 Snowplow iOSTests/TestLogger.m                |  113 -
 Snowplow iOSTests/TestMemoryEventStore.m      |   81 -
 Snowplow iOSTests/TestNetworkConnection.m     |  225 -
 Snowplow iOSTests/TestPayload.m               |  307 --
 Snowplow iOSTests/TestPlatformContext.m       |  200 -
 Snowplow iOSTests/TestRequest.m               |  364 --
 Snowplow iOSTests/TestRequestResponse.m       |   57 -
 Snowplow iOSTests/TestRequestResult.m         |   92 -
 Snowplow iOSTests/TestSQLiteEventStore.m      |  154 -
 Snowplow iOSTests/TestScreenState.m           |  145 -
 Snowplow iOSTests/TestSelfDescribingJson.m    |  165 -
 Snowplow iOSTests/TestSession.m               |  529 ---
 Snowplow iOSTests/TestStateManager.m          |  207 -
 Snowplow iOSTests/TestSubject.m               |   77 -
 Snowplow iOSTests/TestUtils.m                 |  161 -
 Snowplow iOSTests/TestWebViewMessageHandler.m |  143 -
 .../Utils/SPMockDeviceInfoMonitor.h           |   37 -
 .../Utils/SPMockDeviceInfoMonitor.m           |  124 -
 Snowplow iOSTests/Utils/SPMockEventStore.m    |   94 -
 .../Utils/SPMockLoggerDelegate.m              |   47 -
 .../Utils/SPMockNetworkConnection.h           |   40 -
 .../Utils/SPMockNetworkConnection.m           |   61 -
 Snowplow macOSTests/Info.plist                |   22 -
 Snowplow macOSTests/Snowplow_macOSTests.m     |   51 -
 Snowplow.xcodeproj/project.pbxproj            | 3829 +++++++----------
 .../xcschemes/Snowplow-iOS-Static.xcscheme    |    2 +-
 .../xcschemes/Snowplow-iOS.xcscheme           |    6 +-
 .../xcschemes/Snowplow-macOS.xcscheme         |    2 +-
 .../xcschemes/Snowplow-watchOS.xcscheme       |    2 +-
 .../Internal/Configurations/SPConfiguration.h |   38 -
 .../Configurations/SPConfigurationBundle.h    |   52 -
 .../Configurations/SPConfigurationBundle.m    |  114 -
 .../Configurations/SPEmitterConfiguration.h   |  161 -
 .../Configurations/SPEmitterConfiguration.m   |  107 -
 .../Configurations/SPGDPRConfiguration.h      |   73 -
 .../Configurations/SPGDPRConfiguration.m      |   83 -
 .../SPGlobalContextsConfiguration.h           |   59 -
 .../SPGlobalContextsConfiguration.m           |   65 -
 .../Configurations/SPNetworkConfiguration.h   |  100 -
 .../Configurations/SPNetworkConfiguration.m   |  127 -
 .../Configurations/SPRemoteConfiguration.h    |   56 -
 .../Configurations/SPRemoteConfiguration.m    |   72 -
 .../Configurations/SPSessionConfiguration.h   |  104 -
 .../Configurations/SPSessionConfiguration.m   |  117 -
 .../Configurations/SPSubjectConfiguration.h   |  218 -
 .../Configurations/SPSubjectConfiguration.m   |  198 -
 .../Configurations/SPTrackerConfiguration.h   |  217 -
 .../Configurations/SPTrackerConfiguration.m   |  199 -
 Snowplow/Internal/Emitter/SPEmitter.h         |  219 -
 Snowplow/Internal/Emitter/SPEmitter.m         |  464 --
 .../Emitter/SPEmitterConfigurationUpdate.h    |   42 -
 .../Emitter/SPEmitterConfigurationUpdate.m    |   37 -
 .../Internal/Emitter/SPEmitterController.h    |   57 -
 .../Emitter/SPEmitterControllerImpl.m         |  144 -
 Snowplow/Internal/Emitter/SPEmitterEvent.h    |   38 -
 Snowplow/Internal/Emitter/SPRequest.h         |   43 -
 Snowplow/Internal/Emitter/SPRequest.m         |   74 -
 Snowplow/Internal/Emitter/SPRequestResult.h   |   52 -
 Snowplow/Internal/Emitter/SPRequestResult.m   |   74 -
 Snowplow/Internal/Entities/SPDeepLinkEntity.h |   45 -
 Snowplow/Internal/Entities/SPDeepLinkEntity.m |   56 -
 .../Internal/Entities/SPLifecycleEntity.h     |   45 -
 .../Internal/Entities/SPLifecycleEntity.m     |   56 -
 Snowplow/Internal/Events/SNOWError.h          |   44 -
 Snowplow/Internal/Events/SNOWError.m          |   73 -
 Snowplow/Internal/Events/SPConsentDocument.h  |   58 -
 Snowplow/Internal/Events/SPConsentDocument.m  |   70 -
 Snowplow/Internal/Events/SPConsentGranted.h   |   66 -
 Snowplow/Internal/Events/SPConsentGranted.m   |   95 -
 Snowplow/Internal/Events/SPConsentWithdrawn.h |   62 -
 Snowplow/Internal/Events/SPConsentWithdrawn.m |   83 -
 Snowplow/Internal/Events/SPDeepLinkReceived.h |   51 -
 Snowplow/Internal/Events/SPDeepLinkReceived.m |   66 -
 Snowplow/Internal/Events/SPEcommerce.h        |   82 -
 Snowplow/Internal/Events/SPEcommerce.m        |   90 -
 Snowplow/Internal/Events/SPEcommerceItem.h    |   66 -
 Snowplow/Internal/Events/SPEcommerceItem.m    |   73 -
 Snowplow/Internal/Events/SPEvent.h            |   43 -
 Snowplow/Internal/Events/SPEventBase.h        |  116 -
 Snowplow/Internal/Events/SPEventBase.m        |   92 -
 .../Internal/Events/SPMessageNotification.h   |  161 -
 .../Internal/Events/SPMessageNotification.m   |  170 -
 .../Events/SPMessageNotificationAttachment.h  |   43 -
 .../Events/SPMessageNotificationAttachment.m  |   52 -
 Snowplow/Internal/Events/SPPageView.h         |   53 -
 Snowplow/Internal/Events/SPPageView.m         |   63 -
 Snowplow/Internal/Events/SPPushNotification.h |   80 -
 Snowplow/Internal/Events/SPPushNotification.m |  197 -
 Snowplow/Internal/Events/SPScreenView.h       |   77 -
 Snowplow/Internal/Events/SPScreenView.m       |   78 -
 Snowplow/Internal/Events/SPSelfDescribing.h   |   39 -
 Snowplow/Internal/Events/SPSelfDescribing.m   |   68 -
 Snowplow/Internal/Events/SPStructured.h       |   46 -
 Snowplow/Internal/Events/SPStructured.m       |   69 -
 Snowplow/Internal/Events/SPTiming.h           |   44 -
 Snowplow/Internal/Events/SPTiming.m           |   69 -
 Snowplow/Internal/Events/SPTrackerError.h     |   35 -
 Snowplow/Internal/Events/SPTrackerError.m     |   84 -
 .../Internal/GDPR/SPGDPRConfigurationUpdate.h |   38 -
 .../Internal/GDPR/SPGDPRConfigurationUpdate.m |   38 -
 Snowplow/Internal/GDPR/SPGDPRController.h     |   61 -
 Snowplow/Internal/GDPR/SPGDPRControllerImpl.m |   98 -
 Snowplow/Internal/GDPR/SPGdprContext.h        |   53 -
 Snowplow/Internal/GDPR/SPGdprContext.m        |   85 -
 .../Internal/GlobalContexts/SPGlobalContext.h |  129 -
 .../Internal/GlobalContexts/SPGlobalContext.m |   93 -
 .../SPGlobalContextsController.h              |   35 -
 .../SPGlobalContextsControllerImpl.h          |   33 -
 .../SPGlobalContextsControllerImpl.m          |   54 -
 .../Internal/GlobalContexts/SPSchemaRule.h    |   46 -
 .../Internal/GlobalContexts/SPSchemaRule.m    |  155 -
 .../Internal/GlobalContexts/SPSchemaRuleset.h |   65 -
 .../Internal/GlobalContexts/SPSchemaRuleset.m |  123 -
 Snowplow/Internal/Logger/SPLogger.h           |   45 -
 Snowplow/Internal/Logger/SPLogger.m           |  145 -
 Snowplow/Internal/Logger/SPLoggerDelegate.h   |   41 -
 .../SPDefaultNetworkConnection.h              |   93 -
 .../SPDefaultNetworkConnection.m              |  236 -
 .../SPNetworkConfigurationUpdate.h            |   45 -
 .../SPNetworkConfigurationUpdate.m            |   46 -
 .../NetworkConnection/SPNetworkConnection.h   |   74 -
 .../NetworkConnection/SPNetworkController.h   |   52 -
 .../SPNetworkControllerImpl.h                 |   36 -
 .../SPNetworkControllerImpl.m                 |   85 -
 Snowplow/Internal/Payload/SPPayload.h         |  112 -
 Snowplow/Internal/Payload/SPPayload.m         |  163 -
 .../Internal/Payload/SPSelfDescribingJson.h   |  116 -
 .../Internal/Payload/SPSelfDescribingJson.m   |   83 -
 .../SPConfigurationCache.h                    |   37 -
 .../SPConfigurationCache.m                    |  145 -
 .../SPConfigurationFetcher.h                  |   34 -
 .../SPConfigurationFetcher.m                  |   73 -
 .../SPConfigurationProvider.h                 |   45 -
 .../SPConfigurationProvider.m                 |   91 -
 .../SPConfigurationState.h                    |   47 -
 .../SPFetchedConfigurationBundle.h            |   34 -
 .../SPFetchedConfigurationBundle.m            |   81 -
 Snowplow/Internal/SPController.h              |   35 -
 Snowplow/Internal/SPSnowplow.h                |  234 -
 Snowplow/Internal/SPSnowplow.m                |  196 -
 Snowplow/Internal/SPTrackerConstants.h        |  316 --
 Snowplow/Internal/SPTrackerConstants.m        |  303 --
 .../ScreenViewTracking/SPScreenState.h        |   84 -
 .../ScreenViewTracking/SPScreenState.m        |   91 -
 .../ScreenViewTracking/SPScreenStateMachine.m |   88 -
 .../UIViewController+SPScreenView_SWIZZLE.h   |   48 -
 .../UIViewController+SPScreenView_SWIZZLE.m   |  193 -
 Snowplow/Internal/Session/SPSession.h         |  128 -
 Snowplow/Internal/Session/SPSession.m         |  267 --
 .../Session/SPSessionConfigurationUpdate.h    |   38 -
 .../Session/SPSessionConfigurationUpdate.m    |   30 -
 .../Internal/Session/SPSessionController.h    |   77 -
 .../Session/SPSessionControllerImpl.h         |   35 -
 .../Session/SPSessionControllerImpl.m         |  178 -
 Snowplow/Internal/Session/SPSessionState.h    |   45 -
 Snowplow/Internal/Session/SPSessionState.m    |  113 -
 Snowplow/Internal/Storage/SPEventStore.h      |   73 -
 .../Internal/Storage/SPMemoryEventStore.m     |  104 -
 .../Internal/Storage/SPSQLiteEventStore.h     |   83 -
 .../Internal/Storage/SPSQLiteEventStore.m     |  275 --
 Snowplow/Internal/Subject/SPDevicePlatform.h  |   36 -
 Snowplow/Internal/Subject/SPDevicePlatform.m  |   44 -
 Snowplow/Internal/Subject/SPPlatformContext.h |   70 -
 Snowplow/Internal/Subject/SPPlatformContext.m |  132 -
 Snowplow/Internal/Subject/SPSubject.h         |  217 -
 Snowplow/Internal/Subject/SPSubject.m         |  276 --
 .../Subject/SPSubjectConfigurationUpdate.h    |   43 -
 .../Subject/SPSubjectConfigurationUpdate.m    |   37 -
 .../Subject/SPSubjectControllerImpl.h         |   32 -
 .../Subject/SPSubjectControllerImpl.m         |  229 -
 Snowplow/Internal/Tracker/SPDeepLinkState.h   |   37 -
 .../Internal/Tracker/SPDeepLinkStateMachine.m |   84 -
 Snowplow/Internal/Tracker/SPInstallTracker.h  |   38 -
 Snowplow/Internal/Tracker/SPInstallTracker.m  |   75 -
 Snowplow/Internal/Tracker/SPLifecycleState.h  |   36 -
 Snowplow/Internal/Tracker/SPLifecycleState.m  |   48 -
 .../Tracker/SPLifecycleStateMachine.m         |   63 -
 Snowplow/Internal/Tracker/SPServiceProvider.h |   38 -
 Snowplow/Internal/Tracker/SPServiceProvider.m |  396 --
 .../Tracker/SPServiceProviderProtocol.h       |   71 -
 Snowplow/Internal/Tracker/SPStateFuture.h     |   42 -
 Snowplow/Internal/Tracker/SPStateFuture.m     |   58 -
 .../Internal/Tracker/SPStateMachineProtocol.h |   41 -
 Snowplow/Internal/Tracker/SPStateManager.h    |   40 -
 Snowplow/Internal/Tracker/SPStateManager.m    |  175 -
 Snowplow/Internal/Tracker/SPTracker.h         |  352 --
 Snowplow/Internal/Tracker/SPTracker.m         |  743 ----
 .../Tracker/SPTrackerConfigurationUpdate.h    |   53 -
 .../Tracker/SPTrackerConfigurationUpdate.m    |   45 -
 .../Internal/Tracker/SPTrackerController.h    |  106 -
 .../Tracker/SPTrackerControllerImpl.h         |   33 -
 .../Tracker/SPTrackerControllerImpl.m         |  298 --
 Snowplow/Internal/Tracker/SPTrackerEvent.h    |   51 -
 Snowplow/Internal/Tracker/SPTrackerEvent.m    |   70 -
 Snowplow/Internal/Tracker/SPTrackerState.h    |   45 -
 Snowplow/Internal/Tracker/SPTrackerState.m    |   71 -
 .../Internal/Tracker/SPTrackerStateSnapshot.h |   35 -
 .../Tracker/SPWebViewMessageHandler.m         |  153 -
 .../Utils/NSDictionary+SP_TypeMethods.h       |   39 -
 .../Utils/NSDictionary+SP_TypeMethods.m       |   89 -
 Snowplow/Internal/Utils/SNOWReachability.h    |   37 -
 Snowplow/Internal/Utils/SNOWReachability.m    |  130 -
 Snowplow/Internal/Utils/SPDataPersistence.h   |   43 -
 Snowplow/Internal/Utils/SPDataPersistence.m   |  240 --
 Snowplow/Internal/Utils/SPDeviceInfoMonitor.h |  126 -
 Snowplow/Internal/Utils/SPDeviceInfoMonitor.m |  333 --
 Snowplow/Internal/Utils/SPJSONSerialization.h |   33 -
 Snowplow/Internal/Utils/SPJSONSerialization.m |   65 -
 Snowplow/Internal/Utils/SPUtilities.h         |  172 -
 Snowplow/Internal/Utils/SPUtilities.m         |  245 --
 Snowplow/Internal/Utils/SPWeakTimerTarget.h   |   38 -
 Snowplow/Internal/Utils/SPWeakTimerTarget.m   |   50 -
 Snowplow/Snowplow-umbrella-header.h           |   74 -
 .../buildPhaseScript/copy_public_headers.sh   |   46 -
 Snowplow/include/SNOWError.h                  |    1 -
 Snowplow/include/SPBackground.h               |    1 -
 Snowplow/include/SPConfiguration.h            |    1 -
 Snowplow/include/SPConfigurationBundle.h      |    1 -
 Snowplow/include/SPConfigurationState.h       |    1 -
 Snowplow/include/SPConsentDocument.h          |    1 -
 Snowplow/include/SPConsentGranted.h           |    1 -
 Snowplow/include/SPConsentWithdrawn.h         |    1 -
 Snowplow/include/SPDeepLinkEntity.h           |    1 -
 Snowplow/include/SPDeepLinkReceived.h         |    1 -
 Snowplow/include/SPDefaultNetworkConnection.h |    1 -
 Snowplow/include/SPDevicePlatform.h           |    1 -
 Snowplow/include/SPEcommerce.h                |    1 -
 Snowplow/include/SPEcommerceItem.h            |    1 -
 Snowplow/include/SPEmitterConfiguration.h     |    1 -
 Snowplow/include/SPEmitterController.h        |    1 -
 Snowplow/include/SPEmitterEvent.h             |    1 -
 Snowplow/include/SPEventBase.h                |    1 -
 Snowplow/include/SPEventStore.h               |    1 -
 Snowplow/include/SPForeground.h               |    1 -
 Snowplow/include/SPGDPRConfiguration.h        |    1 -
 Snowplow/include/SPGDPRController.h           |    1 -
 Snowplow/include/SPGlobalContext.h            |    1 -
 .../include/SPGlobalContextsConfiguration.h   |    1 -
 Snowplow/include/SPGlobalContextsController.h |    1 -
 Snowplow/include/SPLifecycleEntity.h          |    1 -
 Snowplow/include/SPLoggerDelegate.h           |    1 -
 Snowplow/include/SPMemoryEventStore.h         |    1 -
 Snowplow/include/SPMessageNotification.h      |    1 -
 .../include/SPMessageNotificationAttachment.h |    1 -
 Snowplow/include/SPNetworkConfiguration.h     |    1 -
 Snowplow/include/SPNetworkConnection.h        |    1 -
 Snowplow/include/SPNetworkController.h        |    1 -
 Snowplow/include/SPPageView.h                 |    1 -
 Snowplow/include/SPPayload.h                  |    1 -
 Snowplow/include/SPPushNotification.h         |    1 -
 Snowplow/include/SPRemoteConfiguration.h      |    1 -
 Snowplow/include/SPRequest.h                  |    1 -
 Snowplow/include/SPRequestCallback.h          |    1 -
 Snowplow/include/SPRequestResult.h            |    1 -
 Snowplow/include/SPSQLiteEventStore.h         |    1 -
 Snowplow/include/SPSchemaRule.h               |    1 -
 Snowplow/include/SPSchemaRuleset.h            |    1 -
 Snowplow/include/SPScreenView.h               |    1 -
 Snowplow/include/SPSelfDescribing.h           |    1 -
 Snowplow/include/SPSelfDescribingJson.h       |    1 -
 Snowplow/include/SPSessionConfiguration.h     |    1 -
 Snowplow/include/SPSessionController.h        |    1 -
 Snowplow/include/SPSessionState.h             |    1 -
 Snowplow/include/SPSnowplow.h                 |    1 -
 Snowplow/include/SPState.h                    |    1 -
 Snowplow/include/SPStructured.h               |    1 -
 Snowplow/include/SPSubjectConfiguration.h     |    1 -
 Snowplow/include/SPSubjectController.h        |    1 -
 Snowplow/include/SPTiming.h                   |    1 -
 Snowplow/include/SPTrackerConfiguration.h     |    1 -
 Snowplow/include/SPTrackerConstants.h         |    1 -
 Snowplow/include/SPTrackerController.h        |    1 -
 Snowplow/include/SPTrackerStateSnapshot.h     |    1 -
 Snowplow/ios.modulemap                        |    5 -
 Snowplow/watchos.modulemap                    |    5 -
 SnowplowTracker.podspec                       |  132 +-
 Sources/Core/Emitter/Emitter.swift            |  539 +++
 .../Emitter/EmitterConfigurationUpdate.swift  |  126 +
 .../Core/Emitter/EmitterControllerImpl.swift  |  149 +
 .../Core/Emitter/EmitterEventProcessing.swift |   18 +-
 .../Core/GDPR/GDPRConfigurationUpdate.swift   |   97 +
 Sources/Core/GDPR/GDPRContext.swift           |   81 +
 Sources/Core/GDPR/GDPRControllerImpl.swift    |   99 +
 .../GlobalContextsControllerImpl.swift        |   52 +
 Sources/Core/GlobalContexts/SchemaRule.swift  |  142 +
 Sources/Core/Logger/Logger.swift              |  144 +
 .../NetworkConfigurationUpdate.swift          |   66 +
 .../NetworkControllerImpl.swift               |   82 +
 .../ConfigurationCache.swift                  |  137 +
 .../ConfigurationFetcher.swift                |   57 +
 .../ConfigurationProvider.swift               |   86 +
 .../FetchedConfigurationBundle.swift          |   79 +
 .../Core/ScreenViewTracking/ScreenState.swift |  105 +
 .../ScreenStateMachine.swift                  |   87 +
 .../UIKitScreenViewTracking.swift             |  188 +
 Sources/Core/Session/Session.swift            |  254 ++
 .../Session/SessionConfigurationUpdate.swift  |   60 +
 .../Core/Session/SessionControllerImpl.swift  |  177 +
 Sources/Core/Storage/MemoryEventStore.swift   |  107 +
 Sources/Core/Storage/SQLiteEventStore.swift   |  292 ++
 Sources/Core/Subject/PlatformContext.swift    |  153 +
 Sources/Core/Subject/Subject.swift            |  338 ++
 .../Subject/SubjectConfigurationUpdate.swift  |  135 +
 .../Core/Subject/SubjectControllerImpl.swift  |  225 +
 .../Core/Tracker/DeepLinkState.swift          |   22 +-
 .../Core/Tracker/DeepLinkStateMachine.swift   |   83 +
 Sources/Core/Tracker/InstallTracker.swift     |   71 +
 .../Core/Tracker/LifecycleState.swift         |   24 +-
 .../Core/Tracker/LifecycleStateMachine.swift  |   66 +
 Sources/Core/Tracker/ServiceProvider.swift    |  354 ++
 .../Tracker/ServiceProviderProtocol.swift     |   43 +
 Sources/Core/Tracker/StateFuture.swift        |   54 +
 Sources/Core/Tracker/StateManager.swift       |  165 +
 Sources/Core/Tracker/Tracker.swift            |  682 +++
 .../Tracker/TrackerConfigurationUpdate.swift  |  222 +
 .../Core/Tracker/TrackerControllerImpl.swift  |  298 ++
 Sources/Core/Tracker/TrackerDefaults.swift    |   42 +
 Sources/Core/Tracker/TrackerEvent.swift       |   83 +
 Sources/Core/Tracker/TrackerState.swift       |   67 +
 .../Core/Tracker/WebViewMessageHandler.swift  |  171 +
 Sources/Core/TrackerConstants.swift           |  272 ++
 Sources/Core/Utils/DataPersistence.swift      |  209 +
 Sources/Core/Utils/DeviceInfoMonitor.swift    |  341 ++
 Sources/Core/Utils/SNOWReachability.swift     |  101 +
 Sources/Core/Utils/Utilities.swift            |  254 ++
 .../Configurations/Configuration.swift        |   41 +-
 .../Configurations/ConfigurationBundle.swift  |  125 +
 .../Configurations/ConfigurationState.swift   |   38 +-
 .../Configurations/EmitterConfiguration.swift |  155 +
 .../Configurations/GDPRConfiguration.swift    |  110 +
 .../GlobalContextsConfiguration.swift         |   75 +
 .../Configurations/NetworkConfiguration.swift |  132 +
 .../Configurations/RemoteConfiguration.swift  |   88 +
 .../Configurations/SessionConfiguration.swift |  162 +
 .../Configurations/SubjectConfiguration.swift |  274 ++
 .../Configurations/TrackerConfiguration.swift |  297 ++
 .../Snowplow/Controllers/Controller.swift     |   22 +-
 .../Controllers/EmitterController.swift       |   43 +
 .../Snowplow/Controllers/GDPRController.swift |   48 +
 .../GlobalContextsController.swift            |   17 +-
 .../Controllers/NetworkController.swift       |   39 +
 .../Controllers/SessionController.swift       |   59 +
 .../Controllers/SubjectController.swift       |   26 +
 .../Controllers/TrackerController.swift       |   77 +
 Sources/Snowplow/Emitter/BufferOption.swift   |   35 +
 .../Snowplow/Emitter/EmitterDefaults.swift    |   35 +-
 .../Snowplow/Emitter/EmitterEvent.swift       |   31 +-
 Sources/Snowplow/Emitter/EventStore.swift     |   53 +
 .../Snowplow/Entities/DeepLinkEntity.swift    |   54 +
 .../Snowplow/Entities/LifecycleEntity.swift   |   54 +
 .../Snowplow/Events/Background.swift          |   46 +-
 Sources/Snowplow/Events/ConsentDocument.swift |   65 +
 Sources/Snowplow/Events/ConsentGranted.swift  |   91 +
 .../Snowplow/Events/ConsentWithdrawn.swift    |   79 +
 .../Snowplow/Events/DeepLinkReceived.swift    |   67 +
 Sources/Snowplow/Events/Ecommerce.swift       |   93 +
 Sources/Snowplow/Events/EcommerceItem.swift   |   71 +
 Sources/Snowplow/Events/EventBase.swift       |   78 +
 .../Snowplow/Events/Foreground.swift          |   47 +-
 .../Snowplow/Events/MessageNotification.swift |  194 +
 .../MessageNotificationAttachment.swift       |   53 +
 Sources/Snowplow/Events/PageView.swift        |   60 +
 .../Snowplow/Events/PushNotification.swift    |  177 +
 .../Snowplow/Events/SNOWError.swift           |   60 +-
 Sources/Snowplow/Events/ScreenView.swift      |  108 +
 Sources/Snowplow/Events/SelfDescribing.swift  |   62 +
 Sources/Snowplow/Events/Structured.swift      |   60 +
 Sources/Snowplow/Events/Timing.swift          |   63 +
 Sources/Snowplow/Events/TrackerError.swift    |   74 +
 .../GlobalContexts/ContextGenerator.swift     |   38 +
 .../GlobalContexts/GlobalContext.swift        |  121 +
 .../GlobalContexts/SchemaRuleset.swift        |  110 +
 .../Network/DefaultNetworkConnection.swift    |  242 ++
 .../Snowplow/Network/HttpMethodOptions.swift  |   21 +-
 .../Snowplow/Network/NetworkConnection.swift  |   42 +-
 .../Snowplow/Network/ProtocolOptions.swift    |   31 +
 Sources/Snowplow/Network/Request.swift        |   69 +
 .../Snowplow/Network/RequestCallback.swift    |   19 +-
 Sources/Snowplow/Network/RequestResult.swift  |   85 +
 Sources/Snowplow/Payload/Payload.swift        |  201 +
 .../Snowplow/Payload/SelfDescribingJson.swift |  129 +
 .../Snowplow}/Snowplow-Prefix.pch             |    0
 Sources/Snowplow/Snowplow.swift               |  317 ++
 Sources/Snowplow/Tracker/DevicePlatform.swift |   62 +
 .../Snowplow/Tracker/InspectableEvent.swift   |   44 +
 Sources/Snowplow/Tracker/LogLevel.swift       |   30 +
 .../Snowplow/Tracker/LoggerDelegate.swift     |   26 +-
 Sources/Snowplow/Tracker/SessionState.swift   |  107 +
 .../Snowplow/Tracker/State.swift              |    9 +-
 .../Tracker/StateMachineProtocol.swift        |   40 +
 .../Tracker/TrackerStateSnapshot.swift        |   28 +-
 .../Snowplow/Utils/GDPRProcessingBasis.swift  |   22 +-
 .../Snowplow/Utils/SPSize.swift               |   51 +-
 Sources/Snowplow/ios.modulemap                |    3 +
 Sources/Snowplow/watchos.modulemap            |    3 +
 TestServiceProvider.m                         |  108 -
 .../TestEmitterConfiguration.swift            |   71 +
 .../TestMultipleInstances.swift               |   98 +
 .../TestRemoteConfiguration.swift             |  392 ++
 .../TestTrackerConfiguration.swift            |  400 ++
 .../TestTrackerController.swift               |   75 +
 .../Global Contexts/TestGlobalContexts.swift  |  279 ++
 Tests/Global Contexts/TestSchemaRuleset.swift |  100 +
 {Snowplow iOSTests => Tests}/Info.plist       |    0
 Tests/Legacy Tests/LegacyTestEmitter.swift    |  377 ++
 Tests/Legacy Tests/LegacyTestSubject.swift    |  121 +
 Tests/Legacy Tests/LegacyTestTracker.swift    |  156 +
 .../Products/iglu_resolver.json               |    0
 .../SnowplowTests-Info.plist                  |    0
 Tests/TestDataPersistence.swift               |  102 +
 Tests/TestEvents.swift                        |  371 ++
 Tests/TestGeneratedJsons.swift                |  390 ++
 Tests/TestLifecycleState.swift                |  100 +
 Tests/TestLogger.swift                        |  101 +
 Tests/TestMemoryEventStore.swift              |   75 +
 Tests/TestNetworkConnection.swift             |  195 +
 Tests/TestPayload.swift                       |  326 ++
 Tests/TestPlatformContext.swift               |  197 +
 Tests/TestRequest.swift                       |  236 +
 Tests/TestRequestResult.swift                 |   85 +
 Tests/TestSQLiteEventStore.swift              |  154 +
 Tests/TestScreenState.swift                   |  141 +
 Tests/TestSelfDescribingJson.swift            |  165 +
 Tests/TestServiceProvider.swift               |   90 +
 Tests/TestSession.swift                       |  489 +++
 Tests/TestStateManager.swift                  |  222 +
 Tests/TestSubject.swift                       |   73 +
 Tests/TestUtils.swift                         |  174 +
 Tests/TestWebViewMessageHandler.swift         |  138 +
 .../Tests-bridging-header.h                   |   15 +-
 Tests/Utils/MockDeviceInfoMonitor.swift       |  122 +
 Tests/Utils/MockEventStore.swift              |   90 +
 .../Utils/MockLoggerDelegate.swift            |   28 +-
 Tests/Utils/MockNetworkConnection.swift       |   59 +
 .../Utils/MockWKScriptMessage.swift           |   28 +-
 .../en.lproj/InfoPlist.strings                |    0
 456 files changed, 20571 insertions(+), 28898 deletions(-)
 delete mode 100644 .scripts/copy_public_headers.sh
 delete mode 100644 Snowplow iOSTests/Configurations/TestEmitterConfiguration.m
 delete mode 100644 Snowplow iOSTests/Configurations/TestMultipleInstances.m
 delete mode 100644 Snowplow iOSTests/Configurations/TestRemoteConfiguration.m
 delete mode 100644 Snowplow iOSTests/Configurations/TestTrackerConfiguration.m
 delete mode 100644 Snowplow iOSTests/Configurations/TestTrackerController.m
 delete mode 100644 Snowplow iOSTests/Global Contexts/TestGlobalContexts.m
 delete mode 100644 Snowplow iOSTests/Global Contexts/TestSchemaRuleset.m
 delete mode 100644 Snowplow iOSTests/Legacy Tests/LegacyTestEmitter.m
 delete mode 100644 Snowplow iOSTests/Legacy Tests/LegacyTestEvent.m
 delete mode 100644 Snowplow iOSTests/Legacy Tests/LegacyTestSubject.m
 delete mode 100644 Snowplow iOSTests/Legacy Tests/LegacyTestTracker.m
 delete mode 100644 Snowplow iOSTests/TestDataPersistence.m
 delete mode 100644 Snowplow iOSTests/TestEvents.m
 delete mode 100644 Snowplow iOSTests/TestGeneratedJsons.m
 delete mode 100644 Snowplow iOSTests/TestLifecycleState.m
 delete mode 100644 Snowplow iOSTests/TestLogger.m
 delete mode 100644 Snowplow iOSTests/TestMemoryEventStore.m
 delete mode 100644 Snowplow iOSTests/TestNetworkConnection.m
 delete mode 100644 Snowplow iOSTests/TestPayload.m
 delete mode 100644 Snowplow iOSTests/TestPlatformContext.m
 delete mode 100644 Snowplow iOSTests/TestRequest.m
 delete mode 100644 Snowplow iOSTests/TestRequestResponse.m
 delete mode 100644 Snowplow iOSTests/TestRequestResult.m
 delete mode 100644 Snowplow iOSTests/TestSQLiteEventStore.m
 delete mode 100644 Snowplow iOSTests/TestScreenState.m
 delete mode 100644 Snowplow iOSTests/TestSelfDescribingJson.m
 delete mode 100644 Snowplow iOSTests/TestSession.m
 delete mode 100644 Snowplow iOSTests/TestStateManager.m
 delete mode 100644 Snowplow iOSTests/TestSubject.m
 delete mode 100644 Snowplow iOSTests/TestUtils.m
 delete mode 100644 Snowplow iOSTests/TestWebViewMessageHandler.m
 delete mode 100644 Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.h
 delete mode 100644 Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.m
 delete mode 100644 Snowplow iOSTests/Utils/SPMockEventStore.m
 delete mode 100644 Snowplow iOSTests/Utils/SPMockLoggerDelegate.m
 delete mode 100644 Snowplow iOSTests/Utils/SPMockNetworkConnection.h
 delete mode 100644 Snowplow iOSTests/Utils/SPMockNetworkConnection.m
 delete mode 100644 Snowplow macOSTests/Info.plist
 delete mode 100644 Snowplow macOSTests/Snowplow_macOSTests.m
 delete mode 100644 Snowplow/Internal/Configurations/SPConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPConfigurationBundle.h
 delete mode 100644 Snowplow/Internal/Configurations/SPConfigurationBundle.m
 delete mode 100644 Snowplow/Internal/Configurations/SPEmitterConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPEmitterConfiguration.m
 delete mode 100644 Snowplow/Internal/Configurations/SPGDPRConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPGDPRConfiguration.m
 delete mode 100644 Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.m
 delete mode 100644 Snowplow/Internal/Configurations/SPNetworkConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPNetworkConfiguration.m
 delete mode 100644 Snowplow/Internal/Configurations/SPRemoteConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPRemoteConfiguration.m
 delete mode 100644 Snowplow/Internal/Configurations/SPSessionConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPSessionConfiguration.m
 delete mode 100644 Snowplow/Internal/Configurations/SPSubjectConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPSubjectConfiguration.m
 delete mode 100644 Snowplow/Internal/Configurations/SPTrackerConfiguration.h
 delete mode 100644 Snowplow/Internal/Configurations/SPTrackerConfiguration.m
 delete mode 100644 Snowplow/Internal/Emitter/SPEmitter.h
 delete mode 100644 Snowplow/Internal/Emitter/SPEmitter.m
 delete mode 100644 Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.h
 delete mode 100644 Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.m
 delete mode 100644 Snowplow/Internal/Emitter/SPEmitterController.h
 delete mode 100644 Snowplow/Internal/Emitter/SPEmitterControllerImpl.m
 delete mode 100644 Snowplow/Internal/Emitter/SPEmitterEvent.h
 delete mode 100644 Snowplow/Internal/Emitter/SPRequest.h
 delete mode 100644 Snowplow/Internal/Emitter/SPRequest.m
 delete mode 100644 Snowplow/Internal/Emitter/SPRequestResult.h
 delete mode 100644 Snowplow/Internal/Emitter/SPRequestResult.m
 delete mode 100644 Snowplow/Internal/Entities/SPDeepLinkEntity.h
 delete mode 100644 Snowplow/Internal/Entities/SPDeepLinkEntity.m
 delete mode 100644 Snowplow/Internal/Entities/SPLifecycleEntity.h
 delete mode 100644 Snowplow/Internal/Entities/SPLifecycleEntity.m
 delete mode 100644 Snowplow/Internal/Events/SNOWError.h
 delete mode 100644 Snowplow/Internal/Events/SNOWError.m
 delete mode 100644 Snowplow/Internal/Events/SPConsentDocument.h
 delete mode 100644 Snowplow/Internal/Events/SPConsentDocument.m
 delete mode 100644 Snowplow/Internal/Events/SPConsentGranted.h
 delete mode 100644 Snowplow/Internal/Events/SPConsentGranted.m
 delete mode 100644 Snowplow/Internal/Events/SPConsentWithdrawn.h
 delete mode 100644 Snowplow/Internal/Events/SPConsentWithdrawn.m
 delete mode 100644 Snowplow/Internal/Events/SPDeepLinkReceived.h
 delete mode 100644 Snowplow/Internal/Events/SPDeepLinkReceived.m
 delete mode 100644 Snowplow/Internal/Events/SPEcommerce.h
 delete mode 100644 Snowplow/Internal/Events/SPEcommerce.m
 delete mode 100644 Snowplow/Internal/Events/SPEcommerceItem.h
 delete mode 100644 Snowplow/Internal/Events/SPEcommerceItem.m
 delete mode 100644 Snowplow/Internal/Events/SPEvent.h
 delete mode 100644 Snowplow/Internal/Events/SPEventBase.h
 delete mode 100644 Snowplow/Internal/Events/SPEventBase.m
 delete mode 100644 Snowplow/Internal/Events/SPMessageNotification.h
 delete mode 100644 Snowplow/Internal/Events/SPMessageNotification.m
 delete mode 100644 Snowplow/Internal/Events/SPMessageNotificationAttachment.h
 delete mode 100644 Snowplow/Internal/Events/SPMessageNotificationAttachment.m
 delete mode 100644 Snowplow/Internal/Events/SPPageView.h
 delete mode 100644 Snowplow/Internal/Events/SPPageView.m
 delete mode 100644 Snowplow/Internal/Events/SPPushNotification.h
 delete mode 100644 Snowplow/Internal/Events/SPPushNotification.m
 delete mode 100644 Snowplow/Internal/Events/SPScreenView.h
 delete mode 100644 Snowplow/Internal/Events/SPScreenView.m
 delete mode 100644 Snowplow/Internal/Events/SPSelfDescribing.h
 delete mode 100644 Snowplow/Internal/Events/SPSelfDescribing.m
 delete mode 100644 Snowplow/Internal/Events/SPStructured.h
 delete mode 100644 Snowplow/Internal/Events/SPStructured.m
 delete mode 100644 Snowplow/Internal/Events/SPTiming.h
 delete mode 100644 Snowplow/Internal/Events/SPTiming.m
 delete mode 100644 Snowplow/Internal/Events/SPTrackerError.h
 delete mode 100644 Snowplow/Internal/Events/SPTrackerError.m
 delete mode 100644 Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.h
 delete mode 100644 Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.m
 delete mode 100644 Snowplow/Internal/GDPR/SPGDPRController.h
 delete mode 100644 Snowplow/Internal/GDPR/SPGDPRControllerImpl.m
 delete mode 100644 Snowplow/Internal/GDPR/SPGdprContext.h
 delete mode 100644 Snowplow/Internal/GDPR/SPGdprContext.m
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPGlobalContext.h
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPGlobalContext.m
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPGlobalContextsController.h
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.h
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.m
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPSchemaRule.h
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPSchemaRule.m
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPSchemaRuleset.h
 delete mode 100644 Snowplow/Internal/GlobalContexts/SPSchemaRuleset.m
 delete mode 100644 Snowplow/Internal/Logger/SPLogger.h
 delete mode 100644 Snowplow/Internal/Logger/SPLogger.m
 delete mode 100644 Snowplow/Internal/Logger/SPLoggerDelegate.h
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.h
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.m
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.h
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.m
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPNetworkConnection.h
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPNetworkController.h
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.h
 delete mode 100644 Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.m
 delete mode 100644 Snowplow/Internal/Payload/SPPayload.h
 delete mode 100644 Snowplow/Internal/Payload/SPPayload.m
 delete mode 100644 Snowplow/Internal/Payload/SPSelfDescribingJson.h
 delete mode 100644 Snowplow/Internal/Payload/SPSelfDescribingJson.m
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.h
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.m
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.h
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.m
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.h
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.m
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPConfigurationState.h
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.h
 delete mode 100644 Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.m
 delete mode 100644 Snowplow/Internal/SPController.h
 delete mode 100644 Snowplow/Internal/SPSnowplow.h
 delete mode 100644 Snowplow/Internal/SPSnowplow.m
 delete mode 100644 Snowplow/Internal/SPTrackerConstants.h
 delete mode 100644 Snowplow/Internal/SPTrackerConstants.m
 delete mode 100644 Snowplow/Internal/ScreenViewTracking/SPScreenState.h
 delete mode 100644 Snowplow/Internal/ScreenViewTracking/SPScreenState.m
 delete mode 100644 Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.m
 delete mode 100644 Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.h
 delete mode 100644 Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.m
 delete mode 100644 Snowplow/Internal/Session/SPSession.h
 delete mode 100644 Snowplow/Internal/Session/SPSession.m
 delete mode 100644 Snowplow/Internal/Session/SPSessionConfigurationUpdate.h
 delete mode 100644 Snowplow/Internal/Session/SPSessionConfigurationUpdate.m
 delete mode 100644 Snowplow/Internal/Session/SPSessionController.h
 delete mode 100644 Snowplow/Internal/Session/SPSessionControllerImpl.h
 delete mode 100644 Snowplow/Internal/Session/SPSessionControllerImpl.m
 delete mode 100644 Snowplow/Internal/Session/SPSessionState.h
 delete mode 100644 Snowplow/Internal/Session/SPSessionState.m
 delete mode 100644 Snowplow/Internal/Storage/SPEventStore.h
 delete mode 100644 Snowplow/Internal/Storage/SPMemoryEventStore.m
 delete mode 100644 Snowplow/Internal/Storage/SPSQLiteEventStore.h
 delete mode 100644 Snowplow/Internal/Storage/SPSQLiteEventStore.m
 delete mode 100644 Snowplow/Internal/Subject/SPDevicePlatform.h
 delete mode 100644 Snowplow/Internal/Subject/SPDevicePlatform.m
 delete mode 100644 Snowplow/Internal/Subject/SPPlatformContext.h
 delete mode 100644 Snowplow/Internal/Subject/SPPlatformContext.m
 delete mode 100644 Snowplow/Internal/Subject/SPSubject.h
 delete mode 100644 Snowplow/Internal/Subject/SPSubject.m
 delete mode 100644 Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.h
 delete mode 100644 Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.m
 delete mode 100644 Snowplow/Internal/Subject/SPSubjectControllerImpl.h
 delete mode 100644 Snowplow/Internal/Subject/SPSubjectControllerImpl.m
 delete mode 100644 Snowplow/Internal/Tracker/SPDeepLinkState.h
 delete mode 100644 Snowplow/Internal/Tracker/SPDeepLinkStateMachine.m
 delete mode 100644 Snowplow/Internal/Tracker/SPInstallTracker.h
 delete mode 100644 Snowplow/Internal/Tracker/SPInstallTracker.m
 delete mode 100644 Snowplow/Internal/Tracker/SPLifecycleState.h
 delete mode 100644 Snowplow/Internal/Tracker/SPLifecycleState.m
 delete mode 100644 Snowplow/Internal/Tracker/SPLifecycleStateMachine.m
 delete mode 100644 Snowplow/Internal/Tracker/SPServiceProvider.h
 delete mode 100644 Snowplow/Internal/Tracker/SPServiceProvider.m
 delete mode 100644 Snowplow/Internal/Tracker/SPServiceProviderProtocol.h
 delete mode 100644 Snowplow/Internal/Tracker/SPStateFuture.h
 delete mode 100644 Snowplow/Internal/Tracker/SPStateFuture.m
 delete mode 100644 Snowplow/Internal/Tracker/SPStateMachineProtocol.h
 delete mode 100644 Snowplow/Internal/Tracker/SPStateManager.h
 delete mode 100644 Snowplow/Internal/Tracker/SPStateManager.m
 delete mode 100644 Snowplow/Internal/Tracker/SPTracker.h
 delete mode 100644 Snowplow/Internal/Tracker/SPTracker.m
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.h
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.m
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerController.h
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerControllerImpl.h
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerControllerImpl.m
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerEvent.h
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerEvent.m
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerState.h
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerState.m
 delete mode 100644 Snowplow/Internal/Tracker/SPTrackerStateSnapshot.h
 delete mode 100644 Snowplow/Internal/Tracker/SPWebViewMessageHandler.m
 delete mode 100644 Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.h
 delete mode 100644 Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.m
 delete mode 100644 Snowplow/Internal/Utils/SNOWReachability.h
 delete mode 100644 Snowplow/Internal/Utils/SNOWReachability.m
 delete mode 100644 Snowplow/Internal/Utils/SPDataPersistence.h
 delete mode 100644 Snowplow/Internal/Utils/SPDataPersistence.m
 delete mode 100644 Snowplow/Internal/Utils/SPDeviceInfoMonitor.h
 delete mode 100644 Snowplow/Internal/Utils/SPDeviceInfoMonitor.m
 delete mode 100644 Snowplow/Internal/Utils/SPJSONSerialization.h
 delete mode 100644 Snowplow/Internal/Utils/SPJSONSerialization.m
 delete mode 100644 Snowplow/Internal/Utils/SPUtilities.h
 delete mode 100644 Snowplow/Internal/Utils/SPUtilities.m
 delete mode 100644 Snowplow/Internal/Utils/SPWeakTimerTarget.h
 delete mode 100644 Snowplow/Internal/Utils/SPWeakTimerTarget.m
 delete mode 100644 Snowplow/Snowplow-umbrella-header.h
 delete mode 100644 Snowplow/buildPhaseScript/copy_public_headers.sh
 delete mode 120000 Snowplow/include/SNOWError.h
 delete mode 120000 Snowplow/include/SPBackground.h
 delete mode 120000 Snowplow/include/SPConfiguration.h
 delete mode 120000 Snowplow/include/SPConfigurationBundle.h
 delete mode 120000 Snowplow/include/SPConfigurationState.h
 delete mode 120000 Snowplow/include/SPConsentDocument.h
 delete mode 120000 Snowplow/include/SPConsentGranted.h
 delete mode 120000 Snowplow/include/SPConsentWithdrawn.h
 delete mode 120000 Snowplow/include/SPDeepLinkEntity.h
 delete mode 120000 Snowplow/include/SPDeepLinkReceived.h
 delete mode 120000 Snowplow/include/SPDefaultNetworkConnection.h
 delete mode 120000 Snowplow/include/SPDevicePlatform.h
 delete mode 120000 Snowplow/include/SPEcommerce.h
 delete mode 120000 Snowplow/include/SPEcommerceItem.h
 delete mode 120000 Snowplow/include/SPEmitterConfiguration.h
 delete mode 120000 Snowplow/include/SPEmitterController.h
 delete mode 120000 Snowplow/include/SPEmitterEvent.h
 delete mode 120000 Snowplow/include/SPEventBase.h
 delete mode 120000 Snowplow/include/SPEventStore.h
 delete mode 120000 Snowplow/include/SPForeground.h
 delete mode 120000 Snowplow/include/SPGDPRConfiguration.h
 delete mode 120000 Snowplow/include/SPGDPRController.h
 delete mode 120000 Snowplow/include/SPGlobalContext.h
 delete mode 120000 Snowplow/include/SPGlobalContextsConfiguration.h
 delete mode 120000 Snowplow/include/SPGlobalContextsController.h
 delete mode 120000 Snowplow/include/SPLifecycleEntity.h
 delete mode 120000 Snowplow/include/SPLoggerDelegate.h
 delete mode 120000 Snowplow/include/SPMemoryEventStore.h
 delete mode 120000 Snowplow/include/SPMessageNotification.h
 delete mode 120000 Snowplow/include/SPMessageNotificationAttachment.h
 delete mode 120000 Snowplow/include/SPNetworkConfiguration.h
 delete mode 120000 Snowplow/include/SPNetworkConnection.h
 delete mode 120000 Snowplow/include/SPNetworkController.h
 delete mode 120000 Snowplow/include/SPPageView.h
 delete mode 120000 Snowplow/include/SPPayload.h
 delete mode 120000 Snowplow/include/SPPushNotification.h
 delete mode 120000 Snowplow/include/SPRemoteConfiguration.h
 delete mode 120000 Snowplow/include/SPRequest.h
 delete mode 120000 Snowplow/include/SPRequestCallback.h
 delete mode 120000 Snowplow/include/SPRequestResult.h
 delete mode 120000 Snowplow/include/SPSQLiteEventStore.h
 delete mode 120000 Snowplow/include/SPSchemaRule.h
 delete mode 120000 Snowplow/include/SPSchemaRuleset.h
 delete mode 120000 Snowplow/include/SPScreenView.h
 delete mode 120000 Snowplow/include/SPSelfDescribing.h
 delete mode 120000 Snowplow/include/SPSelfDescribingJson.h
 delete mode 120000 Snowplow/include/SPSessionConfiguration.h
 delete mode 120000 Snowplow/include/SPSessionController.h
 delete mode 120000 Snowplow/include/SPSessionState.h
 delete mode 120000 Snowplow/include/SPSnowplow.h
 delete mode 120000 Snowplow/include/SPState.h
 delete mode 120000 Snowplow/include/SPStructured.h
 delete mode 120000 Snowplow/include/SPSubjectConfiguration.h
 delete mode 120000 Snowplow/include/SPSubjectController.h
 delete mode 120000 Snowplow/include/SPTiming.h
 delete mode 120000 Snowplow/include/SPTrackerConfiguration.h
 delete mode 120000 Snowplow/include/SPTrackerConstants.h
 delete mode 120000 Snowplow/include/SPTrackerController.h
 delete mode 120000 Snowplow/include/SPTrackerStateSnapshot.h
 delete mode 100644 Snowplow/ios.modulemap
 delete mode 100644 Snowplow/watchos.modulemap
 create mode 100644 Sources/Core/Emitter/Emitter.swift
 create mode 100644 Sources/Core/Emitter/EmitterConfigurationUpdate.swift
 create mode 100644 Sources/Core/Emitter/EmitterControllerImpl.swift
 rename Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.h => Sources/Core/Emitter/EmitterEventProcessing.swift (78%)
 create mode 100644 Sources/Core/GDPR/GDPRConfigurationUpdate.swift
 create mode 100644 Sources/Core/GDPR/GDPRContext.swift
 create mode 100644 Sources/Core/GDPR/GDPRControllerImpl.swift
 create mode 100644 Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
 create mode 100644 Sources/Core/GlobalContexts/SchemaRule.swift
 create mode 100644 Sources/Core/Logger/Logger.swift
 create mode 100644 Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift
 create mode 100644 Sources/Core/NetworkConnection/NetworkControllerImpl.swift
 create mode 100644 Sources/Core/RemoteConfiguration/ConfigurationCache.swift
 create mode 100644 Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
 create mode 100644 Sources/Core/RemoteConfiguration/ConfigurationProvider.swift
 create mode 100644 Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
 create mode 100644 Sources/Core/ScreenViewTracking/ScreenState.swift
 create mode 100644 Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
 create mode 100644 Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift
 create mode 100644 Sources/Core/Session/Session.swift
 create mode 100644 Sources/Core/Session/SessionConfigurationUpdate.swift
 create mode 100644 Sources/Core/Session/SessionControllerImpl.swift
 create mode 100644 Sources/Core/Storage/MemoryEventStore.swift
 create mode 100644 Sources/Core/Storage/SQLiteEventStore.swift
 create mode 100644 Sources/Core/Subject/PlatformContext.swift
 create mode 100644 Sources/Core/Subject/Subject.swift
 create mode 100644 Sources/Core/Subject/SubjectConfigurationUpdate.swift
 create mode 100644 Sources/Core/Subject/SubjectControllerImpl.swift
 rename Snowplow/Internal/GDPR/SPGDPRControllerImpl.h => Sources/Core/Tracker/DeepLinkState.swift (73%)
 create mode 100644 Sources/Core/Tracker/DeepLinkStateMachine.swift
 create mode 100644 Sources/Core/Tracker/InstallTracker.swift
 rename Snowplow/Internal/Tracker/SPLifecycleStateMachine.h => Sources/Core/Tracker/LifecycleState.swift (64%)
 create mode 100644 Sources/Core/Tracker/LifecycleStateMachine.swift
 create mode 100644 Sources/Core/Tracker/ServiceProvider.swift
 create mode 100644 Sources/Core/Tracker/ServiceProviderProtocol.swift
 create mode 100644 Sources/Core/Tracker/StateFuture.swift
 create mode 100644 Sources/Core/Tracker/StateManager.swift
 create mode 100644 Sources/Core/Tracker/Tracker.swift
 create mode 100644 Sources/Core/Tracker/TrackerConfigurationUpdate.swift
 create mode 100644 Sources/Core/Tracker/TrackerControllerImpl.swift
 create mode 100644 Sources/Core/Tracker/TrackerDefaults.swift
 create mode 100644 Sources/Core/Tracker/TrackerEvent.swift
 create mode 100644 Sources/Core/Tracker/TrackerState.swift
 create mode 100644 Sources/Core/Tracker/WebViewMessageHandler.swift
 create mode 100644 Sources/Core/TrackerConstants.swift
 create mode 100644 Sources/Core/Utils/DataPersistence.swift
 create mode 100644 Sources/Core/Utils/DeviceInfoMonitor.swift
 create mode 100644 Sources/Core/Utils/SNOWReachability.swift
 create mode 100644 Sources/Core/Utils/Utilities.swift
 rename Snowplow/Internal/Emitter/SPEmitterEvent.m => Sources/Snowplow/Configurations/Configuration.swift (59%)
 create mode 100644 Sources/Snowplow/Configurations/ConfigurationBundle.swift
 rename Snowplow iOSTests/Utils/SPMockWKScriptMessage.m => Sources/Snowplow/Configurations/ConfigurationState.swift (64%)
 create mode 100644 Sources/Snowplow/Configurations/EmitterConfiguration.swift
 create mode 100644 Sources/Snowplow/Configurations/GDPRConfiguration.swift
 create mode 100644 Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
 create mode 100644 Sources/Snowplow/Configurations/NetworkConfiguration.swift
 create mode 100644 Sources/Snowplow/Configurations/RemoteConfiguration.swift
 create mode 100644 Sources/Snowplow/Configurations/SessionConfiguration.swift
 create mode 100644 Sources/Snowplow/Configurations/SubjectConfiguration.swift
 create mode 100644 Sources/Snowplow/Configurations/TrackerConfiguration.swift
 rename Snowplow/Internal/Emitter/SPEmitterControllerImpl.h => Sources/Snowplow/Controllers/Controller.swift (73%)
 create mode 100644 Sources/Snowplow/Controllers/EmitterController.swift
 create mode 100644 Sources/Snowplow/Controllers/GDPRController.swift
 rename Snowplow/Internal/Tracker/SPDeepLinkStateMachine.h => Sources/Snowplow/Controllers/GlobalContextsController.swift (78%)
 create mode 100644 Sources/Snowplow/Controllers/NetworkController.swift
 create mode 100644 Sources/Snowplow/Controllers/SessionController.swift
 create mode 100644 Sources/Snowplow/Controllers/SubjectController.swift
 create mode 100644 Sources/Snowplow/Controllers/TrackerController.swift
 create mode 100644 Sources/Snowplow/Emitter/BufferOption.swift
 rename Snowplow/Internal/Tracker/SPWebViewMessageHandler.h => Sources/Snowplow/Emitter/EmitterDefaults.swift (57%)
 rename Snowplow/Internal/Tracker/SPDeepLinkState.m => Sources/Snowplow/Emitter/EmitterEvent.swift (65%)
 create mode 100644 Sources/Snowplow/Emitter/EventStore.swift
 create mode 100644 Sources/Snowplow/Entities/DeepLinkEntity.swift
 create mode 100644 Sources/Snowplow/Entities/LifecycleEntity.swift
 rename Snowplow/Internal/Events/SPBackground.h => Sources/Snowplow/Events/Background.swift (55%)
 create mode 100644 Sources/Snowplow/Events/ConsentDocument.swift
 create mode 100644 Sources/Snowplow/Events/ConsentGranted.swift
 create mode 100644 Sources/Snowplow/Events/ConsentWithdrawn.swift
 create mode 100644 Sources/Snowplow/Events/DeepLinkReceived.swift
 create mode 100644 Sources/Snowplow/Events/Ecommerce.swift
 create mode 100644 Sources/Snowplow/Events/EcommerceItem.swift
 create mode 100644 Sources/Snowplow/Events/EventBase.swift
 rename Snowplow/Internal/Events/SPForeground.h => Sources/Snowplow/Events/Foreground.swift (56%)
 create mode 100644 Sources/Snowplow/Events/MessageNotification.swift
 create mode 100644 Sources/Snowplow/Events/MessageNotificationAttachment.swift
 create mode 100644 Sources/Snowplow/Events/PageView.swift
 create mode 100644 Sources/Snowplow/Events/PushNotification.swift
 rename Snowplow/Internal/Events/SPBackground.m => Sources/Snowplow/Events/SNOWError.swift (52%)
 create mode 100644 Sources/Snowplow/Events/ScreenView.swift
 create mode 100644 Sources/Snowplow/Events/SelfDescribing.swift
 create mode 100644 Sources/Snowplow/Events/Structured.swift
 create mode 100644 Sources/Snowplow/Events/Timing.swift
 create mode 100644 Sources/Snowplow/Events/TrackerError.swift
 create mode 100644 Sources/Snowplow/GlobalContexts/ContextGenerator.swift
 create mode 100644 Sources/Snowplow/GlobalContexts/GlobalContext.swift
 create mode 100644 Sources/Snowplow/GlobalContexts/SchemaRuleset.swift
 create mode 100644 Sources/Snowplow/Network/DefaultNetworkConnection.swift
 rename Snowplow/Internal/Storage/SPMemoryEventStore.h => Sources/Snowplow/Network/HttpMethodOptions.swift (78%)
 rename Snowplow/Internal/Configurations/SPConfiguration.m => Sources/Snowplow/Network/NetworkConnection.swift (56%)
 create mode 100644 Sources/Snowplow/Network/ProtocolOptions.swift
 create mode 100644 Sources/Snowplow/Network/Request.swift
 rename Snowplow/Internal/Emitter/SPRequestCallback.h => Sources/Snowplow/Network/RequestCallback.swift (74%)
 create mode 100644 Sources/Snowplow/Network/RequestResult.swift
 create mode 100644 Sources/Snowplow/Payload/Payload.swift
 create mode 100644 Sources/Snowplow/Payload/SelfDescribingJson.swift
 rename {Snowplow => Sources/Snowplow}/Snowplow-Prefix.pch (100%)
 create mode 100644 Sources/Snowplow/Snowplow.swift
 create mode 100644 Sources/Snowplow/Tracker/DevicePlatform.swift
 create mode 100644 Sources/Snowplow/Tracker/InspectableEvent.swift
 create mode 100644 Sources/Snowplow/Tracker/LogLevel.swift
 rename Snowplow iOSTests/Utils/SPMockEventStore.h => Sources/Snowplow/Tracker/LoggerDelegate.swift (68%)
 create mode 100644 Sources/Snowplow/Tracker/SessionState.swift
 rename Snowplow/Internal/Tracker/SPState.h => Sources/Snowplow/Tracker/State.swift (90%)
 create mode 100644 Sources/Snowplow/Tracker/StateMachineProtocol.swift
 rename Snowplow/Internal/SPController.m => Sources/Snowplow/Tracker/TrackerStateSnapshot.swift (66%)
 rename Snowplow/Internal/Subject/SPSubjectController.h => Sources/Snowplow/Utils/GDPRProcessingBasis.swift (74%)
 rename Snowplow/Internal/Events/SPForeground.m => Sources/Snowplow/Utils/SPSize.swift (55%)
 create mode 100644 Sources/Snowplow/ios.modulemap
 create mode 100644 Sources/Snowplow/watchos.modulemap
 delete mode 100644 TestServiceProvider.m
 create mode 100644 Tests/Configurations/TestEmitterConfiguration.swift
 create mode 100644 Tests/Configurations/TestMultipleInstances.swift
 create mode 100644 Tests/Configurations/TestRemoteConfiguration.swift
 create mode 100644 Tests/Configurations/TestTrackerConfiguration.swift
 create mode 100644 Tests/Configurations/TestTrackerController.swift
 create mode 100644 Tests/Global Contexts/TestGlobalContexts.swift
 create mode 100644 Tests/Global Contexts/TestSchemaRuleset.swift
 rename {Snowplow iOSTests => Tests}/Info.plist (100%)
 create mode 100644 Tests/Legacy Tests/LegacyTestEmitter.swift
 create mode 100644 Tests/Legacy Tests/LegacyTestSubject.swift
 create mode 100644 Tests/Legacy Tests/LegacyTestTracker.swift
 rename {Snowplow iOSTests => Tests}/Products/iglu_resolver.json (100%)
 rename {Snowplow iOSTests => Tests}/SnowplowTests-Info.plist (100%)
 create mode 100644 Tests/TestDataPersistence.swift
 create mode 100644 Tests/TestEvents.swift
 create mode 100644 Tests/TestGeneratedJsons.swift
 create mode 100644 Tests/TestLifecycleState.swift
 create mode 100644 Tests/TestLogger.swift
 create mode 100644 Tests/TestMemoryEventStore.swift
 create mode 100644 Tests/TestNetworkConnection.swift
 create mode 100644 Tests/TestPayload.swift
 create mode 100644 Tests/TestPlatformContext.swift
 create mode 100644 Tests/TestRequest.swift
 create mode 100644 Tests/TestRequestResult.swift
 create mode 100644 Tests/TestSQLiteEventStore.swift
 create mode 100644 Tests/TestScreenState.swift
 create mode 100644 Tests/TestSelfDescribingJson.swift
 create mode 100644 Tests/TestServiceProvider.swift
 create mode 100644 Tests/TestSession.swift
 create mode 100644 Tests/TestStateManager.swift
 create mode 100644 Tests/TestSubject.swift
 create mode 100644 Tests/TestUtils.swift
 create mode 100644 Tests/TestWebViewMessageHandler.swift
 rename Snowplow iOSTests/Utils/SPMockWKScriptMessage.h => Tests/Tests-bridging-header.h (81%)
 create mode 100644 Tests/Utils/MockDeviceInfoMonitor.swift
 create mode 100644 Tests/Utils/MockEventStore.swift
 rename Snowplow iOSTests/Utils/SPMockLoggerDelegate.h => Tests/Utils/MockLoggerDelegate.swift (61%)
 create mode 100644 Tests/Utils/MockNetworkConnection.swift
 rename Snowplow/Internal/Emitter/SPEmitterEventProcessing.h => Tests/Utils/MockWKScriptMessage.swift (70%)
 rename {Snowplow iOSTests => Tests}/en.lproj/InfoPlist.strings (100%)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3a57fec9f..46d5def7a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -53,11 +53,10 @@ jobs:
         run: |
           DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
           echo ::set-output name=name::${DEMO_BRANCH}
-
       - name: Checkout demo app
         uses: actions/checkout@v2
         with:
-          repository: snowplow-incubator/snowplow-objc-tracker-examples
+          repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
           ref: ${{ steps.example_branch.outputs.name }}
           path: examples
 
@@ -70,58 +69,10 @@ jobs:
         run: |
           cd examples/demo/ 
           . .scripts/setup.sh
-          .scripts/test_ios_demo.sh -app SnowplowDemo -podfile Podfile -ios "${BUILD_WORKSPACE_OBJC_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_OBJC_DEMO}"
-
-  build_swift_carthage_demo_app:
-    name: "Swift demo (Carthage) (iOS ${{ matrix.version.ios }})"
-    needs: test_framework
-    runs-on: macos-${{ matrix.version.macos }}
-    env:
-      DEVELOPER_DIR: /Applications/Xcode_${{ matrix.version.xcode }}.app/Contents/Developer
-
-    strategy:
-      fail-fast: false
-      matrix:
-        version:
-          - {ios: '14.4', iphone: iPhone 11, watchos: '7.2', watch: Apple Watch Series 5 - 44mm, macos: '11', xcode: 12.4}
-
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-
-      - name: Get example branch name
-        id: example_branch
-        run: |
-          DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
-          echo ::set-output name=name::${DEMO_BRANCH}
-
-      - name: Checkout demo app
-        uses: actions/checkout@v2
-        with:
-          repository: snowplow-incubator/snowplow-objc-tracker-examples
-          ref: ${{ steps.example_branch.outputs.name }}
-          path: examples
-
-      - name: Build
-        env:
-          IOS: ${{ matrix.version.ios }}
-          WATCHOS: ${{ matrix.version.watchos }}
-          IPHONE: ${{ matrix.version.iphone }}
-          WATCH: ${{ matrix.version.watch }}
-        run: |
-          cd examples/demo/ 
-          . .scripts/setup.sh
-          .scripts/test_ios_demo.sh -app SnowplowSwiftCarthageDemo -carthage -log $GITHUB_WORKSPACE/cart.log -ios "${BUILD_PROJECT_SWIFT_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_SWIFT_DEMO_IOS}"
-
-      - name: Load logs if failed
-        if: ${{ failure() }}
-        uses: actions/upload-artifact@v2
-        with: 
-          name: carthage-demo-log-${{ matrix.version.ios }}
-          path: cart.log
+          .scripts/test_ios_demo.sh -app SnowplowObjCDemo -podfile Podfile -ios "${BUILD_WORKSPACE_OBJC_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_OBJC_DEMO}"
 
   build_swift_cocoapods_demo_app:
-    name: "Swift demo (Cocoapods ${{ matrix.podfile.type }}) (iOS ${{ matrix.version.ios }})"
+    name: "Swift demo (Cocoapods) (iOS ${{ matrix.version.ios }})"
     needs: test_framework
     runs-on: macos-${{ matrix.version.macos }}
     env:
@@ -132,10 +83,6 @@ jobs:
       matrix:
         version:
           - {ios: '14.4', iphone: iPhone 12 Pro, watchos: '7.2', watch: Apple Watch Series 5 - 44mm, macos: '11', xcode: 12.4}
-        podfile:
-          - {file: Podfile, type: "no directive"}
-          - {file: Podfile_frameworks, type: "use_frameworks! directive"}
-          - {file: Podfile_modular_headers, type: "use_modular_headers! directive"}
 
     steps:
       - name: Checkout
@@ -146,11 +93,10 @@ jobs:
         run: |
           DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
           echo ::set-output name=name::${DEMO_BRANCH}
-
       - name: Checkout demo app
         uses: actions/checkout@v2
         with:
-          repository: snowplow-incubator/snowplow-objc-tracker-examples
+          repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
           ref: ${{ steps.example_branch.outputs.name }}
           path: examples
 
@@ -163,7 +109,7 @@ jobs:
         run: |
           cd examples/demo/ 
           . .scripts/setup.sh
-          .scripts/test_ios_demo.sh -app SnowplowSwiftCocoapodsDemo -podfile ${{matrix.podfile.file}} -ios "${BUILD_WORKSPACE_SWIFT_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_SWIFT_DEMO_IOS}" -watch "${BUILD_DEST_PAIRED}" "${BUILD_SCHEME_SWIFT_DEMO_WATCH}"
+          .scripts/test_ios_demo.sh -app SnowplowSwiftCocoapodsDemo -podfile Podfile -ios "${BUILD_WORKSPACE_SWIFT_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_SWIFT_DEMO_IOS}" -watch "${BUILD_DEST_PAIRED}" "${BUILD_SCHEME_SWIFT_DEMO_WATCH}"
 
   build_swift_spm_demo_app:
     name: "Swift demo (SPM) (iOS ${{ matrix.version.ios }})"
@@ -206,7 +152,7 @@ jobs:
       - name: Checkout demo app
         uses: actions/checkout@v2
         with:
-          repository: snowplow-incubator/snowplow-objc-tracker-examples
+          repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
           ref: ${{ steps.example_branch.outputs.name }}
           path: examples
 
diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml
index b4da86a17..fdbf7f45b 100644
--- a/.github/workflows/snyk.yml
+++ b/.github/workflows/snyk.yml
@@ -16,7 +16,7 @@ jobs:
     - name: Checkout demo app
       uses: actions/checkout@v2
       with:
-        repository: snowplow-incubator/snowplow-objc-tracker-examples
+        repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
         path: examples
       
     - name: Run Snyk to check for vulnerabilities in tracker
diff --git a/.scripts/copy_public_headers.sh b/.scripts/copy_public_headers.sh
deleted file mode 100644
index dd50a25ff..000000000
--- a/.scripts/copy_public_headers.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-
-function create_link {
-    # 1. Store the first parameter to the script in a variable.
-    file_name="$1"
-
-    # 2. Now that we expect a parameter, lets check if it was provided.
-    if [[ -z "$file_name" ]]; then
-        echo "Script expects a parameter"
-        exit 1
-    fi
-
-    # 3. Create the symlink
-    pushd $SRCROOT/Snowplow
-    path=$(find . -name $file_name)
-    ln -vs $path .
-    popd
-}
-
-function check_link {
-    # 1. Store the first parameter to the script in a variable.
-    path="$1"
-
-    # 2. Now that we expect a parameter, lets check if it was provided.
-    if [[ -z "$path" ]]; then
-        echo "Script expects a parameter"
-        exit 1
-    fi
-
-    # 3. Check if the parameter is a symlink.
-    if [[ -L "$path" ]]; then
-        # 4. Check if it links to a valid path.
-        if [[ -e "$path" ]]; then
-            echo "$path is a valid link!"
-        else
-            unlink "$path"
-            echo "Cleaned up broken link: $path"
-        fi
-    else
-        echo "$path is not a symlink."
-    fi
-}
-
-for i in `ls $TARGET_BUILD_DIR/$PUBLIC_HEADERS_FOLDER_PATH`; do create_link $i ; done
-
-for i in $SRCROOT/Snowplow/*.h; do check_link $i ; done 
diff --git a/Package.swift b/Package.swift
index 78fe30755..91ac3299d 100644
--- a/Package.swift
+++ b/Package.swift
@@ -4,6 +4,12 @@ import PackageDescription
 
 let package = Package(
     name: "SnowplowTracker",
+    platforms: [
+        .macOS("10.13"),
+        .iOS("11.0"),
+        .tvOS("11.0"),
+        .watchOS("4.0")
+    ],
     products: [
         .library(
             name: "SnowplowTracker",
@@ -16,30 +22,10 @@ let package = Package(
         .target(
             name: "SnowplowTracker",
             dependencies: ["FMDB"],
-            path: "./Snowplow",
-            publicHeadersPath: "./include",
-            cSettings: [
-                .headerSearchPath("./Internal/RemoteConfiguration"),
-                .headerSearchPath("./Internal/Configurations"),
-                .headerSearchPath("./Internal/Subject"),
-                .headerSearchPath("./Internal/GDPR"),
-                .headerSearchPath("./Internal/ScreenViewTracking"),
-                .headerSearchPath("./Internal/Session"),
-                .headerSearchPath("./Internal/Utils"),
-                .headerSearchPath("./Internal/Storage"),
-                .headerSearchPath("./Internal/NetworkConnection"),
-                .headerSearchPath("./Internal/Tracker"),
-                .headerSearchPath("./Internal/Payload"),
-                .headerSearchPath("./Internal/Logger"),
-                .headerSearchPath("./Internal/GlobalContexts"),
-                .headerSearchPath("./Internal/Emitter"),
-                .headerSearchPath("./Internal/Events"),
-                .headerSearchPath("./Internal/Entities"),
-                .headerSearchPath("./Internal"),
-            ]),
+            path: "./Sources"),
         .testTarget(
-            name: "Snowplow-iOSTests",
+            name: "Tests",
             dependencies: ["SnowplowTracker"],
-            path: "Snowplow iOSTests")
+            path: "Tests")
     ]
 )
diff --git a/README.md b/README.md
index 05770d791..637e3ad30 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # iOS, macOS, tvOS and watchOS Analytics for Snowplow
 
-[![actively-maintained]][tracker-classificiation]
+[![actively-maintained]][tracker-classification]
 [![Build Status][gh-actions-image]][gh-actions]
 [![Coverage Status][coveralls-image]][coveralls]
 [![Platform][cocoa-plaform]][cocoadocs]
@@ -24,7 +24,7 @@ With this tracker you can collect event data from your applications, games or fr
 
 ### Demo apps using the Snowplow iOS Tracker
 
-Some examples of demo apps instrumented with our iOS Tracker can be found in the [snowplow-objc-tracker-examples](https://github.com/snowplow-incubator/snowplow-objc-tracker-examples) repository.
+Some examples of demo apps instrumented with our iOS Tracker can be found in the [snowplow-swift-ios-tracker-examples](https://github.com/snowplow-incubator/snowplow-swift-ios-tracker-examples) repository.
 
 ### Instrument the iOS Tracker
 
@@ -84,5 +84,5 @@ limitations under the License.
 
 [contributing-image]: https://d3i6fms1cm1j0i.cloudfront.net/github/images/contributing.png
 
-[tracker-classificiation]: https://github.com/snowplow/snowplow/wiki/Tracker-Maintenance-Classification
+[tracker-classification]: https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/tracker-maintenance-classification/
 [actively-maintained]: https://img.shields.io/static/v1?style=flat&label=Snowplow&message=Actively%20Maintained&color=6638b8&labelColor=9ba0aa&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAeFBMVEVMaXGXANeYANeXANZbAJmXANeUANSQAM+XANeMAMpaAJhZAJeZANiXANaXANaOAM2WANVnAKWXANZ9ALtmAKVaAJmXANZaAJlXAJZdAJxaAJlZAJdbAJlbAJmQAM+UANKZANhhAJ+EAL+BAL9oAKZnAKVjAKF1ALNBd8J1AAAAKHRSTlMAa1hWXyteBTQJIEwRgUh2JjJon21wcBgNfmc+JlOBQjwezWF2l5dXzkW3/wAAAHpJREFUeNokhQOCA1EAxTL85hi7dXv/E5YPCYBq5DeN4pcqV1XbtW/xTVMIMAZE0cBHEaZhBmIQwCFofeprPUHqjmD/+7peztd62dWQRkvrQayXkn01f/gWp2CrxfjY7rcZ5V7DEMDQgmEozFpZqLUYDsNwOqbnMLwPAJEwCopZxKttAAAAAElFTkSuQmCC
diff --git a/Snowplow iOSTests/Configurations/TestEmitterConfiguration.m b/Snowplow iOSTests/Configurations/TestEmitterConfiguration.m
deleted file mode 100644
index 8b41aba9e..000000000
--- a/Snowplow iOSTests/Configurations/TestEmitterConfiguration.m	
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-//  TestEmitterConfiguration.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPSnowplow.h"
-#import "SPNetworkConfiguration.h"
-#import "SPTrackerConfiguration.h"
-#import "SPSession.h"
-#import "SPMockEventStore.h"
-#import "SPDataPersistence.h"
-#import "SPMockNetworkConnection.h"
-#import "SPLogger.h"
-
-
-@interface TestEmitterConfiguration : XCTestCase
-
-@end
-
-@implementation TestEmitterConfiguration
-
-- (void)setUp {
-    [super setUp];
-    [SPLogger setLogLevel:SPLogLevelVerbose];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testPauseEmitter {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:200];
-    SPEmitterConfiguration *emitterConfig = [[SPEmitterConfiguration alloc] init];
-    emitterConfig.eventStore = [SPMockEventStore new];
-    emitterConfig.bufferOption = SPBufferOptionSingle;
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"" method:SPHttpMethodPost];
-    networkConfig.networkConnection = networkConnection;
-    
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    trackerConfig.installAutotracking = false;
-    trackerConfig.screenViewAutotracking = false;
-    trackerConfig.lifecycleAutotracking = false;
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig, emitterConfig]];
-    XCTAssertNotNil(tracker);
-    
-    [tracker.emitter pause];
-    [tracker track:[[SPStructured alloc] initWithCategory:@"cat" action:@"act"]];
-    [NSThread sleepForTimeInterval:3];
-    XCTAssertEqual(1, [[tracker emitter] dbCount]);
-    XCTAssertEqual(0, networkConnection.previousResults.count);
-
-    [tracker.emitter resume];
-    [NSThread sleepForTimeInterval:3];
-    XCTAssertEqual(1, networkConnection.previousResults.count);
-    XCTAssertEqual(0, [[tracker emitter] dbCount]);
-}
-
-- (void)testActivatesServerAnonymisationInEmitter {
-    SPEmitterConfiguration *emitterConfig = [[SPEmitterConfiguration alloc] init];
-    emitterConfig.serverAnonymisation = YES;
-    
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"" method:SPHttpMethodPost];
-    
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[emitterConfig]];
-    
-    XCTAssertTrue(tracker.emitter.serverAnonymisation);
-}
-
-
-@end
diff --git a/Snowplow iOSTests/Configurations/TestMultipleInstances.m b/Snowplow iOSTests/Configurations/TestMultipleInstances.m
deleted file mode 100644
index b2753f1d7..000000000
--- a/Snowplow iOSTests/Configurations/TestMultipleInstances.m	
+++ /dev/null
@@ -1,104 +0,0 @@
-//
-//  TestMultipleInstances.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPServiceProvider.h"
-
-@interface TestMultipleInstances : XCTestCase
-
-@end
-
-@implementation TestMultipleInstances
-
-- (void)setUp {
-    [SPSnowplow removeAllTrackers];
-}
-
-- (void)tearDown {
-    [SPSnowplow removeAllTrackers];
-}
-
-- (void)testSingleInstanceIsReconfigurable {
-    id<SPTrackerController> t1 = [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    XCTAssertEqualObjects(t1.network.endpoint, @"https://snowplowanalytics.fake/com.snowplowanalytics.snowplow/tp2");
-    id<SPTrackerController> t2 = [SPSnowplow createTrackerWithNamespace:@"t1" network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake2"]];
-    XCTAssertEqualObjects(t2.network.endpoint, @"https://snowplowanalytics.fake2/com.snowplowanalytics.snowplow/tp2");
-    XCTAssertEqualObjects(@[@"t1"], [SPSnowplow instancedTrackerNamespaces]);
-    XCTAssertEqual(t1, t2);
-}
-
-- (void)testMultipleInstances {
-    id<SPTrackerController> t1 = [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    XCTAssertEqualObjects(t1.network.endpoint, @"https://snowplowanalytics.fake/com.snowplowanalytics.snowplow/tp2");
-    id<SPTrackerController> t2 = [SPSnowplow createTrackerWithNamespace:@"t2" network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake2"]];
-    XCTAssertEqualObjects(t2.network.endpoint, @"https://snowplowanalytics.fake2/com.snowplowanalytics.snowplow/tp2");
-    XCTAssertNotEqual(t1, t2);
-    NSSet<NSString *> *expectedNamespaces = [NSSet setWithArray:@[@"t1", @"t2"]];
-    XCTAssertEqualObjects(expectedNamespaces, [NSSet setWithArray:[SPSnowplow instancedTrackerNamespaces]]);
-}
-
-- (void)testDefaultTracker {
-    id<SPTrackerController> t1 = [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    [SPSnowplow createTrackerWithNamespace:@"t2" network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake2"]];
-    id<SPTrackerController> td = [SPSnowplow defaultTracker];
-    XCTAssertEqual(t1, td);
-}
-
-- (void)testUpdateDefaultTracker {
-    [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    id<SPTrackerController> t2 = [SPSnowplow createTrackerWithNamespace:@"t2" network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake2"]];
-    [SPSnowplow setTrackerAsDefault:t2];
-    id<SPTrackerController> td = [SPSnowplow defaultTracker];
-    XCTAssertEqual(t2, td);
-}
-
-- (void)testRemoveTracker {
-    id<SPTrackerController> t1 = [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    id<SPTrackerController> t2 = [SPSnowplow createTrackerWithNamespace:@"t2" network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake2"]];
-    [SPSnowplow removeTracker:t1];
-    XCTAssertNotNil(t2);
-    XCTAssertEqualObjects(@[@"t2"], [SPSnowplow instancedTrackerNamespaces]);
-}
-
-- (void)testRecreateTrackerWhichWasRemovedWithSameNamespace {
-    id<SPTrackerController> t1 = [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    [SPSnowplow removeTracker:t1];
-    id<SPTrackerController> t2 = [SPSnowplow createTrackerWithNamespace:@"t1" network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake2"]];
-    XCTAssertNotEqual(t1, t2);
-    XCTAssertEqualObjects(@[@"t1"], [SPSnowplow instancedTrackerNamespaces]);
-}
-
-- (void)testRemoveDefaultTracker {
-    id<SPTrackerController> t1 = [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    [SPSnowplow removeTracker:t1];
-    id<SPTrackerController> td = [SPSnowplow defaultTracker];
-    XCTAssertNil(td);
-    XCTAssertEqualObjects(@[], [SPSnowplow instancedTrackerNamespaces]);
-}
-
-- (void)testRemoveAllTrackers {
-    [SPSnowplow createTrackerWithNamespace:@"t1"network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake"]];
-    [SPSnowplow createTrackerWithNamespace:@"t2" network:[[SPNetworkConfiguration alloc] initWithEndpoint:@"snowplowanalytics.fake2"]];
-    [SPSnowplow removeAllTrackers];
-    XCTAssertEqualObjects(@[], [SPSnowplow instancedTrackerNamespaces]);
-}
-
-@end
diff --git a/Snowplow iOSTests/Configurations/TestRemoteConfiguration.m b/Snowplow iOSTests/Configurations/TestRemoteConfiguration.m
deleted file mode 100644
index 4e75aee0d..000000000
--- a/Snowplow iOSTests/Configurations/TestRemoteConfiguration.m	
+++ /dev/null
@@ -1,387 +0,0 @@
-//
-//  TestRemoteConfiguration.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import <Nocilla/Nocilla.h>
-
-#import "SPFetchedConfigurationBundle.h"
-#import "SPConfigurationFetcher.h"
-#import "SPConfigurationCache.h"
-#import "SPConfigurationProvider.h"
-
-@interface TestRemoteConfiguration : XCTestCase
-@end
-
-@implementation TestRemoteConfiguration
-
-- (void)testJSONToConfigurations {
-    NSString *config = @"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0\",\"configurationVersion\":12,\"configurationBundle\": [\
-    {\"namespace\": \"default1\",\
-    \"networkConfiguration\": {\"endpoint\":\"https://fake.snowplow.io\",\"method\":\"get\"},\
-    \"trackerConfiguration\": {\"applicationContext\":false,\"screenContext\":false,},\
-    \"sessionConfiguration\": {\"backgroundTimeout\":60,\"foregroundTimeout\":60}\
-    },\
-    {\"namespace\": \"default2\",\
-    \"subjectConfiguration\": {\"userId\":\"testUserId\"}\
-    }\
-    ]}";
-    NSError *jsonError = nil;
-    NSData *jsonData = [config dataUsingEncoding:NSUTF8StringEncoding];
-    NSDictionary *dictionary = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&jsonError];
-    
-    SPFetchedConfigurationBundle *fetchedConfigurationBundle = [[SPFetchedConfigurationBundle alloc] initWithDictionary:dictionary];
-    XCTAssertEqualObjects(@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", fetchedConfigurationBundle.schema);
-    XCTAssertEqual(12, fetchedConfigurationBundle.configurationVersion);
-    XCTAssertEqual(2, fetchedConfigurationBundle.configurationBundle.count);
-
-    // Regular setup
-    SPConfigurationBundle *configurationBundle = fetchedConfigurationBundle.configurationBundle[0];
-    XCTAssertEqualObjects(@"default1", configurationBundle.namespace);
-    XCTAssertNotNil(configurationBundle.networkConfiguration);
-    XCTAssertNotNil(configurationBundle.trackerConfiguration);
-    XCTAssertNotNil(configurationBundle.sessionConfiguration);
-    XCTAssertNil(configurationBundle.subjectConfiguration);
-    SPNetworkConfiguration *networkConfiguration = configurationBundle.networkConfiguration;
-    XCTAssertEqual(SPHttpMethodGet, networkConfiguration.method);
-    SPTrackerConfiguration *trackerConfiguration = configurationBundle.trackerConfiguration;
-    XCTAssertFalse(trackerConfiguration.applicationContext);
-    SPSessionConfiguration *sessionConfiguration = configurationBundle.sessionConfiguration;
-    XCTAssertEqual(60, sessionConfiguration.backgroundTimeoutInSeconds);
-
-    // Regular setup without NetworkConfiguration
-    configurationBundle = fetchedConfigurationBundle.configurationBundle[1];
-    XCTAssertEqualObjects(@"default2", configurationBundle.namespace);
-    XCTAssertNil(configurationBundle.networkConfiguration);
-    XCTAssertNotNil(configurationBundle.subjectConfiguration);
-    SPSubjectConfiguration *subjectConfiguration = configurationBundle.subjectConfiguration;
-    XCTAssertEqualObjects(@"testUserId", subjectConfiguration.userId);
-}
-
-- (void)testDownloadConfiguration {
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(200)
-    .withHeaders(@{@"Content-Type": @"application/json"})
-    .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}");
-    XCTestExpectation *expectation = [XCTestExpectation new];
-
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-    [[SPConfigurationFetcher alloc] initWithRemoteSource:remoteConfig onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTAssertNotNil(fetchedConfigurationBundle);
-        XCTAssertEqualObjects(@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", fetchedConfigurationBundle.schema);
-        XCTAssertEqual(SPConfigurationStateFetched, configurationState);
-        [expectation fulfill];
-    }];
-
-    [self waitForExpectations:@[expectation] timeout:10];
-    [[LSNocilla sharedInstance] stop];
-}
-
-- (void)testConfigurationCache {
-    SPConfigurationBundle *bundle = [[SPConfigurationBundle alloc] initWithNamespace:@"namespace" networkConfiguration:[[SPNetworkConfiguration alloc] initWithEndpoint:@"endpoint"]];
-    SPFetchedConfigurationBundle *expected = [[SPFetchedConfigurationBundle alloc] init];
-    expected.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
-    expected.configurationVersion = 12;
-    expected.configurationBundle = @[bundle];
-    
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:@"http://example.com" method:SPHttpMethodGet];
-    
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    [cache clearCache];
-    [cache writeCache:expected];
-    
-    [NSThread sleepForTimeInterval:5]; // wait the config is written on cache.
-    
-    cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    SPFetchedConfigurationBundle *config = [cache readCache];
-    
-    XCTAssertEqual(expected.configurationVersion, config.configurationVersion);
-    XCTAssertEqualObjects(expected.schema, config.schema);
-    XCTAssertEqual(expected.configurationBundle.count, config.configurationBundle.count);
-    SPConfigurationBundle *expectedBundle = expected.configurationBundle[0];
-    SPConfigurationBundle *configBundle = config.configurationBundle[0];
-    XCTAssertEqualObjects(expectedBundle.networkConfiguration.endpoint, configBundle.networkConfiguration.endpoint);
-    XCTAssertNil(configBundle.trackerConfiguration);
-}
-
-- (void)testConfigurationProvider_notDownloading_fails {
-    // prepare test
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    [cache clearCache];
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(404);
-    
-    // test
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    SPConfigurationProvider *provider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfig];
-    [provider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTFail();
-    }];
-    XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:5];
-    XCTAssertEqual(XCTWaiterResultTimedOut, result);
-
-    // close test
-    [[LSNocilla sharedInstance] stop];
-}
-
-- (void)testConfigurationProvider_downloadOfWrongSchema_fails {
-    // prepare test
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    [cache clearCache];
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(200)
-    .withHeaders(@{@"Content-Type": @"application/json"})
-    .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/2-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}");
-    
-    // test
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    SPConfigurationProvider *provider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfig];
-    [provider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTFail();
-    }];
-    XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:5];
-    XCTAssertEqual(XCTWaiterResultTimedOut, result);
-
-    // close test
-    [[LSNocilla sharedInstance] stop];
-}
-
-- (void)testConfigurationProvider_downloadSameConfigVersionThanCached_dontUpdate {
-    // prepare test
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-    
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    [cache clearCache];
-    SPConfigurationBundle *bundle = [[SPConfigurationBundle alloc] initWithNamespace:@"namespace" networkConfiguration:[[SPNetworkConfiguration alloc] initWithEndpoint:@"endpoint"]];
-    SPFetchedConfigurationBundle *cached = [[SPFetchedConfigurationBundle alloc] init];
-    cached.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
-    cached.configurationVersion = 1;
-    cached.configurationBundle = @[bundle];
-    [cache writeCache:cached];
-    [NSThread sleepForTimeInterval:5]; // wait to write on cache
-    
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(200)
-    .withHeaders(@{@"Content-Type": @"application/json"})
-    .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}");
-    
-    // test
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    SPConfigurationProvider *provider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfig];
-    __block int i = 0;
-    [provider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTAssertEqual(SPConfigurationStateCached, configurationState);
-        if (i == 1 || [fetchedConfigurationBundle.schema isEqualToString:@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0"]) {
-            XCTFail();
-        }
-        if (i == 0 && [fetchedConfigurationBundle.schema isEqualToString:@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0"]) {
-            i++;
-        }
-    }];
-    XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:5];
-    XCTAssertEqual(XCTWaiterResultTimedOut, result);
-    XCTAssertEqual(1, i);
-
-    // close test
-    [[LSNocilla sharedInstance] stop];
-}
-
-- (void)testConfigurationProvider_downloadHigherConfigVersionThanCached_doUpdate {
-    // prepare test
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-    
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    [cache clearCache];
-    SPConfigurationBundle *bundle = [[SPConfigurationBundle alloc] initWithNamespace:@"namespace" networkConfiguration:[[SPNetworkConfiguration alloc] initWithEndpoint:@"endpoint"]];
-    SPFetchedConfigurationBundle *cached = [[SPFetchedConfigurationBundle alloc] init];
-    cached.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
-    cached.configurationVersion = 1;
-    cached.configurationBundle = @[bundle];
-    [cache writeCache:cached];
-    [NSThread sleepForTimeInterval:5]; // wait to write on cache
-    
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(200)
-    .withHeaders(@{@"Content-Type": @"application/json"})
-    .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}");
-    
-    // test
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    SPConfigurationProvider *provider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfig];
-    __block int i = 0;
-    [provider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTAssertEqual(i == 0 ? SPConfigurationStateCached : SPConfigurationStateFetched,
-                       configurationState);
-        if (i == 1 && [fetchedConfigurationBundle.schema isEqualToString:@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0"]) {
-            i++;
-        }
-        if (i == 0 && [fetchedConfigurationBundle.schema isEqualToString:@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0"]) {
-            i++;
-        }
-    }];
-    XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:5];
-    XCTAssertEqual(XCTWaiterResultTimedOut, result);
-    XCTAssertEqual(2, i);
-
-    // close test
-    [[LSNocilla sharedInstance] stop];
-}
-
-- (void)testConfigurationProvider_justRefresh_downloadSameConfigVersionThanCached_dontUpdate {
-    // prepare test
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-    
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    [cache clearCache];
-    SPConfigurationBundle *bundle = [[SPConfigurationBundle alloc] initWithNamespace:@"namespace" networkConfiguration:[[SPNetworkConfiguration alloc] initWithEndpoint:@"endpoint"]];
-    SPFetchedConfigurationBundle *cached = [[SPFetchedConfigurationBundle alloc] init];
-    cached.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
-    cached.configurationVersion = 1;
-    cached.configurationBundle = @[bundle];
-    [cache writeCache:cached];
-    [NSThread sleepForTimeInterval:5]; // wait to write on cache
-    
-    SPConfigurationProvider *provider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfig];
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    [provider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTAssertEqual(SPConfigurationStateCached, configurationState);
-        [expectation fulfill];
-    }];
-    [self waitForExpectations:@[expectation] timeout:5];
-    
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(200)
-    .withHeaders(@{@"Content-Type": @"application/json"})
-    .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}");
-    
-    // test
-    expectation = [XCTestExpectation new];
-    [provider retrieveConfigurationOnlyRemote:YES onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTFail();
-    }];
-    XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:5];
-    XCTAssertEqual(XCTWaiterResultTimedOut, result);
-
-    // close test
-    [[LSNocilla sharedInstance] stop];
-}
-
-- (void)testDoesntUseCachedConfigurationIfDifferentRemoteEndpoint {
-    // prepare test
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-    SPRemoteConfiguration *cachedRemoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:@"https://cached-snowplow.io/config.json" method:SPHttpMethodGet];
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-    
-    // write configuration (version 2) to cache
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:cachedRemoteConfig];
-    [cache clearCache];
-    SPConfigurationBundle *bundle = [[SPConfigurationBundle alloc] initWithNamespace:@"namespace" networkConfiguration:[[SPNetworkConfiguration alloc] initWithEndpoint:@"endpoint"]];
-    SPFetchedConfigurationBundle *cached = [[SPFetchedConfigurationBundle alloc] init];
-    cached.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
-    cached.configurationVersion = 2;
-    cached.configurationBundle = @[bundle];
-    [cache writeCache:cached];
-    [NSThread sleepForTimeInterval:5]; // wait to write on cache
-
-    // stub request for configuration (return version 1)
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(200)
-    .withHeaders(@{@"Content-Type": @"application/json"})
-    .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}");
-
-    // initialize tracker with remote config
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    [[SPConfigurationFetcher alloc] initWithRemoteSource:remoteConfig onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        XCTAssertNotNil(fetchedConfigurationBundle);
-        // should be the non-cache configuration (version 1)
-        XCTAssertEqualObjects(@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0", fetchedConfigurationBundle.schema);
-        XCTAssertEqual(1, fetchedConfigurationBundle.configurationVersion);
-        [expectation fulfill];
-    }];
-
-    [self waitForExpectations:@[expectation] timeout:10];
-    [[LSNocilla sharedInstance] stop];
-}
-
-// TODO: Replace LSNocilla as it's unreliable and unsupported. It causes this test failure.
-/*
-- (void)testConfigurationProvider_justRefresh_downloadHigherConfigVersionThanCached_doUpdate {
-    // prepare test
-    NSString *endpoint = @"https://fake-snowplow.io/config.json";
-    SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
-
-    SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
-    [cache clearCache];
-    SPConfigurationBundle *bundle = [[SPConfigurationBundle alloc] init];
-    bundle.networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"endpoint"];
-    SPFetchedConfigurationBundle *cached = [[SPFetchedConfigurationBundle alloc] init];
-    cached.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
-    cached.configurationVersion = 1;
-    cached.configurationBundle = @[bundle];
-    [cache writeCache:cached];
-    [NSThread sleepForTimeInterval:5]; // wait to write on cache
-    
-    SPConfigurationProvider *provider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfig];
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    [provider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle) {
-        [expectation fulfill];
-    }];
-    [self waitForExpectations:@[expectation] timeout:5];
-
-    [[LSNocilla sharedInstance] start];
-    stubRequest(@"GET", endpoint)
-    .andReturn(200)
-    .withHeaders(@{@"Content-Type": @"application/json"})
-    .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}");
-
-    // test
-    expectation = [XCTestExpectation new];
-    __block int i = 0;
-    [provider retrieveConfigurationOnlyRemote:YES onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle) {
-        if ([fetchedConfigurationBundle.schema isEqualToString:@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0"]) {
-            i++;
-        }
-    }];
-    XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:5];
-    XCTAssertEqual(XCTWaiterResultTimedOut, result);
-    XCTAssertEqual(1, i);
-
-    // close test
-    [[LSNocilla sharedInstance] stop];
-}
-*/
-
-@end
diff --git a/Snowplow iOSTests/Configurations/TestTrackerConfiguration.m b/Snowplow iOSTests/Configurations/TestTrackerConfiguration.m
deleted file mode 100644
index fd89845f9..000000000
--- a/Snowplow iOSTests/Configurations/TestTrackerConfiguration.m	
+++ /dev/null
@@ -1,405 +0,0 @@
-//
-//  TestTrackerConfiguration.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPSnowplow.h"
-#import "SPNetworkConfiguration.h"
-#import "SPTrackerConfiguration.h"
-#import "SPSession.h"
-#import "SPMockEventStore.h"
-#import "SPDataPersistence.h"
-
-
-@interface TestTrackerConfiguration : XCTestCase
-
-@end
-
-@implementation TestTrackerConfiguration
-
-- (void)testNetworkConfiguration_EmptyEndpoint_Fails {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"" method:SPHttpMethodPost];
-    XCTAssertEqualObjects(@"https://", networkConfig.endpoint);
-    XCTAssertEqual(SPProtocolHttps, networkConfig.protocol);
-    XCTAssertEqual(SPHttpMethodPost, networkConfig.method);
-    
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNotNil(tracker);
-}
-
-- (void)testNetworkConfiguration_EndpointWithoutProtocol_SuccessWithHttps {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url.com" method:SPHttpMethodGet];
-    XCTAssertEqualObjects(@"https://fake-url.com", networkConfig.endpoint);
-    XCTAssertEqual(SPProtocolHttps, networkConfig.protocol);
-    XCTAssertEqual(SPHttpMethodGet, networkConfig.method);
-    
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNotNil(tracker);
-}
-
-- (void)testNetworkConfiguration_EndpointWithHttpsProtocol_SuccessWithHttps {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"https://fake-url.com" method:SPHttpMethodGet];
-    XCTAssertEqualObjects(@"https://fake-url.com", networkConfig.endpoint);
-    XCTAssertEqual(SPProtocolHttps, networkConfig.protocol);
-    XCTAssertEqual(SPHttpMethodGet, networkConfig.method);
-    
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNotNil(tracker);
-}
-
-- (void)testNetworkConfiguration_EndpointWithHttpProtocol_SuccessWithHttps {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"http://fake-url.com" method:SPHttpMethodGet];
-    XCTAssertEqualObjects(@"http://fake-url.com", networkConfig.endpoint);
-    XCTAssertEqual(SPProtocolHttp, networkConfig.protocol);
-    XCTAssertEqual(SPHttpMethodGet, networkConfig.method);
-    
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNotNil(tracker);
-}
-
-- (void)testNetworkConfiguration_EndpointWithWrongProtocol_UseItAsEndpoint {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"wrong://fake-url.com" method:SPHttpMethodGet];
-    XCTAssertEqualObjects(@"https://wrong://fake-url.com", networkConfig.endpoint);
-    XCTAssertEqual(SPProtocolHttps, networkConfig.protocol);
-    XCTAssertEqual(SPHttpMethodGet, networkConfig.method);
-    
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNotNil(tracker);
-}
-
-- (void)testNetworkConfiguration_EndpointWithOnlyProtocol_UseItAsEndpoint {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"http://" method:SPHttpMethodGet];
-    XCTAssertEqualObjects(@"http://", networkConfig.endpoint);
-    XCTAssertEqual(SPProtocolHttp, networkConfig.protocol);
-    XCTAssertEqual(SPHttpMethodGet, networkConfig.method);
-    
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNotNil(tracker);
-}
-
-- (void)testBasicInitialization {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"https://fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    trackerConfig.platformContext = YES;
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-
-    XCTAssertNotNil(tracker);
-    XCTAssertNotNil(tracker.emitter);
-    NSURL *url = [NSURL URLWithString:tracker.network.endpoint];
-    XCTAssertNotNil(url);
-    NSString *host = url.host;
-    NSString *scheme = url.scheme;
-    NSString *derivedEndpoint = [NSString stringWithFormat:@"%@://%@", scheme, host];
-
-    NSString *protocol = networkConfig.protocol == SPProtocolHttp ? @"http" : networkConfig.protocol == SPProtocolHttps ? @"https" : nil;
-    
-    XCTAssertEqualObjects(networkConfig.endpoint, derivedEndpoint);
-    XCTAssertEqualObjects(protocol, scheme);
-    
-    XCTAssertEqualObjects(trackerConfig.appId, tracker.appId);
-    XCTAssertEqualObjects(@"namespace", tracker.namespace);
-}
-
-- (void)testSessionInitialization {
-    NSInteger expectedForeground = 42;
-    NSInteger expectedBackground = 24;
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"https://fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    SPSessionConfiguration *sessionConfig = [[SPSessionConfiguration alloc] initWithForegroundTimeoutInSeconds:expectedForeground
-                                                                                    backgroundTimeoutInSeconds:expectedBackground];
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig, sessionConfig]];
-
-    NSInteger foreground = tracker.session.foregroundTimeoutInSeconds;
-    NSInteger background = tracker.session.backgroundTimeoutInSeconds;
-    XCTAssertEqual(expectedForeground, foreground);
-    XCTAssertEqual(expectedBackground, background);
-    
-    NSMeasurement *foregroundMeasure = [tracker.session foregroundTimeout];
-    NSMeasurement *backgroundMeasure = [tracker.session backgroundTimeout];
-    XCTAssertEqualObjects([[NSMeasurement alloc] initWithDoubleValue:expectedForeground unit:NSUnitDuration.seconds], foregroundMeasure);
-    XCTAssertEqualObjects([[NSMeasurement alloc] initWithDoubleValue:expectedBackground unit:NSUnitDuration.seconds], backgroundMeasure);
-}
-
-- (void)testSessionControllerUnavailableWhenContextTurnedOff {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"https://fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    trackerConfig.sessionContext = YES;
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNotNil(tracker.session);
-
-    trackerConfig.sessionContext = NO;
-    tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig]];
-    XCTAssertNil(tracker.session);
-}
-
-- (void)testSessionConfigurationCallback {
-    [SPDataPersistence removeDataPersistenceWithNamespace:@"namespace"];
-    XCTestExpectation *expectation = [XCTestExpectation new];
-
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"https://fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    SPSessionConfiguration *sessionConfig = [[SPSessionConfiguration alloc] initWithForegroundTimeoutInSeconds:100 backgroundTimeoutInSeconds:100];
-
-    sessionConfig.onSessionStateUpdate = ^(SPSessionState * _Nonnull sessionState) {
-        XCTAssertEqual(1, sessionState.sessionIndex);
-        XCTAssertNil(sessionState.previousSessionId);
-        [expectation fulfill];
-    };
-    
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig, sessionConfig]];
-
-    [tracker track:[[SPTiming alloc] initWithCategory:@"cat" variable:@"var" timing:@123]];
-    
-    [self waitForExpectations:@[expectation] timeout:10];
-}
-
-- (void)testSessionConfigurationCallbackAfterNewSession {
-    [SPDataPersistence removeDataPersistenceWithNamespace:@"namespace"];
-    XCTestExpectation *expectation = [XCTestExpectation new];
-
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"https://fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    SPSessionConfiguration *sessionConfig = [[SPSessionConfiguration alloc] initWithForegroundTimeoutInSeconds:100 backgroundTimeoutInSeconds:100];
-    __block NSString *sessionId;
-    sessionConfig.onSessionStateUpdate = ^(SPSessionState * _Nonnull sessionState) {
-        if (sessionState.sessionIndex == 1) {
-            XCTAssertNil(sessionState.previousSessionId);
-            sessionId = sessionState.sessionId;
-        } else {
-            XCTAssertEqual(2, sessionState.sessionIndex);
-            XCTAssertEqualObjects(sessionId, sessionState.previousSessionId);
-            [expectation fulfill];
-        }
-    };
-    
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfig configurations:@[trackerConfig, sessionConfig]];
-
-    [tracker track:[[SPTiming alloc] initWithCategory:@"cat" variable:@"var" timing:@123]];
-    [tracker.session startNewSession];
-    [tracker track:[[SPTiming alloc] initWithCategory:@"cat" variable:@"var" timing:@123]];
-
-    [self waitForExpectations:@[expectation] timeout:10];
-}
-
-
-- (void)testTrackerVersionSuffix {
-    SPTrackerConfiguration *trackerConfiguration = [SPTrackerConfiguration new];
-    trackerConfiguration.trackerVersionSuffix = @"test With Space 1-2-3";
-
-    // Setup tracker
-    trackerConfiguration.base64Encoding = NO;
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, emitterConfiguration]];
-
-    // Track fake event
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    SPPayload *payload = [[events firstObject] payload];
-    
-    // Check v_tracker field
-    NSString *versionTracker = (NSString *)[[payload getAsDictionary] objectForKey:@"tv"];
-    NSString *expected = [NSString stringWithFormat:@"%@ testWithSpace1-2-3", kSPVersion];
-    XCTAssertEqualObjects(expected, versionTracker);
-}
-
-- (void)testGDPRConfiguration {
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfiguration = [[[SPTrackerConfiguration alloc] init] appId:@"appid"];
-    trackerConfiguration.base64Encoding = NO;
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    SPGDPRConfiguration *gdprConfiguration = [[SPGDPRConfiguration alloc] initWithBasis:SPGdprProcessingBasisConsent documentId:@"id" documentVersion:@"ver" documentDescription:@"desc"];
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, gdprConfiguration, emitterConfiguration]];
-    id<SPGDPRController> gdprController = trackerController.gdpr;
-
-    // Check gdpr settings
-    XCTAssertEqual(SPGdprProcessingBasisConsent, gdprController.basisForProcessing);
-    XCTAssertEqualObjects(@"id", gdprController.documentId);
-
-    // Check gdpr settings reset
-    [gdprController resetWithBasis:SPGdprProcessingBasisContract documentId:@"id1" documentVersion:@"ver1" documentDescription:@"desc1"];
-    XCTAssertEqual(SPGdprProcessingBasisContract, gdprController.basisForProcessing);
-    XCTAssertEqualObjects(@"id1", gdprController.documentId);
-    XCTAssertTrue(gdprController.isEnabled);
-
-    // Check gdpr context added
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    SPPayload *payload = [[events firstObject] payload];
-    NSString *contexts = (NSString *)[[payload getAsDictionary] objectForKey:@"co"];
-    XCTAssertTrue([contexts containsString:@"\"basisForProcessing\":\"contract\""]);
-    XCTAssertTrue([contexts containsString:@"\"documentId\":\"id1\""]);
-
-    // Check gdpr disabled
-    [gdprController disable];
-    XCTAssertFalse(gdprController.isEnabled);
-    XCTAssertEqual(SPGdprProcessingBasisContract, gdprController.basisForProcessing);
-    XCTAssertEqualObjects(@"id1", gdprController.documentId);
-    
-    // Check gdpr context not added
-    event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    payload = [[events firstObject] payload];
-    contexts = (NSString *)[[payload getAsDictionary] objectForKey:@"co"];
-    XCTAssertFalse([contexts containsString:@"\"basisForProcessing\":\"contract\""]);
-    XCTAssertFalse([contexts containsString:@"\"documentId\":\"id1\""]);
-    
-    // Check gdpr enabled again
-    [gdprController enable];
-    XCTAssertTrue(gdprController.isEnabled);
-}
-
-- (void)testWithoutGDPRConfiguration {
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfiguration = [[[SPTrackerConfiguration alloc] init] appId:@"appid"];
-    trackerConfiguration.base64Encoding = NO;
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, emitterConfiguration]];
-    id<SPGDPRController> gdprController = trackerController.gdpr;
-
-    // Check gdpr settings
-    XCTAssertFalse(gdprController.isEnabled);
-
-    // Check gdpr context not added
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    SPPayload *payload = [[events firstObject] payload];
-    NSString *contexts = (NSString *)[[payload getAsDictionary] objectForKey:@"co"];
-    XCTAssertFalse([contexts containsString:@"\"basisForProcessing\""]);
-
-    // Check gdpr can be enabled again
-    [gdprController resetWithBasis:SPGdprProcessingBasisContract documentId:@"id1" documentVersion:@"ver1" documentDescription:@"desc1"];
-    XCTAssertEqual(SPGdprProcessingBasisContract, gdprController.basisForProcessing);
-    XCTAssertEqualObjects(@"id1", gdprController.documentId);
-    XCTAssertTrue(gdprController.isEnabled);
-    
-    // Check gdpr context added
-    event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    payload = [[events firstObject] payload];
-    contexts = (NSString *)[[payload getAsDictionary] objectForKey:@"co"];
-    XCTAssertTrue([contexts containsString:@"\"basisForProcessing\":\"contract\""]);
-    XCTAssertTrue([contexts containsString:@"\"documentId\":\"id1\""]);
-}
-
-- (void)testAnonymisesUserIdentifiersIfAnonymousUserTracking {
-    // Initialize a tracker with anonymous user tracking
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfiguration = [[[SPTrackerConfiguration alloc] init] appId:@"appid"];
-    trackerConfiguration.base64Encoding = NO;
-    trackerConfiguration.userAnonymisation = YES;
-    trackerConfiguration.sessionContext = YES;
-    trackerConfiguration.platformContext = YES;
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, emitterConfiguration]];
-
-    // Track an event and retrieve tracked context JSON from event store
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    SPPayload *payload = [[events firstObject] payload];
-    NSString *contexts = (NSString *)[[payload getAsDictionary] objectForKey:@"co"];
-    
-    // Check empty userId in session context
-    XCTAssertTrue([contexts containsString:@"\"userId\":\"00000000-0000-0000-0000-000000000000\""]);
-    // Check no user identifiers in platform context
-    XCTAssertFalse([contexts containsString:@"\"appleIdfa\":\""]);
-    XCTAssertFalse([contexts containsString:@"\"appleIdfv\":\""]);
-}
-
-- (void)testTrackerReturnsTrackedEventId {
-    // Setup tracker
-    SPTrackerConfiguration *trackerConfiguration = [SPTrackerConfiguration new];
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, emitterConfiguration]];
-
-    // Track fake event
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    NSUUID *eventId = [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    SPPayload *payload = [[events firstObject] payload];
-    
-    // Check eid field
-    NSString *trackedEventId = (NSString *)[[payload getAsDictionary] objectForKey:@"eid"];
-    XCTAssertTrue([[eventId UUIDString] isEqualToString:trackedEventId]);
-}
-@end
diff --git a/Snowplow iOSTests/Configurations/TestTrackerController.m b/Snowplow iOSTests/Configurations/TestTrackerController.m
deleted file mode 100644
index fd57488be..000000000
--- a/Snowplow iOSTests/Configurations/TestTrackerController.m	
+++ /dev/null
@@ -1,86 +0,0 @@
-//
-//  TestTrackerController.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPSnowplow.h"
-#import "SPNetworkConfiguration.h"
-#import "SPTrackerConfiguration.h"
-#import "SPTrackerController.h"
-#import "SPTracker.h"
-#import "SPSession.h"
-
-@interface TestTrackerController : XCTestCase
-
-@end
-
-@implementation TestTrackerController
-
-- (void)testSessionAccessibilityWhenEnabledAndDisabled {
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" endpoint:@"https://fake-url" method:SPHttpMethodPost];
-    XCTAssertNotNil(tracker.session);
-    
-    tracker.sessionContext = NO;
-    XCTAssertNil(tracker.session);
-}
-
-- (void)testSubjectUserIdCanBeUpdated {
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" endpoint:@"https://fake-url" method:SPHttpMethodPost];
-    XCTAssertNotNil(tracker.subject);
-    XCTAssertNil(tracker.subject.userId);
-    tracker.subject.userId = @"fakeUserId";
-    XCTAssertEqualObjects(@"fakeUserId", tracker.subject.userId);
-    tracker.subject.userId = nil;
-    XCTAssertNil(tracker.subject.userId);
-}
-
-- (void)testSubjectGeoLocationCanBeUpdated {
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" endpoint:@"https://fake-url" method:SPHttpMethodPost];
-    XCTAssertNotNil(tracker.subject);
-    XCTAssertNil(tracker.subject.geoLatitude);
-    tracker.subject.geoLatitude = @12.3456;
-    XCTAssertEqualObjects([NSNumber numberWithFloat:12.3456], tracker.subject.geoLatitude);
-    tracker.subject.geoLatitude = nil;
-    // TODO: On version 3 setting to nil should get back nil.
-    // Here it should be nil rather than 0 but it's the way the beneith SPSubject works.
-    XCTAssertEqualObjects([NSNumber numberWithFloat:0], tracker.subject.geoLatitude);
-}
-
-- (void)testStartsNewSessionWhenChangingAnonymousTracking {
-    id<SPTrackerController> tracker = [SPSnowplow createTrackerWithNamespace:@"namespace" endpoint:@"https://fake-url" method:SPHttpMethodPost];
-    [tracker.emitter pause];
-    
-    [tracker track:[[SPStructured alloc] initWithCategory:@"c" action:@"a"]];
-    NSString *sessionIdBefore = tracker.session.sessionId;
-    
-    tracker.userAnonymisation = true;
-    [tracker track:[[SPStructured alloc] initWithCategory:@"c" action:@"a"]];
-    NSString *sessionIdAnonymous = tracker.session.sessionId;
-    
-    XCTAssertFalse([sessionIdBefore isEqualToString:sessionIdAnonymous]);
-    
-    tracker.userAnonymisation = false;
-    [tracker track:[[SPStructured alloc] initWithCategory:@"c" action:@"a"]];
-    NSString *sessionIdNotAnonymous = tracker.session.sessionId;
-    
-    XCTAssertFalse([sessionIdAnonymous isEqualToString:sessionIdNotAnonymous]);
-}
-
-@end
diff --git a/Snowplow iOSTests/Global Contexts/TestGlobalContexts.m b/Snowplow iOSTests/Global Contexts/TestGlobalContexts.m
deleted file mode 100644
index 91f8c0d53..000000000
--- a/Snowplow iOSTests/Global Contexts/TestGlobalContexts.m	
+++ /dev/null
@@ -1,244 +0,0 @@
-//
-//  TestGlobalContexts.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  Copyright: Copyright © 2020 Snowplow Analytics.
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTracker.h"
-#import "SPTrackerEvent.h"
-#import "SPSelfDescribingJson.h"
-#import "SPServiceProvider.h"
-
-/// Category needed to make the private methods testable.
-@interface SPTracker (Testing)
-- (void)addGlobalContextsToContexts:(NSMutableArray<SPSelfDescribingJson *> *)contexts event:(id<SPInspectableEvent>)event;
-@end
-
-#pragma mark - GlobalContextGenerator
-
-@interface GlobalContextGenerator: NSObject <SPContextGenerator>
-@end
-
-@implementation GlobalContextGenerator
-
-- (BOOL)filterFromEvent:(id<SPInspectableEvent>)event {
-    return [@"StringToMatch" isEqualToString:(NSString *)event.payload[kSPStuctCategory]];
-}
-
-- (NSArray<SPSelfDescribingJson *> *)generatorFromEvent:(id<SPInspectableEvent>)event {
-    return @[
-        [[SPSelfDescribingJson alloc] initWithSchema:@"schema" andData:@{@"key": @"value"}],
-    ];
-}
-
-@end
-
-#pragma mark - TestGlobalContexts
-
-@interface TestGlobalContexts : XCTestCase
-@end
-
-@implementation TestGlobalContexts
-
-- (void)testGlobalContexts {
-    SPGlobalContext *staticGC = [[SPGlobalContext alloc] initWithStaticContexts:@[[[SPSelfDescribingJson alloc] initWithSchema:@"schema" andData:@{@"key": @"value"}]]];
-    SPGlobalContext *generatorGC = [[SPGlobalContext alloc] initWithContextGenerator:[GlobalContextGenerator new]];
-    SPGlobalContext *blockGC = [[SPGlobalContext alloc] initWithGenerator:^NSArray<SPSelfDescribingJson *> *(id<SPInspectableEvent> event) {
-        return @[
-            [[SPSelfDescribingJson alloc] initWithSchema:@"schemaBlock" andData:@{@"key": @"value"}],
-        ];
-    }];
-    SPTracker *tracker = [self getTrackerWithGlobalContextGenerators:@{
-        @"static": staticGC,
-        @"generator": generatorGC,
-        @"block": blockGC,
-    }.mutableCopy];
-    
-    NSSet *result = [NSSet setWithArray:tracker.globalContextTags];
-    NSSet *expected = [NSSet setWithArray:@[@"static", @"generator", @"block"]];
-    XCTAssertEqualObjects(result, expected);
-    
-    // Can't remove a not existing tag
-    SPGlobalContext *removedGC = [tracker removeGlobalContext:@"notExistingTag"];
-    XCTAssertNil(removedGC);
-    result = [NSSet setWithArray:tracker.globalContextTags];
-    expected = [NSSet setWithArray:@[@"static", @"generator", @"block"]];
-    XCTAssertTrue([result isEqualToSet:expected]);
-    
-    // Remove an existing tag
-    removedGC = [tracker removeGlobalContext:@"static"];
-    XCTAssertNotNil(removedGC);
-    result = [NSSet setWithArray:tracker.globalContextTags];
-    expected = [NSSet setWithArray:@[@"generator", @"block"]];
-    XCTAssertTrue([result isEqualToSet:expected]);
-    
-    // Add a not existing tag
-    XCTAssertTrue([tracker addGlobalContext:staticGC tag:@"static"]);
-    result = [NSSet setWithArray:tracker.globalContextTags];
-    expected = [NSSet setWithArray:@[@"generator", @"block", @"static"]];
-    XCTAssertTrue([result isEqualToSet:expected]);
-    
-    // Can't add an existing tag
-    XCTAssertFalse([tracker addGlobalContext:staticGC tag:@"static"]);
-    result = [NSSet setWithArray:tracker.globalContextTags];
-    expected = [NSSet setWithArray:@[@"generator", @"block", @"static"]];
-    XCTAssertTrue([result isEqualToSet:expected]);
-}
-
-- (void)testAddRemoveGlobalContexts {
-    SPGlobalContext *staticGC = [[SPGlobalContext alloc] initWithStaticContexts:@[[[SPSelfDescribingJson alloc] initWithSchema:@"schema" andData:@{@"key": @"value"}]]];
-    SPTracker *tracker = [self getTrackerWithGlobalContextGenerators:nil];
-
-    NSSet *result = [NSSet setWithArray:tracker.globalContextTags];
-    NSSet *expected = [NSSet setWithArray:@[]];
-    XCTAssertTrue([result isEqualToSet:expected]);
-    
-    // Can't remove a not existing tag
-    SPGlobalContext *removedGC = [tracker removeGlobalContext:@"notExistingTag"];
-    XCTAssertNil(removedGC);
-    
-    // Add a not existing tag
-    XCTAssertTrue([tracker addGlobalContext:staticGC tag:@"static"]);
-    result = [NSSet setWithArray:tracker.globalContextTags];
-    expected = [NSSet setWithArray:@[@"static"]];
-    XCTAssertTrue([result isEqualToSet:expected]);
-    
-    // Remove an existing tag
-    removedGC = [tracker removeGlobalContext:@"static"];
-    XCTAssertNotNil(removedGC);
-    result = [NSSet setWithArray:tracker.globalContextTags];
-    expected = [NSSet setWithArray:@[]];
-    XCTAssertTrue([result isEqualToSet:expected]);
-}
-
-- (void)testStaticGenerator {
-    SPGlobalContext *staticGC = [[SPGlobalContext alloc] initWithStaticContexts:@[[[SPSelfDescribingJson alloc] initWithSchema:@"schema" andData:@{@"key": @"value"}]]];
-    SPTracker *tracker = [self getTrackerWithGlobalContextGenerators:@{@"static": staticGC}.mutableCopy];
-    
-    SPPrimitiveAbstract *event = [[SPStructured alloc] initWithCategory:@"Category" action:@"Action"];
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    
-    NSMutableArray<SPSelfDescribingJson *> *contexts = [NSMutableArray array];
-    [tracker addGlobalContextsToContexts:contexts event:trackerEvent];
-    XCTAssertTrue(contexts.count == 1);
-    XCTAssertEqual(contexts[0].schema, @"schema");
-}
-
-- (void)testStaticGeneratortWithFilter {
-    NSString *stringToMatch = @"StringToMatch";
-    SPGlobalContext *filterMatchingGC = [[SPGlobalContext alloc] initWithStaticContexts:@[[[SPSelfDescribingJson alloc] initWithSchema:@"schema" andData:@{@"key": @"value"}]]
-                                                                                 filter:^BOOL(id<SPInspectableEvent> event) {
-        return [stringToMatch isEqualToString:(NSString *)event.payload[kSPStuctCategory]];
-    }];
-    SPGlobalContext *filterNotMatchingGC = [[SPGlobalContext alloc] initWithStaticContexts:@[[[SPSelfDescribingJson alloc] initWithSchema:@"schemaNotMatching" andData:@{@"key": @"value"}]] filter:^BOOL(id<SPInspectableEvent> event) {
-        return NO;
-    }];
-    SPTracker *tracker = [self getTrackerWithGlobalContextGenerators:@{
-        @"matching": filterMatchingGC,
-        @"notMatching": filterNotMatchingGC,
-    }.mutableCopy];
-
-    SPPrimitiveAbstract *event = [[SPStructured alloc] initWithCategory:stringToMatch action:@"Action"];
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    
-    NSMutableArray<SPSelfDescribingJson *> *contexts = [NSMutableArray array];
-    [tracker addGlobalContextsToContexts:contexts event:trackerEvent];
-    XCTAssertTrue(contexts.count == 1);
-    XCTAssertEqual(contexts[0].schema, @"schema");
-}
-
-- (void)testStaticGeneratorWithRuleset {
-    NSString *allowed = @"iglu:com.snowplowanalytics.*/*/jsonschema/*-*-*";
-    NSString *denied = @"iglu:com.snowplowanalytics.mobile/*/jsonschema/*-*-*";
-    SPSchemaRuleset *ruleset = [SPSchemaRuleset rulesetWithAllowedList:@[allowed] andDeniedList:@[denied]];
-    
-    SPGlobalContext *rulesetGC = [[SPGlobalContext alloc] initWithStaticContexts:@[[[SPSelfDescribingJson alloc] initWithSchema:@"schema" andData:@{@"key": @"value"}]] ruleset:ruleset];
-    SPTracker *tracker = [self getTrackerWithGlobalContextGenerators:@{@"ruleset": rulesetGC}.mutableCopy];
-
-    NSMutableArray<SPSelfDescribingJson *> *contexts = [NSMutableArray array];
-
-    // Not matching primitive event
-    SPPrimitiveAbstract *primitiveEvent = [[SPStructured alloc] initWithCategory:@"Category" action:@"Action"];
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:primitiveEvent];
-    [tracker addGlobalContextsToContexts:contexts event:trackerEvent];
-    XCTAssertTrue(contexts.count == 0);
-
-    // Not matching self-describing event with mobile schema
-    SPSelfDescribingAbstract *selfDescribingEvent = [[[SPScreenView alloc] initWithName:@"Name" screenId:nil] type:@"Type"];
-    trackerEvent = [[SPTrackerEvent alloc] initWithEvent:selfDescribingEvent];
-    [tracker addGlobalContextsToContexts:contexts event:trackerEvent];
-    XCTAssertTrue(contexts.count == 0);
-
-    // Matching self-describing event with general schema
-    selfDescribingEvent = [[[SPTiming alloc] initWithCategory:@"Category" variable:@"Variable" timing:@123] label:@"Label"];
-    trackerEvent = [[SPTrackerEvent alloc] initWithEvent:selfDescribingEvent];
-    [tracker addGlobalContextsToContexts:contexts event:trackerEvent];
-    XCTAssertTrue(contexts.count == 1);
-    XCTAssertEqual(contexts[0].schema, @"schema");
-}
-
-- (void)testBlockGenerator {
-    SPGlobalContext *generatorGC = [[SPGlobalContext alloc] initWithGenerator:^NSArray<SPSelfDescribingJson *> *(id<SPInspectableEvent> event) {
-        return @[[[SPSelfDescribingJson alloc] initWithSchema:@"schema" andData:@{@"key": @"value"}]];
-    }];
-    SPTracker *tracker = [self getTrackerWithGlobalContextGenerators:@{@"generator": generatorGC}.mutableCopy];
-
-    SPPrimitiveAbstract *event = [[SPStructured alloc] initWithCategory:@"Category" action:@"Action"];
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    
-    NSMutableArray<SPSelfDescribingJson *> *contexts = [NSMutableArray array];
-    [tracker addGlobalContextsToContexts:contexts event:trackerEvent];
-    XCTAssertTrue(contexts.count == 1);
-    XCTAssertEqual(contexts[0].schema, @"schema");
-}
-
-- (void)testContextGenerator {
-    SPGlobalContext *contextGeneratorGC = [[SPGlobalContext alloc] initWithContextGenerator:[GlobalContextGenerator new]];
-    SPTracker *tracker = [self getTrackerWithGlobalContextGenerators:@{@"contextGenerator": contextGeneratorGC}.mutableCopy];
-    
-    SPPrimitiveAbstract *event = [[SPStructured alloc] initWithCategory:@"StringToMatch" action:@"Action"];
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    
-    NSMutableArray<SPSelfDescribingJson *> *contexts = [NSMutableArray array];
-    [tracker addGlobalContextsToContexts:contexts event:trackerEvent];
-    XCTAssertTrue(contexts.count == 1);
-    XCTAssertEqual(contexts[0].schema, @"schema");
-}
-
-// MARK: - Utility function
-
-    - (SPTracker *)getTrackerWithGlobalContextGenerators:(NSMutableDictionary<NSString *,SPGlobalContext *> *)generators {
-        SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"https://com.acme.fake"
-                                                                                          method:SPHttpMethodPost];
-        SPTrackerConfiguration *trackerConfig = [SPTrackerConfiguration new];
-        trackerConfig.appId = @"anAppId";
-        trackerConfig.platformContext = YES;
-        trackerConfig.geoLocationContext = NO;
-        trackerConfig.base64Encoding = NO;
-        trackerConfig.sessionContext = YES;
-        SPGlobalContextsConfiguration *gcConfig = [[SPGlobalContextsConfiguration alloc] init];
-        gcConfig.contextGenerators = generators;
-        SPServiceProvider *serviceProvider = [[SPServiceProvider alloc] initWithNamespace:@"aNamespace"
-                                                                                  network:networkConfig
-                                                                           configurations:@[gcConfig]];
-        return serviceProvider.tracker;
-    }
-
-@end
diff --git a/Snowplow iOSTests/Global Contexts/TestSchemaRuleset.m b/Snowplow iOSTests/Global Contexts/TestSchemaRuleset.m
deleted file mode 100644
index 5b15c575a..000000000
--- a/Snowplow iOSTests/Global Contexts/TestSchemaRuleset.m	
+++ /dev/null
@@ -1,109 +0,0 @@
-//
-//  TestSchemaRuleset.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  Copyright: Copyright © 2020 Snowplow Analytics.
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTrackerConstants.h"
-#import "SPSchemaRule.h"
-#import "SPSchemaRuleset.h"
-
-@interface TestSchemaRuleset : XCTestCase
-
-@end
-
-@implementation TestSchemaRuleset
-
-- (void)testSchemaRules {
-    SPSchemaRule *twoPartVendor = [[SPSchemaRule alloc] initWithRule:@"iglu:com.acme/*/jsonschema/*-*-*"];
-    XCTAssertNotNil(twoPartVendor);
-    
-    // version and event wildcard
-    XCTAssertTrue([twoPartVendor matchWithUri:@"iglu:com.acme/event/jsonschema/1-0-0"]);
-    XCTAssertFalse([twoPartVendor matchWithUri:@"iglu:com.snowplow/event/jsonschema/1-0-0"]);
-    SPSchemaRule *equalRule = [[SPSchemaRule alloc] initWithRule:@"iglu:com.acme/*/jsonschema/*-*-*"];
-    XCTAssertEqualObjects(twoPartVendor, equalRule);
-    
-    SPSchemaRule *threePartVendor = [[SPSchemaRule alloc] initWithRule:@"iglu:com.acme.marketing/*/jsonschema/*-*-*"];
-    XCTAssertNotNil(threePartVendor);
-    
-    SPSchemaRule *validVendorWildcard = [[SPSchemaRule alloc] initWithRule:@"iglu:com.acme.*/*/jsonschema/*-*-*"];
-    XCTAssertNotNil(validVendorWildcard);
-    
-    SPSchemaRule *invalidVendorWildcard = [[SPSchemaRule alloc] initWithRule:@"iglu:com.acme.*.whoops/*/jsonschema/*-*-*"];
-    XCTAssertNil(invalidVendorWildcard);
-    
-    // vendor matching
-    XCTAssertTrue([validVendorWildcard matchWithUri:@"iglu:com.acme.marketing/event/jsonschema/1-0-0"]);
-    XCTAssertFalse([validVendorWildcard matchWithUri:@"iglu:com.snowplow/event/jsonschema/1-0-0"]);
-    
-    // vendor parts need to match in length, i.e. com.acme.* will not match com.acme.marketing.foo, only vendors of the form com.acme.x
-    XCTAssertFalse([validVendorWildcard matchWithUri:@"iglu:com.acme.marketing.foo/event/jsonschema/1-0-0"]);
-}
-
-- (void)testSchemaRuleset {
-    NSString *acme = @"iglu:com.acme.*/*/jsonschema/*-*-*";
-    NSString *snowplow = @"iglu:com.snowplow.*/*/jsonschema/*-*-*";
-    NSString *snowplowTest = @"iglu:com.snowplow.test/*/jsonschema/*-*-*";
-    SPSchemaRuleset *ruleset = [SPSchemaRuleset rulesetWithAllowedList:@[acme, snowplow] andDeniedList:@[snowplowTest]];
-    NSArray<NSString *> *allowed = @[acme, snowplow];
-    XCTAssertEqualObjects(ruleset.allowed, allowed);
-    NSArray<NSString *> *denied = @[snowplowTest];
-    XCTAssertEqualObjects(ruleset.denied, denied);
-    
-    // matching
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.acme.marketing/event/jsonschema/1-0-0"]);
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.snowplow.marketing/event/jsonschema/1-0-0"]);
-    XCTAssertFalse([ruleset matchWithUri:@"iglu:com.snowplow.test/event/jsonschema/1-0-0"]);
-    XCTAssertFalse([ruleset matchWithUri:@"iglu:com.brand/event/jsonschema/1-0-0"]);
-}
-
-- (void)testSchemaRulesetOnlyDenied {
-    NSString *snowplowTest = @"iglu:com.snowplow.test/*/jsonschema/*-*-*";
-    SPSchemaRuleset *ruleset = [SPSchemaRuleset rulesetWithDeniedList:@[snowplowTest]];
-    NSArray<NSString *> *allowed = @[];
-    XCTAssertEqualObjects(ruleset.allowed, allowed);
-    NSArray<NSString *> *denied = @[snowplowTest];
-    XCTAssertEqualObjects(ruleset.denied, denied);
-    
-    // matching
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.acme.marketing/event/jsonschema/1-0-0"]);
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.snowplow.marketing/event/jsonschema/1-0-0"]);
-    XCTAssertFalse([ruleset matchWithUri:@"iglu:com.snowplow.test/event/jsonschema/1-0-0"]);
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.brand/event/jsonschema/1-0-0"]);
-}
-
-- (void)testSchemaRulesetOnlyAllowed {
-    NSString *acme = @"iglu:com.acme.*/*/jsonschema/*-*-*";
-    NSString *snowplow = @"iglu:com.snowplow.*/*/jsonschema/*-*-*";
-    SPSchemaRuleset *ruleset = [SPSchemaRuleset rulesetWithAllowedList:@[acme, snowplow]];
-    NSArray<NSString *> *allowed = @[acme, snowplow];
-    XCTAssertEqualObjects(ruleset.allowed, allowed);
-    NSArray<NSString *> *denied = @[];
-    XCTAssertEqualObjects(ruleset.denied, denied);
-    
-    // matching
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.acme.marketing/event/jsonschema/1-0-0"]);
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.snowplow.marketing/event/jsonschema/1-0-0"]);
-    XCTAssertTrue([ruleset matchWithUri:@"iglu:com.snowplow.test/event/jsonschema/1-0-0"]);
-    XCTAssertFalse([ruleset matchWithUri:@"iglu:com.brand/event/jsonschema/1-0-0"]);
-}
-
-@end
diff --git a/Snowplow iOSTests/Legacy Tests/LegacyTestEmitter.m b/Snowplow iOSTests/Legacy Tests/LegacyTestEmitter.m
deleted file mode 100644
index 0cf639035..000000000
--- a/Snowplow iOSTests/Legacy Tests/LegacyTestEmitter.m	
+++ /dev/null
@@ -1,404 +0,0 @@
-//
-//  LegacyTestEmitter.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTrackerConstants.h"
-#import "SPEmitter.h"
-#import "SPLogger.h"
-#import "SPMockEventStore.h"
-#import "SPMockNetworkConnection.h"
-
-
-@interface SPBrokenNetworkConnection : NSObject <SPNetworkConnection>
-@end
-
-@implementation SPBrokenNetworkConnection
-
-- (NSArray<SPRequestResult *> *)sendRequests:(NSArray<SPRequest *> *)requests {
-    [NSException raise:@"BrokenNetworkConnection" format:@"Fake exception on network connection."];
-    return nil;
-}
-
-- (NSURL *)url {
-    [NSException raise:@"BrokenNetworkConnection" format:@"Fake exception on network connection."];
-    return nil;
-}
-
-- (SPHttpMethod)httpMethod {
-    [NSException raise:@"BrokenNetworkConnection" format:@"Fake exception on network connection."];
-    return SPHttpMethodGet;
-}
-
-@end
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-@interface LegacyTestEmitter : XCTestCase
-
-@end
-
-@implementation LegacyTestEmitter
-
-NSString *const TEST_SERVER_EMITTER = @"www.notarealurl.com";
-
-- (void)setUp {
-    [super setUp];
-    [SPLogger setLogLevel:SPLogLevelVerbose];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testEmitterBuilderAndOptions {
-    NSString * protocol = @"https";
-    
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:TEST_SERVER_EMITTER];
-        [builder setHttpMethod:SPHttpMethodPost];
-        [builder setEmitRange:500];
-        [builder setEmitThreadPoolSize:30];
-        [builder setByteLimitGet:30000];
-        [builder setByteLimitPost:35000];
-        [builder setProtocol:SPProtocolHttps];
-    }];
-    
-    NSString * url = [[NSString alloc] initWithFormat:@"%@://%@/com.snowplowanalytics.snowplow/tp2", protocol, TEST_SERVER_EMITTER];
-    
-    // Test builder setting properly
-    
-    XCTAssertNil([emitter callback]);
-    XCTAssertTrue([[[emitter urlEndpoint] absoluteString] isEqualToString:url]);
-    XCTAssertEqual([emitter httpMethod], SPHttpMethodPost);
-    XCTAssertEqual([emitter emitRange], 500);
-    XCTAssertEqual([emitter emitThreadPoolSize], 30);
-    XCTAssertEqual([emitter byteLimitGet], 30000);
-    XCTAssertEqual([emitter byteLimitPost], 35000);
-    XCTAssertEqual([emitter protocol], SPProtocolHttps);
-    
-    SPEmitter * customPathEmitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:TEST_SERVER_EMITTER];
-        [builder setHttpMethod:SPHttpMethodPost];
-        [builder setCustomPostPath:@"/com.acme.company/tpx"];
-        [builder setEmitRange:500];
-        [builder setEmitThreadPoolSize:30];
-        [builder setByteLimitGet:30000];
-        [builder setByteLimitPost:35000];
-        [builder setProtocol:SPProtocolHttps];
-    }];
-    
-    NSString * customUrl = [[NSString alloc] initWithFormat:@"%@://%@/com.acme.company/tpx", protocol, TEST_SERVER_EMITTER];
-    XCTAssertTrue([[[customPathEmitter urlEndpoint] absoluteString] isEqualToString:customUrl]);
-    
-    // Test setting variables to new values
-    
-    [emitter setUrlEndpoint:@"www.test.com"];
-    url = [[NSString alloc] initWithFormat:@"%@://www.test.com/com.snowplowanalytics.snowplow/tp2", protocol];
-    XCTAssertTrue([[[emitter urlEndpoint] absoluteString] isEqualToString:url]);
-    [emitter setHttpMethod:SPHttpMethodGet];
-    XCTAssertEqual([emitter httpMethod], SPHttpMethodGet);
-    url = [[NSString alloc] initWithFormat:@"%@://www.test.com/i", protocol];
-    XCTAssertTrue([[[emitter urlEndpoint] absoluteString] isEqualToString:url]);
-    [emitter setEmitRange:1000];
-    XCTAssertEqual([emitter emitRange], 1000);
-    [emitter setEmitThreadPoolSize:50];
-    XCTAssertEqual([emitter emitThreadPoolSize], 50);
-    [emitter setByteLimitGet:1000];
-    XCTAssertEqual([emitter byteLimitGet], 1000);
-    [emitter setByteLimitPost:50];
-    XCTAssertEqual([emitter byteLimitPost], 50);
-    
-    // Test extra functions
-    XCTAssertTrue(![emitter getSendingStatus]);
-    XCTAssertTrue([emitter getDbCount] >= 0);
-    
-    // Allow timer to be set
-    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
-    [emitter resumeTimer];
-}
-
-// MARK: - Emitting tests
-
-- (void)testEmitEventWithBrokenNetworkConnectionDoesntFreezeEmitterStatus {
-    id<SPNetworkConnection> networkConnection = [SPBrokenNetworkConnection new];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-    [emitter addPayloadToBuffer:[self generatePayloads:1].firstObject];
-    
-    [NSThread sleepForTimeInterval:1];
-    
-    XCTAssertFalse([emitter getSendingStatus]);
-    
-    [emitter flush];
-}
-
-- (void)testEmitSingleGetEventWithSuccess {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodGet statusCode:200];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-    [emitter addPayloadToBuffer:[self generatePayloads:1].firstObject];
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] < 1 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(1, networkConnection.previousResults.count);
-    XCTAssertEqual(1, networkConnection.previousResults.firstObject.count);
-    XCTAssertTrue([networkConnection.previousResults.firstObject.firstObject isSuccessful]);
-    XCTAssertEqual(0, [emitter getDbCount]);
-    
-    [emitter flush];
-}
-
-- (void)testEmitSingleGetEventWithNoSuccess {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodGet statusCode:500];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-    [emitter addPayloadToBuffer:[self generatePayloads:1].firstObject];
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] < 1 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(1, networkConnection.previousResults.count);
-    XCTAssertEqual(1, networkConnection.previousResults.firstObject.count);
-    XCTAssertFalse([networkConnection.previousResults.firstObject.firstObject isSuccessful]);
-    XCTAssertEqual(1, [emitter getDbCount]);
-    
-    [emitter flush];
-}
-
-- (void)testEmitTwoGetEventsWithSuccess {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodGet statusCode:200];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-    
-    for (SPPayload *payload in [self generatePayloads:2]) {
-        [emitter addPayloadToBuffer:payload];
-    }
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] < 2 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(0, [emitter getDbCount]);
-    int totEvents = 0;
-    for (NSArray<SPRequestResult *> *results in networkConnection.previousResults) {
-        for (SPRequestResult *result in results) {
-            XCTAssertTrue(result.isSuccessful);
-            totEvents += result.storeIds.count;
-        }
-    }
-    XCTAssertEqual(2, totEvents);
-    
-    [emitter flush];
-}
-
-- (void)testEmitTwoGetEventsWithNoSuccess {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodGet statusCode:500];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-
-    for (SPPayload *payload in [self generatePayloads:2]) {
-        [emitter addPayloadToBuffer:payload];
-    }
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] < 2 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(2, [emitter getDbCount]);
-    for (NSArray<SPRequestResult *> *results in networkConnection.previousResults) {
-        for (SPRequestResult *result in results) {
-            XCTAssertFalse(result.isSuccessful);
-        }
-    }
-    
-    [emitter flush];
-}
-
-- (void)testEmitSinglePostEventWithSuccess {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:200];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-    
-    [emitter addPayloadToBuffer:[self generatePayloads:1].firstObject];
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] < 1 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(1, networkConnection.previousResults.count);
-    XCTAssertEqual(1, networkConnection.previousResults.firstObject.count);
-    XCTAssertTrue([networkConnection.previousResults.firstObject.firstObject isSuccessful]);
-    XCTAssertEqual(0, [emitter getDbCount]);
-    
-    [emitter flush];
-}
-
-- (void)testEmitEventsPostAsGroup {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:500];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionDefaultGroup];
-    
-    NSArray<SPPayload *> *payloads = [self generatePayloads:15];
-    for (int i = 0; i < 14; i++) {
-        [emitter addPayloadToBuffer:payloads[i]];
-    }
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] < 1 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(14, [emitter getDbCount]);
-    networkConnection.statusCode = 200;
-    NSUInteger prevSendingCount = [networkConnection sendingCount];
-    [emitter addPayloadToBuffer:payloads[14]];
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] - prevSendingCount < 1 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(0, [emitter getDbCount]);
-    int totEvents = 0;
-    BOOL areGrouped = NO;
-    NSArray<NSArray<SPRequestResult *> *> *prevResults =
-    [networkConnection.previousResults subarrayWithRange:NSMakeRange(prevSendingCount, networkConnection.previousResults.count - prevSendingCount)];
-    for (NSArray<SPRequestResult *> *results in prevResults) {
-        for (SPRequestResult *result in results) {
-            XCTAssertTrue(result.isSuccessful);
-            NSUInteger ids = result.storeIds.count;
-            totEvents += ids;
-            areGrouped = areGrouped || ids > 1;
-        }
-    }
-    XCTAssertEqual(15, totEvents);
-    XCTAssertTrue(areGrouped);
-    
-    [emitter flush];
-}
-
-- (void)testEmitOversizeEventsPostAsGroup {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:500];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection build:^(id<SPEmitterBuilder> builder) {
-        [builder setBufferOption:SPBufferOptionDefaultGroup];
-        [builder setByteLimitPost:5];
-    }];
-    
-    NSArray<SPPayload *> *payloads = [self generatePayloads:15];
-    for (int i = 0; i < 14; i++) {
-        [emitter addPayloadToBuffer:payloads[i]];
-    }
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] < 1 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(0, [emitter getDbCount]);
-    networkConnection.statusCode = 200;
-    NSUInteger prevSendingCount = [networkConnection sendingCount];
-    [emitter addPayloadToBuffer:payloads[14]];
-    
-    for (int i = 0; i < 10 && ([networkConnection sendingCount] - prevSendingCount < 1 || [emitter getSendingStatus]); i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-
-    XCTAssertEqual(0, [emitter getDbCount]);
-    
-    [emitter flush];
-}
-
-// MARK: - Emitter builder
-
-- (SPEmitter *)emitterWithNetworkConnection:(id<SPNetworkConnection>)networkConnection bufferOption:(SPBufferOption)bufferOption {
-    return [self emitterWithNetworkConnection:networkConnection build:^(id<SPEmitterBuilder> builder) {
-        [builder setBufferOption:bufferOption];
-    }];
-}
-
-- (SPEmitter *)emitterWithNetworkConnection:(id<SPNetworkConnection>)networkConnection build:(void(^)(id<SPEmitterBuilder>builder))buildBlock {
-    return [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setNetworkConnection:networkConnection];
-        [builder setBufferOption:SPBufferOptionSingle];
-        [builder setEmitRange:200];
-        [builder setByteLimitGet:20000];
-        [builder setByteLimitPost:25000];
-        [builder setEventStore:[SPMockEventStore new]];
-        buildBlock(builder);
-    }];
-}
-
-- (void)testRemovesEventsFromQueueOnNoRetryStatus {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodGet statusCode:403];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-
-    [emitter addPayloadToBuffer:[self generatePayloads:1].firstObject];
-    
-    [NSThread sleepForTimeInterval:1];
-
-    XCTAssertEqual(0, [emitter getDbCount]);
-    for (NSArray<SPRequestResult *> *results in networkConnection.previousResults) {
-        for (SPRequestResult *result in results) {
-            XCTAssertFalse(result.isSuccessful);
-        }
-    }
-
-    [emitter flush];
-}
-
-- (void)testFollowCustomRetryRules {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodGet statusCode:500];
-    SPEmitter *emitter = [self emitterWithNetworkConnection:networkConnection bufferOption:SPBufferOptionSingle];
-    
-    NSMutableDictionary *customRules = [[NSMutableDictionary alloc] init];
-    [customRules setObject:@YES forKey:@403];
-    [customRules setObject:@NO forKey:@500];
-    [emitter setCustomRetryForStatusCodes:customRules];
-
-    [emitter addPayloadToBuffer:[self generatePayloads:1].firstObject];
-    
-    [NSThread sleepForTimeInterval:1];
-
-    // no events in queue since they were dropped because retrying is disabled for 500
-    XCTAssertEqual(0, [emitter getDbCount]);
-    
-    networkConnection.statusCode = 403;
-
-    [emitter addPayloadToBuffer:[self generatePayloads:1].firstObject];
-    
-    [NSThread sleepForTimeInterval:1];
-
-    // event still in queue because retrying is enabled for 403
-    XCTAssertEqual(1, [emitter getDbCount]);
-
-    [emitter flush];
-}
-
-// MARK: - Service methods
-
-- (NSArray<SPPayload *> *)generatePayloads:(int)count {
-    NSMutableArray<SPPayload *> *payloads = [NSMutableArray new];
-    for (int i = 0; i < count; i++) {
-        SPPayload *payload = [SPPayload new];
-        [payload addValueToPayload:@(i).description forKey:@"a"];
-        [payloads addObject:payload];
-    }
-    return payloads;
-}
-
-@end
-
-#pragma clang diagnostic pop
diff --git a/Snowplow iOSTests/Legacy Tests/LegacyTestEvent.m b/Snowplow iOSTests/Legacy Tests/LegacyTestEvent.m
deleted file mode 100644
index 069cc4f8d..000000000
--- a/Snowplow iOSTests/Legacy Tests/LegacyTestEvent.m	
+++ /dev/null
@@ -1,388 +0,0 @@
-//
-//  TestEvent.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPEvent.h"
-#import "SPTrackerError.h"
-#import "SPSelfDescribingJson.h"
-
-@interface LegacyTestEvent : XCTestCase
-
-@end
-
-@implementation LegacyTestEvent
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-- (void)testEventBuilderConditions {
-    // Valid construction
-    SPPageView *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    [event setContexts:[self getCustomContext]];
-    XCTAssertNotNil(event);
-    event = nil;    
-}
- 
-- (void)testTrueTimestamp {
-    SPEvent *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    XCTAssertNil(event.trueTimestamp);
-
-    // Set trueTimestamp
-    NSDate *testDate = [NSDate date];
-    event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    [event setTrueTimestamp:testDate];
-    XCTAssertEqual(event.trueTimestamp, testDate);
-}
-
-- (void)testPageViewBuilderConditions {
-    // Valid construction
-    SPPageView *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // PageURL is empty
-    @try {
-        event = [[SPPageView alloc] initWithPageUrl:@""];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"PageURL cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testStructuredBuilderConditions {
-    // Valid construction
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // Category is empty
-    @try {
-        event = [[SPStructured alloc] initWithCategory:@"" action:@"action"];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Category cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-        
-    // Action is empty
-    @try {
-        event = [[SPStructured alloc] initWithCategory:@"category" action:@""];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Action cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testUnstructuredBuilderEmptyCondition {
-    // Valid construction
-    NSMutableDictionary * data = [[NSMutableDictionary alloc] init];
-    [data setObject:[NSNumber numberWithInt:23] forKey:@"level"];
-    [data setObject:[NSNumber numberWithInt:56473] forKey:@"score"];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0"
-                                                                      andData:data];
-    SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithEventData:sdj];
-    XCTAssertNotNil(event);
-    event = nil;
-}
-
-- (void)testUnstructuredBuilderWrongDataCondition {
-    // Invalid dictionary
-    NSMutableDictionary * data = [[NSMutableDictionary alloc] init];
-    [data setObject:[NSNumber numberWithInt:12] forKey:[NSNumber numberWithInt:12]];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0"
-                                                                      andData:data];
-    // Data is wrong
-    SPSelfDescribing *event;
-    @try {
-        event = [[SPSelfDescribing alloc] initWithEventData:sdj];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"EventData payload has to be JSON serializable.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testConsentWithdrawnBuilderConditions {
-    // Valid construction
-    SPConsentWithdrawn *event = [[SPConsentWithdrawn alloc] init];
-    [event setName:@"name"];
-    [event setAll:false];
-    [event setVersion:@"3"];
-    [event setDocumentId:@"1000"];
-    [event setDocumentDescription:@"description"];
-    XCTAssertNotNil(event);
-}
-
-- (void)testConsentGrantedBuilderConditions {
-    // Valid construction
-    SPConsentGranted *event = [[SPConsentGranted alloc] initWithExpiry:@"expiry" documentId:@"1000" version:@"3"];
-    event.name = @"name";
-    event.documentDescription = @"description";
-    XCTAssertNotNil(event);
-    event = nil;
-}
-
-- (void)testConsentDocumentBuilderConditions {
-    // Valid construction
-    SPConsentDocument *event = [[SPConsentDocument alloc] initWithDocumentId:@"1000" version:@"3"];
-    [event setName:@"name"];
-    [event setDocumentDescription:@"description"];
-    XCTAssertNotNil(event);
-    event = nil;
-}
-
-- (void)testScreenViewBuilderConditions {
-    NSUUID *screenId = [NSUUID UUID];
-    
-    // Valid construction
-    SPScreenView *event = [[SPScreenView alloc] initWithName:@"name" screenId:screenId];
-    XCTAssertNotNil(event);
-    event = nil;
-
-    @try {
-        event = [[SPScreenView alloc] initWithName:@"" screenId:screenId];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Name cannot be empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testTimingBuilderConditions {
-    // Valid construction
-    SPTiming *event = [[SPTiming alloc] initWithCategory:@"category" variable:@"variable" timing:@5];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // Category is empty
-    @try {
-        event = [[SPTiming alloc] initWithCategory:@"" variable:@"variable" timing:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Category cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-        
-    // Variable is empty
-    @try {
-        event = [[SPTiming alloc] initWithCategory:@"category" variable:@"" timing:@5];
-
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Variable cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testEcommerceBuilderConditions {
-    // Valid construction
-    SPEcommerce *event = [[SPEcommerce alloc] initWithOrderId:@"orderId" totalValue:@5 items:@[]];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // OrderID is empty
-    @try {
-        event = [[SPEcommerce alloc] initWithOrderId:@"" totalValue:@5 items:@[]];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"OrderId cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testEcommerceItemBuilderConditions {
-    // Valid construction
-    SPEcommerceItem *event = [[SPEcommerceItem alloc] initWithSku:@"sku" price:@5 quantity:@1];
-    event.orderId = @"orderId";
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // Sku is empty
-    @try {
-        event = [[SPEcommerceItem alloc] initWithSku:@"" price:@5 quantity:@1];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"SKU cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testPushNotificationContentBuilderConditions {
-    // Valid construction
-    NSArray * attachments = @[ @{ @"identifier": @"id",
-                                  @"url": @"www.test.com",
-                                  @"type": @"test"
-                                  },
-                               @{ @"identifier": @"id2",
-                                  @"url": @"www.test2.com",
-                                  @"type": @"test2"
-                                  }
-                               ];
-
-    NSDictionary * userInfo = @{ @"aps" : @{ @"alert": @"test",
-                                             @"sound": @"sound",
-                                             @"category": @"category"
-                                             }
-                                 };
-    
-    SPNotificationContent *event = [[SPNotificationContent alloc] initWithTitle:@"title" body:@"body" badge:@5];
-    [event setSubtitle:@"subtitle"];
-    [event setSound:@"sound"];
-    [event setLaunchImageName:@"image"];
-    [event setUserInfo:userInfo];
-    [event setAttachments:attachments];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // Title is empty
-    @try {
-        event = [[SPNotificationContent alloc] initWithTitle:@"" body:@"body" badge:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Title cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-
-    // Body is empty
-    @try {
-        event = [[SPNotificationContent alloc] initWithTitle:@"title" body:@"" badge:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Body cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testPushNotificationBuilderConditions {
-    // Valid construction
-    NSArray * attachments = @[ @{ @"identifier": @"id",
-                                  @"url": @"www.test.com",
-                                  @"type": @"test"
-                                  },
-                               @{ @"identifier": @"id2",
-                                  @"url": @"www.test2.com",
-                                  @"type": @"test2"
-                                  }
-                               ];
-
-    NSDictionary * userInfo = @{ @"aps" : @{ @"alert": @{@"title": @"test-title",
-                                                         @"body": @"test-body"
-                                                         },
-                                             }
-                                 };
-
-    SPNotificationContent *content = [[SPNotificationContent alloc] initWithTitle:@"title" body:@"body" badge:@5];
-        [content setSubtitle:@"subtitle"];
-        [content setSound:@"sound"];
-        [content setLaunchImageName:@"image"];
-        [content setUserInfo:userInfo];
-        [content setAttachments:attachments];
-
-    SPPushNotification *event = [[SPPushNotification alloc] initWithDate:@"date" action:@"action" trigger:@"PUSH" category:@"category" thread:@"thread" notification:content];
-    XCTAssertNotNil(event);
-    event = nil;
-
-    // Action is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date" action:@"" trigger:@"PUSH" category:@"category" thread:@"thread" notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Action cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-
-    // Trigger is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date" action:@"action" trigger:@"" category:@"category" thread:@"thread" notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Trigger cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-
-    // Date is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"" action:@"action" trigger:@"PUSH" category:@"category" thread:@"thread" notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Delivery date cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-
-    // CategoryId is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date" action:@"action" trigger:@"PUSH" category:@"" thread:@"thread" notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Category identifier cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-
-    // ThreadId is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date" action:@"action" trigger:@"PUSH" category:@"category" thread:@"" notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Thread identifier cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testErrorBuilderConditions {
-    // Valid construction
-    SNOWError *event = [[SNOWError alloc] initWithMessage:@"message"];
-    [event setName:@"name"];
-    [event setStackTrace:@"stackTrace"];
-    XCTAssertNotNil(event);
-}
-
-- (void)testTrackerErrorContainsStacktrace {
-    @try {
-        @throw([NSException exceptionWithName:@"CustomException" reason:@"reason" userInfo:nil]);
-    } @catch (NSException *exception) {
-        SPTrackerError *trackerError = [[SPTrackerError alloc] initWithSource:@"classname" message:@"message" error:nil exception:exception];
-        NSDictionary<NSString *, NSObject *> *payload = trackerError.payload;
-        XCTAssertEqualObjects(payload[@"message"], @"message");
-        XCTAssertEqualObjects(payload[@"className"], @"classname");
-        XCTAssertEqualObjects(payload[@"exceptionName"], @"CustomException");
-        XCTAssertTrue([(NSString *)payload[@"stackTrace"] length]);
-    }
-}
-
-// --- Helpers
-
-- (NSMutableArray *) getCustomContext {
-    NSDictionary * data = @{@"snowplow": @"demo-tracker"};
-    SPSelfDescribingJson * context = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios/jsonschema/1-0-0"
-                                                                          andData:data];
-    return [NSMutableArray arrayWithArray:@[context]];
-}
-
-- (NSMutableArray *) getBadCustomContext {
-    NSDictionary *data = @{@"snowplow": @"demo-tracker"};
-    return [NSMutableArray arrayWithArray:@[data]];
-}
-
-#pragma clang diagnostic pop
-
-@end
diff --git a/Snowplow iOSTests/Legacy Tests/LegacyTestSubject.m b/Snowplow iOSTests/Legacy Tests/LegacyTestSubject.m
deleted file mode 100644
index e4f66aa5d..000000000
--- a/Snowplow iOSTests/Legacy Tests/LegacyTestSubject.m	
+++ /dev/null
@@ -1,130 +0,0 @@
-//
-//  LegacyTestSubject.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTrackerConstants.h"
-#import "SPSubject.h"
-#import "SPPayload.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-@interface LegacyTestSubject : XCTestCase
-
-@end
-
-@implementation LegacyTestSubject
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testSubjectInit {
-    SPSubject * subject = [[SPSubject alloc] init];
-    XCTAssertNotNil([subject getStandardDictWithUserAnonymisation:NO]);
-}
-
-- (void)testSubjectInitWithOptions {
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:YES andGeoContext:NO];
-    XCTAssertNotNil([subject getPlatformDictWithUserAnonymisation:NO]);
-    XCTAssertNotNil([subject getStandardDictWithUserAnonymisation:NO]);
-}
-
-- (void)testSubjectSetterFunctions {
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:NO andGeoContext:YES];
-    [subject setUserId:@"aUserId"];
-    [subject setResolutionWithWidth:1920 andHeight:1080];
-    [subject setViewPortWithWidth:1080 andHeight:1920];
-    [subject setColorDepth:20];
-    [subject setTimezone:@"UTC"];
-    [subject setLanguage:@"EN"];
-    [subject setIpAddress:@"127.0.0.1"];
-    [subject setUseragent:@"aUseragent"];
-    [subject setNetworkUserId:@"aNuid"];
-    [subject setDomainUserId:@"aDuid"];
-    
-    NSDictionary * values = [[subject getStandardDictWithUserAnonymisation:NO] getAsDictionary];
-    
-    XCTAssertEqual([values valueForKey:kSPUid], @"aUserId");
-    XCTAssertTrue([[values valueForKey:kSPResolution] isEqualToString:@"1920x1080" ]);
-    XCTAssertTrue([[values valueForKey:kSPViewPort] isEqualToString:@"1080x1920"]);
-    XCTAssertTrue([[values valueForKey:kSPColorDepth] isEqualToString:@"20"]);
-    XCTAssertEqual([values valueForKey:kSPTimezone], @"UTC");
-    XCTAssertEqual([values valueForKey:kSPLanguage], @"EN");
-    XCTAssertEqual([values valueForKey:kSPIpAddress], @"127.0.0.1");
-    XCTAssertEqual([values valueForKey:kSPUseragent], @"aUseragent");
-    XCTAssertEqual([values valueForKey:kSPNetworkUid], @"aNuid");
-    XCTAssertEqual([values valueForKey:kSPDomainUid], @"aDuid");
-    
-    // Setup GeoLocation
-    [subject setGeoDict];
-    [subject setGeoLongitude:5];
-    [subject setGeoLatitude:89.2];
-    [subject setGeoTimestamp:@5];
-    [subject setGeoLatitudeLongitudeAccuracy:5.5];
-    [subject setGeoSpeed:6.2];
-    [subject setGeoBearing:82.3];
-    [subject setGeoAltitude:62.3];
-    [subject setGeoAltitudeAccuracy:16.3];
-    
-    values = [subject getGeoLocationDict];
-    
-    XCTAssertEqualObjects([NSNumber numberWithFloat:5], [values objectForKey:kSPGeoLongitude]);
-    XCTAssertEqualObjects([NSNumber numberWithFloat:89.2], [values objectForKey:kSPGeoLatitude]);
-    XCTAssertEqualObjects([NSNumber numberWithFloat:5.5], [values objectForKey:kSPGeoLatLongAccuracy]);
-    XCTAssertEqualObjects([NSNumber numberWithFloat:6.2], [values objectForKey:kSPGeoSpeed]);
-    XCTAssertEqualObjects([NSNumber numberWithFloat:82.3], [values objectForKey:kSPGeoBearing]);
-    XCTAssertEqualObjects([NSNumber numberWithFloat:62.3], [values objectForKey:kSPGeoAltitude]);
-    XCTAssertEqualObjects([NSNumber numberWithFloat:16.3], [values objectForKey:kSPGeoAltitudeAccuracy]);
-    XCTAssertEqualObjects([NSNumber numberWithInt:5], [values objectForKey:kSPGeoTimestamp]);
-}
-
-- (void) testGeoLocationGetWithoutNeededKeys {
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:NO andGeoContext:YES];
-    XCTAssertNil([subject getGeoLocationDict]);
-    
-    [subject setGeoLongitude:5];
-    [subject setGeoLatitude:89.2];
-    
-    XCTAssertNotNil([subject getGeoLocationDict]);
-}
-
-- (void)testGeoLocationWithSubjectConfiguration {
-    SPSubjectConfiguration *config = [[SPSubjectConfiguration alloc] init];
-    config.geoLatitude = @12.12;
-    config.geoLongitude = @24.24;
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:NO geoLocationContext:YES subjectConfiguration:config];
-    
-    NSDictionary *values = [subject getGeoLocationDict];
-    
-    XCTAssertEqualObjects([NSNumber numberWithFloat:12.12], [values objectForKey:kSPGeoLatitude]);
-    XCTAssertEqualObjects([NSNumber numberWithFloat:24.24], [values objectForKey:kSPGeoLongitude]);
-    XCTAssertNil(values[kSPGeoAltitude]);
-}
-
-@end
-
-#pragma clang diagnostic pop
diff --git a/Snowplow iOSTests/Legacy Tests/LegacyTestTracker.m b/Snowplow iOSTests/Legacy Tests/LegacyTestTracker.m
deleted file mode 100644
index 27959af76..000000000
--- a/Snowplow iOSTests/Legacy Tests/LegacyTestTracker.m	
+++ /dev/null
@@ -1,212 +0,0 @@
-//
-//  LegacyTestTracker.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPPayload.h"
-#import "SPSubject.h"
-#import "SPDevicePlatform.h"
-#import "SPTrackerEvent.h"
-#import "SPUtilities.h"
-#import "SPStructured.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-/// Category needed to make the private methods testable.
-@interface SPTracker (Testing)
-- (SPPayload *)payloadWithEvent:(SPTrackerEvent *)event;
-@end
-
-
-@interface LegacyTestTracker : XCTestCase
-@end
-
-@implementation LegacyTestTracker
-
-NSString *const TEST_SERVER_TRACKER = @"http://www.notarealurl.com";
-
-- (void)testTrackerSetup {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@"not-real.com"];
-    }];
-    
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:YES andGeoContext:YES];
-    
-    SPTracker * tracker = [SPTracker build:^(id<SPTrackerBuilder> builder) {
-        [builder setEmitter:emitter];
-        [builder setSubject:subject];
-        [builder setAppId:@"anAppId"];
-        [builder setBase64Encoded:NO];
-        [builder setTrackerNamespace:@"aNamespace"];
-        [builder setSessionContext:YES];
-    }];
-    
-    XCTAssertNotNil(subject);
-    XCTAssertNotNil(emitter);
-    XCTAssertNotNil(tracker);
-}
-
-- (void)testTrackerBuilderAndOptions {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:TEST_SERVER_TRACKER];
-    }];
-    
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:YES andGeoContext:YES];
-    
-    SPTracker * tracker = [SPTracker build:^(id<SPTrackerBuilder> builder) {
-        [builder setEmitter:emitter];
-        [builder setSubject:subject];
-        [builder setAppId:@"anAppId"];
-        [builder setBase64Encoded:NO];
-        [builder setTrackerNamespace:@"aNamespace"];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:300];
-        [builder setBackgroundTimeout:150];
-    }];
-    
-    // Test builder setting properly
-    
-    XCTAssertNotNil([tracker emitter]);
-    XCTAssertEqual([tracker emitter], emitter);
-    XCTAssertNotNil([tracker subject]);
-    XCTAssertEqual([tracker subject], subject);
-    XCTAssertEqual([tracker devicePlatform], [SPUtilities getPlatform]);
-    XCTAssertEqual([tracker appId], @"anAppId");
-    XCTAssertEqual([tracker trackerNamespace], @"aNamespace");
-    XCTAssertEqual([tracker base64Encoded], NO);
-    XCTAssertEqual([tracker getInBackground], NO);
-    XCTAssertEqual([tracker getIsTracking], YES);
-    
-    // Test Pause/Resume logic
-    
-    [tracker pauseEventTracking];
-    XCTAssertEqual([tracker getIsTracking], NO);
-    XCTAssertNil([tracker track:[[SPStructured alloc] initWithCategory:@"c" action:@"a"]]);
-    [tracker resumeEventTracking];
-    XCTAssertEqual([tracker getIsTracking], YES);
-    
-    // Test setting variables to new values
-    
-    [tracker setAppId:@"newAppId"];
-    XCTAssertEqual([tracker appId], @"newAppId");
-    [tracker setTrackerNamespace:@"newNamespace"];
-    XCTAssertEqual([tracker trackerNamespace], @"newNamespace");
-    [tracker setBase64Encoded:YES];
-    XCTAssertEqual([tracker base64Encoded], YES);
-    [tracker setDevicePlatform:SPDevicePlatformGeneral];
-    XCTAssertEqual([tracker devicePlatform], SPDevicePlatformGeneral);
-    
-    SPSubject * subject2 = [[SPSubject alloc] initWithPlatformContext:YES andGeoContext:YES];
-    [tracker setSubject:subject2];
-    XCTAssertNotEqual([tracker subject], subject);
-    XCTAssertEqual([tracker subject], subject2);
-    
-    SPEmitter * emitter2 = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:TEST_SERVER_TRACKER];
-    }];
-    [tracker setEmitter:emitter2];
-    XCTAssertNotEqual([tracker emitter], emitter);
-    XCTAssertEqual([tracker emitter], emitter2);
-    
-    // Test Session Switch on/off
-    
-    SPSession *oldSessionManager = tracker.session;
-    [tracker setSessionContext:NO];
-    XCTAssertNil(tracker.session);
-    
-    [tracker setSessionContext:YES];
-    XCTAssertNotNil(tracker.session);
-    XCTAssertNotEqual(oldSessionManager, tracker.session);
-
-    // Test Emitter nil
-    
-    @try {
-        tracker = [SPTracker build:^(id<SPTrackerBuilder> builder) {
-            [builder setSubject:subject];
-            [builder setAppId:@"anAppId"];
-            [builder setBase64Encoded:NO];
-            [builder setTrackerNamespace:@"aNamespace"];
-            [builder setSessionContext:YES];
-            [builder setForegroundTimeout:300];
-            [builder setBackgroundTimeout:150];
-        }];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Emitter cannot be nil.", exception.reason);
-    }
-}
-
-- (void)testTrackerPayload {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:TEST_SERVER_TRACKER];
-    }];
-    
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:YES andGeoContext:YES];
-    
-    SPTracker * tracker = [SPTracker build:^(id<SPTrackerBuilder> builder) {
-        [builder setEmitter:emitter];
-        [builder setSubject:subject];
-        [builder setDevicePlatform: SPDevicePlatformGeneral];
-        [builder setAppId:@"anAppId"];
-        [builder setBase64Encoded:NO];
-        [builder setTrackerNamespace:@"aNamespace"];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:300];
-        [builder setBackgroundTimeout:150];
-    }];
-    
-    SPPrimitiveAbstract *event = [[SPStructured alloc] initWithCategory:@"Category" action:@"Action"];
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    SPPayload *payload = [tracker payloadWithEvent:trackerEvent];
-    NSDictionary *payloadDict = [payload getAsDictionary];
-
-    XCTAssertEqual(payloadDict[kSPPlatform], SPDevicePlatformToString(SPDevicePlatformGeneral));
-    XCTAssertEqual(payloadDict[kSPAppId], @"anAppId");
-    XCTAssertEqual(payloadDict[kSPNamespace], @"aNamespace");
-
-    // Test setting variables to new values
-
-    [tracker setDevicePlatform:-13];
-    [tracker setAppId:@"newAppId"];
-    [tracker setTrackerNamespace:@"newNamespace"];
-
-    payload = [tracker payloadWithEvent:trackerEvent];
-    payloadDict = [payload getAsDictionary];
-
-    XCTAssertEqual(payloadDict[kSPPlatform], nil);
-    XCTAssertEqual(payloadDict[kSPAppId], @"newAppId");
-    XCTAssertEqual(payloadDict[kSPNamespace], @"newNamespace");
-}
-
-- (void)testEventIdNotDuplicated {
-    SPPrimitiveAbstract *event = [[SPStructured alloc] initWithCategory:@"Category" action:@"Action"];
-    NSUUID *eventId = [[SPTrackerEvent alloc] initWithEvent:event].eventId;
-    XCTAssertNotNil(eventId);
-    NSUUID *newEventId = [[SPTrackerEvent alloc] initWithEvent:event].eventId;
-    XCTAssertNotNil(newEventId);
-    XCTAssertNotEqualObjects(eventId, newEventId);
-}
-
-@end
-
-#pragma clang diagnostic pop
diff --git a/Snowplow iOSTests/TestDataPersistence.m b/Snowplow iOSTests/TestDataPersistence.m
deleted file mode 100644
index d0eecbbf3..000000000
--- a/Snowplow iOSTests/TestDataPersistence.m	
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-//  TestDataPersistence.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPDataPersistence.h"
-
-@interface TestDataPersistence : XCTestCase
-
-@end
-
-@implementation TestDataPersistence
-
-- (void)setUp {
-    [SPDataPersistence removeDataPersistenceWithNamespace:@"namespace"];
-    [SPDataPersistence removeDataPersistenceWithNamespace:@"namespace1"];
-    [SPDataPersistence removeDataPersistenceWithNamespace:@"namespace2"];
-}
-
-- (void)testStringFromNamespace {
-    XCTAssertEqualObjects(@"abc-1_2_3", [SPDataPersistence stringFromNamespace:@"abc 1_2_3"]);
-}
-
-- (void)testDataPersistenceForNamespaceWithDifferentNamespaces {
-    SPDataPersistence *dp1 = [SPDataPersistence dataPersistenceForNamespace:@"namespace1"];
-    SPDataPersistence *dp2 = [SPDataPersistence dataPersistenceForNamespace:@"namespace2"];
-    XCTAssertNotEqual(dp1, dp2);
-}
-
-- (void)testDataPersistenceForNamespaceWithSameNamespaces {
-    SPDataPersistence *dp1 = [SPDataPersistence dataPersistenceForNamespace:@"namespace"];
-    SPDataPersistence *dp2 = [SPDataPersistence dataPersistenceForNamespace:@"namespace"];
-    XCTAssertEqual(dp1, dp2);
-}
-
-- (void)testRemoveDataPersistenceForNamespace {
-    SPDataPersistence *dp1 = [SPDataPersistence dataPersistenceForNamespace:@"namespace"];
-    [SPDataPersistence removeDataPersistenceWithNamespace:@"namespace"];
-    SPDataPersistence *dp2 = [SPDataPersistence dataPersistenceForNamespace:@"namespace"];
-    XCTAssertNotEqual(dp1, dp2);
-}
-
-- (void)testDataIsCorrectlyStored {
-    [self commonTestDataIsCorrectlyStoredOnFile:YES];
-}
-
-- (void)testDataIsCorrectlyStoredWhenNotStoredOnFile {
-    [self commonTestDataIsCorrectlyStoredOnFile:NO];
-}
-
-- (void)commonTestDataIsCorrectlyStoredOnFile:(BOOL)isStoredOnFile {
-    SPDataPersistence *dp = [SPDataPersistence dataPersistenceForNamespace:@"namespace" storedOnFile:isStoredOnFile];
-    NSDictionary<NSString *, NSObject *> *session = @{@"key": @"value"};
-    dp.session = session;
-    XCTAssertEqualObjects(session, dp.session);
-    XCTAssertEqualObjects(session, dp.data[@"session"]);
-    // Override session
-    session = @{@"key2": @"value2"};
-    dp.session = session;
-    XCTAssertEqualObjects(session, dp.session);
-    XCTAssertEqualObjects(session, dp.data[@"session"]);
-}
-
-- (void)testDataIsStoredWithoutInterference {
-    [self commonTestDataIsStoredWithoutInterferenceStoredOnFile:YES];
-}
-
-- (void)testDataIsStoredWithoutInterferenceWhenNotStoredOnFile {
-    [self commonTestDataIsStoredWithoutInterferenceStoredOnFile:NO];
-}
-
-- (void)commonTestDataIsStoredWithoutInterferenceStoredOnFile:(BOOL)isStoredOnFile {
-    SPDataPersistence *dp1 = [SPDataPersistence dataPersistenceForNamespace:@"namespace1" storedOnFile:isStoredOnFile];
-    SPDataPersistence *dp2 = [SPDataPersistence dataPersistenceForNamespace:@"namespace2" storedOnFile:isStoredOnFile];
-    NSDictionary<NSString *, NSObject *> *session = @{@"key": @"value"};
-    dp1.session = session;
-    // Check dp1
-    XCTAssertEqualObjects(session, dp1.session);
-    XCTAssertEqualObjects(session, dp1.data[@"session"]);
-    // Check dp2
-    XCTAssertNotEqualObjects(session, dp2.session);
-    XCTAssertNotEqualObjects(session, dp2.data[@"session"]);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestEvents.m b/Snowplow iOSTests/TestEvents.m
deleted file mode 100644
index e891e2d02..000000000
--- a/Snowplow iOSTests/TestEvents.m	
+++ /dev/null
@@ -1,557 +0,0 @@
-//
-//  TestEvent.m
-//  Snowplow
-//
-
-#import <XCTest/XCTest.h>
-#import "SPEvent.h"
-#import "SPTrackerError.h"
-#import "SPSelfDescribingJson.h"
-#import "SPSelfDescribing.h"
-#import "SPMockEventStore.h"
-
-@interface TestEvent : XCTestCase
-
-@end
-
-@implementation TestEvent
-
-- (void)testTrueTimestamp {
-    SPPageView *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    XCTAssertNil(event.trueTimestamp);
-
-    // Set trueTimestamp
-    NSDate *testDate = [NSDate date];
-    event.trueTimestamp = testDate;
-    XCTAssertEqual(event.trueTimestamp, testDate);
-}
-
-- (void)testApplicationInstall {
-    // Prepare ApplicationInstall event
-    SPSelfDescribingJson *installEvent = [[SPSelfDescribingJson alloc] initWithSchema:kSPApplicationInstallSchema andData:@{}];
-    SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithEventData:installEvent];
-    NSDate *currentTimestamp = [NSDate dateWithTimeIntervalSince1970:12345L];
-    event.trueTimestamp = currentTimestamp;
-    
-    // Setup tracker
-    SPTrackerConfiguration *trackerConfiguration = [SPTrackerConfiguration new];
-    trackerConfiguration.base64Encoding = NO;
-    trackerConfiguration.installAutotracking = NO;
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, emitterConfiguration]];
-
-    // Track event
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    SPPayload *payload = [[events firstObject] payload];
-    
-    // Check v_tracker field
-    NSString *deviceTimestamp = (NSString *)[[payload getAsDictionary] objectForKey:@"dtm"];
-    NSString *expected = [NSString stringWithFormat:@"%lld", (long long)(currentTimestamp.timeIntervalSince1970 * 1000)];
-    XCTAssertEqualObjects(expected, deviceTimestamp);
-}
-
-- (void)testWorkaroundForCampaignAttributionEnrichment {
-    // Prepare DeepLinkReceived event
-    SPDeepLinkReceived *event = [[SPDeepLinkReceived alloc] initWithUrl:@"url"];
-    event.referrer = @"referrer";
-    
-    // Setup tracker
-    SPTrackerConfiguration *trackerConfiguration = [SPTrackerConfiguration new];
-    trackerConfiguration.base64Encoding = NO;
-    trackerConfiguration.installAutotracking = NO;
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, emitterConfiguration]];
-
-    // Track event
-    [trackerController track:event];
-    for (int i=0; eventStore.count < 1 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(1, events.count);
-    SPPayload *payload = [[events firstObject] payload];
-    
-    // Check url and referrer fields
-    NSString *url = (NSString *)[[payload getAsDictionary] objectForKey:kSPPageUrl];
-    NSString *referrer = (NSString *)[[payload getAsDictionary] objectForKey:kSPPageRefr];
-    XCTAssertEqualObjects(url, @"url");
-    XCTAssertEqualObjects(referrer, @"referrer");
-}
-
-- (void)testDeepLinkContextAndAtomicPropertiesAddedToScreenView {
-    // Prepare DeepLinkReceived event
-    SPDeepLinkReceived *deepLink = [[SPDeepLinkReceived alloc] initWithUrl:@"the_url"];
-    deepLink.referrer = @"the_referrer";
-    
-    // Prepare ScreenView event
-    SPScreenView *screenView = [[SPScreenView alloc] initWithName:@"SV" screenId:[NSUUID UUID]];
-    
-    // Setup tracker
-    SPTrackerConfiguration *trackerConfiguration = [SPTrackerConfiguration new];
-    trackerConfiguration.base64Encoding = NO;
-    trackerConfiguration.installAutotracking = NO;
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"fake-url" method:SPHttpMethodPost];
-    SPEmitterConfiguration *emitterConfiguration = [[SPEmitterConfiguration alloc] init];
-    emitterConfiguration.eventStore = eventStore;
-    emitterConfiguration.threadPoolSize = 10;
-    id<SPTrackerController> trackerController = [SPSnowplow createTrackerWithNamespace:@"namespace" network:networkConfiguration configurations:@[trackerConfiguration, emitterConfiguration]];
-
-    // Track event
-    [trackerController track:deepLink];
-    NSUUID *screenViewId = [trackerController track:screenView];
-    for (int i=0; eventStore.count < 2 && i < 10; i++) {
-        [NSThread sleepForTimeInterval:1];
-    }
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:10];
-    [eventStore removeAllEvents];
-    XCTAssertEqual(2, events.count);
-    
-    SPPayload *screenViewPayload = nil;
-    for (SPEmitterEvent *event in events) {
-        if ([(NSString *)[[[event payload] getAsDictionary] objectForKey:@"eid"] isEqualToString:[screenViewId UUIDString]]) {
-            screenViewPayload = [event payload];
-        }
-    }
-    XCTAssertNotNil(screenViewPayload);
-    
-    // Check the DeepLink context entity properties
-    NSString *screenViewContext = (NSString *)[[screenViewPayload getAsDictionary] objectForKey:@"co"];
-    XCTAssertTrue([screenViewContext containsString:@"\"referrer\":\"the_referrer\""]);
-    XCTAssertTrue([screenViewContext containsString:@"\"url\":\"the_url\""]);
-    
-    // Check url and referrer fields for atomic table
-    NSString *url = (NSString *)[[screenViewPayload getAsDictionary] objectForKey:kSPPageUrl];
-    NSString *referrer = (NSString *)[[screenViewPayload getAsDictionary] objectForKey:kSPPageRefr];
-    XCTAssertEqualObjects(url, @"the_url");
-    XCTAssertEqualObjects(referrer, @"the_referrer");
-}
-
-- (void)testPageView {
-    // Valid construction
-    SPPageView *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // PageURL is empty
-    @try {
-        event = [[SPPageView alloc] initWithPageUrl:@""];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"PageURL cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testStructured {
-    // Valid construction
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"category" action:@"action"];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // Category is empty
-    @try {
-        event = [[SPStructured alloc] initWithCategory:@"" action:@"action"];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Category cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-        
-    // Action is empty
-    @try {
-        event = [[SPStructured alloc] initWithCategory:@"category" action:@""];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Action cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testUnstructured {
-    // Valid construction
-    NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
-    [data setObject:@23 forKey:@"level"];
-    [data setObject:@56473 forKey:@"score"];
-    SPSelfDescribingJson *sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0"
-                                                                      andData:data];
-    SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithEventData:sdj];
-    XCTAssertNotNil(event);
-}
-
-- (void)testUnstructuredWithWrongData {
-    // Invalid dictionary
-    NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
-    [data setObject:@12 forKey:@12];
-    SPSelfDescribingJson *sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0"
-                                                                      andData:data];
-    // Data is wrong
-    SPSelfDescribing *event;
-    @try {
-        event = [[SPSelfDescribing alloc] initWithEventData:sdj];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"EventData payload has to be JSON serializable.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testConsentWithdrawn {
-    // Valid construction
-    SPConsentWithdrawn *event = [[[[[[[SPConsentWithdrawn alloc] init]
-                                     name:@"name"]
-                                    all:NO]
-                                   version:@"3"]
-                                  documentId:@"1000"]
-                                 documentDescription:@"description"];
-    XCTAssertNotNil(event);
-}
-
-- (void)testConsentGranted {
-    // Valid construction
-    SPConsentGranted *event = [[SPConsentGranted alloc] initWithExpiry:@"expiry" documentId:@"1000" version:@"3"];
-    event.name = @"name";
-    event.documentDescription = @"description";
-    XCTAssertNotNil(event);
-}
-
-- (void)testConsentDocument {
-    // Valid construction
-    SPConsentGranted *event = [[SPConsentGranted alloc] initWithExpiry:@"expiry" documentId:@"1000" version:@"3"];
-    event.name = @"name";
-    event.documentDescription = @"description";
-    XCTAssertNotNil(event);
-}
-
-- (void)testScreenView {
-    NSUUID *screenId = [NSUUID UUID];
-    
-    // Valid construction
-    SPScreenView *event = [[SPScreenView alloc] initWithName:@"name" screenId:screenId];
-    XCTAssertNotNil(event);
-    event = nil;
-
-    @try {
-        event = [[SPScreenView alloc] initWithName:@"" screenId:screenId];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Name cannot be empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testTiming {
-    // Valid construction
-    SPTiming *event = [[SPTiming alloc] initWithCategory:@"cat" variable:@"var" timing:@5];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // Category is empty
-    @try {
-        event = [[SPTiming alloc] initWithCategory:@"" variable:@"var" timing:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Category cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-    
-    // Variable is empty
-    @try {
-        event = [[SPTiming alloc] initWithCategory:@"cat" variable:@"" timing:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Variable cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testEcommerce {
-    // Valid construction
-    SPEcommerce *event = [[SPEcommerce alloc] initWithOrderId:@"id" totalValue:@5 items:@[]];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // OrderID is empty
-    @try {
-        event = [[SPEcommerce alloc] initWithOrderId:@"" totalValue:@5 items:@[]];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"OrderId cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testEcommerceItem {
-    // Valid construction
-    SPEcommerceItem *event = [[SPEcommerceItem alloc] initWithSku:@"sku" price:@5.3 quantity:@5];
-    XCTAssertNotNil(event);
-    event = nil;
-    
-    // Sku is empty
-    @try {
-        event = [[SPEcommerceItem alloc] initWithSku:@"" price:@5.3 quantity:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"SKU cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testPushNotificationContent {
-    // Valid construction
-    NSArray *attachments = @[ @{ @"identifier": @"id",
-                                 @"url": @"www.test.com",
-                                 @"type": @"test"
-    },
-                              @{ @"identifier": @"id2",
-                                 @"url": @"www.test2.com",
-                                 @"type": @"test2"
-                              }
-    ];
-    
-    NSDictionary *userInfo = @{ @"aps" : @{ @"alert": @"test",
-                                            @"sound": @"sound",
-                                            @"category": @"category"
-    }
-    };
-    
-    SPNotificationContent *event = [[SPNotificationContent alloc] initWithTitle:@"title" body:@"body" badge:@5];
-    event.subtitle = @"subtitle";
-    event.sound = @"sound";
-    event.launchImageName = @"image";
-    event.userInfo = userInfo;
-    event.attachments = attachments;
-    XCTAssertNotNil(event);
-    event = nil;
-
-    // Title is empty
-    @try {
-        event = [[SPNotificationContent alloc] initWithTitle:@"" body:@"body" badge:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Title cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-
-    // Body is empty
-    @try {
-        event = [[SPNotificationContent alloc] initWithTitle:@"title" body:@"" badge:@5];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Body cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testPushNotification {
-    // Valid construction
-    NSArray *attachments = @[ @{ @"identifier": @"id",
-                                 @"url": @"www.test.com",
-                                 @"type": @"test"
-    },
-                              @{ @"identifier": @"id2",
-                                 @"url": @"www.test2.com",
-                                 @"type": @"test2"
-                              }
-    ];
-    
-    NSDictionary *userInfo = @{ @"aps":
-                                    @{ @"alert":
-                                           @{
-                                               @"title": @"test-title",
-                                               @"body": @"test-body"
-                                           },
-                                    }
-    };
-    
-    SPNotificationContent *content = [[SPNotificationContent alloc] initWithTitle:@"title" body:@"body" badge:@5];
-    content.subtitle = @"subtitle";
-    content.sound = @"sound";
-    content.launchImageName = @"image";
-    content.userInfo = userInfo;
-    content.attachments = attachments;
-
-    SPPushNotification *event = [[SPPushNotification alloc] initWithDate:@"date"
-                                                                  action:@"action"
-                                                                 trigger:@"PUSH"
-                                                                category:@"category"
-                                                                  thread:@"thread"
-                                                            notification:content];
-    XCTAssertNotNil(event);
-    event = nil;
-
-    // Action is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date"
-                                                  action:@""
-                                                 trigger:@"PUSH"
-                                                category:@"category"
-                                                  thread:@"thread"
-                                            notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Action cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-
-    // Trigger is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date"
-                                                  action:@"action"
-                                                 trigger:@""
-                                                category:@"category"
-                                                  thread:@"thread"
-                                            notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Trigger cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-    
-    // Date is nil
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@""
-                                                  action:@"action"
-                                                 trigger:@"PUSH"
-                                                category:@"category"
-                                                  thread:@"thread"
-                                            notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Delivery date cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-    
-    // CategoryId is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date"
-                                                  action:@"action"
-                                                 trigger:@"PUSH"
-                                                category:@""
-                                                  thread:@"thread"
-                                            notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Category identifier cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-    
-    // ThreadId is empty
-    @try {
-        event = [[SPPushNotification alloc] initWithDate:@"date"
-                                                  action:@"action"
-                                                 trigger:@"PUSH"
-                                                category:@"category"
-                                                  thread:@""
-                                            notification:content];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(@"Thread identifier cannot be nil or empty.", exception.reason);
-    }
-    XCTAssertNil(event);
-}
-
-- (void)testMessageNotification {
-    SPMessageNotification *event = [[SPMessageNotification alloc] initWithTitle:@"title" body:@"body" trigger:SPMessageNotificationTriggerPush];
-    event.notificationTimestamp = @"2020-12-31T15:59:60-08:00";
-    event.action = @"action";
-    event.bodyLocKey = @"loc key";
-    event.bodyLocArgs = @[@"loc arg1", @"loc arg2"];
-    event.sound = @"chime.mp3";
-    event.notificationCount = @9;
-    event.category = @"category1";
-    event.attachments = @[[[SPMessageNotificationAttachment alloc] initWithIdentifier:@"id" type:@"type" url:@"url"]];
-
-    NSDictionary<NSString *, NSObject *> *payload = event.payload;
-    XCTAssertEqualObjects(@"title", payload[kSPMessageNotificationParamTitle]);
-    XCTAssertEqualObjects(@"body", payload[kSPMessageNotificationParamBody]);
-    XCTAssertEqualObjects(@"2020-12-31T15:59:60-08:00", payload[kSPMessageNotificationParamNotificationTimestamp]);
-    XCTAssertEqualObjects(@"push", payload[kSPMessageNotificationParamTrigger]);
-    XCTAssertEqualObjects(@"action", payload[kSPMessageNotificationParamAction]);
-    XCTAssertEqualObjects(@"loc key", payload[kSPMessageNotificationParamBodyLocKey]);
-    NSArray<NSString *> *locArgs = (NSArray<NSString *> *)(payload[kSPMessageNotificationParamBodyLocArgs]);
-    XCTAssertNotNil(locArgs);
-    XCTAssertEqual(2, locArgs.count);
-    XCTAssertEqualObjects(@"loc arg1", locArgs[0]);
-    XCTAssertEqualObjects(@"loc arg2", locArgs[1]);
-    XCTAssertEqualObjects(@"chime.mp3", payload[kSPMessageNotificationParamSound]);
-    XCTAssertEqualObjects(@9, payload[kSPMessageNotificationParamNotificationCount]);
-    XCTAssertEqualObjects(@"category1", payload[kSPMessageNotificationParamCategory]);
-    NSArray<NSDictionary<NSString *, NSObject *> *> *attachments = (NSArray<NSDictionary<NSString *, NSObject *> *> *)(payload[kSPMessageNotificationParamMessageNotificationAttachments]);
-    XCTAssertNotNil(attachments);
-    XCTAssertEqual(1, attachments.count);
-    NSDictionary<NSString *, NSObject *> *attachment = attachments[0];
-    XCTAssertEqualObjects(@"id", attachment[kSPMessageNotificationAttachmentParamIdentifier]);
-    XCTAssertEqualObjects(@"type", attachment[kSPMessageNotificationAttachmentParamType]);
-    XCTAssertEqualObjects(@"url", attachment[kSPMessageNotificationAttachmentParamUrl]);
-}
-
-- (void)testMessageNotificationWithUserInfo {
-    NSDictionary *userInfo = @{ @"aps":
-                                    @{ @"alert":
-                                           @{
-                                               @"title": @"test-title",
-                                               @"body": @"test-body",
-                                               @"loc-key": @"loc key",
-                                               @"loc-args": @[@"loc arg1", @"loc arg2"]
-                                           },
-                                       @"sound": @"chime.aiff",
-                                       @"badge": @9,
-                                       @"category": @"category1",
-                                       @"content-available": @1
-                                    },
-                                @"custom-element": @1
-    };
-    SPMessageNotification *event = [SPMessageNotification messageNotificationWithUserInfo:userInfo defaultTitle:nil defaultBody:nil];
-    XCTAssertNotNil(event);
-    NSDictionary<NSString *, NSObject *> *payload = event.payload;
-    XCTAssertEqualObjects(@"test-title", payload[kSPMessageNotificationParamTitle]);
-    XCTAssertEqualObjects(@"test-body", payload[kSPMessageNotificationParamBody]);
-    XCTAssertEqualObjects(@"loc key", payload[kSPMessageNotificationParamBodyLocKey]);
-    NSArray *locArgs = (NSArray *)payload[kSPMessageNotificationParamBodyLocArgs];
-    XCTAssertEqual(2, locArgs.count);
-    XCTAssertEqualObjects(@"loc arg1", locArgs[0]);
-    XCTAssertEqualObjects(@"loc arg2", locArgs[1]);
-    XCTAssertEqualObjects(@9, payload[kSPMessageNotificationParamNotificationCount]);
-    XCTAssertEqualObjects(@"chime.aiff", payload[kSPMessageNotificationParamSound]);
-    XCTAssertEqualObjects(@"category1", payload[kSPMessageNotificationParamCategory]);
-    XCTAssertEqualObjects(@YES, payload[kSPMessageNotificationParamContentAvailable]);
-}
-
-- (void)testError {
-    // Valid construction
-    SNOWError *error = [[[[SNOWError alloc] initWithMessage:@"message"]
-                         name:@"name"]
-                        stackTrace:@"stacktrace"];
-    XCTAssertNotNil(error);
-}
-
-- (void)testTrackerErrorContainsStacktrace {
-    @try {
-        @throw([NSException exceptionWithName:@"CustomException" reason:@"reason" userInfo:nil]);
-    } @catch (NSException *exception) {
-        SPTrackerError *trackerError = [[SPTrackerError alloc] initWithSource:@"classname" message:@"message" error:nil exception:exception];
-        NSDictionary<NSString *, NSObject *> *payload = trackerError.payload;
-        XCTAssertEqualObjects(payload[@"message"], @"message");
-        XCTAssertEqualObjects(payload[@"className"], @"classname");
-        XCTAssertEqualObjects(payload[@"exceptionName"], @"CustomException");
-        XCTAssertTrue([(NSString *)payload[@"stackTrace"] length]);
-    }
-}
-
-@end
diff --git a/Snowplow iOSTests/TestGeneratedJsons.m b/Snowplow iOSTests/TestGeneratedJsons.m
deleted file mode 100644
index cac3169d7..000000000
--- a/Snowplow iOSTests/TestGeneratedJsons.m	
+++ /dev/null
@@ -1,396 +0,0 @@
-//
-//  TestGeneratedJsons.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import <SnowplowIgluClient/IGLUClient.h>
-#import "SPTrackerConstants.h"
-#import "SPEmitter.h"
-#import "SPTracker.h"
-#import "SPSession.h"
-#import "SPSubject.h"
-#import "SPGdprContext.h"
-#import "SPPayload.h"
-#import "SPEvent.h"
-#import "SPSelfDescribingJson.h"
-#import "SPUtilities.h"
-#import "SPTrackerEvent.h"
-#import "SPServiceProvider.h"
-
-#import "SPScreenStateMachine.h"
-#import "SPScreenState.h"
-
-
-/// Category needed to make the private methods testable.
-@interface SPTracker (Testing)
-- (SPPayload *)payloadWithEvent:(SPTrackerEvent *)event;
-@end
-
-
-@interface TestGeneratedJsons : XCTestCase
-@end
-
-@implementation TestGeneratedJsons {
-    IGLUClient * validator;
-}
-
-const NSString* IGLU_PATH = @"http://raw.githubusercontent.com/snowplow/iglu-central/master/schemas/";
-
-- (void)setUp {
-    [super setUp];
-    validator = [[IGLUClient alloc] initWithJsonString:[self getJSONAsStringWithFilePath:@"iglu_resolver.json"] andBundles:[[NSMutableArray alloc] initWithObjects:[NSBundle bundleForClass:[self class]], nil]];
-}
-
-- (void)tearDown {
-    validator = nil;
-    [super tearDown];
-}
-
-- (void)testScreenContextJson {
-    SPScreenStateMachine *stateMachine = [[SPScreenStateMachine alloc] init];
-    SPTrackerEvent *fakeEvent = [[SPTrackerEvent alloc] initWithEvent:[[SPStructured alloc] initWithCategory:@"fake" action:@"fake"]];
-    id<SPState> screenState = [[SPScreenState alloc] initWithName:@"name" type:@"type" screenId:nil transitionType:@"transition" topViewControllerClassName:@"topVCname" viewControllerClassName:@"VCname"];
-    NSArray<SPSelfDescribingJson *> *entities = [stateMachine entitiesFromEvent:fakeEvent state:screenState];
-    SPSelfDescribingJson *screenContext = [entities firstObject];
-    XCTAssertNotNil(screenContext);
-    XCTAssertTrue([validator validateJson:[screenContext getAsDictionary]]);
-}
-
-- (void)testClientSessionContextJson {
-    SPSession * session = [[SPSession alloc] initWithForegroundTimeout:1800 andBackgroundTimeout:1800];
-    NSDictionary * data = [session getSessionDictWithEventId:@"first-event-id" eventTimestamp:1654496481346 userAnonymisation:NO];
-    NSDictionary * json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPSessionContextSchema andData:data] getAsDictionary];
-    XCTAssertTrue([validator validateJson:json]);
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-- (void)testPlatformContextJson {
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:YES andGeoContext:YES];
-    NSDictionary * data = [[subject getPlatformDictWithUserAnonymisation:NO] getAsDictionary];
-    NSDictionary * json;
-#if TARGET_OS_IPHONE
-    json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPMobileContextSchema andData:data] getAsDictionary];
-#else
-    json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPDesktopContextSchema andData:data] getAsDictionary];
-#endif
-    XCTAssertTrue([validator validateJson:json]);
-}
-
-- (void)testGeoContextJson {
-    SPSubject * subject = [[SPSubject alloc] initWithPlatformContext:NO andGeoContext:YES];
-    [subject setGeoLongitude:5];
-    [subject setGeoLatitude:89.2];
-    [subject setGeoTimestamp:@5];
-    [subject setGeoLatitudeLongitudeAccuracy:5.5];
-    [subject setGeoSpeed:6.2];
-    [subject setGeoBearing:82.3];
-    [subject setGeoAltitude:62.3];
-    [subject setGeoAltitudeAccuracy:16.3];
-    NSDictionary * data = [subject getGeoLocationDict];
-    NSDictionary * json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPGeoContextSchema andData:data] getAsDictionary];
-    XCTAssertTrue([validator validateJson:json]);
-}
-
-#pragma clang diagnostic pop
-
-- (void)testGdprContextJson {
-    SPGdprContext *gdpr = [[SPGdprContext alloc] initWithBasis:SPGdprProcessingBasisConsent
-                                                    documentId:@"id"
-                                               documentVersion:@"version"
-                                           documentDescription:@"description"];
-    XCTAssertTrue([validator validateJson:[gdpr.context getAsDictionary]]);
-}
-
-- (void)testStructuredEventPayloadJson  {
-    SPTracker * tracker = [self getTracker:@"acme.fake.url"];
-    [tracker setBase64Encoded:false];
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"DemoCategory" action:@"DemoAction"];
-    event.label = @"DemoLabel";
-    event.property = @"DemoProperty";
-    event.value = @5;
-    
-    // Check that the final payload passes validation
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    NSDictionary *data = [[tracker payloadWithEvent:trackerEvent] getAsDictionary];
-
-    NSArray * dataArray = [NSArray arrayWithObject:data];
-    NSDictionary * json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:dataArray] getAsDictionary];
-    
-    XCTAssertTrue([validator validateJson:json]);
-}
-
-- (void)testUnstructuredEventPayloadJson  {
-    SPTracker * tracker = [self getTracker:@"acme.fake.url"];
-    [tracker setBase64Encoded:false];
-    NSMutableDictionary * input = [[NSMutableDictionary alloc] init];
-    [input setObject:[NSNumber numberWithInt:23] forKey:@"level"];
-    [input setObject:[NSNumber numberWithInt:56473] forKey:@"score"];
-    SPSelfDescribingJson *sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0"
-                                                                      andData:input];
-    SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithEventData:sdj];
-    
-    // Check that the final payload passes validation
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    NSDictionary<NSString *, NSObject *> *data = [[tracker payloadWithEvent:trackerEvent] getAsDictionary];
-
-    NSArray *dataArray = [NSArray arrayWithObject:data];
-    NSDictionary<NSString *, NSObject *> *json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:dataArray] getAsDictionary];
-
-    XCTAssertTrue([validator validateJson:json]);
-    
-    // Check that the nested unstructured event passes validation
-    NSString * ue_pr = (NSString *)[data objectForKey:@"ue_pr"];
-    NSDictionary *unstructDictionary = [NSJSONSerialization JSONObjectWithData:[ue_pr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
-    
-    XCTAssertTrue([validator validateJson:unstructDictionary]);
-}
-
-- (void)testSelfDescribingEventPayloadJson  {
-    SPTracker * tracker = [self getTracker:@"acme.fake.url"];
-    [tracker setBase64Encoded:false];
-    NSMutableDictionary * input = [[NSMutableDictionary alloc] init];
-    [input setObject:[NSNumber numberWithInt:23] forKey:@"level"];
-    [input setObject:[NSNumber numberWithInt:56473] forKey:@"score"];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0"
-                                                                      andData:input];
-    SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithEventData:sdj];
-
-    // Check that the final payload passes validation
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    NSDictionary<NSString *, NSObject *> *data = [[tracker payloadWithEvent:trackerEvent] getAsDictionary];
-
-    NSArray *dataArray = [NSArray arrayWithObject:data];
-    NSDictionary<NSString *, NSObject *> *json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:dataArray] getAsDictionary];
-
-    XCTAssertTrue([validator validateJson:json]);
-
-    // Check that the nested unstructured event passes validation
-    NSString *ue_pr = (NSString *)[data objectForKey:@"ue_pr"];
-    NSDictionary *unstructDictionary = [NSJSONSerialization JSONObjectWithData:[ue_pr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
-
-    XCTAssertTrue([validator validateJson:unstructDictionary]);
-}
-
-- (void)testConsentWithdrawnEventPayloadJson {
-    SPConsentWithdrawn *event = [[SPConsentWithdrawn alloc] init];
-    [event documentDescription:@"Description"];
-    [event documentId:@"1234"];
-    [event version:@"10"];
-    [event all:false];
-    [event name:@"Name"];
-
-    NSDictionary<NSString *, NSObject *> *sdj = [[[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testConsentDocumentEventPayloadJson {
-    SPConsentDocument *event = [[SPConsentDocument alloc] initWithDocumentId:@"1234" version:@"10"];
-    [event documentDescription:@"Description"];
-    [event name:@"Name"];
-    
-    NSDictionary<NSString *, NSObject *> *sdj = [[event getPayload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testConsentGrantedEventPayloadJson {
-    SPConsentGranted *event = [[SPConsentGranted alloc] initWithExpiry:@"2012-04-23T18:25:43.511Z" documentId:@"1234" version:@"10"];
-    [event documentDescription:@"Description"];
-    [event name:@"Name"];
-    
-    NSDictionary<NSString *, NSObject *> *sdj = [[[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testPageViewEventPayloadJson {
-    SPTracker *tracker = [self getTracker:@"acme.fake.url"];
-    SPPageView *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    event.pageTitle = @"DemoPageTitle";
-    event.referrer = @"DemoPageReferrer";
-
-    // Check that the final payload passes validation
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    NSDictionary<NSString *, NSObject *> *data = [[tracker payloadWithEvent:trackerEvent] getAsDictionary];
-
-    NSArray *dataArray = [NSArray arrayWithObject:data];
-    NSDictionary<NSString *, NSObject *> *json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:dataArray] getAsDictionary];
-    
-    XCTAssertTrue([validator validateJson:json]);
-}
-
-- (void)testEcommerceEventPayloadJson {
-    SPTracker * tracker = [self getTracker:@"acme.fake.url"];
-    
-    NSString *transactionID = @"6a8078be";
-    NSMutableArray *itemArray = [NSMutableArray array];
-    SPEcommerceItem *item = [[SPEcommerceItem alloc] initWithSku:@"DemoItemSku" price:@0.75F quantity:@1];
-    [item name:@"DemoItemName"];
-    [item category:@"DemoItemCategory"];
-    [item currency:@"USD"];
-    
-    [itemArray addObject:item];
-    SPEcommerce *event = [[SPEcommerce alloc] initWithOrderId:transactionID totalValue:@350 items:itemArray];
-    [event affiliation:@"DemoTranAffiliation"];
-    [event taxValue:@10];
-    [event shipping:@15];
-    [event city:@"Boston"];
-    [event state:@"Massachusetts"];
-    [event country:@"USA"];
-    [event currency:@"USD"];
-
-    // Check that the main payload passes validation
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    NSDictionary<NSString *, NSObject *> *data = [[tracker payloadWithEvent:trackerEvent] getAsDictionary];
-
-    NSArray *dataArray = [NSArray arrayWithObject:data];
-    NSDictionary<NSString *, NSObject *> *json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:dataArray] getAsDictionary];
-    
-    XCTAssertTrue([validator validateJson:json]);
-    
-    // Check that the item payload passes validation
-    data = [[tracker payloadWithEvent:trackerEvent] getAsDictionary];
-
-    dataArray = [NSArray arrayWithObject:data];
-    json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:dataArray] getAsDictionary];
-    
-    XCTAssertTrue([validator validateJson:json]);
-}
-
-- (void)testTimingEventJson {
-    SPTiming *event = [[SPTiming alloc] initWithCategory:@"DemoTimingCategory" variable:@"DemoTimingVariable" timing:@5];
-    NSDictionary<NSString *, NSObject *> *sdj = [[[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testScreenViewEventJson {
-    SPScreenView *event = [[SPScreenView alloc] initWithName:@"DemoScreenName" screenId:[NSUUID UUID]];
-    NSDictionary<NSString *, NSObject *> *sdj = [[[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testPushNotificationEventJson {
-    NSMutableArray *attachments = [[NSMutableArray alloc] init];
-    [attachments addObject:@{ kSPPnAttachmentId : @"identifier",
-                              kSPPnAttachmentUrl : @"url",
-                              kSPPnAttachmentType : @"type"
-                              }];
-
-    NSDictionary *userInfo = @{@"aps":
-                                    @{@"alert":
-                                          @{@"title": @"test title",
-                                            @"body": @"test",
-                                            @"loc-key": @"test key"
-                                            },
-                                      @"content-available": @0
-                                          }
-                                    };
-
-    SPNotificationContent *content = [[SPNotificationContent alloc] initWithTitle:@"title" body:@"body" badge:@5];
-    [content subtitle:@"subtitle"];
-    [content sound:@"sound"];
-    [content launchImageName:@"launchImageName"];
-    [content userInfo: userInfo];
-
-    SPPushNotification *event = [[SPPushNotification alloc] initWithDate:@"date" action:@"action" trigger:@"PUSH" category:@"category" thread:@"thread" notification:content];
-    
-    NSDictionary *sdj = [[[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testMessageNotificationEventJson {
-    NSDictionary *userInfo = @{@"aps":
-                                    @{@"alert":
-                                          @{@"title": @"test title",
-                                            @"body": @"test",
-                                            @"loc-key": @"test key"
-                                            },
-                                      @"content-available": @0
-                                          }
-                                    };
-    SPMessageNotification *event = [SPMessageNotification messageNotificationWithUserInfo:userInfo defaultTitle:nil defaultBody:nil];
-    NSDictionary *sdj = [[[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testApplicationInstallJson {
-    SPSelfDescribingJson *installEvent = [[SPSelfDescribingJson alloc] initWithSchema:kSPApplicationInstallSchema andData:@{}];
-    NSDictionary<NSString *, NSObject *> *json = [installEvent getAsDictionary];
-    XCTAssertTrue([validator validateJson:json]);
-}
-
-- (void)testApplicationContextJson {
-    SPSelfDescribingJson *json = [SPUtilities getApplicationContextWithVersion:@"testversion" andBuild:@"testbuild"];
-    XCTAssertTrue([validator validateJson:[json getAsDictionary]]);
-}
-
-- (void)testErrorEventJson {
-    SNOWError *event = [[SNOWError alloc] initWithMessage:@"some error message"];
-    event.name = @"some exception name";
-    event.stackTrace = @"some stack trace";
-    NSDictionary<NSString *, NSObject *> *sdj = [[[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload] getAsDictionary];
-    XCTAssertTrue([validator validateJson:sdj]);
-}
-
-- (void)testFinalEventPayloadJson {
-    SPTracker *tracker = [self getTracker:@"acme.fake.url"];
-    SPPageView *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    [event pageTitle:@"DemoPageTitle"];
-    [event referrer:@"DemoPageReferrer"];
-    
-    // Check that the final payload passes validation
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event];
-    NSDictionary<NSString *, NSObject *> *data = [[tracker payloadWithEvent:trackerEvent] getAsDictionary];
-
-    NSArray *dataArray = [NSArray arrayWithObject:data];
-    NSDictionary<NSString *, NSObject *> *json = [[[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:dataArray] getAsDictionary];
-    XCTAssertTrue([validator validateJson:json]);
-    
-    // Check that the nested context json passes validation
-    NSString *contextsJson = (NSString *)[data objectForKey:@"co"];
-    NSDictionary *contextDictionary = [NSJSONSerialization JSONObjectWithData:[contextsJson dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
-    XCTAssertTrue([validator validateJson:contextDictionary]);
-}
-
-- (NSString *)getJSONAsStringWithFilePath:(NSString *)filePath {
-    NSString * path = [[NSBundle bundleForClass:[self class]] pathForResource:filePath ofType:nil inDirectory:@"Products"];
-    @try {
-        NSData * data = [NSData dataWithContentsOfFile:path];
-        return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
-    }
-    @catch (NSException *exception) {
-        return nil;
-    }
-}
-
-- (SPTracker *)getTracker:(NSString *)url {
-    NSString *endpoint = [NSString stringWithFormat:@"https://%@", url];
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodPost];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"anAppId"];
-    trackerConfig.platformContext = YES;
-    trackerConfig.geoLocationContext = YES;
-    trackerConfig.base64Encoding = NO;
-    trackerConfig.sessionContext = YES;
-    SPServiceProvider *serviceProvider = [[SPServiceProvider alloc] initWithNamespace:@"aNamespace" network:networkConfig configurations:@[trackerConfig]];
-    return serviceProvider.tracker;
-}
-
-@end
diff --git a/Snowplow iOSTests/TestLifecycleState.m b/Snowplow iOSTests/TestLifecycleState.m
deleted file mode 100644
index 3c892557d..000000000
--- a/Snowplow iOSTests/TestLifecycleState.m	
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-//  TestLifecycleState.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPPayload.h"
-#import "SPEvent.h"
-#import "SPMockEventStore.h"
-
-@interface TestLifecycleState : XCTestCase
-
-@end
-
-@implementation TestLifecycleState
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testLifeycleStateMachine {
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setEmitter:[SPEmitter build:^(id<SPEmitterBuilder> builder) {
-            [builder setUrlEndpoint:@"http://snowplow-fake-url.com"];
-            [builder setEventStore:eventStore];
-        }]];
-        [builder setTrackerNamespace:@"namespace"];
-        [builder setBase64Encoded:NO];
-        [builder setLifecycleEvents:YES];
-    }];
-    
-    // Send events
-    [tracker track:[[SPTiming alloc] initWithCategory:@"category" variable:@"variable" timing:@123]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    SPPayload *payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    NSString *entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertNotNil(entities);
-    XCTAssertTrue([entities containsString:@"\"isVisible\":true"]);
-    
-    [tracker track:[[SPBackground alloc] initWithIndex:@1]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertNotNil(entities);
-    XCTAssertTrue([entities containsString:@"\"isVisible\":false"]);
-
-    [tracker track:[[SPTiming alloc] initWithCategory:@"category" variable:@"variable" timing:@123]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertTrue([entities containsString:@"\"isVisible\":false"]);
-    
-    [tracker track:[[SPForeground alloc] initWithIndex:@1]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertNotNil(entities);
-    XCTAssertTrue([entities containsString:@"\"isVisible\":true"]);
-
-    NSUUID *uuid = [NSUUID UUID];
-    [tracker track:[[SPScreenView alloc] initWithName:@"screen1" screenId:uuid]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertTrue([entities containsString:@"\"isVisible\":true"]);
-}
-
-@end
-
diff --git a/Snowplow iOSTests/TestLogger.m b/Snowplow iOSTests/TestLogger.m
deleted file mode 100644
index a846b3dc3..000000000
--- a/Snowplow iOSTests/TestLogger.m	
+++ /dev/null
@@ -1,113 +0,0 @@
-//
-//  TestLogger.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPLogger.h"
-
-@interface MockDiagnosticLogger : NSObject
-@property (nonatomic) void (^callback)(NSString *tag, NSString *message, NSError *error, NSException *exception);
-@end
-
-@implementation MockDiagnosticLogger
-
-- (instancetype)init {
-    if (self = [super init]) {
-        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logDiagnosticError:) name:@"SPTrackerDiagnostic" object:nil];
-    }
-    return self;
-}
-
-- (void)logDiagnosticError:(NSNotification *)notification {
-    NSDictionary *userInfo = [notification userInfo];
-    NSString *tag = [userInfo objectForKey:@"tag"];
-    NSString *message = [userInfo objectForKey:@"message"];
-    NSError *error = [userInfo objectForKey:@"error"];
-    NSException *exception = [userInfo objectForKey:@"exception"];
-    self.callback(tag, message, error, exception);
-}
-
-- (void)dealloc {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-}
-
-@end
-
-@interface TestLogger : XCTestCase
-
-@end
-
-@implementation TestLogger
-
-- (void)setUp {
-    // Put setup code here. This method is called before the invocation of each test method in the class.
-}
-
-- (void)tearDown {
-    // Put teardown code here. This method is called after the invocation of each test method in the class.
-}
-
-- (void)testDiagnosticTracking {
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    MockDiagnosticLogger *diagnostic = [[MockDiagnosticLogger alloc] init];
-    diagnostic.callback = ^(NSString *tag, NSString *message, NSError *error, NSException *exception) {
-        XCTAssertEqualObjects(tag, NSStringFromClass(self.class));
-        NSString *expectedMessage = [NSString stringWithFormat:@"Error test %d %@", 1, @12.3];
-        XCTAssertEqualObjects(message, expectedMessage);
-        [expectation fulfill];
-    };
-
-    SPLogTrack(nil, @"Error test %d %@", 1, @12.3);
-    [self waitForExpectations:@[expectation] timeout:10];
-}
-
-- (void)testDiagnosticTrackingWithError {
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    NSError *raisedError = [NSError errorWithDomain:NSURLErrorDomain code:400 userInfo:nil];
-
-    MockDiagnosticLogger *diagnostic = [[MockDiagnosticLogger alloc] init];
-    diagnostic.callback = ^(NSString *tag, NSString *message, NSError *error, NSException *exception) {
-        XCTAssertEqualObjects(tag, NSStringFromClass(self.class));
-        XCTAssertEqualObjects(message, @"Error test");
-        XCTAssertEqual(error, raisedError);
-        [expectation fulfill];
-    };
-
-    SPLogTrack(raisedError, @"Error test");
-    [self waitForExpectations:@[expectation] timeout:10];
-}
-
-- (void)testDiagnosticTrackingWithException {
-    XCTestExpectation *expectation = [XCTestExpectation new];
-    NSException *raisedException = [NSException exceptionWithName:NSInvalidArgumentException reason:nil userInfo:nil];
-
-    MockDiagnosticLogger *diagnostic = [[MockDiagnosticLogger alloc] init];
-    diagnostic.callback = ^(NSString *tag, NSString *message, NSError *error, NSException *exception) {
-        XCTAssertEqualObjects(tag, NSStringFromClass(self.class));
-        XCTAssertEqualObjects(message, @"Exception test");
-        XCTAssertEqual(exception, raisedException);
-        [expectation fulfill];
-    };
-
-    SPLogTrack(raisedException, @"Exception test");
-    [self waitForExpectations:@[expectation] timeout:10];
-}
-
-@end
diff --git a/Snowplow iOSTests/TestMemoryEventStore.m b/Snowplow iOSTests/TestMemoryEventStore.m
deleted file mode 100644
index 483223831..000000000
--- a/Snowplow iOSTests/TestMemoryEventStore.m	
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-//  TestMemoryEventStore.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPMemoryEventStore.h"
-#import "SPPayload.h"
-
-@interface TestMemoryEventStore : XCTestCase
-@end
-
-@implementation TestMemoryEventStore
-
-- (void)testInit {
-    SPMemoryEventStore * eventStore = [[SPMemoryEventStore alloc] init];
-    XCTAssertNotNil(eventStore);
-}
-
-- (void)testInsertPayload {
-    SPMemoryEventStore * eventStore = [[SPMemoryEventStore alloc] init];
-    [eventStore removeAllEvents];
-    
-    // Build an event
-    SPPayload * payload = [[SPPayload alloc] init];
-    [payload addValueToPayload:@"pv"                 forKey:@"e"];
-    [payload addValueToPayload:@"www.foobar.com"     forKey:@"url"];
-    [payload addValueToPayload:@"Welcome to foobar!" forKey:@"page"];
-    [payload addValueToPayload:@"MEEEE"              forKey:@"refr"];
-    
-    // Insert an event
-    [eventStore addEvent:payload];
-    
-    XCTAssertEqual([eventStore count], 1);
-    NSArray<SPEmitterEvent *> *events = [eventStore emittableEventsWithQueryLimit:1];
-    XCTAssertEqualObjects([events[0].payload getAsDictionary], [payload getAsDictionary]);
-    [eventStore removeEventWithId:0];
-    
-    XCTAssertEqual([eventStore count], 0);
-}
-
-- (void)testInsertManyPayloads {
-    SPMemoryEventStore * eventStore = [[SPMemoryEventStore alloc] init];
-    [eventStore removeAllEvents];
-    
-    // Build an event
-    SPPayload * payload = [[SPPayload alloc] init];
-    [payload addValueToPayload:@"pv"                 forKey:@"e"];
-    [payload addValueToPayload:@"www.foobar.com"     forKey:@"url"];
-    [payload addValueToPayload:@"Welcome to foobar!" forKey:@"page"];
-    [payload addValueToPayload:@"MEEEE"              forKey:@"refr"];
-    
-    for (int i = 0; i < 250; i++) {
-        [eventStore addEvent:payload];
-    }
-    
-    XCTAssertEqual([eventStore count], 250);
-    XCTAssertEqual([eventStore emittableEventsWithQueryLimit:600].count, 250);
-    XCTAssertEqual([eventStore emittableEventsWithQueryLimit:150].count, 150);
-    
-    [eventStore removeAllEvents];
-    XCTAssertEqual([eventStore count], 0);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestNetworkConnection.m b/Snowplow iOSTests/TestNetworkConnection.m
deleted file mode 100644
index 298e172cc..000000000
--- a/Snowplow iOSTests/TestNetworkConnection.m	
+++ /dev/null
@@ -1,225 +0,0 @@
-//
-//  TestNetworkConnection.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import <Nocilla/Nocilla.h>
-
-#import "SPDefaultNetworkConnection.h"
-
-@interface TestNetworkConnection : XCTestCase
-
-@end
-
-@implementation TestNetworkConnection
-
-NSString *const TEST_URL_ENDPOINT = @"acme.test.url.com";
-
-- (void)setUp {
-    [super setUp];
-    if ([[LSNocilla sharedInstance] isStarted]) {
-        [[LSNocilla sharedInstance] stop];
-    }
-    [[LSNocilla sharedInstance] start];
-}
-
-- (void)tearDown {
-    [super tearDown];
-    [[LSNocilla sharedInstance] clearStubs];
-    [[LSNocilla sharedInstance] stop];
-}
-
-- (void)testGetRequestWithSuccess {
-    stubRequest(@"GET", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex).andReturn(200);
-    
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:TEST_URL_ENDPOINT];
-        [builder setHttpMethod:SPHttpMethodGet];
-    }];
-    
-    SPPayload *payload = [SPPayload new];
-    [payload addValueToPayload:@"value" forKey:@"key"];
-    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:1];
-    NSArray<SPRequestResult *> *results = [connection sendRequests:@[request]];
-    
-    // Check successful result
-    SPRequestResult *result = [results objectAtIndex:0];
-    XCTAssertTrue(result.isSuccessful);
-    XCTAssertEqualObjects(@1, result.storeIds[0]);
-}
-
-- (void)testGetRequestWithNoSuccess {
-    stubRequest(@"GET", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex).andReturn(404);
-    
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:TEST_URL_ENDPOINT];
-        [builder setHttpMethod:SPHttpMethodGet];
-    }];
-    
-    SPPayload *payload = [SPPayload new];
-    [payload addValueToPayload:@"value" forKey:@"key"];
-    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:1];
-    NSArray<SPRequestResult *> *results = [connection sendRequests:@[request]];
-    
-    // Check unsuccessful result
-    SPRequestResult *result = [results objectAtIndex:0];
-    XCTAssertFalse(result.isSuccessful);
-    XCTAssertEqualObjects(@1, [result.storeIds objectAtIndex:0]);
-}
-
-- (void)testPostRequestWithSuccess {
-    stubRequest(@"POST", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex).andReturn(200);
-    
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:TEST_URL_ENDPOINT];
-        [builder setHttpMethod:SPHttpMethodPost];
-    }];
-    
-    SPPayload *payload = [SPPayload new];
-    [payload addValueToPayload:@"value" forKey:@"key"];
-    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:1];
-    NSArray<SPRequestResult *> *results = [connection sendRequests:@[request]];
-    
-    // Check successful result
-    SPRequestResult *result = [results objectAtIndex:0];
-    XCTAssertTrue(result.isSuccessful);
-    XCTAssertEqualObjects(@1, [result.storeIds objectAtIndex:0]);
-}
-
-- (void)testPostRequestWithNoSuccess {
-    stubRequest(@"POST", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex).andReturn(404);
-    
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:TEST_URL_ENDPOINT];
-        [builder setHttpMethod:SPHttpMethodPost];
-    }];
-    
-    SPPayload *payload = [SPPayload new];
-    [payload addValueToPayload:@"value" forKey:@"key"];
-    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:1];
-    NSArray<SPRequestResult *> *results = [connection sendRequests:@[request]];
-    
-    // Check unsuccessful result
-    SPRequestResult *result = [results objectAtIndex:0];
-    XCTAssertFalse(result.isSuccessful);
-    XCTAssertEqualObjects(@1, [result.storeIds objectAtIndex:0]);
-}
-
-- (void)testFreeEndpoint_GetHttpsUrl {
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:@"acme.test.url.com"];
-        [builder setHttpMethod:SPHttpMethodPost];
-    }];
-    XCTAssertTrue([connection.url.absoluteString hasPrefix:@"https://acme.test.url.com"]);
-}
-
-- (void)testHttpsEndpoint_GetHttpsUrl {
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:@"https://acme.test.url.com"];
-        [builder setHttpMethod:SPHttpMethodPost];
-    }];
-    XCTAssertTrue([connection.url.absoluteString hasPrefix:@"https://acme.test.url.com"]);
-}
-
-- (void)testHttpEndpoint_GetHttpUrl {
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:@"http://acme.test.url.com"];
-        [builder setHttpMethod:SPHttpMethodPost];
-    }];
-    XCTAssertTrue([connection.url.absoluteString hasPrefix:@"http://acme.test.url.com"]);
-}
-
-- (void)testStripsTrailingSlashInEndpoint {
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:@"http://acme.test.url.com/"];
-        [builder setHttpMethod:SPHttpMethodGet];
-    }];
-    XCTAssertTrue([[[connection url] absoluteString] isEqualToString:@"http://acme.test.url.com/i"]);
-}
-
-- (void)testDoesntAddHeaderWithoutServerAnonymisation {
-    stubRequest(@"POST", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex)
-        .withHeader(@"SP-Anonymous", @"*")
-        .andReturn(500);
-    stubRequest(@"POST", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex)
-        .andReturn(200);
-    
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:TEST_URL_ENDPOINT];
-        [builder setHttpMethod:SPHttpMethodPost];
-        [builder setServerAnonymisation:NO];
-    }];
-    
-    SPPayload *payload = [SPPayload new];
-    [payload addValueToPayload:@"value" forKey:@"key"];
-    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:1];
-    NSArray<SPRequestResult *> *results = [connection sendRequests:@[request]];
-    
-    // Check successful result
-    SPRequestResult *result = [results objectAtIndex:0];
-    XCTAssertTrue(result.isSuccessful);
-    XCTAssertEqualObjects(@1, result.storeIds[0]);
-}
-
-- (void)testAddsHeaderForServerAnonymisationForPostRequest {
-    stubRequest(@"POST", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex)
-        .withHeader(@"SP-Anonymous", @"*")
-        .andReturn(200);
-    
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:TEST_URL_ENDPOINT];
-        [builder setHttpMethod:SPHttpMethodPost];
-        [builder setServerAnonymisation:YES];
-    }];
-    
-    SPPayload *payload = [SPPayload new];
-    [payload addValueToPayload:@"value" forKey:@"key"];
-    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:1];
-    NSArray<SPRequestResult *> *results = [connection sendRequests:@[request]];
-    
-    // Check successful result
-    SPRequestResult *result = [results objectAtIndex:0];
-    XCTAssertTrue(result.isSuccessful);
-    XCTAssertEqualObjects(@1, result.storeIds[0]);
-}
-
-- (void)testAddsHeaderForServerAnonymisationForGetRequest {
-    stubRequest(@"GET", [[NSString alloc] initWithFormat:@"^%@://%@/i?(.*?)", @"https", TEST_URL_ENDPOINT].regex)
-        .withHeader(@"SP-Anonymous", @"*")
-        .andReturn(200);
-    
-    SPDefaultNetworkConnection *connection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        [builder setUrlEndpoint:TEST_URL_ENDPOINT];
-        [builder setHttpMethod:SPHttpMethodGet];
-        [builder setServerAnonymisation:YES];
-    }];
-    
-    SPPayload *payload = [SPPayload new];
-    [payload addValueToPayload:@"value" forKey:@"key"];
-    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:1];
-    NSArray<SPRequestResult *> *results = [connection sendRequests:@[request]];
-    
-    // Check successful result
-    SPRequestResult *result = [results objectAtIndex:0];
-    XCTAssertTrue(result.isSuccessful);
-    XCTAssertEqualObjects(@1, result.storeIds[0]);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestPayload.m b/Snowplow iOSTests/TestPayload.m
deleted file mode 100644
index 6d8a1644e..000000000
--- a/Snowplow iOSTests/TestPayload.m	
+++ /dev/null
@@ -1,307 +0,0 @@
-//
-//  TestPayload.m
-//  SnowplowTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPPayload.h"
-
-@interface TestPayload : XCTestCase
-
-@end
-
-@implementation TestPayload
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testInit {
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-   
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          [[NSDictionary alloc] init],
-                          @"Payload is not initilized to null on init");
-
-}
-
-- (void)testInitWithNSDictionary {
-    NSDictionary *sample_dict = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 @"Value1", @"Key1",
-                                 @"Value2", @"Key2", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:sample_dict];
-
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict,
-                          @"Payload is not initialized with the correct JSON or NSDictionary");
-    XCTAssertTrue([[sample_payload description] isEqualToString:@"{\n    Key1 = Value1;\n    Key2 = Value2;\n}"]);
-}
-
-- (void)testInitWithWrongDictionary {
-    NSDictionary *sample_dict = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 @"Value1", @"Key1",
-                                 @"Value2", @"Key2", nil];
-    NSDictionary *sample_dict2 = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                  @"Value1", @"Key2",
-                                  @"Value2", @"Key1", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:sample_dict];
-    
-    XCTAssertNotEqualObjects(sample_payload.getAsDictionary,
-                             sample_dict2,
-                             @"Payload is not initialized with the correct JSON or NSDictionary");
-}
-
-- (void)testInitWithNullDictionary {
-    NSDictionary *sample_dict = nil;
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:sample_dict];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          [[NSDictionary alloc] init],
-                          @"Payload should be initialized to an empty NSDictionary");
-}
-
-- (void)testAddValueToPayload {
-    NSDictionary *sample_dict = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 @"Value1", @"Key1", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addValueToPayload:@"Value1" forKey:@"Key1"];
-    
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict,
-                          @"Payload should have the correctly added payload");
-}
-
-- (void)testAddValueToPayload2 {
-    NSDictionary *sample_dict = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 @"Value2", @"Key2", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addValueToPayload:@"Value1" forKey:@"Key1"];
-    
-    
-    XCTAssertNotEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict,
-                          @"Payload should not be the same as sample_dict");
-}
-
-- (void)testAddValueToPayload3 {
-    NSDictionary *sample_dict_init = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 @"Value1", @"Key1", nil];
-    NSDictionary *sample_dict_final = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 @"Value1", @"Key1",
-                                 @"Value2", @"Key2", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:sample_dict_init];
-    [sample_payload addValueToPayload:@"Value2" forKey:@"Key2"];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict_final,
-                          @"Payload should have the same data as sample_dict_final");
-}
-
-- (void)testAddNilValueToPayload {
-    SPPayload *payload = [[SPPayload alloc] init];
-    [payload addValueToPayload:nil forKey:@"foo"];
-    XCTAssertEqualObjects(payload.getAsDictionary, [[NSDictionary alloc] init]);
-}
-
-- (void)testAddNilValueToPayloadUnsetsKey {
-    SPPayload *payload = [[SPPayload alloc] initWithNSDictionary:@{@"foo":@"bar"}];
-    [payload addValueToPayload:nil forKey:@"foo"];
-    XCTAssertEqualObjects(payload.getAsDictionary, [[NSDictionary alloc] init]);
-}
-
-- (void)testAddNumericValueToPayload {
-    NSDictionary *sample_dict = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 @100, @"Key1", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addNumericValueToPayload:@100 forKey:@"Key1"];
-    
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict,
-                          @"Payload should have the correctly added payload");
-}
-
-- (void)testAddNilNumericValueToPayload {
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addNumericValueToPayload:nil forKey:@"Key1"];
-    
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          [[NSDictionary alloc] init],
-                          @"Payload should be empty");
-}
-
-- (void)testAddNilNumericValueToPayloadUnsetsKey {
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:@{@"Key1":@100}];
-    [sample_payload addNumericValueToPayload:nil forKey:@"Key1"];
-    
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          [[NSDictionary alloc] init],
-                          @"Payload should be empty");
-}
-
-- (void)testAddDictToPayload {
-    NSDictionary *sample_dic = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"Value1", @"Key1", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addDictionaryToPayload:sample_dic];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dic,
-                          @"Payload should contain the exact same contents added from sample_dic");
-}
-
-- (void)testAddDictToPayload2 {
-    NSDictionary *sample_dic = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"Value1", @"Key1", nil];
-    NSDictionary *sample_dic2 = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"Value2", @"Key2", nil];
-    NSDictionary *sample_dict_final = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                       @"Value1", @"Key1",
-                                       @"Value2", @"Key2", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:sample_dic];
-    [sample_payload addDictionaryToPayload:sample_dic2];
-
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict_final,
-                          @"Payload should contain the exact same contents added from sample_dic_final");
-}
-
-- (void)testAddDictToPayload3 {
-    NSDictionary *sample_dic = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"Value1", @"Key1", nil];
-    NSDictionary *sample_dic2 = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                 [[NSNumber alloc] initWithInt:2], @"Key2", nil];
-    NSDictionary *sample_dict_final = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                       @"Value1", @"Key1", nil];
-    
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:sample_dic];
-    [sample_payload addDictionaryToPayload:sample_dic2];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict_final,
-                          @"Payload should contain the exact same contents added from sample_dic_final");
-}
-
-- (void)testJsonToPayload {
-    // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
-
-    NSDictionary *sample_dic = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"Value1", @"Key1", nil];
-    NSDictionary *sample_enc = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"eyJLZXkxIjoiVmFsdWUxIn0", @"type_enc", nil];
-    
-    // NSDictionary conversion to JSON string
-    NSData *somedata = [NSJSONSerialization dataWithJSONObject:sample_dic options:0 error:0];
-    
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addJsonToPayload:somedata base64Encoded:true
-                     typeWhenEncoded:@"type_enc" typeWhenNotEncoded:@"type_notenc"];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_enc,
-                          @"Payload doesn't match sample_enc, might be a b64 encoding problem.");
-}
-
-- (void)testJsonToPayload2 {
-    // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
-
-    NSDictionary *sample_dic = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"Value1", @"Key1", nil];
-    NSDictionary *sample_enc = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"{\"Key1\":\"Value1\"}", @"type_notenc", nil];
-    
-    // NSDictionary conversion to JSON string
-    NSData *somedata = [NSJSONSerialization dataWithJSONObject:sample_dic options:0 error:0];
-    
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addJsonToPayload:somedata base64Encoded:false
-                     typeWhenEncoded:@"type_enc" typeWhenNotEncoded:@"type_notenc"];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_enc,
-                          @"Payload doesn't match sample_enc, might be a b64 encoding problem.");
-}
-
-- (void)testJsonToPayload3 {
-    NSData *somedata = [[NSData alloc] initWithBase64EncodedString:@"baddata" options:0];
-    
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addJsonToPayload:somedata base64Encoded:false
-                     typeWhenEncoded:@"type_enc" typeWhenNotEncoded:@"type_notenc"];
-    
-    XCTAssertTrue([sample_payload.getAsDictionary count] == 0);
-}
-
-- (void)testJsonStringToPayload {
-    // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
-
-    NSDictionary *sample_enc = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"{\"Key1\":\"Value1\"}", @"type_notenc", nil];
-    NSString *json_str = @"{\"Key1\":\"Value1\"}";
-    
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addJsonStringToPayload:json_str base64Encoded:false
-                           typeWhenEncoded:@"type_enc" typeWhenNotEncoded:@"type_notenc"];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_enc,
-                          @"Payload doesn't match sample_enc, might be a b64 encoding problem.");
-}
-
-- (void)testJsonStringToPayload2 {
-    // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
-
-    NSDictionary *sample_enc = [[NSDictionary alloc] initWithObjectsAndKeys:
-                                @"eyJLZXkxIjoiVmFsdWUxIn0", @"type_enc", nil];
-    NSString *json_str = @"{\"Key1\":\"Value1\"}";
-    
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    [sample_payload addJsonStringToPayload:json_str base64Encoded:true
-                           typeWhenEncoded:@"type_enc" typeWhenNotEncoded:@"type_notenc"];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_enc,
-                          @"Payload doesn't match sample_enc, might be a b64 encoding problem.");
-}
-
-- (void)testgetPayloadAsDictionary {
-    SPPayload *sample_payload = [[SPPayload alloc] init];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          [[NSDictionary alloc] init],
-                          @"Payload should be initialized to an empty dictionary");
-}
-
-- (void)testgetPayloadAsDictionary2 {
-    NSDictionary *sample_dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"Value1", @"Key1", nil];
-    SPPayload *sample_payload = [[SPPayload alloc] initWithNSDictionary:@{@"Key1": @"Value1"}];
-    
-    XCTAssertEqualObjects(sample_payload.getAsDictionary,
-                          sample_dict,
-                          @"Payload should be initialized to an empty dictionary");
-}
-
-@end
diff --git a/Snowplow iOSTests/TestPlatformContext.m b/Snowplow iOSTests/TestPlatformContext.m
deleted file mode 100644
index 122836ab4..000000000
--- a/Snowplow iOSTests/TestPlatformContext.m	
+++ /dev/null
@@ -1,200 +0,0 @@
-//
-//  TestPlatformContext.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPPlatformContext.h"
-#import "SPTrackerConstants.h"
-#import "SPMockDeviceInfoMonitor.h"
-
-@interface TestPlatformContext : XCTestCase
-
-@end
-
-@implementation TestPlatformContext
-
-- (void)testContainsPlatformInfo {
-    SPPlatformContext *context = [[SPPlatformContext alloc] init];
-    NSDictionary *platformDict = [[context fetchPlatformDictWithUserAnonymisation:NO] getAsDictionary];
-    XCTAssertNotNil([platformDict objectForKey:kSPPlatformOsType]);
-    XCTAssertNotNil([platformDict objectForKey:kSPPlatformOsVersion]);
-}
-
-- (void)testContainsMobileInfo {
-#if SNOWPLOW_TARGET_IOS
-    SPPlatformContext *context = [[SPPlatformContext alloc] init];
-    NSDictionary *platformDict = [[context fetchPlatformDictWithUserAnonymisation:NO] getAsDictionary];
-    XCTAssertNotNil([platformDict objectForKey:kSPMobileAvailableStorage]);
-    XCTAssertNotNil([platformDict objectForKey:kSPMobileTotalStorage]);
-#endif
-}
-
-- (void)testAddsAllMockedInfo {
-    SPDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:1 deviceInfoMonitor:deviceInfoMonitor];
-    NSDictionary *platformDict = [[context fetchPlatformDictWithUserAnonymisation:NO] getAsDictionary];
-    XCTAssertTrue([@"appleIdfa" isEqualToString: [platformDict valueForKey:kSPMobileAppleIdfa]]);
-    XCTAssertTrue([@"appleIdfv" isEqualToString: [platformDict valueForKey:kSPMobileAppleIdfv]]);
-    XCTAssertTrue([@"Apple Inc." isEqualToString: [platformDict valueForKey:kSPPlatformDeviceManu]]);
-    XCTAssertTrue([@"deviceModel" isEqualToString: [platformDict valueForKey:kSPPlatformDeviceModel]]);
-    XCTAssertTrue([@"13.0.0" isEqualToString: [platformDict valueForKey:kSPPlatformOsVersion]]);
-    XCTAssertTrue([@"ios" isEqualToString: [platformDict valueForKey:kSPPlatformOsType]]);
-    XCTAssertTrue([@"att" isEqualToString: [platformDict valueForKey:kSPMobileCarrier]]);
-    XCTAssertTrue([@"3g" isEqualToString: [platformDict valueForKey:kSPMobileNetworkTech]]);
-    XCTAssertTrue([@"wifi" isEqualToString: [platformDict valueForKey:kSPMobileNetworkType]]);
-    XCTAssertTrue([@20 isEqualToNumber: [platformDict valueForKey:kSPMobileBatteryLevel]]);
-    XCTAssertTrue([@"charging" isEqualToString: [platformDict valueForKey:kSPMobileBatteryState]]);
-    XCTAssertTrue([@NO isEqualToNumber: [platformDict valueForKey:kSPMobileLowPowerMode]]);
-    XCTAssertTrue([@100000L isEqualToNumber: [platformDict valueForKey:kSPMobilePhysicalMemory]]);
-    XCTAssertTrue([@1000L isEqualToNumber: [platformDict valueForKey:kSPMobileAppAvailableMemory]]);
-    XCTAssertTrue([@9000L isEqualToNumber: [platformDict valueForKey:kSPMobileAvailableStorage]]);
-    XCTAssertTrue([@900000L isEqualToNumber: [platformDict valueForKey:kSPMobileTotalStorage]]);
-}
-
-- (void)testUpdatesMobileInfo {
-#if SNOWPLOW_TARGET_IOS
-    SPMockDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:1 deviceInfoMonitor:deviceInfoMonitor];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"batteryLevel"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appAvailableMemory"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(2, [deviceInfoMonitor accessCount:@"batteryLevel"]);
-    XCTAssertEqual(2, [deviceInfoMonitor accessCount:@"appAvailableMemory"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(3, [deviceInfoMonitor accessCount:@"batteryLevel"]);
-    XCTAssertEqual(3, [deviceInfoMonitor accessCount:@"appAvailableMemory"]);
-#endif
-}
-
-- (void)testDoesntUpdateMobileInfoWithinUpdateWindow {
-#if SNOWPLOW_TARGET_IOS
-    SPMockDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:1000 networkDictUpdateFrequency:1 deviceInfoMonitor:deviceInfoMonitor];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"batteryLevel"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appAvailableMemory"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"batteryLevel"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appAvailableMemory"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"batteryLevel"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appAvailableMemory"]);
-#endif
-}
-
-- (void)testUpdatesNetworkInfo {
-#if SNOWPLOW_TARGET_IOS
-    SPMockDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:1 networkDictUpdateFrequency:0 deviceInfoMonitor:deviceInfoMonitor];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkTechnology"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkType"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(2, [deviceInfoMonitor accessCount:@"networkTechnology"]);
-    XCTAssertEqual(2, [deviceInfoMonitor accessCount:@"networkType"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(3, [deviceInfoMonitor accessCount:@"networkTechnology"]);
-    XCTAssertEqual(3, [deviceInfoMonitor accessCount:@"networkType"]);
-#endif
-}
-
-- (void)testDoesntUpdateNetworkInfoWithinUpdateWindow {
-#if SNOWPLOW_TARGET_IOS
-    SPMockDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:1000 deviceInfoMonitor:deviceInfoMonitor];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkTechnology"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkType"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkTechnology"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkType"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkTechnology"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"networkType"]);
-#endif
-}
-
-- (void)testDoesntUpdateNonEphemeralInfo {
-#if SNOWPLOW_TARGET_IOS
-    SPMockDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:0 deviceInfoMonitor:deviceInfoMonitor];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"physicalMemory"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"totalStorage"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"physicalMemory"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"totalStorage"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"physicalMemory"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"totalStorage"]);
-#endif
-}
-
-- (void)testDoesntUpdateIdfaAndIdfvIfNotNil {
-#if SNOWPLOW_TARGET_IOS
-    SPMockDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:1 deviceInfoMonitor:deviceInfoMonitor];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appleIdfa"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appleIdfv"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appleIdfa"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appleIdfv"]);
-#endif
-}
-
-- (void)testUpdatesIdfaAndIdfvIfNil {
-#if SNOWPLOW_TARGET_IOS
-    SPMockDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    deviceInfoMonitor.customAppleIdfa = nil;
-    deviceInfoMonitor.customAppleIdfv = nil;
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:1 deviceInfoMonitor:deviceInfoMonitor];
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appleIdfa"]);
-    XCTAssertEqual(1, [deviceInfoMonitor accessCount:@"appleIdfv"]);
-    [context fetchPlatformDictWithUserAnonymisation:NO];
-    XCTAssertEqual(2, [deviceInfoMonitor accessCount:@"appleIdfa"]);
-    XCTAssertEqual(2, [deviceInfoMonitor accessCount:@"appleIdfv"]);
-#endif
-}
-
-- (void)testAnonymisesUserIdentifiers {
-#if SNOWPLOW_TARGET_IOS
-    SPDeviceInfoMonitor *deviceInfoMonitor = [[SPMockDeviceInfoMonitor alloc] init];
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:1 deviceInfoMonitor:deviceInfoMonitor];
-    NSDictionary *platformDict = [[context fetchPlatformDictWithUserAnonymisation:YES] getAsDictionary];
-    XCTAssertNil([platformDict valueForKey:kSPMobileAppleIdfa]);
-    XCTAssertNil([platformDict valueForKey:kSPMobileAppleIdfv]);
-#endif
-}
-
-- (void)testPerformanceOfFetchingNetworkDict {
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:1000 networkDictUpdateFrequency:0];
-    [self measureBlock:^{
-        for (int i = 0; i < 100; i++) {
-            [context fetchPlatformDictWithUserAnonymisation:NO];
-        }
-    }];
-}
-
-- (void)testPerformanceOfFetchingMobileDict {
-    SPPlatformContext *context = [[SPPlatformContext alloc] initWithMobileDictUpdateFrequency:0 networkDictUpdateFrequency:1000];
-    [self measureBlock:^{
-        for (int i = 0; i < 10000; i++) {
-            [context fetchPlatformDictWithUserAnonymisation:NO];
-        }
-    }];
-}
-
-@end
diff --git a/Snowplow iOSTests/TestRequest.m b/Snowplow iOSTests/TestRequest.m
deleted file mode 100644
index 8512185a1..000000000
--- a/Snowplow iOSTests/TestRequest.m	
+++ /dev/null
@@ -1,364 +0,0 @@
-//
-//  TestRequest.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTrackerConstants.h"
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPSubject.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "SPRequestCallback.h"
-#import "SPEvent.h"
-#import "SPServiceProvider.h"
-
-// MARK: - Mocks
-
-@interface SPMockStore : NSObject <SPEventStore>
-
-@property (nonatomic) NSMutableDictionary<NSNumber *, SPPayload *> *db;
-@property (nonatomic) long lastInsertedRow;
-
-@end
-
-@implementation SPMockStore
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.db = [NSMutableDictionary new];
-        self.lastInsertedRow = -1;
-    }
-    return self;
-}
-
-- (void)addEvent:(nonnull SPPayload *)payload {
-    @synchronized (self) {
-        self.lastInsertedRow++;
-        [self.db setObject:payload forKey:@(self.lastInsertedRow)];
-    }
-}
-
-- (BOOL)removeEventWithId:(long long)storeId {
-    @synchronized (self) {
-        BOOL exist = [self.db objectForKey:@(storeId)];
-        [self.db removeObjectForKey:@(storeId)];
-        return exist;
-    }
-}
-
-- (BOOL)removeEventsWithIds:(nonnull NSArray<NSNumber *> *)storeIds {
-    BOOL result = YES;
-    for (NSNumber *storeId in storeIds) {
-        result = [self.db objectForKey:storeId];
-        [self.db removeObjectForKey:storeId];
-    }
-    return result;
-}
-
-- (BOOL)removeAllEvents {
-    @synchronized (self) {
-        [self.db removeAllObjects];
-        self.lastInsertedRow = -1;
-    }
-    return YES;
-}
-
-- (NSUInteger)count {
-    @synchronized (self) {
-        return self.db.count;
-    }
-}
-
-- (nonnull NSArray<SPEmitterEvent *> *)emittableEventsWithQueryLimit:(NSUInteger)queryLimit {
-    @synchronized (self) {
-        NSMutableArray<NSNumber *> *eventIds = [NSMutableArray new];
-        NSMutableArray<SPEmitterEvent *> *events = [NSMutableArray new];
-        [self.db enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, SPPayload *obj, BOOL *stop) {
-            SPPayload *payloadCopy = [[SPPayload alloc] initWithNSDictionary:[obj getAsDictionary]];
-            SPEmitterEvent *event = [[SPEmitterEvent alloc] initWithPayload:payloadCopy storeId:key.longLongValue];
-            [events addObject:event];
-            [eventIds addObject:@(event.storeId)];
-        }];
-        if (queryLimit < events.count) {
-            events = [events subarrayWithRange:NSMakeRange(0, queryLimit)].mutableCopy;
-        }
-        return events;
-    }
-}
-
-@end
-
-
-@interface SPMockConnection: NSObject <SPNetworkConnection>
-
-@property NSInteger resultCode;
-@property SPHttpMethod httpMethod;
-@property NSURL *url;
-
-- (instancetype)initWithResultCode:(NSInteger)resultCode method:(SPHttpMethod)httpMethod url:(NSString *)url;
-
-@end
-
-@implementation SPMockConnection
-
-- (instancetype)initWithResultCode:(NSInteger)resultCode method:(SPHttpMethod)httpMethod url:(NSString *)url {
-    if (self = [super init]) {
-        self.resultCode = resultCode;
-        self.httpMethod = httpMethod;
-        self.url = [NSURL URLWithString:url];
-    }
-    return self;
-}
-
-- (NSArray<SPRequestResult *> *)sendRequests:(NSArray<SPRequest *> *)requests {
-    NSMutableArray<SPRequestResult *> *results = [NSMutableArray new];
-    for (SPRequest *request in requests) {
-        SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:self.resultCode oversize:request.oversize storeIds:request.emitterEventIds];
-        [results addObject:result];
-    }
-    return results;
-}
-
-@end
-
-// MARK: - Tests
-
-@interface TestRequest : XCTestCase <SPRequestCallback>
-
-@end
-
-@implementation TestRequest {
-    NSInteger _successCount;
-    NSInteger _failureCount;
-}
-
-- (void)setUp {
-    [super setUp];
-    _successCount = 0;
-    _failureCount = 0;
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-// Tests
-
-- (void)testRequestSendWithPost {
-    SPMockStore *mockStore = [[SPMockStore alloc] init];
-    SPTracker * tracker = [self getTrackerWithRequestType:SPHttpMethodPost resultCode:200 eventStore:mockStore];
-    int sentEventsCount = [self sendAll:tracker];
-    [self forceFlushes:5 emitter:tracker.emitter];
-
-    XCTAssertEqual(_successCount, sentEventsCount);
-    XCTAssertEqual([tracker.emitter getDbCount], 0, @"Error on mockStore db: %@", mockStore.db);
-}
-
-
-- (void)testRequestSendWithGet {
-    SPMockStore *mockStore = [[SPMockStore alloc] init];
-    SPTracker * tracker = [self getTrackerWithRequestType:SPHttpMethodGet resultCode:200 eventStore:mockStore];
-    int sentEventsCount = [self sendAll:tracker];
-    [self forceFlushes:5 emitter:tracker.emitter];
-    XCTAssertEqual(_successCount, sentEventsCount);
-    XCTAssertEqual([tracker.emitter getDbCount], 0, @"Error on mockStore db: %@", mockStore.db);
-}
-
-- (void)testRequestSendWithBadUrl {
-    SPMockConnection *mockConnection = [[SPMockConnection alloc] initWithResultCode:404
-                                                                             method:SPHttpMethodPost
-                                                                                url:@"https://acme.test.url.com/tp2"];
-    SPMockStore *mockStore = [[SPMockStore alloc] init];
-
-    // Send all events with a bad URL
-    SPTracker *tracker = [self getTrackerWithConnection:mockConnection eventStore:mockStore];
-    int sentEventsCount = [self sendAll:tracker];
-    [self forceFlushes:5 emitter:tracker.emitter];
-    XCTAssertGreaterThan(_failureCount, 0);
-    XCTAssertEqual(_successCount, 0);
-    XCTAssertEqual([tracker.emitter getDbCount], sentEventsCount, @"Error on mockStore db: %@", mockStore.db);
-    
-    // Update the URL and flush
-    [tracker pauseEventTracking];
-    [NSThread sleepForTimeInterval:5];
-    mockConnection.resultCode = 200;
-    [tracker resumeEventTracking];
-    
-    [self forceFlushes:5 emitter:tracker.emitter];
-    XCTAssertEqual(_successCount, 7);
-    XCTAssertEqual([tracker.emitter getDbCount], 0, @"Error on mockStore db: %@", mockStore.db);
-}
-
-- (void)testRequestSendWithoutSubject {
-    SPMockStore *mockStore = [[SPMockStore alloc] init];
-    SPTracker * tracker = [self getTrackerWithRequestType:SPHttpMethodGet resultCode:200 eventStore:mockStore];
-    [tracker setSubject:nil];
-    int sentEventsCount = [self sendAll:tracker];
-    [self forceFlushes:5 emitter:tracker.emitter];
-    XCTAssertEqual(_successCount, sentEventsCount);
-    XCTAssertEqual([tracker.emitter getDbCount], 0, @"Error on mockStore db: %@", mockStore.db);
-}
-
-- (void)testRequestSendWithCollectionOff {
-    SPMockStore *mockStore = [[SPMockStore alloc] init];
-    SPTracker * tracker = [self getTrackerWithRequestType:SPHttpMethodPost resultCode:200 eventStore:mockStore];
-    [tracker pauseEventTracking];
-    [self sendAll:tracker];
-    [self forceFlushes:5 emitter:tracker.emitter];
-    XCTAssertEqual(_failureCount, 0);
-    XCTAssertEqual(_successCount, 0);
-    XCTAssertEqual([tracker.emitter getDbCount], 0, @"Error on mockStore db: %@", mockStore.db);
-}
-
-// Helpers
-
-- (SPTracker *)getTrackerWithConnection:(id<SPNetworkConnection>)mockNetworkConnection eventStore:(id<SPEventStore>)mockEventStore {
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithNetworkConnection:mockNetworkConnection];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"anAppId"];
-    trackerConfig.platformContext = YES;
-    trackerConfig.geoLocationContext = YES;
-    trackerConfig.base64Encoding = NO;
-    trackerConfig.sessionContext = YES;
-    SPEmitterConfiguration *emitterConfig = [[SPEmitterConfiguration alloc] init];
-    emitterConfig.requestCallback = self;
-    emitterConfig.eventStore = mockEventStore;
-    SPServiceProvider *serviceProvider = [[SPServiceProvider alloc] initWithNamespace:@"aNamespace" network:networkConfig configurations:@[trackerConfig, emitterConfig]];
-    return serviceProvider.tracker;
-}
-
-- (SPTracker *)getTrackerWithRequestType:(SPHttpMethod)type resultCode:(NSInteger)resultCode eventStore:(id<SPEventStore>)mockEventStore {
-    SPMockConnection *mockConnection = [[SPMockConnection alloc] initWithResultCode:resultCode method:type url:@"https://acme.test.url.com/tp2"];
-    return [self getTrackerWithConnection:mockConnection eventStore:mockEventStore];
-}
-
-- (void)forceFlushes:(NSInteger)count emitter:(SPEmitter *)emitter {
-    [NSThread sleepForTimeInterval:3];
-    for (int i = 0; i < count; i++) {
-        if ([emitter getDbCount] == 0) {
-            break;
-        }
-        [emitter flush];
-        [NSThread sleepForTimeInterval:5];
-    }
-    [NSThread sleepForTimeInterval:3];
-}
-
-// Callback
-
-- (void)onSuccessWithCount:(NSInteger)successCount {
-    _successCount += successCount;
-}
-
-- (void)onFailureWithCount:(NSInteger)failureCount successCount:(NSInteger)successCount {
-    _successCount += successCount;
-    _failureCount += failureCount;
-}
-
-// Pre-Built Events for sending!
-
-- (int)sendAll:(SPTracker *)tracker {
-    return  [self trackStructuredEventWithTracker:tracker]
-    + [self trackUnstructuredEventWithTracker:tracker]
-    + [self trackPageViewWithTracker:tracker]
-    + [self trackScreenViewWithTracker:tracker]
-    + [self trackTimingWithCategoryWithTracker:tracker]
-    + [self trackEcommerceTransactionWithTracker:tracker];
-}
-
-- (int)trackStructuredEventWithTracker:(SPTracker *)tracker_ {
-    SPStructured *event = [[SPStructured alloc] initWithCategory:@"DemoCategory" action:@"DemoAction"];
-    event.label = @"DemoLabel";
-    event.property = @"DemoProperty";
-    event.value = @5;
-    event.contexts = self.customContext;
-    [tracker_ track:event];
-    return 1;
-}
-
-- (int)trackUnstructuredEventWithTracker:(SPTracker *)tracker_ {
-    NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
-    [data setObject:@23 forKey:@"level"];
-    [data setObject:@56473 forKey:@"score"];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0"
-                                                                      andData:data];
-    SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithEventData:sdj];
-    event.contexts = self.customContext;
-    [tracker_ track:event];
-    return 1;
-}
-
-- (int)trackPageViewWithTracker:(SPTracker *)tracker_ {
-    SPPageView *event = [[SPPageView alloc] initWithPageUrl:@"DemoPageUrl"];
-    event.pageTitle = @"DemoPageTitle";
-    event.referrer = @"DemoPageReferrer";
-    event.contexts = self.customContext;
-    [tracker_ track:event];
-    return 1;
-}
-
-- (int)trackScreenViewWithTracker:(SPTracker *)tracker_ {
-    SPScreenView *event = [[SPScreenView alloc] initWithName:@"DemoScreenName" screenId:nil];
-    event.contexts = self.customContext;
-    [tracker_ track:event];
-    return 1;
-}
-
-- (int)trackTimingWithCategoryWithTracker:(SPTracker *)tracker_ {
-    SPTiming *event = [[SPTiming alloc] initWithCategory:@"DemoTimingCategory" variable:@"DemoTimingVariable" timing:@5];
-    event.label = @"DemoTimingLabel";
-    event.contexts = self.customContext;
-    [tracker_ track:event];
-    return 1;
-}
-
-- (int)trackEcommerceTransactionWithTracker:(SPTracker *)tracker_ {
-    NSString *transactionID = @"6a8078be";
-    NSMutableArray *itemArray = [NSMutableArray array];
-    
-    SPEcommerceItem *item = [[SPEcommerceItem alloc] initWithSku:@"DemoItemSku" price:@0.75F quantity:@1];
-    [item name:@"DemoItemName"];
-    [item category:@"DemoItemCategory"];
-    [item currency:@"USD"];
-    [item contexts:self.customContext];
-
-    [itemArray addObject:item];
-    
-    SPEcommerce *event = [[SPEcommerce alloc] initWithOrderId:transactionID totalValue:@350 items:itemArray];
-    [event affiliation:@"DemoTranAffiliation"];
-    [event taxValue:@10];
-    [event shipping:@15];
-    [event city:@"Boston"];
-    [event state:@"Massachusetts"];
-    [event country:@"USA"];
-    [event currency:@"USD"];
-    [event contexts:self.customContext];
-    [tracker_ track:event];
-    return 2;
-}
-
-- (NSMutableArray *)customContext {
-    NSDictionary *data = @{@"snowplow": @"demo-tracker"};
-    SPSelfDescribingJson *context = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:com.acme_company/demo_ios/jsonschema/1-0-0"
-                                                                         andData:data];
-    return [NSMutableArray arrayWithArray:@[context]];
-}
-
-@end
diff --git a/Snowplow iOSTests/TestRequestResponse.m b/Snowplow iOSTests/TestRequestResponse.m
deleted file mode 100644
index e53836bd9..000000000
--- a/Snowplow iOSTests/TestRequestResponse.m	
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-//  TestRequestResult.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2020 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "Snowplow.h"
-#import "SPRequestResult.h"
-
-@interface TestRequestResult : XCTestCase
-
-@end
-
-@implementation TestRequestResult
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testInit {
-    NSMutableArray<NSNumber *> *emitterEventIds = [NSMutableArray new];
-    [emitterEventIds addObject:@1];
-    SPRequestResult *result = [[SPRequestResult alloc] initWithSuccess:YES storeIds:emitterEventIds];
-    
-    XCTAssertNotNil(result);
-    XCTAssertEqual(result.isSuccessful, YES);
-    XCTAssertEqual(result.storeIds, emitterEventIds);
-    
-    result = [[SPRequestResult alloc] init];
-    
-    XCTAssertNotNil(result);
-    XCTAssertEqual(result.isSuccessful, NO);
-    XCTAssertNil(result.storeIds);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestRequestResult.m b/Snowplow iOSTests/TestRequestResult.m
deleted file mode 100644
index 52c57fe1f..000000000
--- a/Snowplow iOSTests/TestRequestResult.m	
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-//  TestRequestResult.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTrackerConstants.h"
-#import "SPRequestResult.h"
-
-@interface TestRequestResult : XCTestCase
-
-@end
-
-@implementation TestRequestResult
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testSuccessfulRequest {
-    NSMutableArray<NSNumber *> *emitterEventIds = [NSMutableArray new];
-    [emitterEventIds addObject:@1];
-    SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:200 oversize:NO storeIds:emitterEventIds];
-
-    XCTAssertNotNil(result);
-    XCTAssertEqual(result.isSuccessful, YES);
-    XCTAssertEqual([result shouldRetry:@{}], NO);
-    XCTAssertEqual(result.storeIds, emitterEventIds);
-}
-
-- (void)testFailedRequest {
-    NSMutableArray<NSNumber *> *emitterEventIds = [NSMutableArray new];
-    [emitterEventIds addObject:@1];
-    SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:500 oversize:NO storeIds:emitterEventIds];
-    XCTAssertEqual(result.isSuccessful, NO);
-    XCTAssertEqual([result shouldRetry:@{}], YES);
-}
-
-- (void)testDefaultResult {
-    SPRequestResult *result = [SPRequestResult new];
-
-    XCTAssertNotNil(result);
-    XCTAssertEqual(result.isSuccessful, NO);
-    XCTAssertEqual(result.storeIds.count, 0);
-}
-
-- (void)testOversizedFailedRequest {
-    SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:500 oversize:YES storeIds:@[]];
-    XCTAssertEqual(result.isSuccessful, NO);
-    XCTAssertEqual([result shouldRetry:@{}], NO);
-}
-
-- (void)testFailedRequestWithNoRetryStatus {
-    SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:403 oversize:NO storeIds:@[]];
-    XCTAssertEqual(result.isSuccessful, NO);
-    XCTAssertEqual([result shouldRetry:@{}], NO);
-}
-
-- (void)testFailedRequestWithCustomNoRetryStatus {
-    NSMutableDictionary *customRetryRules = [[NSMutableDictionary alloc] init];
-    [customRetryRules setObject:@YES forKey:@403];
-    [customRetryRules setObject:@NO forKey:@500];
-    
-    SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:403 oversize:NO storeIds:@[]];
-    XCTAssertEqual([result shouldRetry:customRetryRules], YES);
-
-    result = [[SPRequestResult alloc] initWithStatusCode:500 oversize:NO storeIds:@[]];
-    XCTAssertEqual([result shouldRetry:customRetryRules], NO);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestSQLiteEventStore.m b/Snowplow iOSTests/TestSQLiteEventStore.m
deleted file mode 100644
index 5ce9c33fa..000000000
--- a/Snowplow iOSTests/TestSQLiteEventStore.m	
+++ /dev/null
@@ -1,154 +0,0 @@
-//
-//  TestSQLiteEventStore.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPSQLiteEventStore.h"
-#import "SPPayload.h"
-
-@interface TestSQLiteEventStore : XCTestCase
-@end
-
-@implementation TestSQLiteEventStore
-
-- (void)setUp {
-    [SPSQLiteEventStore removeUnsentEventsExceptForNamespaces:@[]];
-}
-
-- (void)testInit {
-    SPSQLiteEventStore * eventStore = [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace"];
-    XCTAssertNotNil(eventStore);
-}
-
-- (void)testInsertPayload {
-    SPSQLiteEventStore * eventStore = [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace"];
-    [eventStore removeAllEvents];
-    
-    // Build an event
-    SPPayload * payload = [[SPPayload alloc] init];
-    [payload addValueToPayload:@"pv"                 forKey:@"e"];
-    [payload addValueToPayload:@"www.foobar.com"     forKey:@"url"];
-    [payload addValueToPayload:@"Welcome to foobar!" forKey:@"page"];
-    [payload addValueToPayload:@"MEEEE"              forKey:@"refr"];
-    
-    // Insert an event
-    [eventStore insertEvent:payload];
-    
-    XCTAssertEqual([eventStore count], 1);
-    XCTAssertEqualObjects([[eventStore getEventWithId:1].payload getAsDictionary], [payload getAsDictionary]);
-    XCTAssertEqual([eventStore getLastInsertedRowId], 1);
-    [eventStore removeEventWithId:1];
-    
-    XCTAssertEqual([eventStore count], 0);
-}
-
-- (void)testInsertManyPayloads {
-    SPSQLiteEventStore * eventStore = [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace"];
-    [eventStore removeAllEvents];
-    
-    // Build an event
-    SPPayload * payload = [[SPPayload alloc] init];
-    [payload addValueToPayload:@"pv"                 forKey:@"e"];
-    [payload addValueToPayload:@"www.foobar.com"     forKey:@"url"];
-    [payload addValueToPayload:@"Welcome to foobar!" forKey:@"page"];
-    [payload addValueToPayload:@"MEEEE"              forKey:@"refr"];
-    
-    for (int i = 0; i < 250; i++) {
-        [eventStore insertEvent:payload];
-    }
-    
-    XCTAssertEqual([eventStore count], 250);
-    XCTAssertEqual([eventStore getAllEventsLimited:600].count, 250);
-    XCTAssertEqual([eventStore getAllEventsLimited:150].count, 150);
-    XCTAssertEqual([eventStore getAllEvents].count, 250);
-    
-    [eventStore removeAllEvents];
-    XCTAssertEqual([eventStore count], 0);
-}
-
-- (void)testSQLiteEventStoreCreateSQLiteFile {
-    [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace"];
-    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-    NSString *snowplowDirPath = [libraryPath stringByAppendingPathComponent:@"snowplow"];
-    NSString *dbPath = [snowplowDirPath stringByAppendingPathComponent:@"snowplowEvents-aNamespace.sqlite"];
-    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:dbPath]);
-}
-
-- (void)testSQLiteEventStoreRemoveFiles {
-    [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace1"];
-    [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace2"];
-    [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace3"];
-    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-    NSString *snowplowDirPath = [libraryPath stringByAppendingPathComponent:@"snowplow"];
-    [SPSQLiteEventStore removeUnsentEventsExceptForNamespaces:@[@"aNamespace2"]];
-    NSString *dbPath = [snowplowDirPath stringByAppendingPathComponent:@"snowplowEvents-aNamespace1.sqlite"];
-    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:dbPath]);
-    dbPath = [snowplowDirPath stringByAppendingPathComponent:@"snowplowEvents-aNamespace2.sqlite"];
-    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:dbPath]);
-    dbPath = [snowplowDirPath stringByAppendingPathComponent:@"snowplowEvents-aNamespace3.sqlite"];
-    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:dbPath]);
-}
-
-- (void)testSQLiteEventStoreInvalidNamespaceConversion {
-    [[SPSQLiteEventStore alloc] initWithNamespace:@"namespace*.^?1ò2@"];
-    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-    NSString *snowplowDirPath = [libraryPath stringByAppendingPathComponent:@"snowplow"];
-    NSString *dbPath = [snowplowDirPath stringByAppendingPathComponent:@"snowplowEvents-namespace-1-2-.sqlite"];
-    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:dbPath]);
-}
-
-- (void)testMigrationFromLegacyToNamespacedEventStore {
-    SPSQLiteEventStore *eventStore = [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace"];
-    [eventStore addEvent:[[SPPayload alloc] initWithNSDictionary:@{@"key": @"value"}]];
-    XCTAssertEqual(1, [eventStore count]);
-    
-    // Create fake legacy database
-    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-    NSString *snowplowDirPath = [libraryPath stringByAppendingPathComponent:@"snowplow"];
-    NSString *newDbPath = [snowplowDirPath stringByAppendingPathComponent:@"snowplowEvents-aNamespace.sqlite"];
-    NSString *oldDbPath = [libraryPath stringByAppendingPathComponent:@"snowplowEvents.sqlite"];
-    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:oldDbPath]);
-    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:newDbPath]);
-    [[NSFileManager defaultManager] moveItemAtPath:newDbPath toPath:oldDbPath error:nil];
-    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:oldDbPath]);
-    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:newDbPath]);
-    
-    // Migrate database when SQLiteEventStore is launched the first time
-    eventStore = [[SPSQLiteEventStore alloc] initWithNamespace:@"aNewNamespace"];
-    newDbPath = [snowplowDirPath stringByAppendingPathComponent:@"snowplowEvents-aNewNamespace.sqlite"];
-    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:oldDbPath]);
-    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:newDbPath]);
-    XCTAssertEqual(1, [eventStore count]);
-    for (SPEmitterEvent *event in [eventStore getAllEvents]) {
-        XCTAssertEqualObjects(@"value", [[event.payload getAsDictionary] objectForKey:@"key"]);
-    }
-}
-
-- (void)testMultipleAccessToSameSQLiteFile {
-    SPSQLiteEventStore *eventStore1 = [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace"];
-    [eventStore1 addEvent:[[SPPayload alloc] initWithNSDictionary:@{@"key1": @"value1"}]];
-    XCTAssertEqual(1, [eventStore1 count]);
-
-    SPSQLiteEventStore *eventStore2 = [[SPSQLiteEventStore alloc] initWithNamespace:@"aNamespace"];
-    [eventStore2 addEvent:[[SPPayload alloc] initWithNSDictionary:@{@"key2": @"value2"}]];
-    XCTAssertEqual(2, [eventStore2 count]);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestScreenState.m b/Snowplow iOSTests/TestScreenState.m
deleted file mode 100644
index f6706570e..000000000
--- a/Snowplow iOSTests/TestScreenState.m	
+++ /dev/null
@@ -1,145 +0,0 @@
-//
-//  TestScreenState.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPPayload.h"
-#import "SPSubject.h"
-#import "SPTrackerConstants.h"
-#import "SPEvent.h"
-#import "SPScreenState.h"
-#import "SPMockEventStore.h"
-
-@interface TestScreenState : XCTestCase
-
-@end
-
-@implementation TestScreenState
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testInvalidScreenState {
-    SPScreenState * screenState = [[SPScreenState alloc] initWithName:@"name" screenId:@"some id"];
-    
-    // Test builder setting properly
-    XCTAssertEqual([screenState isValid], NO);
-    
-    // ID and name required
-    screenState = [[SPScreenState alloc] initWithName:nil type:@"some type" screenId:@"some id"];
-    
-    // Test builder setting properly
-    XCTAssertEqual([screenState isValid], NO);
-    
-    // ID and name required (screen id generated)
-    screenState = [[SPScreenState alloc] initWithName:@"some name" type:@"some type" screenId:nil];
-    
-    // Test builder setting properly
-    XCTAssertEqual([screenState isValid], YES);
-}
-
-- (void)testValidScreenState {
-    NSString *uuid = [NSUUID UUID].UUIDString;
-    SPScreenState * screenState = [[SPScreenState alloc] initWithName:@"some name" type:@"some type" screenId:uuid];
-    
-    // Test builder
-    XCTAssertEqual([screenState isValid], YES);
-    XCTAssertNotNil([screenState payload]);
-    
-    // ID and name required
-    screenState = [[SPScreenState alloc] initWithName:@"some name" screenId:uuid];
-    
-    // Test builder setting properly
-    XCTAssertEqual([screenState isValid], YES);
-    XCTAssertNotNil([screenState payload]);
-    SPPayload * payload = [screenState payload];
-    NSDictionary * dictionary = [payload getAsDictionary];
-    XCTAssertEqual([dictionary objectForKey:kSPScreenName], @"some name");
-    XCTAssertEqual([dictionary objectForKey:kSPScreenId], uuid);
-}
-
-- (void)testScreenStateMachine {
-    SPMockEventStore *eventStore = [SPMockEventStore new];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setEmitter:[SPEmitter build:^(id<SPEmitterBuilder> builder) {
-            [builder setUrlEndpoint:@"http://snowplow-fake-url.com"];
-            [builder setEventStore:eventStore];
-        }]];
-        [builder setTrackerNamespace:@"namespace"];
-        [builder setBase64Encoded:NO];
-        [builder setScreenContext:YES];
-    }];
-    
-    // Send events
-    [tracker track:[[SPTiming alloc] initWithCategory:@"category" variable:@"variable" timing:@123]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    SPPayload *payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    NSString *entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertNil(entities);
-    
-    NSUUID *uuid = [NSUUID UUID];
-    [tracker track:[[SPScreenView alloc] initWithName:@"screen1" screenId:uuid]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertNotNil(entities);
-    XCTAssertTrue([entities containsString:uuid.UUIDString]);
-
-    [tracker track:[[SPTiming alloc] initWithCategory:@"category" variable:@"variable" timing:@123]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertTrue([entities containsString:uuid.UUIDString]);
-
-    NSUUID *uuid2 = [NSUUID UUID];
-    [tracker track:[[SPScreenView alloc] initWithName:@"screen2" screenId:uuid2]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertTrue([entities containsString:uuid2.UUIDString]);
-    NSString *eventPayload = (NSString *)(payload.getAsDictionary[@"ue_pr"]);
-    XCTAssertTrue([eventPayload containsString:uuid.UUIDString]);
-    XCTAssertTrue([eventPayload containsString:uuid2.UUIDString]);
-
-    [tracker track:[[SPTiming alloc] initWithCategory:@"category" variable:@"variable" timing:@123]];
-    [NSThread sleepForTimeInterval:1];
-    if (eventStore.lastInsertedRow == -1) XCTFail();
-    payload = eventStore.db[@(eventStore.lastInsertedRow)];
-    [eventStore removeAllEvents];
-    entities = (NSString *)(payload.getAsDictionary[@"co"]);
-    XCTAssertTrue([entities containsString:uuid2.UUIDString]);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestSelfDescribingJson.m b/Snowplow iOSTests/TestSelfDescribingJson.m
deleted file mode 100644
index 95cc28e8b..000000000
--- a/Snowplow iOSTests/TestSelfDescribingJson.m	
+++ /dev/null
@@ -1,165 +0,0 @@
-//
-//  TestSelfDescribingJson.m
-//  SnowplowTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-@interface TestSelfDescribingJson : XCTestCase
-
-@end
-
-@implementation TestSelfDescribingJson
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testInitWithBadSchema {
-    SPSelfDescribingJson * sdj;
-    @try {
-        sdj = [[SPSelfDescribingJson alloc] initWithSchema:nil andData:nil];
-    }
-    @catch (NSException *exception) {
-        // formally this function would generate an exception, now only emits a log line
-    }
-    
-    @try {
-        sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"" andData:nil];
-    }
-    @catch (NSException *exception) {
-        // formally this function would generate an exception, now only emits a log line
-    }
-}
-
-- (void)testInitWithObject {
-    NSDictionary * expected = @{
-                                @"schema":@"iglu:acme.com/test_event/jsonschema/1-0-0",
-                                @"data":@{
-                                        @"hello":@"world"
-                                        }
-                                };
-    NSDictionary * data = @{@"hello":@"world"};
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/test_event/jsonschema/1-0-0"
-                                                                      andData:data];
-    XCTAssertEqualObjects(expected, [sdj getAsDictionary]);
-    XCTAssertEqualObjects([expected description], [sdj description]);
-}
-
-- (void)testInitWithSPPayload {
-    NSDictionary * expected = @{
-                                @"schema":@"iglu:acme.com/test_event/jsonschema/1-0-0",
-                                @"data":@{
-                                        @"hello":@"world"
-                                        }
-                                };
-    SPPayload * data = [[SPPayload alloc] init];
-    [data addValueToPayload:@"world" forKey:@"hello"];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/test_event/jsonschema/1-0-0"
-                                                                      andPayload:data];
-    XCTAssertEqualObjects(expected, [sdj getAsDictionary]);
-}
-
-- (void)testInitWithSPSelfDescribingJson {
-    NSDictionary * expected = @{
-                                @"schema":@"iglu:acme.com/test_event/jsonschema/1-0-0",
-                                @"data":@{
-                                        @"schema":@"iglu:acme.com/nested_event/jsonschema/1-0-0",
-                                        @"data":@{
-                                                @"hello":@"world"
-                                                }
-                                        }
-                                };
-    NSDictionary * nestedData = @{@"hello":@"world"};
-    SPSelfDescribingJson * data = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/nested_event/jsonschema/1-0-0"
-                                                                       andData:nestedData];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/test_event/jsonschema/1-0-0"
-                                                        andSelfDescribingJson:data];
-    XCTAssertEqualObjects(expected, [sdj getAsDictionary]);
-}
-
-- (void)testUpdateSchema {
-    NSDictionary * expected = @{
-                                @"schema":@"iglu:acme.com/test_event_2/jsonschema/1-0-0",
-                                @"data":@{
-                                        @"hello":@"world"
-                                        }
-                                };
-    NSDictionary * data = @{@"hello":@"world"};
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/test_event/jsonschema/1-0-0"
-                                                                      andData:data];
-    [sdj setSchema:@"iglu:acme.com/test_event_2/jsonschema/1-0-0"];
-    XCTAssertEqualObjects(expected, [sdj getAsDictionary]);
-}
-
-- (void)testUpdateDataWithObject {
-    NSDictionary * expected = @{
-                                @"schema":@"iglu:acme.com/test_event/jsonschema/1-0-0",
-                                @"data":@{
-                                        @"world":@"hello"
-                                        }
-                                };
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/test_event/jsonschema/1-0-0"
-                                                                      andData:@{@"hello":@"world"}];
-    [sdj setDataWithObject:@{@"world":@"hello"}];
-    XCTAssertEqualObjects(expected, [sdj getAsDictionary]);
-}
-
-- (void)testUpdateDataWithSPPayload {
-    NSDictionary * expected = @{
-                                @"schema":@"iglu:acme.com/test_event/jsonschema/1-0-0",
-                                @"data":@{
-                                        @"world":@"hello"
-                                        }
-                                };
-    SPPayload * data = [[SPPayload alloc] init];
-    [data addValueToPayload:@"hello" forKey:@"world"];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/test_event/jsonschema/1-0-0"
-                                                                      andData:@{@"hello":@"world"}];
-    [sdj setDataWithPayload:data];
-    XCTAssertEqualObjects(expected, [sdj getAsDictionary]);
-}
-
-- (void)testUpdateDataWithSPSelfDescribingJson {
-    NSDictionary * expected = @{
-                                @"schema":@"iglu:acme.com/test_event/jsonschema/1-0-0",
-                                @"data":@{
-                                        @"schema":@"iglu:acme.com/nested_event/jsonschema/1-0-0",
-                                        @"data":@{
-                                                @"hello":@"world"
-                                                }
-                                        }
-                                };
-    NSDictionary * nestedData = @{@"hello":@"world"};
-    SPSelfDescribingJson * data = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/nested_event/jsonschema/1-0-0"
-                                                                       andData:nestedData];
-    SPSelfDescribingJson * sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"iglu:acme.com/test_event/jsonschema/1-0-0"
-                                                                      andData:@{@"hello":@"world"}];
-    [sdj setDataWithSelfDescribingJson:data];
-    XCTAssertEqualObjects(expected, [sdj getAsDictionary]);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestSession.m b/Snowplow iOSTests/TestSession.m
deleted file mode 100644
index 2ff5e892c..000000000
--- a/Snowplow iOSTests/TestSession.m	
+++ /dev/null
@@ -1,529 +0,0 @@
-//
-//  TestSession.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPSession.h"
-#import "SPDataPersistence.h"
-#import "SPTrackerConstants.h"
-#import "SPEmitter.h"
-
-/// Category needed to make the private methods testable.
-@interface SPSession (Testing)
-
-- (void)updateInBackground;
-- (void)updateInForeground;
-
-@end
-
-@interface TestSession : XCTestCase
-
-@end
-
-@implementation TestSession
-
-- (void)setUp {
-    [super setUp];
-    [self cleanSessionFileWithNamespace:@"tracker"];
-    [[NSUserDefaults standardUserDefaults] removeObjectForKey:kSPInstallationUserId];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-
-- (void)testInit {
-    SPSession * session = [[SPSession alloc] initWithForegroundTimeout:600 andBackgroundTimeout:300];
-    XCTAssertNil([session getTracker]);
-    XCTAssertTrue(![session getInBackground]);
-    XCTAssertNotNil([session getSessionDictWithEventId:@"eventid-1" eventTimestamp:1654496481346 userAnonymisation:NO]);
-    XCTAssertTrue(session.state.sessionIndex >= 1);
-    XCTAssertEqual([session getForegroundTimeout], 600000);
-    XCTAssertEqual([session getBackgroundTimeout], 300000);
-}
-
-- (void)testInitWithOptions {
-    SPSession * session = [[SPSession alloc] initWithForegroundTimeout:5 andBackgroundTimeout:300 andTracker:nil];
-    XCTAssertEqual([session getForegroundTimeout], 5000);
-    XCTAssertEqual([session getBackgroundTimeout], 300000);
-    
-    [session setBackgroundTimeout:5];
-    [session setForegroundTimeout:10];
-    
-    XCTAssertEqual([session getForegroundTimeout], 10);
-    XCTAssertEqual([session getBackgroundTimeout], 5);
-}
-
-- (void)testInitInBgThread {
-    __block SPSession * session = nil;
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        session = [[SPSession alloc] initWithForegroundTimeout:1 andBackgroundTimeout:1 andTracker:nil];
-    });
-    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
-    [NSThread sleepForTimeInterval:1];
-    XCTAssertNotNil(session);
-}
-
-- (void)testFirstSession {
-    SPSession *session = [[SPSession alloc] initWithForegroundTimeout:3 andBackgroundTimeout:3 andTracker:nil];
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481346 userAnonymisation:NO];
-    NSInteger sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.346Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-}
-
-- (void)testForegroundEventsOnSameSession {
-    SPSession *session = [[SPSession alloc] initWithForegroundTimeout:3 andBackgroundTimeout:3 andTracker:nil];
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481346 userAnonymisation:NO];
-    NSInteger sessionIndex = session.state.sessionIndex;
-    NSString *sessionId = [sessionContext objectForKey:kSPSessionId];
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.346Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    
-    [NSThread sleepForTimeInterval:1];
-
-    sessionContext = [session getSessionDictWithEventId:@"event_2" eventTimestamp:1654496481347 userAnonymisation:NO];
-    sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.346Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertEqualObjects(sessionId, [sessionContext objectForKey:kSPSessionId]);
-    
-    [NSThread sleepForTimeInterval:1];
-
-    sessionContext = [session getSessionDictWithEventId:@"event_3" eventTimestamp:1654496481348 userAnonymisation:NO];
-    sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.346Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertEqualObjects(sessionId, [sessionContext objectForKey:kSPSessionId]);
-
-    [NSThread sleepForTimeInterval:3.1];
-
-    sessionContext = [session getSessionDictWithEventId:@"event_4" eventTimestamp:1654496481349 userAnonymisation:NO];
-    sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(2, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_4", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.349Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertNotEqualObjects(sessionId, [sessionContext objectForKey:kSPSessionId]);
-}
-
-- (void)testBackgroundEventsOnWhenLifecycleEventsDisabled {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@""];
-    }];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker"];
-        [builder setEmitter:emitter];
-        [builder setLifecycleEvents:NO];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:3];
-        [builder setBackgroundTimeout:2];
-    }];
-    SPSession *session = tracker.session;
-    
-    [session updateInBackground];
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481346 userAnonymisation:NO];
-    NSInteger sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.346Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertFalse([session getInBackground]);
-    XCTAssertEqual(0, [session getBackgroundIndex]);
-}
-
-- (void)testBackgroundEventsOnSameSession {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@""];
-    }];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker"];
-        [builder setEmitter:emitter];
-        [builder setInstallEvent:NO];
-        [builder setLifecycleEvents:YES];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:3];
-        [builder setBackgroundTimeout:2];
-    }];
-    SPSession *session = tracker.session;
-    
-    [session updateInBackground]; // It sends a background event
-
-    NSString *sessionId = session.state.sessionId;
-
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481346 userAnonymisation:NO];
-    NSInteger sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(sessionId, [sessionContext objectForKey:kSPSessionId]);
-    XCTAssertTrue([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-    
-    [NSThread sleepForTimeInterval:1];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_2" eventTimestamp:1654496481347 userAnonymisation:NO];
-    sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(sessionId, [sessionContext objectForKey:kSPSessionId]);
-    XCTAssertTrue([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-    
-    [NSThread sleepForTimeInterval:1];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_3" eventTimestamp:1654496481348 userAnonymisation:NO];
-    sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(1, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(sessionId, [sessionContext objectForKey:kSPSessionId]);
-    XCTAssertTrue([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-    
-    [NSThread sleepForTimeInterval:2.1];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_4" eventTimestamp:1654496481349 userAnonymisation:NO];
-    sessionIndex = session.state.sessionIndex;
-    XCTAssertEqual(2, sessionIndex);
-    XCTAssertEqual(sessionIndex, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_4", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.349Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertNotEqualObjects(sessionId, [sessionContext objectForKey:kSPSessionId]);
-    XCTAssertTrue([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-}
-
-- (void)testMixedEventsOnManySessions {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@""];
-    }];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker"];
-        [builder setEmitter:emitter];
-        [builder setLifecycleEvents:YES];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:1];
-        [builder setBackgroundTimeout:1];
-    }];
-    SPSession *session = tracker.session;
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481351 userAnonymisation:NO];
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.351Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertFalse([session getInBackground]);
-    XCTAssertEqual(0, [session getBackgroundIndex]);
-    XCTAssertEqual(0, [session getForegroundIndex]);
-    NSString *oldSessionId = [sessionContext objectForKey:kSPSessionId];
-    
-    [session updateInBackground];
-    [NSThread sleepForTimeInterval:1.1];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_2" eventTimestamp:1654496481352 userAnonymisation:NO];
-    XCTAssertEqualObjects(oldSessionId, [sessionContext objectForKey:kSPSessionPreviousId]);
-    XCTAssertEqualObjects(@"event_2", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.352Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertTrue([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-    XCTAssertEqual(0, [session getForegroundIndex]);
-    oldSessionId = [sessionContext objectForKey:kSPSessionId];
-
-    [session updateInForeground];
-    [NSThread sleepForTimeInterval:1.1];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_3" eventTimestamp:1654496481353 userAnonymisation:NO];
-    XCTAssertEqualObjects(oldSessionId, [sessionContext objectForKey:kSPSessionPreviousId]);
-    XCTAssertEqualObjects(@"event_3", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.353Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertFalse([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-    XCTAssertEqual(1, [session getForegroundIndex]);
-    oldSessionId = [sessionContext objectForKey:kSPSessionId];
-
-    [session updateInBackground];
-    [NSThread sleepForTimeInterval:1.1];
-
-    sessionContext = [session getSessionDictWithEventId:@"event_4" eventTimestamp:1654496481354 userAnonymisation:NO];
-    XCTAssertEqualObjects(oldSessionId, [sessionContext objectForKey:kSPSessionPreviousId]);
-    XCTAssertEqualObjects(@"event_4", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.354Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    XCTAssertTrue([session getInBackground]);
-    XCTAssertEqual(2, [session getBackgroundIndex]);
-    XCTAssertEqual(1, [session getForegroundIndex]);
-}
-
-- (void)testTimeoutSessionWhenPauseAndResume {
-    SPSession *session = [[SPSession alloc] initWithForegroundTimeout:1 andBackgroundTimeout:1 andTracker:nil];
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481355 userAnonymisation:NO];
-    NSString *prevSessionId = [sessionContext objectForKey:kSPSessionId];
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.355Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    
-    [session stopChecker];
-    [NSThread sleepForTimeInterval:2];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_2" eventTimestamp:1654496481356 userAnonymisation:NO];
-    XCTAssertEqual(1, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(prevSessionId, [sessionContext objectForKey:kSPSessionId]);
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.355Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-    prevSessionId = [sessionContext objectForKey:kSPSessionId];
-    
-    [session startChecker];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_3" eventTimestamp:1654496481357 userAnonymisation:NO];
-    XCTAssertEqual(2, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(prevSessionId, [sessionContext objectForKey:kSPSessionPreviousId]);
-    XCTAssertEqualObjects(@"event_3", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertEqualObjects(@"2022-06-06T06:21:21.357Z", [sessionContext objectForKey:kSPSessionFirstEventTimestamp]);
-}
-
-- (void)testBackgroundTimeBiggerThanBackgroundTimeoutCausesNewSession {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@""];
-    }];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker"];
-        [builder setEmitter:emitter];
-        [builder setLifecycleEvents:YES];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:100];
-        [builder setBackgroundTimeout:2];
-    }];
-    SPSession *session = tracker.session;
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481361 userAnonymisation:NO];
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertFalse([session getInBackground]);
-    XCTAssertEqual(0, [session getBackgroundIndex]);
-    XCTAssertEqual(0, [session getForegroundIndex]);
-    NSString *oldSessionId = [sessionContext objectForKey:kSPSessionId];
-    
-    [NSThread sleepForTimeInterval:1]; // Smaller than background timeout
-    [session updateInBackground]; // Sends a background event
-    [NSThread sleepForTimeInterval:3]; // Bigger than background timeout
-    [session updateInForeground]; // Sends a foreground event
-
-    XCTAssertEqualObjects(oldSessionId, session.state.previousSessionId);
-    XCTAssertEqual(2, session.state.sessionIndex);
-    XCTAssertFalse([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-    XCTAssertEqual(1, [session getForegroundIndex]);
-}
-
-- (void)testBackgroundTimeSmallerThanBackgroundTimeoutDoesntCauseNewSession {
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@""];
-    }];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker"];
-        [builder setEmitter:emitter];
-        [builder setLifecycleEvents:YES];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:100];
-        [builder setBackgroundTimeout:2];
-    }];
-    SPSession *session = tracker.session;
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481358 userAnonymisation:NO];
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    XCTAssertFalse([session getInBackground]);
-    XCTAssertEqual(0, [session getBackgroundIndex]);
-    XCTAssertEqual(0, [session getForegroundIndex]);
-    NSString *oldSessionId = [sessionContext objectForKey:kSPSessionId];
-    
-    [NSThread sleepForTimeInterval:3]; // Bigger than background timeout
-    [session updateInBackground]; // Sends a background event
-    [NSThread sleepForTimeInterval:1]; // Smaller than background timeout
-    [session updateInForeground]; // Sends a foreground event
-
-    XCTAssertEqualObjects(oldSessionId, session.state.sessionId);
-    XCTAssertEqual(1, session.state.sessionIndex);
-    XCTAssertFalse([session getInBackground]);
-    XCTAssertEqual(1, [session getBackgroundIndex]);
-    XCTAssertEqual(1, [session getForegroundIndex]);
-}
-
-- (void)testNoEventsForLongTimeDontIncreaseSessionIndexMultipleTimes {
-    SPSession *session = [[SPSession alloc] initWithForegroundTimeout:1 andBackgroundTimeout:1 andTracker:nil];
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481359 userAnonymisation:NO];
-    XCTAssertEqualObjects(@"event_1", [sessionContext objectForKey:kSPSessionFirstEventId]);
-    
-    [NSThread sleepForTimeInterval:4];
-    
-    sessionContext = [session getSessionDictWithEventId:@"event_2" eventTimestamp:1654496481360 userAnonymisation:NO];
-    XCTAssertEqual(2, [[sessionContext objectForKey:kSPSessionIndex] intValue]);
-    XCTAssertEqualObjects(@"event_2", [sessionContext objectForKey:kSPSessionFirstEventId]);
-}
-
-- (void)testMultipleTrackersUpdateDifferentSessions {
-    [self cleanSessionFileWithNamespace:@"tracker1"];
-    [self cleanSessionFileWithNamespace:@"tracker2"];
-
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@""];
-    }];
-    SPTracker *tracker1 = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker1"];
-        [builder setEmitter:emitter];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:10];
-        [builder setBackgroundTimeout:10];
-    }];
-    SPTracker *tracker2 = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker2"];
-        [builder setEmitter:emitter];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:10];
-        [builder setBackgroundTimeout:10];
-    }];
-    SPEvent *event = [[SPStructured alloc] initWithCategory:@"c" action:@"a"];
-    [tracker1 track:event];
-    [tracker2 track:event];
-
-    NSInteger initialValue1 = tracker1.session.state.sessionIndex;
-    NSString *id1 = tracker1.session.state.sessionId;
-    NSInteger initialValue2 = tracker2.session.state.sessionIndex;
-    NSString *id2 = tracker2.session.state.sessionId;
-
-    // Retrigger session in tracker1
-    [NSThread sleepForTimeInterval:7];
-    [tracker1 track:event];
-    [NSThread sleepForTimeInterval:5];
-
-    // Send event to force update of session on tracker2
-    [tracker2 track:event];
-    id2 = tracker2.session.state.sessionId;
-
-    // Check sessions have the correct state
-    XCTAssertEqual(0, tracker1.session.state.sessionIndex - initialValue1); // retriggered
-    XCTAssertEqual(1, tracker2.session.state.sessionIndex - initialValue2); // timed out
-    
-    //Recreate tracker2
-    SPTracker *tracker2b = [SPTracker build:^(id<SPTrackerBuilder> _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker2"];
-        [builder setEmitter:emitter];
-        [builder setSessionContext:YES];
-        [builder setForegroundTimeout:5];
-        [builder setBackgroundTimeout:5];
-    }];
-    [tracker2b track:event];
-    NSInteger initialValue2b = tracker2b.session.state.sessionIndex;
-    NSString *previousId2b = tracker2b.session.state.previousSessionId;
-
-    // Check the new tracker session gets the data from the old tracker2 session
-    XCTAssertEqual(initialValue2 + 2, initialValue2b);
-    XCTAssertEqualObjects(id2, previousId2b);
-    XCTAssertNotEqualObjects(id1, previousId2b);
-}
-
-- (void)testMigrateSessionFromV3_0 {
-    [self cleanSessionFileWithNamespace:@"tracker"];
-    [self storeSessionAsV3_0WithNamespace:@"tracker" eventId:@"eventId" sessionId:@"sessionId" sessionIndex:123 userId:@"userId"];
-
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        [builder setUrlEndpoint:@""];
-    }];
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder>  _Nonnull builder) {
-        [builder setTrackerNamespace:@"tracker"];
-        [builder setEmitter:emitter];
-        [builder setSessionContext:YES];
-    }];
-    SPEvent *event = [[SPStructured alloc] initWithCategory:@"c" action:@"a"];
-    [tracker track:event];
-
-    SPSessionState *sessionState = tracker.session.state;
-    XCTAssertEqualObjects(@"sessionId", sessionState.previousSessionId);
-    XCTAssertEqual(124, sessionState.sessionIndex);
-    XCTAssertEqualObjects(@"userId", sessionState.userId);
-    XCTAssertNotEqualObjects(@"eventId", sessionState.firstEventId);
-}
-
-- (void)testIncrementsEventIndex {
-    SPSession *session = [[SPSession alloc] initWithForegroundTimeout:3 andBackgroundTimeout:3 andTracker:nil];
-    
-    NSDictionary *sessionContext = [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481346 userAnonymisation:NO];
-    XCTAssertEqualObjects(@1, [sessionContext objectForKey:kSPSessionEventIndex]);
-    
-    [NSThread sleepForTimeInterval:1];
-
-    sessionContext = [session getSessionDictWithEventId:@"event_2" eventTimestamp:1654496481347 userAnonymisation:NO];
-    XCTAssertEqualObjects(@2, [sessionContext objectForKey:kSPSessionEventIndex]);
-    
-    [NSThread sleepForTimeInterval:1];
-
-    sessionContext = [session getSessionDictWithEventId:@"event_3" eventTimestamp:1654496481348 userAnonymisation:NO];
-    XCTAssertEqualObjects(@3, [sessionContext objectForKey:kSPSessionEventIndex]);
-
-    [NSThread sleepForTimeInterval:3.1];
-
-    sessionContext = [session getSessionDictWithEventId:@"event_4" eventTimestamp:1654496481349 userAnonymisation:NO];
-    XCTAssertEqualObjects(@1, [sessionContext objectForKey:kSPSessionEventIndex]);
-}
-
-- (void)testAnonymisesUserIdentifiers {
-    SPSession *session = [[SPSession alloc] initWithForegroundTimeout:3 andBackgroundTimeout:3 andTracker:nil];
-    [session getSessionDictWithEventId:@"event_1" eventTimestamp:1654496481345 userAnonymisation:NO];
-    [session startNewSession]; // create previous session ID reference
-    
-    NSDictionary *withoutAnonymisation = [session getSessionDictWithEventId:@"event_2" eventTimestamp:1654496481346 userAnonymisation:NO];
-    XCTAssertFalse([[withoutAnonymisation objectForKey:kSPSessionUserId] isEqualToString:@"00000000-0000-0000-0000-000000000000"]);
-    XCTAssertNotNil([withoutAnonymisation objectForKey:kSPSessionPreviousId]);
-    
-    NSDictionary *withAnonymisation = [session getSessionDictWithEventId:@"event_3" eventTimestamp:1654496481347 userAnonymisation:YES];
-    XCTAssertTrue([[withAnonymisation objectForKey:kSPSessionUserId] isEqualToString:@"00000000-0000-0000-0000-000000000000"]);
-    XCTAssertEqualObjects([NSNull null], [withAnonymisation objectForKey:kSPSessionPreviousId]);
-}
-
-// Service methods
-
-- (void)cleanSessionFileWithNamespace:(NSString *)namespace {
-    [SPDataPersistence removeDataPersistenceWithNamespace:namespace];
-}
-
-// Migration methods
-
-- (void)storeSessionAsV3_0WithNamespace:(NSString *)namespace eventId:(NSString *)eventId sessionId:(NSString *)sessionId sessionIndex:(int)sessionIndex userId:(NSString *)userId {
-    SPDataPersistence *dataPersistence = [SPDataPersistence dataPersistenceForNamespace:namespace];
-    NSMutableDictionary *newSessionDict = [NSMutableDictionary new];
-    [newSessionDict setObject:eventId forKey:kSPSessionFirstEventId];
-    [newSessionDict setObject:sessionId forKey:kSPSessionId];
-    [newSessionDict setObject:[NSNumber numberWithInt:sessionIndex] forKey:kSPSessionIndex];
-    dataPersistence.session = newSessionDict;
-    
-    //Store userId
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    [userDefaults setObject:userId forKey:kSPInstallationUserId];
-}
-
-@end
diff --git a/Snowplow iOSTests/TestStateManager.m b/Snowplow iOSTests/TestStateManager.m
deleted file mode 100644
index a2a2ce338..000000000
--- a/Snowplow iOSTests/TestStateManager.m	
+++ /dev/null
@@ -1,207 +0,0 @@
-//
-//  TestStateManager.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPStateManager.h"
-#import "SPTrackerEvent.h"
-
-// MARK: - MockState
-
-@interface MockState : NSObject <SPState>
-@property NSInteger value;
-- (instancetype)initWithValue:(NSInteger)value;
-@end
-
-@implementation MockState
-- (instancetype)initWithValue:(NSInteger)value {
-    if (self = [super init]) {
-        self.value = value;
-    }
-    return self;
-}
-@end
-
-// MARK: - MockStateMachine
-
-@interface MockStateMachine : NSObject <SPStateMachineProtocol>
-@end
-
-@implementation MockStateMachine
-
-- (NSArray<NSString *> *)subscribedEventSchemasForTransitions {
-    return @[@"inc",@"dec"];
-}
-
-- (id<SPState>)transitionFromEvent:(SPEvent *)event state:(id<SPState>)currentState {
-    SPSelfDescribingAbstract *e = (SPSelfDescribingAbstract *)event;
-    MockState *state = (MockState *)currentState ?: [[MockState alloc] initWithValue:0];
-    if ([e.schema isEqualToString:@"inc"]) {
-        return [[MockState alloc] initWithValue:state.value+1];
-    } else if ([e.schema isEqualToString:@"dec"]) {
-        return [[MockState alloc] initWithValue:state.value-1];
-    } else {
-        return [[MockState alloc] initWithValue:0];
-    }
-}
-
-- (NSArray<NSString *> *)subscribedEventSchemasForEntitiesGeneration {
-    return @[@"*"];
-}
-
-- (NSArray<SPSelfDescribingJson *> *)entitiesFromEvent:(id<SPInspectableEvent>)event state:(id<SPState>)state {
-    MockState *mockState = (MockState *)state;
-    SPSelfDescribingJson *sdj = [[SPSelfDescribingJson alloc] initWithSchema:@"entity" andDictionary:@{@"value":@(mockState.value)}];
-    return @[sdj];
-}
-
-- (nonnull NSArray<NSString *> *)subscribedEventSchemasForPayloadUpdating {
-    return @[@"event"];
-}
-
-- (nullable NSDictionary<NSString *,NSObject *> *)payloadValuesFromEvent:(nonnull id<SPInspectableEvent>)event state:(nullable id<SPState>)state {
-    return @{@"newParam": @"value"};
-}
-
-@end
-
-@interface MockStateMachine1 : MockStateMachine
-@end
-@implementation MockStateMachine1
-@end
-
-@interface MockStateMachine2 : MockStateMachine
-@end
-@implementation MockStateMachine2
-@end
-
-// MARK: - Test
-
-@interface TestStateManager : XCTestCase
-@end
-
-@implementation TestStateManager
-
-- (void)testStateManager {
-    SPStateManager *stateManager = [SPStateManager new];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier"];
-    
-    SPSelfDescribing *eventInc = [[SPSelfDescribing alloc] initWithSchema:@"inc" payload:@{@"value": @1}];
-    SPSelfDescribing *eventDec = [[SPSelfDescribing alloc] initWithSchema:@"dec" payload:@{@"value": @2}];
-    SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithSchema:@"event" payload:@{@"value": @3}];
-
-    id<SPTrackerStateSnapshot> trackerState = [stateManager trackerStateForProcessedEvent:eventInc];
-    MockState *mockState = (MockState *)[trackerState stateWithIdentifier:@"identifier"];
-    XCTAssertEqual(1, mockState.value);
-    id<SPInspectableEvent> e = [[SPTrackerEvent alloc] initWithEvent:eventInc state:trackerState];
-    NSArray<SPSelfDescribingJson *> *entities = [stateManager entitiesForProcessedEvent:e];
-    XCTAssertEqualObjects(@1, ((NSDictionary<NSString *, NSNumber *> *)(entities[0].data))[@"value"]);
-    XCTAssertTrue([stateManager addPayloadValuesToEvent:e]);
-    XCTAssertNil((e.payload)[@"newParam"]);
-
-    trackerState = [stateManager trackerStateForProcessedEvent:eventInc];
-    XCTAssertEqual(2, [(MockState *)[trackerState stateWithIdentifier:@"identifier"] value]);
-    e = [[SPTrackerEvent alloc] initWithEvent:eventInc state:trackerState];
-    entities = [stateManager entitiesForProcessedEvent:e];
-    XCTAssertEqualObjects(@2, ((NSDictionary<NSString *, NSNumber *> *)(entities[0].data))[@"value"]);
-    XCTAssertTrue([stateManager addPayloadValuesToEvent:e]);
-    XCTAssertNil((e.payload)[@"newParam"]);
-
-    trackerState = [stateManager trackerStateForProcessedEvent:eventDec];
-    XCTAssertEqual(1, [(MockState *)[trackerState stateWithIdentifier:@"identifier"] value]);
-    e = [[SPTrackerEvent alloc] initWithEvent:eventDec state:trackerState];
-    entities = [stateManager entitiesForProcessedEvent:e];
-    XCTAssertEqualObjects(@1, ((NSDictionary<NSString *, NSNumber *> *)(entities[0].data))[@"value"]);
-    XCTAssertTrue([stateManager addPayloadValuesToEvent:e]);
-    XCTAssertNil((e.payload)[@"newParam"]);
-
-    trackerState = [stateManager trackerStateForProcessedEvent:event];
-    XCTAssertEqual(1, [(MockState *)[trackerState stateWithIdentifier:@"identifier"] value]);
-    e = [[SPTrackerEvent alloc] initWithEvent:event state:trackerState];
-    entities = [stateManager entitiesForProcessedEvent:e];
-    XCTAssertEqualObjects(@1, ((NSDictionary<NSString *, NSNumber *> *)(entities[0].data))[@"value"]);
-    XCTAssertTrue([stateManager addPayloadValuesToEvent:e]);
-    XCTAssertEqualObjects(@"value", (e.payload)[@"newParam"]);
-}
-
-- (void)testAddRemoveStateMachine {
-    SPStateManager *stateManager = [SPStateManager new];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier"];
-    [stateManager removeStateMachine:@"identifier"];
-    
-    SPSelfDescribing *eventInc = [[SPSelfDescribing alloc] initWithSchema:@"inc" payload:@{@"value": @1}];
-
-    id<SPTrackerStateSnapshot> trackerState = [stateManager trackerStateForProcessedEvent:eventInc];
-    MockState *mockState = (MockState *)[trackerState stateWithIdentifier:@"identifier"];
-    XCTAssertNil(mockState);
-    id<SPInspectableEvent> e = [[SPTrackerEvent alloc] initWithEvent:eventInc state:trackerState];
-    NSArray<SPSelfDescribingJson *> *entities = [stateManager entitiesForProcessedEvent:e];
-    XCTAssertEqual(0, entities.count);
-}
-
-- (void)testAllowsMultipleStateMachines {
-    SPStateManager *stateManager = [SPStateManager new];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier1"];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier2"];
-    
-    SPSelfDescribing *eventInc = [[SPSelfDescribing alloc] initWithSchema:@"inc" payload:@{@"value": @1}];
-
-    id<SPTrackerStateSnapshot> trackerState = [stateManager trackerStateForProcessedEvent:eventInc];
-    id<SPInspectableEvent> e = [[SPTrackerEvent alloc] initWithEvent:eventInc state:trackerState];
-    NSArray<SPSelfDescribingJson *> *entities = [stateManager entitiesForProcessedEvent:e];
-    XCTAssertEqual(2, entities.count);
-}
-
-- (void)testDoesntDuplicateStateFromStateMachinesWithSameId {
-    SPStateManager *stateManager = [SPStateManager new];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier"];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier"];
-    
-    SPSelfDescribing *eventInc = [[SPSelfDescribing alloc] initWithSchema:@"inc" payload:@{@"value": @1}];
-
-    id<SPTrackerStateSnapshot> trackerState = [stateManager trackerStateForProcessedEvent:eventInc];
-    id<SPInspectableEvent> e = [[SPTrackerEvent alloc] initWithEvent:eventInc state:trackerState];
-    NSArray<SPSelfDescribingJson *> *entities = [stateManager entitiesForProcessedEvent:e];
-    XCTAssertEqual(1, entities.count);
-}
-
-- (void)testReplacingStateMachineDoesntResetTrackerState {
-    SPStateManager *stateManager = [SPStateManager new];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier"];
-    id<SPTrackerStateSnapshot> trackerState1 = [stateManager trackerStateForProcessedEvent:[[SPSelfDescribing alloc] initWithSchema:@"inc" payload:@{@"value": @1}]];
-    XCTAssertEqual(1, [(MockState *)[trackerState1 stateWithIdentifier:@"identifier"] value]);
-    
-    [stateManager addOrReplaceStateMachine:[MockStateMachine new] identifier:@"identifier"];
-    id<SPTrackerStateSnapshot> trackerState2 = [stateManager trackerStateForProcessedEvent:[[SPStructured alloc] initWithCategory:@"category" action:@"action"]];
-    XCTAssertEqual(1, [(MockState *)[trackerState2 stateWithIdentifier:@"identifier"] value]);
-}
-
-- (void)testReplacingStateMachineWithDifferentOneResetsTrackerState {
-    SPStateManager *stateManager = [SPStateManager new];
-    [stateManager addOrReplaceStateMachine:[MockStateMachine1 new] identifier:@"identifier"];
-    id<SPTrackerStateSnapshot> trackerState1 = [stateManager trackerStateForProcessedEvent:[[SPSelfDescribing alloc] initWithSchema:@"inc" payload:@{@"value": @1}]];
-    XCTAssertEqual(1, [(MockState *)[trackerState1 stateWithIdentifier:@"identifier"] value]);
-    
-    [stateManager addOrReplaceStateMachine:[MockStateMachine2 new] identifier:@"identifier"];
-    id<SPTrackerStateSnapshot> trackerState2 = [stateManager trackerStateForProcessedEvent:[[SPStructured alloc] initWithCategory:@"category" action:@"action"]];
-    XCTAssertEqual(0, [(MockState *)[trackerState2 stateWithIdentifier:@"identifier"] value]);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestSubject.m b/Snowplow iOSTests/TestSubject.m
deleted file mode 100644
index 896387966..000000000
--- a/Snowplow iOSTests/TestSubject.m	
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-//  TestSubject.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPTrackerConstants.h"
-#import "SPSubject.h"
-
-@interface TestSubject : XCTestCase
-
-@end
-
-@implementation TestSubject
-
-- (void)testReturnsPlatformContextIfEnabled {
-    SPSubject *subject = [[SPSubject alloc] initWithPlatformContext:YES andGeoContext:NO];
-    SPPayload *platformDict = [subject getPlatformDictWithUserAnonymisation:NO];
-    XCTAssertNotNil(platformDict);
-    XCTAssertNotNil([[platformDict getAsDictionary] objectForKey:kSPPlatformOsType]);
-}
-
-- (void)testDoesntReturnPlatformContextIfDisabled {
-    SPSubject *subject = [[SPSubject alloc] initWithPlatformContext:NO andGeoContext:NO];
-    SPPayload *platformDict = [subject getPlatformDictWithUserAnonymisation:NO];
-    XCTAssertNil(platformDict);
-}
-
-- (void)testReturnsGeolocationContextIfEnabled {
-    SPSubject *subject = [[SPSubject alloc] initWithPlatformContext:NO andGeoContext:YES];
-    [subject setGeoLatitude:10.0];
-    [subject setGeoLongitude:10.0];
-    NSDictionary *geoLocationDict = [subject getGeoLocationDict];
-    XCTAssertNotNil(geoLocationDict);
-    XCTAssertNotNil([geoLocationDict objectForKey:kSPGeoLatitude]);
-}
-
-- (void)testDoesntReturnGeolocationContextIfDisabled {
-    SPSubject *subject = [[SPSubject alloc] initWithPlatformContext:NO andGeoContext:NO];
-    [subject setGeoLatitude:10.0];
-    [subject setGeoLongitude:10.0];
-    NSDictionary *geoLocationDict = [subject getGeoLocationDict];
-    XCTAssertNil(geoLocationDict);
-}
-
-- (void)testAnonymisesUserIdentifiers {
-    SPSubject *subject = [[SPSubject alloc] initWithPlatformContext:NO andGeoContext:NO];
-    [subject setUserId:@"aUserId"];
-    [subject setIpAddress:@"127.0.0.1"];
-    [subject setNetworkUserId:@"aNuid"];
-    [subject setDomainUserId:@"aDuid"];
-    [subject setLanguage:@"EN"];
-
-    NSDictionary *values = [[subject getStandardDictWithUserAnonymisation:YES] getAsDictionary];
-    XCTAssertNil([values valueForKey:kSPUid]);
-    XCTAssertNil([values valueForKey:kSPIpAddress]);
-    XCTAssertNil([values valueForKey:kSPNetworkUid]);
-    XCTAssertNil([values valueForKey:kSPDomainUid]);
-    XCTAssertEqual([values valueForKey:kSPLanguage], @"EN");
-}
-@end
diff --git a/Snowplow iOSTests/TestUtils.m b/Snowplow iOSTests/TestUtils.m
deleted file mode 100644
index 3000ac46c..000000000
--- a/Snowplow iOSTests/TestUtils.m	
+++ /dev/null
@@ -1,161 +0,0 @@
-//
-//  TestUtils.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import <AdSupport/AdSupport.h>
-#import "SPUtilities.h"
-#import "SPTrackerConstants.h"
-
-@interface TestUtils : XCTestCase
-
-@end
-
-@implementation TestUtils
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testGetTimeZone {
-    XCTAssertEqualObjects([SPUtilities getTimezone],
-                          [[NSTimeZone systemTimeZone] name],
-                          @"Incorrect timezone expected");
-}
-
-- (void)testGetLanguage {
-    XCTAssertNotNil([SPUtilities getLanguage]);
-}
-
-- (void)testGetPlatform {
-#if TARGET_OS_IPHONE
-    XCTAssertEqual([SPUtilities getPlatform], SPDevicePlatformMobile);
-#else
-    XCTAssertEqual([SPUtilities getPlatform], SPDevicePlatformDesktop);
-#endif
-}
-
-- (void)testGetResolution {
-    NSString *actualResolution = [SPUtilities getResolution];
-    XCTAssertTrue(actualResolution != nil);
-}
-
-- (void)testGetEventId {
-    NSString *sample_uuid = [SPUtilities getUUIDString];
-
-    // For regex pattern matching to verify if it's of UUID type 4
-    NSString *pattern = @"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}";
-    NSRange searchRange = NSMakeRange(0, [sample_uuid length]);
-    NSError *error = NULL;
-    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
-    NSArray *matches = [regex matchesInString:sample_uuid options:0 range:searchRange];
-    
-    XCTAssertEqual([matches count], (NSUInteger)1,
-                   @"UUID generated doesn't match the type 4 UUID RFC");
-}
-
-- (void)testGetTimestamp {
-    NSString *sample_rand = [NSString stringWithFormat:@"%lld", [[SPUtilities getTimestamp] longLongValue]];
-    
-    // For regex pattern matching to verify if it's of UUID type 4
-    NSString *pattern = @"[0-9]+";
-    NSRange searchRange = NSMakeRange(0, [sample_rand length]);
-    NSError *error = NULL;
-    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
-    NSArray *matches = [regex matchesInString:sample_rand options:0 range:searchRange];
-    
-    XCTAssertEqual([matches count], (NSUInteger)1,
-                   @"Timestamp generated doesn't match the correct format 1234.567");
-
-}
-
-- (void) testTimestampToISOString {
-    XCTAssertEqualObjects([SPUtilities timestampToISOString:1654496481347], @"2022-06-06T06:21:21.347Z");
-    XCTAssertEqualObjects([SPUtilities timestampToISOString:1654498990916], @"2022-06-06T07:03:10.916Z");
-}
-
-- (void)testAppId {
-    // This is always NULL in a test environment
-    NSLog(@"appId: %@", [SPUtilities getAppId]);
-}
-
-- (void)testUrlEncodingString {
-    XCTAssertEqualObjects([SPUtilities urlEncodeString:@""], @"");
-    XCTAssertEqualObjects([SPUtilities urlEncodeString:nil], @"");
-    XCTAssertEqualObjects([SPUtilities urlEncodeString:@"a"], @"a");
-    XCTAssertEqualObjects([SPUtilities urlEncodeString:@"a b"], @"a%20b");
-    XCTAssertEqualObjects([SPUtilities urlEncodeString:@"a=&"], @"a%3D%26");
-}
-
-- (void)testUrlEncodingDictionary {
-    XCTAssertEqualObjects([SPUtilities urlEncodeDictionary:nil], @"");
-    XCTAssertEqualObjects([SPUtilities urlEncodeDictionary:@{@"a": @"b"}], @"a=b");
-    
-    id twoKeys = @{@"a" : @"b", @"c" : @"d" };
-    XCTAssertEqualObjects([SPUtilities urlEncodeDictionary:twoKeys], @"a=b&c=d");
-    
-    id intValues = @{@"a" : @(-5), @"c" : @(3) };
-    XCTAssertEqualObjects([SPUtilities urlEncodeDictionary:intValues], @"a=-5&c=3");
-    
-    id boolValues = @{@"a" : @(NO), @"c" : @(YES) };
-    XCTAssertEqualObjects([SPUtilities urlEncodeDictionary:boolValues], @"a=0&c=1");
-
-    id encodedValues = @{@"a" : @" ", @"c" : @"=" };
-    XCTAssertEqualObjects([SPUtilities urlEncodeDictionary:encodedValues], @"a=%20&c=%3D");
-}
-
-- (void)testEscapedQueryString {
-    id testValues = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-                     [NSNull null], @"null_value_key",
-                     @"Not null", @"string_value_key",
-                     @"|!\" £$%&/()=?^짰ç*éùàò+{}◊∞DZ¿≈ ⁄›‰¢’”»ıè¶#@][ˆ¡≠`´÷‹~~¥‘“«`", @"characters_key",
-                     nil];
-    NSString *now = [SPUtilities urlEncodeDictionary:testValues];
-    NSString *then = @"string_value_key=Not%20null&characters_key=%7C%21%22%20%C2%A3%24%25%26%2F%28%29%3D%3F%5E%C3%AC%C2%A7%C2%B0%C3%A7%2A%C3%A9%C3%B9%C3%A0%C3%B2%2B%7B%7D%E2%97%8A%E2%88%9E%C3%87%C2%B1%C2%BF%E2%89%88%20%EF%A3%BF%E2%81%84%E2%80%BA%E2%80%B0%C2%A2%E2%80%99%E2%80%9D%C2%BB%C4%B1%C3%A8%C2%B6%23%40%5D%5B%CB%86%C2%A1%E2%89%A0%60%C2%B4%C3%B7%E2%80%B9~~%C2%A5%E2%80%98%E2%80%9C%C2%AB%60&null_value_key=%3Cnull%3E";
-    XCTAssertEqualObjects(now, then);
-    NSString *urlString = [NSString stringWithFormat:@"%@?%@", @"http://www.snowplow.com", now];
-    NSURL *url = [NSURL URLWithString:urlString];
-    XCTAssertNotNil(url);
-}
-
-- (void)testCheckArgument {
-    @try {
-        [SPUtilities checkArgument:NO withMessage:@"This will throw an exception."];
-    }
-    @catch (NSException *exception) {
-        XCTAssertEqualObjects(NSInvalidArgumentException, exception.name);
-        XCTAssertEqualObjects(@"This will throw an exception.", exception.reason);
-    }
-}
-
-- (void)testDictionaryNullRemover {
-    NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-                                  [NSNull null], @"a_null_value",
-                                  @"Not null!", @"a_string_value", nil];
-    XCTAssertEqual(dict.count, 2);
-    NSDictionary * result = [SPUtilities removeNullValuesFromDictWithDict:dict];
-    XCTAssertEqual(result.count, 1);
-}
-
-@end
diff --git a/Snowplow iOSTests/TestWebViewMessageHandler.m b/Snowplow iOSTests/TestWebViewMessageHandler.m
deleted file mode 100644
index 3b18ec4c0..000000000
--- a/Snowplow iOSTests/TestWebViewMessageHandler.m	
+++ /dev/null
@@ -1,143 +0,0 @@
-//
-//  TestWebViewMessageHandler.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-
-#import "SPSnowplow.h"
-#import "SPWebViewMessageHandler.h"
-#import "Utils/SPMockNetworkConnection.h"
-#import "Utils/SPMockWKScriptMessage.h"
-
-@interface TestWebViewMessageHandler : XCTestCase
-
-@property (nonatomic) SPWebViewMessageHandler *webViewMessageHandler;
-@property (nonatomic) SPMockNetworkConnection *networkConnection;
-
-@end
-
-@implementation TestWebViewMessageHandler
-
-- (void)setUp {
-    self.webViewMessageHandler = [[SPWebViewMessageHandler alloc] init];
-    self.networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:200];
-    
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithNetworkConnection:self.networkConnection];
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration alloc] init];
-    [trackerConfig base64Encoding:NO];
-    [trackerConfig sessionContext:NO];
-    [trackerConfig platformContext:NO];
-
-    [SPSnowplow removeAllTrackers];
-    [SPSnowplow createTrackerWithNamespace:[[NSUUID UUID] UUIDString] network:networkConfig configurations:@[trackerConfig]];
-}
-
-- (void)tearDown {
-    [SPSnowplow removeAllTrackers];
-}
-
-- (void)testTracksStructuredEventWithAllProperties {
-    SPMockWKScriptMessage *message = [[SPMockWKScriptMessage alloc] initWithBody:@{
-        @"command": @"trackStructEvent",
-        @"event": @{
-            @"category": @"cat",
-            @"action": @"act",
-            @"label": @"lbl",
-            @"property": @"prop",
-            @"value": @10.0,
-        }
-    }];
-    [self.webViewMessageHandler userContentController:nil didReceiveScriptMessage:message];
-    
-    for (int i = 0; i < 10 && [self.networkConnection sendingCount] == 0; i++) {
-        [NSThread sleepForTimeInterval:0.5];
-    }
-    
-    XCTAssertEqual(1, [self.networkConnection sendingCount]);
-    XCTAssertEqual(1, [[self.networkConnection.previousRequests objectAtIndex:0] count]);
-    SPRequest *request = [[self.networkConnection.previousRequests objectAtIndex:0] objectAtIndex:0];
-    NSDictionary *payload = [(NSArray *)[[[request payload] getAsDictionary] objectForKey:@"data"] objectAtIndex:0];
-    XCTAssert([[payload objectForKey:@"se_ca"] isEqualToString:@"cat"]);
-    XCTAssert([[payload objectForKey:@"se_ac"] isEqualToString:@"act"]);
-    XCTAssert([[payload objectForKey:@"se_pr"] isEqualToString:@"prop"]);
-    XCTAssert([[payload objectForKey:@"se_la"] isEqualToString:@"lbl"]);
-    XCTAssert([[payload objectForKey:@"se_va"] isEqualToString:@"10"]);
-}
-
-- (void)testTracksEventWithCorrectTracker {
-    // create the second tracker
-    SPMockNetworkConnection *networkConnection2 = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:200];
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithNetworkConnection:networkConnection2];
-    [SPSnowplow createTrackerWithNamespace:@"ns2" network:networkConfig configurations:@[]];
-
-    // track an event using the second tracker
-    SPMockWKScriptMessage *message = [[SPMockWKScriptMessage alloc] initWithBody:@{
-        @"command": @"trackPageView",
-        @"event": @{
-            @"url": @"http://localhost"
-        },
-        @"trackers": @[@"ns2"]
-    }];
-    [self.webViewMessageHandler userContentController:nil didReceiveScriptMessage:message];
-
-    // wait and check for the event
-    for (int i = 0; i < 10 && [networkConnection2 sendingCount] == 0; i++) {
-        [NSThread sleepForTimeInterval:0.5];
-    }
-
-    XCTAssertEqual(0, [self.networkConnection sendingCount]);
-    XCTAssertEqual(1, [networkConnection2 sendingCount]);
-    XCTAssertEqual(1, [[[networkConnection2 previousRequests] objectAtIndex:0] count]);
-}
-
-- (void)testTracksEventWithContext {
-    SPMockWKScriptMessage *message = [[SPMockWKScriptMessage alloc] initWithBody:@{
-        @"command": @"trackSelfDescribingEvent",
-        @"event": @{
-            @"schema": @"http://schema.com",
-            @"data": @{
-                @"key": @"val"
-            }
-        },
-        @"context": @[
-            @{
-                @"schema": @"http://context-schema.com",
-                @"data": @{
-                    @"a": @"b"
-                }
-            }
-        ]
-    }];
-    [self.webViewMessageHandler userContentController:nil didReceiveScriptMessage:message];
-
-    for (int i = 0; i < 10 && [self.networkConnection sendingCount] == 0; i++) {
-        [NSThread sleepForTimeInterval:0.5];
-    }
-
-    XCTAssertEqual(1, [self.networkConnection sendingCount]);
-    XCTAssertEqual(1, [[self.networkConnection.previousRequests objectAtIndex:0] count]);
-    SPRequest *request = [[self.networkConnection.previousRequests objectAtIndex:0] objectAtIndex:0];
-    NSDictionary *payload = [(NSArray *)[[[request payload] getAsDictionary] objectForKey:@"data"] objectAtIndex:0];
-
-    NSString *context = [payload objectForKey:@"co"];
-    XCTAssert([context containsString:@"{\"a\":\"b\"}"]);
-}
-
-@end
diff --git a/Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.h b/Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.h
deleted file mode 100644
index 9f0ee3dc3..000000000
--- a/Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.h	
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  SPMockDeviceInfoMonitor.h
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import "SPDeviceInfoMonitor.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPMockDeviceInfoMonitor : SPDeviceInfoMonitor
-
-- (instancetype) init;
-- (int) accessCount:(NSString *) method;
-
-@property (strong, nonatomic) NSDictionary<NSString *, NSNumber *> *methodAccessCounts;
-@property (strong, nonatomic, nullable) NSString *customAppleIdfa;
-@property (strong, nonatomic, nullable) NSString *customAppleIdfv;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.m b/Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.m
deleted file mode 100644
index b8db81d70..000000000
--- a/Snowplow iOSTests/Utils/SPMockDeviceInfoMonitor.m	
+++ /dev/null
@@ -1,124 +0,0 @@
-//
-//  SPMockDeviceInfoMonitor.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import "SPMockDeviceInfoMonitor.h"
-
-@implementation SPMockDeviceInfoMonitor
-
-- (instancetype) init {
-    if (self = [super init]) {
-        self.methodAccessCounts = [[NSMutableDictionary alloc] init];
-        self.customAppleIdfa = @"appleIdfa";
-        self.customAppleIdfv = @"appleIdfv";
-    }
-    return self;
-}
-
-- (NSString *) appleIdfa {
-    [self increaseMethodAccessCount:@"appleIdfa"];
-    return self.customAppleIdfa;
-}
-
-- (NSString *) appleIdfv {
-    [self increaseMethodAccessCount:@"appleIdfv"];
-    return self.customAppleIdfv;
-}
-
-- (NSString *) deviceVendor {
-    [self increaseMethodAccessCount:@"deviceVendor"];
-    return @"Apple Inc.";
-}
-
-- (NSString *) deviceModel {
-    [self increaseMethodAccessCount:@"deviceModel"];
-    return @"deviceModel";
-}
-
-- (NSString *) osVersion {
-    [self increaseMethodAccessCount:@"osVersion"];
-    return @"13.0.0";
-}
-
-- (NSString *) osType {
-    [self increaseMethodAccessCount:@"osType"];
-    return @"ios";
-}
-
-- (NSString *) carrierName {
-    [self increaseMethodAccessCount:@"carrierName"];
-    return @"att";
-}
-
-- (NSString *) networkTechnology {
-    [self increaseMethodAccessCount:@"networkTechnology"];
-    return @"3g";
-}
-
-- (NSString *) networkType {
-    [self increaseMethodAccessCount:@"networkType"];
-    return @"wifi";
-}
-
-- (NSNumber *) batteryLevel {
-    [self increaseMethodAccessCount:@"batteryLevel"];
-    return @20;
-}
-
-- (NSString *) batteryState {
-    [self increaseMethodAccessCount:@"batteryState"];
-    return @"charging";
-}
-
-- (NSNumber *) isLowPowerModeEnabled {
-    [self increaseMethodAccessCount:@"isLowPowerModeEnabled"];
-    return @NO;
-}
-
-- (NSNumber *) physicalMemory {
-    [self increaseMethodAccessCount:@"physicalMemory"];
-    return @100000L;
-}
-
-- (NSNumber *) appAvailableMemory {
-    [self increaseMethodAccessCount:@"appAvailableMemory"];
-    return @1000L;
-}
-
-- (NSNumber *) availableStorage {
-    [self increaseMethodAccessCount:@"availableStorage"];
-    return @9000L;
-}
-
-- (NSNumber *) totalStorage {
-    [self increaseMethodAccessCount:@"totalStorage"];
-    return @900000L;
-}
-
-- (int) accessCount:(NSString *) method {
-    NSNumber *count = [self.methodAccessCounts valueForKey:method] ?: @0;
-    return [count intValue];
-}
-
-- (void) increaseMethodAccessCount:(NSString *) method {
-    [self.methodAccessCounts setValue:[NSNumber numberWithInt:[self accessCount:method] + 1] forKey:method];
-}
-
-@end
diff --git a/Snowplow iOSTests/Utils/SPMockEventStore.m b/Snowplow iOSTests/Utils/SPMockEventStore.m
deleted file mode 100644
index 38a82af95..000000000
--- a/Snowplow iOSTests/Utils/SPMockEventStore.m	
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-//  SPMockEventStore.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPMockEventStore.h"
-#import "SPLogger.h"
-
-@implementation SPMockEventStore
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.db = [NSMutableDictionary new];
-        self.lastInsertedRow = -1;
-    }
-    return self;
-}
-
-- (void)addEvent:(nonnull SPPayload *)payload {
-    @synchronized (self) {
-        self.lastInsertedRow++;
-        SPLogVerbose(@"Add %@", payload);
-        [self.db setObject:payload forKey:@(self.lastInsertedRow)];
-    }
-}
-
-- (BOOL)removeEventWithId:(long long)storeId {
-    @synchronized (self) {
-        SPLogVerbose(@"Remove %lld", storeId);
-        BOOL exist = [self.db objectForKey:@(storeId)];
-        [self.db removeObjectForKey:@(storeId)];
-        return exist;
-    }
-}
-
-- (BOOL)removeEventsWithIds:(nonnull NSArray<NSNumber *> *)storeIds {
-    BOOL result = YES;
-    for (NSNumber *storeId in storeIds) {
-        result = [self.db objectForKey:storeId];
-        [self.db removeObjectForKey:storeId];
-    }
-    return result;
-}
-
-- (BOOL)removeAllEvents {
-    @synchronized (self) {
-        [self.db removeAllObjects];
-        self.lastInsertedRow = -1;
-    }
-    return YES;
-}
-
-- (NSUInteger)count {
-    @synchronized (self) {
-        return self.db.count;
-    }
-}
-
-- (nonnull NSArray<SPEmitterEvent *> *)emittableEventsWithQueryLimit:(NSUInteger)queryLimit {
-    @synchronized (self) {
-        NSMutableArray<NSNumber *> *eventIds = [NSMutableArray new];
-        NSMutableArray<SPEmitterEvent *> *events = [NSMutableArray new];
-        [self.db enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, SPPayload *obj, BOOL *stop) {
-            SPPayload *payloadCopy = [[SPPayload alloc] initWithNSDictionary:[obj getAsDictionary]];
-            SPEmitterEvent *event = [[SPEmitterEvent alloc] initWithPayload:payloadCopy storeId:key.longLongValue];
-            [events addObject:event];
-            [eventIds addObject:@(event.storeId)];
-        }];
-        if (queryLimit < events.count) {
-            events = [events subarrayWithRange:NSMakeRange(0, queryLimit)].mutableCopy;
-        }
-        SPLogVerbose(@"emittableEventsWithQueryLimit: %@", eventIds);
-        return events;
-    }
-}
-
-@end
-
diff --git a/Snowplow iOSTests/Utils/SPMockLoggerDelegate.m b/Snowplow iOSTests/Utils/SPMockLoggerDelegate.m
deleted file mode 100644
index 9886d5970..000000000
--- a/Snowplow iOSTests/Utils/SPMockLoggerDelegate.m	
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-//  SPMockLoggerDelegate.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import "SPMockLoggerDelegate.h"
-
-@implementation SPMockLoggerDelegate
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.errorLogs = [NSMutableArray new];
-        self.debugLogs = [NSMutableArray new];
-        self.verboseLogs = [NSMutableArray new];
-    }
-    return self;
-}
-
-- (void)debug:(nonnull NSString *)tag message:(nonnull NSString *)message {
-    [self.debugLogs addObject:message];
-}
-
-- (void)error:(nonnull NSString *)tag message:(nonnull NSString *)message {
-    [self.errorLogs addObject:message];
-}
-
-- (void)verbose:(nonnull NSString *)tag message:(nonnull NSString *)message {
-    [self.verboseLogs addObject:message];
-}
-
-@end
diff --git a/Snowplow iOSTests/Utils/SPMockNetworkConnection.h b/Snowplow iOSTests/Utils/SPMockNetworkConnection.h
deleted file mode 100644
index 96e49a57b..000000000
--- a/Snowplow iOSTests/Utils/SPMockNetworkConnection.h	
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-//  SPMockNetworkConnection.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPEmitter.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPMockNetworkConnection : NSObject <SPNetworkConnection>
-
-- (instancetype)initWithRequestOption:(SPHttpMethod)httpMethod statusCode:(NSInteger)statusCode;
-
-@property (nonatomic) NSInteger statusCode;
-@property (nonatomic) SPHttpMethod httpMethod;
-@property (nonatomic) NSMutableArray<NSMutableArray<SPRequestResult *> *> *previousResults;
-@property (nonatomic) NSMutableArray<NSArray<SPRequest *> *> *previousRequests;
-@property (nonatomic) NSUInteger sendingCount;
-
-@end
-
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow iOSTests/Utils/SPMockNetworkConnection.m b/Snowplow iOSTests/Utils/SPMockNetworkConnection.m
deleted file mode 100644
index 433f77ccf..000000000
--- a/Snowplow iOSTests/Utils/SPMockNetworkConnection.m	
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-//  SPMockNetworkConnection.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import "SPMockNetworkConnection.h"
-#import "SPLogger.h"
-
-@implementation SPMockNetworkConnection
-
-- initWithRequestOption:(SPHttpMethod)httpMethod statusCode:(NSInteger)statusCode {
-    if (self = [super init]) {
-        self.httpMethod = httpMethod;
-        self.statusCode = statusCode;
-        self.previousResults = [NSMutableArray new];
-        self.previousRequests = [NSMutableArray new];
-    }
-    return self;
-}
-
-- (nonnull NSArray<SPRequestResult *> *)sendRequests:(nonnull NSArray<SPRequest *> *)requests {
-    NSMutableArray<SPRequestResult *> *requestResults = [NSMutableArray new];
-    for (SPRequest *request in requests) {
-        SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:_statusCode oversize:request.oversize storeIds:request.emitterEventIds];
-        SPLogVerbose(@"Sent %@ with success %@", request.emitterEventIds, [result isSuccessful] ? @"YES" : @"NO");
-        [requestResults addObject:result];
-    }
-    [self.previousRequests addObject:requests];
-    [self.previousResults addObject:requestResults];
-    return requestResults;
-}
-
-- (SPHttpMethod)httpMethod {
-    return _httpMethod;
-}
-
-- (nonnull NSURL *)url {
-    return [NSURL URLWithString:@"http://fake-url.com"];
-}
-
-- (NSUInteger)sendingCount {
-    return self.previousResults.count;
-}
-
-@end
diff --git a/Snowplow macOSTests/Info.plist b/Snowplow macOSTests/Info.plist
deleted file mode 100644
index 6c40a6cd0..000000000
--- a/Snowplow macOSTests/Info.plist	
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>BNDL</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleVersion</key>
-	<string>1</string>
-</dict>
-</plist>
diff --git a/Snowplow macOSTests/Snowplow_macOSTests.m b/Snowplow macOSTests/Snowplow_macOSTests.m
deleted file mode 100644
index 3172f9299..000000000
--- a/Snowplow macOSTests/Snowplow_macOSTests.m	
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  Snowplow_macOSTests.m
-//  Snowplow macOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-
-@interface Snowplow_macOSTests : XCTestCase
-
-@end
-
-@implementation Snowplow_macOSTests
-
-- (void)setUp {
-    // Put setup code here. This method is called before the invocation of each test method in the class.
-}
-
-- (void)tearDown {
-    // Put teardown code here. This method is called after the invocation of each test method in the class.
-}
-
-- (void)testExample {
-    // This is an example of a functional test case.
-    // Use XCTAssert and related functions to verify your tests produce the correct results.
-    XCTAssert(YES, @"Pass");
-}
-
-- (void)testPerformanceExample {
-    // This is an example of a performance test case.
-    [self measureBlock:^{
-        // Put the code you want to measure the time of here.
-    }];
-}
-
-@end
diff --git a/Snowplow.xcodeproj/project.pbxproj b/Snowplow.xcodeproj/project.pbxproj
index fc9fa7c0b..30a259d4e 100644
--- a/Snowplow.xcodeproj/project.pbxproj
+++ b/Snowplow.xcodeproj/project.pbxproj
@@ -13,878 +13,522 @@
 		23DFEC802362FAD500BD19C4 /* FMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DAC0C21CC3EEA0065F874 /* FMDB.framework */; };
 		23DFEC822362FAED00BD19C4 /* SnowplowIgluClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75CAC3B921F28BD600271FB3 /* SnowplowIgluClient.framework */; };
 		23DFEC832362FAF800BD19C4 /* VVJSONSchemaValidation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75CAC3C021F2930100271FB3 /* VVJSONSchemaValidation.framework */; };
-		6B07CDAD287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B07CDAB287721C600E510D6 /* SPWebViewMessageHandler.h */; };
-		6B07CDAE287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B07CDAB287721C600E510D6 /* SPWebViewMessageHandler.h */; };
-		6B07CDAF287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B07CDAB287721C600E510D6 /* SPWebViewMessageHandler.h */; };
-		6B07CDB0287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B07CDAB287721C600E510D6 /* SPWebViewMessageHandler.h */; };
-		6B07CDB1287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B07CDAC287721C600E510D6 /* SPWebViewMessageHandler.m */; };
-		6B07CDB2287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B07CDAC287721C600E510D6 /* SPWebViewMessageHandler.m */; };
-		6B07CDB3287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B07CDAC287721C600E510D6 /* SPWebViewMessageHandler.m */; };
-		6B07CDB4287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B07CDAC287721C600E510D6 /* SPWebViewMessageHandler.m */; };
+		6B0CCFB5292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
+		6B0CCFB6292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
+		6B0CCFB7292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
+		6B0CCFB8292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
+		6B0CCFB9292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
+		6B0CCFBA292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
+		6B0CCFBB292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
+		6B0CCFBC292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
+		6B0CCFBE292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
+		6B0CCFBF292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
+		6B0CCFC0292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
+		6B0CCFC1292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
+		6B0CCFC9292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
+		6B0CCFCA292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
+		6B0CCFCB292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
+		6B0CCFCC292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
+		6B0CCFCE29236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
+		6B0CCFCF29236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
+		6B0CCFD029236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
+		6B0CCFD129236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
+		6B0CCFD429236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
+		6B0CCFD529236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
+		6B0CCFD629236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
+		6B0CCFD729236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
+		6B0CCFD829236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
+		6B0CCFD929236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
+		6B0CCFDA29236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
+		6B0CCFDB29236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
 		6B4B3087287730D200B4C16B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B4B3086287730D200B4C16B /* WebKit.framework */; };
 		6B4B3089287730E000B4C16B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B4B3088287730E000B4C16B /* WebKit.framework */; };
 		6B4B308A287730F000B4C16B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B4B3086287730D200B4C16B /* WebKit.framework */; };
-		6B4B77D227C64F6000F4E878 /* TestServiceProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B4B77D127C64F6000F4E878 /* TestServiceProvider.m */; };
-		6B871F6127C3913300BCF742 /* TestEmitterConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B871F6027C3913300BCF742 /* TestEmitterConfiguration.m */; };
-		6B871F6427C3928900BCF742 /* SPMockNetworkConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B871F6327C3928900BCF742 /* SPMockNetworkConnection.m */; };
-		6B871F6627C3976B00BCF742 /* SPMockNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B871F6227C3928900BCF742 /* SPMockNetworkConnection.h */; };
-		6B871F6727C3976C00BCF742 /* SPMockNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B871F6227C3928900BCF742 /* SPMockNetworkConnection.h */; };
-		6B871F6827C3976C00BCF742 /* SPMockNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B871F6227C3928900BCF742 /* SPMockNetworkConnection.h */; };
-		6B871F6927C3976D00BCF742 /* SPMockNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B871F6227C3928900BCF742 /* SPMockNetworkConnection.h */; };
-		6BA149392900607C00407200 /* SPMockLoggerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BA1493029005EF700407200 /* SPMockLoggerDelegate.m */; };
-		6BABC50E270B40450043BB5C /* TestSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF15D1227035A480048F376 /* TestSubject.m */; };
-		6BACDF922897C2580013276E /* SPConfigurationState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BACDF912897C2580013276E /* SPConfigurationState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		6BACDF932897C2580013276E /* SPConfigurationState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BACDF912897C2580013276E /* SPConfigurationState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		6BACDF942897C2580013276E /* SPConfigurationState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BACDF912897C2580013276E /* SPConfigurationState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		6BACDF952897C2630013276E /* SPConfigurationState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BACDF912897C2580013276E /* SPConfigurationState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		6BBDCD4227019AF4001B547F /* SPPlatformContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BBDCD4027019AF4001B547F /* SPPlatformContext.h */; };
-		6BBDCD4327019AF4001B547F /* SPPlatformContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BBDCD4027019AF4001B547F /* SPPlatformContext.h */; };
-		6BBDCD4427019AF4001B547F /* SPPlatformContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BBDCD4027019AF4001B547F /* SPPlatformContext.h */; };
-		6BBDCD4527019AF4001B547F /* SPPlatformContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BBDCD4027019AF4001B547F /* SPPlatformContext.h */; };
-		6BBDCD4627019AF4001B547F /* SPPlatformContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BBDCD4127019AF4001B547F /* SPPlatformContext.m */; };
-		6BBDCD4727019AF4001B547F /* SPPlatformContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BBDCD4127019AF4001B547F /* SPPlatformContext.m */; };
-		6BBDCD4827019AF4001B547F /* SPPlatformContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BBDCD4127019AF4001B547F /* SPPlatformContext.m */; };
-		6BBDCD4927019AF4001B547F /* SPPlatformContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BBDCD4127019AF4001B547F /* SPPlatformContext.m */; };
-		6BD6A6A928871262002D6D40 /* TestWebViewMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BD6A6A828871262002D6D40 /* TestWebViewMessageHandler.m */; };
-		6BD6A6AC288719C7002D6D40 /* SPMockWKScriptMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD6A6AA288719C7002D6D40 /* SPMockWKScriptMessage.h */; };
-		6BD6A6AD288719C7002D6D40 /* SPMockWKScriptMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BD6A6AB288719C7002D6D40 /* SPMockWKScriptMessage.m */; };
-		6BF08DA6270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DA4270DEED6009C7E2B /* SPDeviceInfoMonitor.h */; };
-		6BF08DA7270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DA4270DEED6009C7E2B /* SPDeviceInfoMonitor.h */; };
-		6BF08DA8270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DA4270DEED6009C7E2B /* SPDeviceInfoMonitor.h */; };
-		6BF08DA9270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DA4270DEED6009C7E2B /* SPDeviceInfoMonitor.h */; };
-		6BF08DAA270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DA5270DEED6009C7E2B /* SPDeviceInfoMonitor.m */; };
-		6BF08DAB270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DA5270DEED6009C7E2B /* SPDeviceInfoMonitor.m */; };
-		6BF08DAC270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DA5270DEED6009C7E2B /* SPDeviceInfoMonitor.m */; };
-		6BF08DAD270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DA5270DEED6009C7E2B /* SPDeviceInfoMonitor.m */; };
-		6BF08DB0270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DAE270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h */; };
-		6BF08DB1270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DAE270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h */; };
-		6BF08DB2270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DAE270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h */; };
-		6BF08DB3270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF08DAE270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h */; };
-		6BF08DB4270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DAF270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m */; };
-		6BF08DB5270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DAF270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m */; };
-		6BF08DB6270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DAF270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m */; };
-		6BF08DB7270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF08DAF270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m */; };
-		6BF15D0C2702ECD70048F376 /* TestPlatformContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF15D0B2702ECD70048F376 /* TestPlatformContext.m */; };
-		75264A30224E5DBC000E0E9B /* SPInstallTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 75264A2F224E5DBC000E0E9B /* SPInstallTracker.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		75264A32224E5DD2000E0E9B /* SPInstallTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 75264A31224E5DD2000E0E9B /* SPInstallTracker.m */; };
+		6B8586F6292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
+		6B8586F7292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
+		6B8586F8292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
+		6B8586F9292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
+		6B8586FA292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
+		6B8586FB292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
+		6B8586FC292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
+		6B8586FD292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
+		6B85870129239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
+		6B85870229239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
+		6B85870329239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
+		6B85870429239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
+		6B85870929239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
+		6B85870A29239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
+		6B85870B29239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
+		6B85870C29239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
+		6B85870E2923C18D006E4A5F /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
+		6B85872D2923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
+		6B85872E2923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
+		6B85872F2923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
+		6B8587302923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
+		6B8587312923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
+		6B8587322923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
+		6B8587332923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
+		6B8587342923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
+		6B8587362923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
+		6B8587372923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
+		6B8587382923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
+		6B8587392923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
+		6B85873C2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
+		6B85873D2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
+		6B85873E2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
+		6B85873F2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
+		6B8587402923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
+		6B8587412923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
+		6B8587422923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
+		6B8587432923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
+		6B8587462923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
+		6B8587472923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
+		6B8587482923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
+		6B8587492923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
+		6B85874A2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
+		6B85874B2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
+		6B85874C2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
+		6B85874D2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
+		6B8587502923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
+		6B8587512923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
+		6B8587522923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
+		6B8587532923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
+		6B8587542923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
+		6B8587552923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
+		6B8587562923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
+		6B8587572923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
+		6B8587592924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
+		6B85875A2924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
+		6B85875B2924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
+		6B85875C2924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
+		6B85875E2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
+		6B85875F2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
+		6B8587602924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
+		6B8587612924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
+		6B8587632924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
+		6B8587642924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
+		6B8587652924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
+		6B8587662924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
+		6B858769292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
+		6B85876A292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
+		6B85876B292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
+		6B85876C292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
+		6B85876D292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
+		6B85876E292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
+		6B85876F292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
+		6B858770292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
+		6B85877229251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
+		6B85877329251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
+		6B85877429251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
+		6B85877529251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
+		6B85877829251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
+		6B85877929251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
+		6B85877A29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
+		6B85877B29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
+		6B85877C29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
+		6B85877D29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
+		6B85877E29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
+		6B85877F29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
+		6B85878229251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
+		6B85878329251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
+		6B85878429251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
+		6B85878529251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
+		6B85878629251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
+		6B85878729251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
+		6B85878829251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
+		6B85878929251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
+		6B8F5605291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
+		6B8F5606291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
+		6B8F5607291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
+		6B8F5608291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
+		6B9DA53F29253951006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
+		6B9DA54029253952006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
+		6B9DA54129253952006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
+		6B9DA54229253952006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
+		6B9DA54329253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
+		6B9DA54429253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
+		6B9DA54529253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
+		6B9DA54629253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
+		6B9DA55329261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
+		6B9DA55429261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
+		6B9DA55529261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
+		6B9DA55629261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
+		6B9DA55729261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
+		6B9DA55829261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
+		6B9DA55929261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
+		6B9DA55A29261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
+		6B9DA55B29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
+		6B9DA55C29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
+		6B9DA55D29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
+		6B9DA55E29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
+		6B9DA55F29261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
+		6B9DA56029261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
+		6B9DA56129261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
+		6B9DA56229261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
+		6B9DA56329261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
+		6B9DA56429261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
+		6B9DA56529261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
+		6B9DA56629261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
+		6B9DA56B29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
+		6B9DA56C29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
+		6B9DA56D29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
+		6B9DA56E29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
+		6B9DA56F29261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
+		6B9DA57029261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
+		6B9DA57129261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
+		6B9DA57229261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
+		6B9DA57329261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
+		6B9DA57429261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
+		6B9DA57529261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
+		6B9DA57629261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
+		6B9DA57729261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
+		6B9DA57829261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
+		6B9DA57929261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
+		6B9DA57A29261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
+		6B9DA57B29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
+		6B9DA57C29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
+		6B9DA57D29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
+		6B9DA57E29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
+		6B9DA57F29261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
+		6B9DA58029261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
+		6B9DA58129261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
+		6B9DA58229261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
+		6B9DA5842926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
+		6B9DA5852926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
+		6B9DA5862926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
+		6B9DA5872926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
+		6B9DA58929264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
+		6B9DA58A29264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
+		6B9DA58B29264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
+		6B9DA58C29264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
+		6B9DA598292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
+		6B9DA599292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
+		6B9DA59A292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
+		6B9DA59B292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
+		6B9DA59D29267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
+		6B9DA59E29267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
+		6B9DA59F29267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
+		6B9DA5A029267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
+		6B9DA5A229268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
+		6B9DA5A329268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
+		6B9DA5A429268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
+		6B9DA5A529268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
+		6B9DA5A729268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
+		6B9DA5A829268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
+		6B9DA5A929268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
+		6B9DA5AA29268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
+		6B9DA5AC29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
+		6B9DA5AD29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
+		6B9DA5AE29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
+		6B9DA5AF29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
+		6B9DA5B129269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
+		6B9DA5B229269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
+		6B9DA5B329269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
+		6B9DA5B429269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
+		6B9DA5B629269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
+		6B9DA5B729269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
+		6B9DA5B829269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
+		6B9DA5B929269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
+		6B9DA5C42927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
+		6B9DA5C52927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
+		6B9DA5C62927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
+		6B9DA5C72927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
+		6B9DA5C82927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
+		6B9DA5C92927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
+		6B9DA5CA2927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
+		6B9DA5CB2927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
+		6B9DA5CC2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
+		6B9DA5CD2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
+		6B9DA5CE2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
+		6B9DA5CF2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
+		6B9DA5D02927BCD6006D721A /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
+		6B9DA5D42927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
+		6B9DA5D52927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
+		6B9DA5D62927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
+		6B9DA5D72927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
+		6B9DA5E32927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
+		6B9DA5E42927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
+		6B9DA5E52927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
+		6B9DA5E62927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
+		6B9DA5EC2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
+		6B9DA5ED2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
+		6B9DA5EE2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
+		6B9DA5EF2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
+		6B9DA5F02927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
+		6B9DA5F12927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
+		6B9DA5F22927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
+		6B9DA5F32927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
+		6B9DA5F42927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
+		6B9DA5F52927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
+		6B9DA5F62927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
+		6B9DA5F72927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
+		6B9DA5F82927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
+		6B9DA5F92927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
+		6B9DA5FA2927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
+		6B9DA5FB2927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
+		6B9DA5FC2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
+		6B9DA5FD2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
+		6B9DA5FE2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
+		6B9DA5FF2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
+		6B9DA6012927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
+		6B9DA6022927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
+		6B9DA6032927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
+		6B9DA6042927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
+		6B9DA6062927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
+		6B9DA6072927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
+		6B9DA6082927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
+		6B9DA6092927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
+		6B9DA60F2927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
+		6B9DA6102927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
+		6B9DA6112927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
+		6B9DA6122927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
+		6B9DA6132927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
+		6B9DA6142927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
+		6B9DA6152927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
+		6B9DA6162927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
+		6B9DA6172927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
+		6B9DA6182927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
+		6B9DA6192927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
+		6B9DA61A2927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
+		6B9DA61B2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
+		6B9DA61C2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
+		6B9DA61D2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
+		6B9DA61E2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
+		6B9DA61F2927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
+		6B9DA6202927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
+		6B9DA6212927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
+		6B9DA6222927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
+		6B9DA6252928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
+		6B9DA6262928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
+		6B9DA6272928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
+		6B9DA6282928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
+		6B9DA6292928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
+		6B9DA62A2928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
+		6B9DA62B2928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
+		6B9DA62C2928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
+		6B9DA62E2928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
+		6B9DA62F2928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
+		6B9DA6302928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
+		6B9DA6312928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
+		6B9DA6332928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
+		6B9DA6342928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
+		6B9DA6352928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
+		6B9DA6362928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
+		6B9DA63E292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
+		6B9DA63F292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
+		6B9DA640292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
+		6B9DA641292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
+		6B9DA642292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
+		6B9DA643292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
+		6B9DA644292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
+		6B9DA645292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
+		6B9DA648292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
+		6B9DA649292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
+		6B9DA64A292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
+		6B9DA64B292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
+		6B9DA64C292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
+		6B9DA64D292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
+		6B9DA64E292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
+		6B9DA64F292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
+		6B9DA651292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
+		6B9DA652292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
+		6B9DA653292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
+		6B9DA654292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
+		6B9DA656292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
+		6B9DA657292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
+		6B9DA658292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
+		6B9DA659292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
+		6B9DA65B292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
+		6B9DA65C292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
+		6B9DA65D292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
+		6B9DA65E292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
+		6B9DA661292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
+		6B9DA662292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
+		6B9DA663292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
+		6B9DA664292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
+		6B9DA665292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
+		6B9DA666292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
+		6B9DA667292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
+		6B9DA668292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
+		6B9DA66A292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
+		6B9DA66B292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
+		6B9DA66C292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
+		6B9DA66D292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
+		6B9DA66F292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
+		6B9DA670292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
+		6B9DA671292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
+		6B9DA672292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
+		6B9DA676292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
+		6B9DA677292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
+		6B9DA678292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
+		6B9DA679292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
+		6B9DA67A292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
+		6B9DA67B292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
+		6B9DA67C292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
+		6B9DA67D292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
+		6B9DA67E292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
+		6B9DA67F292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
+		6B9DA680292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
+		6B9DA681292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
+		6B9DA684292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
+		6B9DA685292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
+		6B9DA686292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
+		6B9DA687292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
+		6B9DA688292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
+		6B9DA689292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
+		6B9DA68A292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
+		6B9DA68B292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
+		6B9DA68D292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
+		6B9DA68E292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
+		6B9DA68F292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
+		6B9DA690292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
+		6B9DA692292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
+		6B9DA693292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
+		6B9DA694292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
+		6B9DA695292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
+		6B9DA69B292CB9A3006D721A /* MockNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA696292CB9A3006D721A /* MockNetworkConnection.swift */; };
+		6B9DA69C292CB9A3006D721A /* MockLoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA697292CB9A3006D721A /* MockLoggerDelegate.swift */; };
+		6B9DA69D292CB9A3006D721A /* MockEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA698292CB9A3006D721A /* MockEventStore.swift */; };
+		6B9DA69E292CB9A3006D721A /* MockDeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA699292CB9A3006D721A /* MockDeviceInfoMonitor.swift */; };
+		6B9DA69F292CB9A3006D721A /* MockWKScriptMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA69A292CB9A3006D721A /* MockWKScriptMessage.swift */; };
+		6B9DA6A1292CBFEB006D721A /* TestEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A0292CBFEA006D721A /* TestEvents.swift */; };
+		6B9DA6A3292CFF47006D721A /* TestPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A2292CFF47006D721A /* TestPayload.swift */; };
+		6B9DA6A5292D051A006D721A /* TestRequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A4292D051A006D721A /* TestRequestResult.swift */; };
+		6B9DA6AB292D060F006D721A /* TestTrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A6292D060E006D721A /* TestTrackerConfiguration.swift */; };
+		6B9DA6AC292D060F006D721A /* TestEmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A7292D060E006D721A /* TestEmitterConfiguration.swift */; };
+		6B9DA6AD292D060F006D721A /* TestMultipleInstances.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A8292D060E006D721A /* TestMultipleInstances.swift */; };
+		6B9DA6AE292D060F006D721A /* TestRemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A9292D060F006D721A /* TestRemoteConfiguration.swift */; };
+		6B9DA6AF292D060F006D721A /* TestTrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6AA292D060F006D721A /* TestTrackerController.swift */; };
+		6B9DA6B2292D203E006D721A /* TestStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B0292D203E006D721A /* TestStateManager.swift */; };
+		6B9DA6B3292D203E006D721A /* TestSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B1292D203E006D721A /* TestSession.swift */; };
+		6B9DA6B5292E1181006D721A /* TestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B4292E1181006D721A /* TestRequest.swift */; };
+		6B9DA6B8292E15FF006D721A /* TestSchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B6292E15FE006D721A /* TestSchemaRuleset.swift */; };
+		6B9DA6B9292E15FF006D721A /* TestGlobalContexts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B7292E15FF006D721A /* TestGlobalContexts.swift */; };
+		6B9DA6BB292E258A006D721A /* TestNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6BA292E258A006D721A /* TestNetworkConnection.swift */; };
+		6B9DA6BD292E2923006D721A /* TestGeneratedJsons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6BC292E2923006D721A /* TestGeneratedJsons.swift */; };
+		6B9DA6C1292E4D75006D721A /* LegacyTestEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6BF292E4D74006D721A /* LegacyTestEmitter.swift */; };
+		6B9DA6C2292E4D75006D721A /* LegacyTestTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C0292E4D74006D721A /* LegacyTestTracker.swift */; };
+		6B9DA6C4292E5761006D721A /* TestSQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C3292E5760006D721A /* TestSQLiteEventStore.swift */; };
+		6B9DA6C6292E589F006D721A /* TestWebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C5292E589E006D721A /* TestWebViewMessageHandler.swift */; };
+		6B9DA6C8292E5B2B006D721A /* TestPlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C7292E5B2A006D721A /* TestPlatformContext.swift */; };
+		6B9DA6CA292E5DD9006D721A /* TestMemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C9292E5DD9006D721A /* TestMemoryEventStore.swift */; };
+		6B9DA6CC292E5DF9006D721A /* TestSelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6CB292E5DF9006D721A /* TestSelfDescribingJson.swift */; };
+		6B9DA6CE292E5E39006D721A /* TestScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6CD292E5E39006D721A /* TestScreenState.swift */; };
+		6B9DA6D0292E5E7F006D721A /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6CF292E5E7F006D721A /* TestUtils.swift */; };
+		6B9DA6D2292E5F5E006D721A /* TestLifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D1292E5F5E006D721A /* TestLifecycleState.swift */; };
+		6B9DA6D4292E5F71006D721A /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D3292E5F71006D721A /* TestLogger.swift */; };
+		6B9DA6D6292E5FB6006D721A /* TestDataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D5292E5FB6006D721A /* TestDataPersistence.swift */; };
+		6B9DA6D8292E5FE6006D721A /* TestSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D7292E5FE6006D721A /* TestSubject.swift */; };
+		6B9DA6DA292E6010006D721A /* TestServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D9292E6010006D721A /* TestServiceProvider.swift */; };
+		6B9DA6DC292E6034006D721A /* LegacyTestSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DB292E6034006D721A /* LegacyTestSubject.swift */; };
+		6B9DA6DF292F4A0C006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
+		6B9DA6E0292F4A0D006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
+		6B9DA6E1292F4A0D006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
+		6B9DA6E2292F4A0E006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
+		6B9DA6E4292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
+		6B9DA6E5292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
+		6B9DA6E6292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
+		6B9DA6E7292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
+		6BABE8BD291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
+		6BABE8BE291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
+		6BABE8BF291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
+		6BABE8C0291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
+		6BABEB6C291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
+		6BABEB6D291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
+		6BABEB6E291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
+		6BABEB6F291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
+		6BABEB71291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
+		6BABEB72291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
+		6BABEB73291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
+		6BABEB74291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
+		6BABEB7C291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
+		6BABEB7D291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
+		6BABEB7E291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
+		6BABEB7F291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
+		6BABED21291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
+		6BABED22291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
+		6BABED23291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
+		6BABED24291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
+		6BABED26291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
+		6BABED27291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
+		6BABED28291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
+		6BABED29291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
+		6BABED35291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
+		6BABED36291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
+		6BABED37291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
+		6BABED38291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
+		6BABED3A291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
+		6BABED3B291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
+		6BABED3C291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
+		6BABED3D291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
+		6BABED3F291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
+		6BABED40291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
+		6BABED41291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
+		6BABED42291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
+		6BABED44291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
+		6BABED45291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
+		6BABED46291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
+		6BABED47291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
+		6BABED49291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
+		6BABED4A291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
+		6BABED4B291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
+		6BABED4C291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
+		6BABED4E291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
+		6BABED4F291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
+		6BABED50291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
+		6BABED51291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
+		6BABED53291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
+		6BABED54291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
+		6BABED55291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
+		6BABED56291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
+		6BE7084A292F64A700911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
+		6BE7084D292F654D00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
+		6BE7084E292F656500911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
+		6BE7084F292F656500911E55 /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
+		6BE70850292F656500911E55 /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
+		6BE70851292F656500911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
+		6BE70852292F656500911E55 /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
+		6BE70853292F656500911E55 /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
+		6BE70854292F656700911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
+		6BE70855292F656700911E55 /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
+		6BE70856292F656700911E55 /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
+		6BE70857292F657E00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
+		6BE70858292F657F00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
+		6BE70859292F657F00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
+		6BE7085C292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
+		6BE7085D292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
+		6BE7085E292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
+		6BE7085F292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
+		6BE70861292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
+		6BE70862292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
+		6BE70863292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
+		6BE70864292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
+		6BE70868292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
+		6BE70869292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
+		6BE7086A292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
+		6BE7086B292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
+		6BE7086D292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
+		6BE7086E292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
+		6BE7086F292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
+		6BE70870292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
+		6BE70873292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
+		6BE70874292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
+		6BE70875292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
+		6BE70876292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
 		752DABD521CC38560065F874 /* SnowplowTracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DABCC21CC38550065F874 /* SnowplowTracker.framework */; };
 		752DABFE21CC3B380065F874 /* Snowplow_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DABF521CC3B380065F874 /* Snowplow_macOS.framework */; };
-		752DAC0321CC3B380065F874 /* Snowplow_macOSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 752DAC0221CC3B380065F874 /* Snowplow_macOSTests.m */; };
-		752DAC1721CC42BC0065F874 /* SPTrackerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5E61B8F224900294081 /* SPTrackerConstants.m */; };
-		752DAC1921CC42BC0065F874 /* SPTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9E8211192DD336006744C9 /* SPTracker.m */; };
-		752DAC1B21CC42BC0065F874 /* SPEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27EA191B43D600018557 /* SPEmitter.m */; };
-		752DAC1D21CC42BC0065F874 /* SPSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 04062D751B8390870019B8D1 /* SPSubject.m */; };
-		752DAC1F21CC42BC0065F874 /* SPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5DF1B8F049200294081 /* SPSession.m */; };
-		752DAC2121CC42BC0065F874 /* SPPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27F4191C67CD00018557 /* SPPayload.m */; };
-		752DAC2321CC42BC0065F874 /* SPSelfDescribingJson.m in Sources */ = {isa = PBXBuildFile; fileRef = 0485CA151BAC65A300214BC5 /* SPSelfDescribingJson.m */; };
-		752DAC2521CC42BC0065F874 /* SPSQLiteEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = ABB767AF194974D3006275D1 /* SPSQLiteEventStore.m */; };
-		752DAC2721CC42BC0065F874 /* SPUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = ABFCC3751922984A00FAE8FE /* SPUtilities.m */; };
-		752DAC2921CC42BC0065F874 /* SPRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413DD761B78D643000D2112 /* SPRequestResult.m */; };
-		752DAC2B21CC42BC0065F874 /* SPWeakTimerTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */; };
-		752DAC3221CC43C60065F874 /* SPTrackerConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27C5191B408200018557 /* SPTrackerConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		752DAC3321CC43C70065F874 /* SPTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = AB9E8210192DD336006744C9 /* SPTracker.h */; };
-		752DAC3421CC43C70065F874 /* SPEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27E9191B43D600018557 /* SPEmitter.h */; };
-		752DAC3521CC43C70065F874 /* SPSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 04062D741B8390710019B8D1 /* SPSubject.h */; };
-		752DAC3621CC43C70065F874 /* SPSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 043EC5DD1B8F048500294081 /* SPSession.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		752DAC3721CC43C70065F874 /* SPPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27F3191C67CD00018557 /* SPPayload.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		752DAC3821CC43C70065F874 /* SPSelfDescribingJson.h in Headers */ = {isa = PBXBuildFile; fileRef = 0485CA141BAC658500214BC5 /* SPSelfDescribingJson.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		752DAC3921CC43C70065F874 /* SPSQLiteEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = ABB767AE194974D3006275D1 /* SPSQLiteEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		752DAC3A21CC43C70065F874 /* SPUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ABFCC3741922984A00FAE8FE /* SPUtilities.h */; };
-		752DAC3B21CC43C70065F874 /* SPRequestResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413DD751B78D635000D2112 /* SPRequestResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		752DAC3C21CC43C70065F874 /* SPWeakTimerTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 044CA88B1B94791E000EA3B1 /* SPWeakTimerTarget.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		752DAC3E21CC43C70065F874 /* SPRequestCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 049B2BDA1B7A203200BD82FC /* SPRequestCallback.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		752DAC3F21CC4A7B0065F874 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
 		752DAC4021CC4A830065F874 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABB67D8C192D9552009A1ECE /* UIKit.framework */; };
-		752DAC4221CC60F20065F874 /* Snowplow-umbrella-header.h in Headers */ = {isa = PBXBuildFile; fileRef = 75D6061E21C9CA8A00C7B016 /* Snowplow-umbrella-header.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		7534D20122569BED00904EE5 /* SPScreenState.m in Sources */ = {isa = PBXBuildFile; fileRef = 754774BB2225FBA60043B814 /* SPScreenState.m */; };
-		7534D20222569BED00904EE5 /* UIViewController+SPScreenView_SWIZZLE.m in Sources */ = {isa = PBXBuildFile; fileRef = 754774CC222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.m */; };
-		7534D20322569BED00904EE5 /* SPInstallTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 75264A31224E5DD2000E0E9B /* SPInstallTracker.m */; };
-		7534D20422569BFF00904EE5 /* SPScreenState.h in Headers */ = {isa = PBXBuildFile; fileRef = 754774BF2225FBB90043B814 /* SPScreenState.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		7534D20522569BFF00904EE5 /* UIViewController+SPScreenView_SWIZZLE.h in Headers */ = {isa = PBXBuildFile; fileRef = 754774CB222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		7534D20622569BFF00904EE5 /* SPInstallTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 75264A2F224E5DBC000E0E9B /* SPInstallTracker.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		7534D20822569F3400904EE5 /* TestScreenState.m in Sources */ = {isa = PBXBuildFile; fileRef = 7534D20722569F3400904EE5 /* TestScreenState.m */; };
 		753DDA6D21F803B10007C3AE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 753DDA6C21F803B10007C3AE /* Cocoa.framework */; };
-		754774BC2225FBA60043B814 /* SPScreenState.m in Sources */ = {isa = PBXBuildFile; fileRef = 754774BB2225FBA60043B814 /* SPScreenState.m */; };
-		754774BD2225FBA60043B814 /* SPScreenState.m in Sources */ = {isa = PBXBuildFile; fileRef = 754774BB2225FBA60043B814 /* SPScreenState.m */; };
-		754774BE2225FBA60043B814 /* SPScreenState.m in Sources */ = {isa = PBXBuildFile; fileRef = 754774BB2225FBA60043B814 /* SPScreenState.m */; };
-		754774C02225FBB90043B814 /* SPScreenState.h in Headers */ = {isa = PBXBuildFile; fileRef = 754774BF2225FBB90043B814 /* SPScreenState.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		754774C12225FBB90043B814 /* SPScreenState.h in Headers */ = {isa = PBXBuildFile; fileRef = 754774BF2225FBB90043B814 /* SPScreenState.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		754774C22225FBB90043B814 /* SPScreenState.h in Headers */ = {isa = PBXBuildFile; fileRef = 754774BF2225FBB90043B814 /* SPScreenState.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		754774CD222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.h in Headers */ = {isa = PBXBuildFile; fileRef = 754774CB222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		754774D0222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.m in Sources */ = {isa = PBXBuildFile; fileRef = 754774CC222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.m */; };
-		75CAC40521F2955100271FB3 /* TestSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3F121F2955000271FB3 /* TestSession.m */; };
-		75CAC40621F2955100271FB3 /* TestSQLiteEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3F221F2955000271FB3 /* TestSQLiteEventStore.m */; };
-		75CAC40721F2955100271FB3 /* TestPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3F321F2955000271FB3 /* TestPayload.m */; };
-		75CAC40821F2955100271FB3 /* LegacyTestSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3F421F2955000271FB3 /* LegacyTestSubject.m */; };
-		75CAC40921F2955100271FB3 /* TestSelfDescribingJson.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3F521F2955000271FB3 /* TestSelfDescribingJson.m */; };
-		75CAC40A21F2955100271FB3 /* TestGeneratedJsons.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3F621F2955000271FB3 /* TestGeneratedJsons.m */; };
-		75CAC40C21F2955100271FB3 /* LegacyTestEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3FA21F2955000271FB3 /* LegacyTestEvent.m */; };
-		75CAC40D21F2955100271FB3 /* LegacyTestEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3FB21F2955100271FB3 /* LegacyTestEmitter.m */; };
-		75CAC40E21F2955100271FB3 /* TestRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC3FC21F2955100271FB3 /* TestRequestResult.m */; };
-		75CAC41121F2955100271FB3 /* LegacyTestTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC40021F2955100271FB3 /* LegacyTestTracker.m */; };
-		75CAC41221F2955100271FB3 /* TestRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC40121F2955100271FB3 /* TestRequest.m */; };
-		75CAC41321F2955100271FB3 /* TestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CAC40221F2955100271FB3 /* TestUtils.m */; };
 		75CAC41A21F2959800271FB3 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
 		75CAC42221F2962E00271FB3 /* CoreTelephony.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
-		75CAC42A21F29E6C00271FB3 /* iglu_resolver.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 75CAC3FF21F2955100271FB3 /* iglu_resolver.json */; };
-		75CAC42B21F2A0CC00271FB3 /* SPTrackerConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27C5191B408200018557 /* SPTrackerConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC42C21F2A0CC00271FB3 /* SPTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = AB9E8210192DD336006744C9 /* SPTracker.h */; };
-		75CAC42D21F2A0CC00271FB3 /* SPEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27E9191B43D600018557 /* SPEmitter.h */; };
-		75CAC42E21F2A0CC00271FB3 /* SPSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 04062D741B8390710019B8D1 /* SPSubject.h */; };
-		75CAC42F21F2A0CC00271FB3 /* SPSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 043EC5DD1B8F048500294081 /* SPSession.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		75CAC43021F2A0CC00271FB3 /* SPPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27F3191C67CD00018557 /* SPPayload.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC43121F2A0CC00271FB3 /* SPSelfDescribingJson.h in Headers */ = {isa = PBXBuildFile; fileRef = 0485CA141BAC658500214BC5 /* SPSelfDescribingJson.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC43221F2A0CC00271FB3 /* SPSQLiteEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = ABB767AE194974D3006275D1 /* SPSQLiteEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC43321F2A0CC00271FB3 /* SPUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ABFCC3741922984A00FAE8FE /* SPUtilities.h */; };
-		75CAC43421F2A0CC00271FB3 /* SPRequestResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413DD751B78D635000D2112 /* SPRequestResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC43521F2A0CC00271FB3 /* SPWeakTimerTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 044CA88B1B94791E000EA3B1 /* SPWeakTimerTarget.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		75CAC43721F2A0CC00271FB3 /* SPRequestCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 049B2BDA1B7A203200BD82FC /* SPRequestCallback.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC43821F2A0CC00271FB3 /* Snowplow-umbrella-header.h in Headers */ = {isa = PBXBuildFile; fileRef = 75D6061E21C9CA8A00C7B016 /* Snowplow-umbrella-header.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC43A21F2A17500271FB3 /* SPTrackerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5E61B8F224900294081 /* SPTrackerConstants.m */; };
-		75CAC43B21F2A17500271FB3 /* SPTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9E8211192DD336006744C9 /* SPTracker.m */; };
-		75CAC43C21F2A17500271FB3 /* SPEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27EA191B43D600018557 /* SPEmitter.m */; };
-		75CAC43D21F2A17500271FB3 /* SPSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 04062D751B8390870019B8D1 /* SPSubject.m */; };
-		75CAC43E21F2A17500271FB3 /* SPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5DF1B8F049200294081 /* SPSession.m */; };
-		75CAC43F21F2A17500271FB3 /* SPPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27F4191C67CD00018557 /* SPPayload.m */; };
-		75CAC44021F2A17500271FB3 /* SPSelfDescribingJson.m in Sources */ = {isa = PBXBuildFile; fileRef = 0485CA151BAC65A300214BC5 /* SPSelfDescribingJson.m */; };
-		75CAC44121F2A17500271FB3 /* SPSQLiteEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = ABB767AF194974D3006275D1 /* SPSQLiteEventStore.m */; };
-		75CAC44221F2A17500271FB3 /* SPUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = ABFCC3751922984A00FAE8FE /* SPUtilities.m */; };
-		75CAC44321F2A17500271FB3 /* SPRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413DD761B78D643000D2112 /* SPRequestResult.m */; };
-		75CAC44421F2A17500271FB3 /* SPWeakTimerTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */; };
-		75CAC44721F2A17500271FB3 /* Snowplow-umbrella-header.h in Sources */ = {isa = PBXBuildFile; fileRef = 75D6061E21C9CA8A00C7B016 /* Snowplow-umbrella-header.h */; };
-		75CAC44821F2A19500271FB3 /* SPTrackerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5E61B8F224900294081 /* SPTrackerConstants.m */; };
-		75CAC44921F2A19500271FB3 /* SPTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9E8211192DD336006744C9 /* SPTracker.m */; };
-		75CAC44A21F2A19500271FB3 /* SPEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27EA191B43D600018557 /* SPEmitter.m */; };
-		75CAC44B21F2A19500271FB3 /* SPSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 04062D751B8390870019B8D1 /* SPSubject.m */; };
-		75CAC44C21F2A19500271FB3 /* SPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5DF1B8F049200294081 /* SPSession.m */; };
-		75CAC44D21F2A19500271FB3 /* SPPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27F4191C67CD00018557 /* SPPayload.m */; };
-		75CAC44E21F2A19500271FB3 /* SPSelfDescribingJson.m in Sources */ = {isa = PBXBuildFile; fileRef = 0485CA151BAC65A300214BC5 /* SPSelfDescribingJson.m */; };
-		75CAC44F21F2A19500271FB3 /* SPSQLiteEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = ABB767AF194974D3006275D1 /* SPSQLiteEventStore.m */; };
-		75CAC45021F2A19500271FB3 /* SPUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = ABFCC3751922984A00FAE8FE /* SPUtilities.m */; };
-		75CAC45121F2A19500271FB3 /* SPRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413DD761B78D643000D2112 /* SPRequestResult.m */; };
-		75CAC45221F2A19500271FB3 /* SPWeakTimerTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */; };
+		75CAC42A21F29E6C00271FB3 /* iglu_resolver.json in Copy Files */ = {isa = PBXBuildFile; fileRef = 75CAC3FF21F2955100271FB3 /* iglu_resolver.json */; };
 		75CAC45621F2A1CC00271FB3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0C27C0191B408200018557 /* Foundation.framework */; };
-		75CAC45821F2A21B00271FB3 /* Snowplow-umbrella-header.h in Headers */ = {isa = PBXBuildFile; fileRef = 75D6061E21C9CA8A00C7B016 /* Snowplow-umbrella-header.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC45921F2A21B00271FB3 /* SPTrackerConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27C5191B408200018557 /* SPTrackerConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC45A21F2A21B00271FB3 /* SPTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = AB9E8210192DD336006744C9 /* SPTracker.h */; };
-		75CAC45B21F2A21B00271FB3 /* SPEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27E9191B43D600018557 /* SPEmitter.h */; };
-		75CAC45C21F2A21B00271FB3 /* SPSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 04062D741B8390710019B8D1 /* SPSubject.h */; };
-		75CAC45D21F2A21B00271FB3 /* SPSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 043EC5DD1B8F048500294081 /* SPSession.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		75CAC45E21F2A21B00271FB3 /* SPPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27F3191C67CD00018557 /* SPPayload.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC45F21F2A21B00271FB3 /* SPSelfDescribingJson.h in Headers */ = {isa = PBXBuildFile; fileRef = 0485CA141BAC658500214BC5 /* SPSelfDescribingJson.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC46021F2A21B00271FB3 /* SPSQLiteEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = ABB767AE194974D3006275D1 /* SPSQLiteEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC46121F2A21B00271FB3 /* SPUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ABFCC3741922984A00FAE8FE /* SPUtilities.h */; };
-		75CAC46221F2A21B00271FB3 /* SPRequestResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413DD751B78D635000D2112 /* SPRequestResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75CAC46321F2A21B00271FB3 /* SPWeakTimerTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 044CA88B1B94791E000EA3B1 /* SPWeakTimerTarget.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		75CAC46521F2A21B00271FB3 /* SPRequestCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 049B2BDA1B7A203200BD82FC /* SPRequestCallback.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		75CAC46921F2A25B00271FB3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0C27C0191B408200018557 /* Foundation.framework */; };
 		75F9C5D121FA2E8B00A5B8FC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABB67D8C192D9552009A1ECE /* UIKit.framework */; };
 		75F9C5D221FA2E9F00A5B8FC /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
 		75F9C5D321FA352800A5B8FC /* FMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DAC0C21CC3EEA0065F874 /* FMDB.framework */; };
-		75F9C5D621FA357100A5B8FC /* SPTrackerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5E61B8F224900294081 /* SPTrackerConstants.m */; };
-		75F9C5D721FA357100A5B8FC /* SPTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9E8211192DD336006744C9 /* SPTracker.m */; };
-		75F9C5D821FA357100A5B8FC /* SPEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27EA191B43D600018557 /* SPEmitter.m */; };
-		75F9C5D921FA357100A5B8FC /* SPSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 04062D751B8390870019B8D1 /* SPSubject.m */; };
-		75F9C5DA21FA357100A5B8FC /* SPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5DF1B8F049200294081 /* SPSession.m */; };
-		75F9C5DB21FA357100A5B8FC /* SPPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27F4191C67CD00018557 /* SPPayload.m */; };
-		75F9C5DC21FA357100A5B8FC /* SPSelfDescribingJson.m in Sources */ = {isa = PBXBuildFile; fileRef = 0485CA151BAC65A300214BC5 /* SPSelfDescribingJson.m */; };
-		75F9C5DD21FA357100A5B8FC /* SPSQLiteEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = ABB767AF194974D3006275D1 /* SPSQLiteEventStore.m */; };
-		75F9C5DE21FA357100A5B8FC /* SPUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = ABFCC3751922984A00FAE8FE /* SPUtilities.m */; };
-		75F9C5DF21FA357100A5B8FC /* SPRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413DD761B78D643000D2112 /* SPRequestResult.m */; };
-		75F9C5E021FA357100A5B8FC /* SPWeakTimerTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */; };
-		75F9C5E521FA35BC00A5B8FC /* Snowplow-umbrella-header.h in Headers */ = {isa = PBXBuildFile; fileRef = 75D6061E21C9CA8A00C7B016 /* Snowplow-umbrella-header.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75F9C5E621FA35BC00A5B8FC /* SPTrackerConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27C5191B408200018557 /* SPTrackerConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75F9C5E721FA35BC00A5B8FC /* SPTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = AB9E8210192DD336006744C9 /* SPTracker.h */; };
-		75F9C5E821FA35BC00A5B8FC /* SPEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27E9191B43D600018557 /* SPEmitter.h */; };
-		75F9C5E921FA35BC00A5B8FC /* SPSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 04062D741B8390710019B8D1 /* SPSubject.h */; };
-		75F9C5EA21FA35BC00A5B8FC /* SPSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 043EC5DD1B8F048500294081 /* SPSession.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		75F9C5EB21FA35BC00A5B8FC /* SPPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27F3191C67CD00018557 /* SPPayload.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75F9C5EC21FA35BC00A5B8FC /* SPSelfDescribingJson.h in Headers */ = {isa = PBXBuildFile; fileRef = 0485CA141BAC658500214BC5 /* SPSelfDescribingJson.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75F9C5ED21FA35BC00A5B8FC /* SPSQLiteEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = ABB767AE194974D3006275D1 /* SPSQLiteEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75F9C5EE21FA35BC00A5B8FC /* SPUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ABFCC3741922984A00FAE8FE /* SPUtilities.h */; };
-		75F9C5EF21FA35BC00A5B8FC /* SPRequestResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413DD751B78D635000D2112 /* SPRequestResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		75F9C5F021FA35BC00A5B8FC /* SPWeakTimerTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 044CA88B1B94791E000EA3B1 /* SPWeakTimerTarget.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		75F9C5F221FA35BC00A5B8FC /* SPRequestCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 049B2BDA1B7A203200BD82FC /* SPRequestCallback.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9C86244B066500968CFC /* SPTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C5F244B066400968CFC /* SPTiming.m */; };
-		CE4F9C87244B066500968CFC /* SPTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C5F244B066400968CFC /* SPTiming.m */; };
-		CE4F9C88244B066500968CFC /* SPTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C5F244B066400968CFC /* SPTiming.m */; };
-		CE4F9C89244B066500968CFC /* SPTiming.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C5F244B066400968CFC /* SPTiming.m */; };
-		CE4F9C8A244B066500968CFC /* SPConsentDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C60244B066400968CFC /* SPConsentDocument.m */; };
-		CE4F9C8B244B066500968CFC /* SPConsentDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C60244B066400968CFC /* SPConsentDocument.m */; };
-		CE4F9C8C244B066500968CFC /* SPConsentDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C60244B066400968CFC /* SPConsentDocument.m */; };
-		CE4F9C8D244B066500968CFC /* SPConsentDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C60244B066400968CFC /* SPConsentDocument.m */; };
-		CE4F9C8E244B066500968CFC /* SPConsentWithdrawn.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C61244B066400968CFC /* SPConsentWithdrawn.m */; };
-		CE4F9C8F244B066500968CFC /* SPConsentWithdrawn.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C61244B066400968CFC /* SPConsentWithdrawn.m */; };
-		CE4F9C90244B066500968CFC /* SPConsentWithdrawn.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C61244B066400968CFC /* SPConsentWithdrawn.m */; };
-		CE4F9C91244B066500968CFC /* SPConsentWithdrawn.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C61244B066400968CFC /* SPConsentWithdrawn.m */; };
-		CE4F9C92244B066500968CFC /* SPForeground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C62244B066400968CFC /* SPForeground.m */; };
-		CE4F9C93244B066500968CFC /* SPForeground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C62244B066400968CFC /* SPForeground.m */; };
-		CE4F9C94244B066500968CFC /* SPForeground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C62244B066400968CFC /* SPForeground.m */; };
-		CE4F9C95244B066500968CFC /* SPForeground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C62244B066400968CFC /* SPForeground.m */; };
-		CE4F9C96244B066500968CFC /* SPEcommerceItem.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C63244B066400968CFC /* SPEcommerceItem.m */; };
-		CE4F9C97244B066500968CFC /* SPEcommerceItem.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C63244B066400968CFC /* SPEcommerceItem.m */; };
-		CE4F9C98244B066500968CFC /* SPEcommerceItem.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C63244B066400968CFC /* SPEcommerceItem.m */; };
-		CE4F9C99244B066500968CFC /* SPEcommerceItem.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C63244B066400968CFC /* SPEcommerceItem.m */; };
-		CE4F9C9A244B066500968CFC /* SPEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C64244B066400968CFC /* SPEvent.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		CE4F9C9B244B066500968CFC /* SPEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C64244B066400968CFC /* SPEvent.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		CE4F9C9C244B066500968CFC /* SPEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C64244B066400968CFC /* SPEvent.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		CE4F9C9D244B066500968CFC /* SPEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C64244B066400968CFC /* SPEvent.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		CE4F9C9E244B066500968CFC /* SPSchemaRule.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C65244B066400968CFC /* SPSchemaRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9C9F244B066500968CFC /* SPSchemaRule.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C65244B066400968CFC /* SPSchemaRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA0244B066500968CFC /* SPSchemaRule.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C65244B066400968CFC /* SPSchemaRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA1244B066500968CFC /* SPSchemaRule.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C65244B066400968CFC /* SPSchemaRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA2244B066500968CFC /* SPPageView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C66244B066400968CFC /* SPPageView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA3244B066500968CFC /* SPPageView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C66244B066400968CFC /* SPPageView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA4244B066500968CFC /* SPPageView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C66244B066400968CFC /* SPPageView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA5244B066500968CFC /* SPPageView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C66244B066400968CFC /* SPPageView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA6244B066500968CFC /* SPTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C67244B066400968CFC /* SPTiming.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA7244B066500968CFC /* SPTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C67244B066400968CFC /* SPTiming.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA8244B066500968CFC /* SPTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C67244B066400968CFC /* SPTiming.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CA9244B066500968CFC /* SPTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C67244B066400968CFC /* SPTiming.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CAA244B066500968CFC /* SPTrackerEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C68244B066400968CFC /* SPTrackerEvent.h */; };
-		CE4F9CAB244B066500968CFC /* SPTrackerEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C68244B066400968CFC /* SPTrackerEvent.h */; };
-		CE4F9CAC244B066500968CFC /* SPTrackerEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C68244B066400968CFC /* SPTrackerEvent.h */; };
-		CE4F9CAD244B066500968CFC /* SPTrackerEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C68244B066400968CFC /* SPTrackerEvent.h */; };
-		CE4F9CAE244B066500968CFC /* SPConsentWithdrawn.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C69244B066400968CFC /* SPConsentWithdrawn.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CAF244B066500968CFC /* SPConsentWithdrawn.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C69244B066400968CFC /* SPConsentWithdrawn.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB0244B066500968CFC /* SPConsentWithdrawn.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C69244B066400968CFC /* SPConsentWithdrawn.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB1244B066500968CFC /* SPConsentWithdrawn.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C69244B066400968CFC /* SPConsentWithdrawn.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB2244B066500968CFC /* SNOWError.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6A244B066400968CFC /* SNOWError.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB3244B066500968CFC /* SNOWError.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6A244B066400968CFC /* SNOWError.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB4244B066500968CFC /* SNOWError.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6A244B066400968CFC /* SNOWError.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB5244B066500968CFC /* SNOWError.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6A244B066400968CFC /* SNOWError.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB6244B066500968CFC /* SPSelfDescribing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6B244B066400968CFC /* SPSelfDescribing.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB7244B066500968CFC /* SPSelfDescribing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6B244B066400968CFC /* SPSelfDescribing.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB8244B066500968CFC /* SPSelfDescribing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6B244B066400968CFC /* SPSelfDescribing.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CB9244B066500968CFC /* SPSelfDescribing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6B244B066400968CFC /* SPSelfDescribing.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CBA244B066500968CFC /* SPSelfDescribing.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6C244B066400968CFC /* SPSelfDescribing.m */; };
-		CE4F9CBB244B066500968CFC /* SPSelfDescribing.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6C244B066400968CFC /* SPSelfDescribing.m */; };
-		CE4F9CBC244B066500968CFC /* SPSelfDescribing.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6C244B066400968CFC /* SPSelfDescribing.m */; };
-		CE4F9CBD244B066500968CFC /* SPSelfDescribing.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6C244B066400968CFC /* SPSelfDescribing.m */; };
-		CE4F9CBE244B066500968CFC /* SPForeground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6D244B066400968CFC /* SPForeground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CBF244B066500968CFC /* SPForeground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6D244B066400968CFC /* SPForeground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CC0244B066500968CFC /* SPForeground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6D244B066400968CFC /* SPForeground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CC1244B066500968CFC /* SPForeground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C6D244B066400968CFC /* SPForeground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CC2244B066500968CFC /* SPConsentGranted.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6E244B066400968CFC /* SPConsentGranted.m */; };
-		CE4F9CC3244B066500968CFC /* SPConsentGranted.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6E244B066400968CFC /* SPConsentGranted.m */; };
-		CE4F9CC4244B066500968CFC /* SPConsentGranted.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6E244B066400968CFC /* SPConsentGranted.m */; };
-		CE4F9CC5244B066500968CFC /* SPConsentGranted.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6E244B066400968CFC /* SPConsentGranted.m */; };
-		CE4F9CC6244B066500968CFC /* SPSchemaRuleset.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6F244B066400968CFC /* SPSchemaRuleset.m */; };
-		CE4F9CC7244B066500968CFC /* SPSchemaRuleset.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6F244B066400968CFC /* SPSchemaRuleset.m */; };
-		CE4F9CC8244B066500968CFC /* SPSchemaRuleset.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6F244B066400968CFC /* SPSchemaRuleset.m */; };
-		CE4F9CC9244B066500968CFC /* SPSchemaRuleset.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C6F244B066400968CFC /* SPSchemaRuleset.m */; };
-		CE4F9CCA244B066500968CFC /* SPGlobalContext.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C70244B066400968CFC /* SPGlobalContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CCB244B066500968CFC /* SPGlobalContext.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C70244B066400968CFC /* SPGlobalContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CCC244B066500968CFC /* SPGlobalContext.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C70244B066400968CFC /* SPGlobalContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CCD244B066500968CFC /* SPGlobalContext.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C70244B066400968CFC /* SPGlobalContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CCE244B066500968CFC /* SNOWError.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C71244B066400968CFC /* SNOWError.m */; };
-		CE4F9CCF244B066500968CFC /* SNOWError.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C71244B066400968CFC /* SNOWError.m */; };
-		CE4F9CD0244B066500968CFC /* SNOWError.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C71244B066400968CFC /* SNOWError.m */; };
-		CE4F9CD1244B066500968CFC /* SNOWError.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C71244B066400968CFC /* SNOWError.m */; };
-		CE4F9CD2244B066500968CFC /* SPTrackerEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C72244B066400968CFC /* SPTrackerEvent.m */; };
-		CE4F9CD3244B066500968CFC /* SPTrackerEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C72244B066400968CFC /* SPTrackerEvent.m */; };
-		CE4F9CD4244B066500968CFC /* SPTrackerEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C72244B066400968CFC /* SPTrackerEvent.m */; };
-		CE4F9CD5244B066500968CFC /* SPTrackerEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C72244B066400968CFC /* SPTrackerEvent.m */; };
-		CE4F9CD6244B066500968CFC /* SPScreenView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C73244B066400968CFC /* SPScreenView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CD7244B066500968CFC /* SPScreenView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C73244B066400968CFC /* SPScreenView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CD8244B066500968CFC /* SPScreenView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C73244B066400968CFC /* SPScreenView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CD9244B066500968CFC /* SPScreenView.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C73244B066400968CFC /* SPScreenView.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CDA244B066500968CFC /* SPPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C74244B066400968CFC /* SPPageView.m */; };
-		CE4F9CDB244B066500968CFC /* SPPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C74244B066400968CFC /* SPPageView.m */; };
-		CE4F9CDC244B066500968CFC /* SPPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C74244B066400968CFC /* SPPageView.m */; };
-		CE4F9CDD244B066500968CFC /* SPPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C74244B066400968CFC /* SPPageView.m */; };
-		CE4F9CDE244B066500968CFC /* SPStructured.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C75244B066400968CFC /* SPStructured.m */; };
-		CE4F9CDF244B066500968CFC /* SPStructured.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C75244B066400968CFC /* SPStructured.m */; };
-		CE4F9CE0244B066500968CFC /* SPStructured.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C75244B066400968CFC /* SPStructured.m */; };
-		CE4F9CE1244B066500968CFC /* SPStructured.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C75244B066400968CFC /* SPStructured.m */; };
-		CE4F9CE2244B066500968CFC /* SPConsentGranted.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C76244B066400968CFC /* SPConsentGranted.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CE3244B066500968CFC /* SPConsentGranted.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C76244B066400968CFC /* SPConsentGranted.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CE4244B066500968CFC /* SPConsentGranted.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C76244B066400968CFC /* SPConsentGranted.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CE5244B066500968CFC /* SPConsentGranted.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C76244B066400968CFC /* SPConsentGranted.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CE6244B066500968CFC /* SPPushNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C77244B066400968CFC /* SPPushNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CE7244B066500968CFC /* SPPushNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C77244B066400968CFC /* SPPushNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CE8244B066500968CFC /* SPPushNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C77244B066400968CFC /* SPPushNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CE9244B066500968CFC /* SPPushNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C77244B066400968CFC /* SPPushNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CEA244B066500968CFC /* SPPushNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C78244B066400968CFC /* SPPushNotification.m */; };
-		CE4F9CEB244B066500968CFC /* SPPushNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C78244B066400968CFC /* SPPushNotification.m */; };
-		CE4F9CEC244B066500968CFC /* SPPushNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C78244B066400968CFC /* SPPushNotification.m */; };
-		CE4F9CED244B066500968CFC /* SPPushNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C78244B066400968CFC /* SPPushNotification.m */; };
-		CE4F9CEE244B066500968CFC /* SPSchemaRule.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C79244B066400968CFC /* SPSchemaRule.m */; };
-		CE4F9CEF244B066500968CFC /* SPSchemaRule.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C79244B066400968CFC /* SPSchemaRule.m */; };
-		CE4F9CF0244B066500968CFC /* SPSchemaRule.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C79244B066400968CFC /* SPSchemaRule.m */; };
-		CE4F9CF1244B066500968CFC /* SPSchemaRule.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C79244B066400968CFC /* SPSchemaRule.m */; };
-		CE4F9CF2244B066500968CFC /* SPStructured.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7A244B066400968CFC /* SPStructured.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CF3244B066500968CFC /* SPStructured.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7A244B066400968CFC /* SPStructured.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CF4244B066500968CFC /* SPStructured.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7A244B066400968CFC /* SPStructured.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CF5244B066500968CFC /* SPStructured.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7A244B066400968CFC /* SPStructured.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CF6244B066500968CFC /* SPEventBase.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7B244B066400968CFC /* SPEventBase.m */; };
-		CE4F9CF7244B066500968CFC /* SPEventBase.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7B244B066400968CFC /* SPEventBase.m */; };
-		CE4F9CF8244B066500968CFC /* SPEventBase.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7B244B066400968CFC /* SPEventBase.m */; };
-		CE4F9CF9244B066500968CFC /* SPEventBase.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7B244B066400968CFC /* SPEventBase.m */; };
-		CE4F9CFA244B066500968CFC /* SPEventBase.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7C244B066400968CFC /* SPEventBase.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CFB244B066500968CFC /* SPEventBase.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7C244B066400968CFC /* SPEventBase.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CFC244B066500968CFC /* SPEventBase.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7C244B066400968CFC /* SPEventBase.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CFD244B066500968CFC /* SPEventBase.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7C244B066400968CFC /* SPEventBase.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9CFE244B066500968CFC /* SPBackground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7D244B066500968CFC /* SPBackground.m */; };
-		CE4F9CFF244B066500968CFC /* SPBackground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7D244B066500968CFC /* SPBackground.m */; };
-		CE4F9D00244B066500968CFC /* SPBackground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7D244B066500968CFC /* SPBackground.m */; };
-		CE4F9D01244B066500968CFC /* SPBackground.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C7D244B066500968CFC /* SPBackground.m */; };
-		CE4F9D02244B066500968CFC /* SPEcommerceItem.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7E244B066500968CFC /* SPEcommerceItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D03244B066500968CFC /* SPEcommerceItem.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7E244B066500968CFC /* SPEcommerceItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D04244B066500968CFC /* SPEcommerceItem.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7E244B066500968CFC /* SPEcommerceItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D05244B066500968CFC /* SPEcommerceItem.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7E244B066500968CFC /* SPEcommerceItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D06244B066500968CFC /* SPSchemaRuleset.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7F244B066500968CFC /* SPSchemaRuleset.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D07244B066500968CFC /* SPSchemaRuleset.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7F244B066500968CFC /* SPSchemaRuleset.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D08244B066500968CFC /* SPSchemaRuleset.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7F244B066500968CFC /* SPSchemaRuleset.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D09244B066500968CFC /* SPSchemaRuleset.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C7F244B066500968CFC /* SPSchemaRuleset.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D0A244B066500968CFC /* SPScreenView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C80244B066500968CFC /* SPScreenView.m */; };
-		CE4F9D0B244B066500968CFC /* SPScreenView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C80244B066500968CFC /* SPScreenView.m */; };
-		CE4F9D0C244B066500968CFC /* SPScreenView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C80244B066500968CFC /* SPScreenView.m */; };
-		CE4F9D0D244B066500968CFC /* SPScreenView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C80244B066500968CFC /* SPScreenView.m */; };
-		CE4F9D0E244B066500968CFC /* SPConsentDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C81244B066500968CFC /* SPConsentDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D0F244B066500968CFC /* SPConsentDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C81244B066500968CFC /* SPConsentDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D10244B066500968CFC /* SPConsentDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C81244B066500968CFC /* SPConsentDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D11244B066500968CFC /* SPConsentDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C81244B066500968CFC /* SPConsentDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D12244B066500968CFC /* SPBackground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C82244B066500968CFC /* SPBackground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D13244B066500968CFC /* SPBackground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C82244B066500968CFC /* SPBackground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D14244B066500968CFC /* SPBackground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C82244B066500968CFC /* SPBackground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D15244B066500968CFC /* SPBackground.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C82244B066500968CFC /* SPBackground.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D16244B066500968CFC /* SPGlobalContext.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C83244B066500968CFC /* SPGlobalContext.m */; };
-		CE4F9D17244B066500968CFC /* SPGlobalContext.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C83244B066500968CFC /* SPGlobalContext.m */; };
-		CE4F9D18244B066500968CFC /* SPGlobalContext.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C83244B066500968CFC /* SPGlobalContext.m */; };
-		CE4F9D19244B066500968CFC /* SPGlobalContext.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C83244B066500968CFC /* SPGlobalContext.m */; };
-		CE4F9D1A244B066500968CFC /* SPEcommerce.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C84244B066500968CFC /* SPEcommerce.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D1B244B066500968CFC /* SPEcommerce.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C84244B066500968CFC /* SPEcommerce.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D1C244B066500968CFC /* SPEcommerce.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C84244B066500968CFC /* SPEcommerce.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D1D244B066500968CFC /* SPEcommerce.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4F9C84244B066500968CFC /* SPEcommerce.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		CE4F9D1E244B066500968CFC /* SPEcommerce.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C85244B066500968CFC /* SPEcommerce.m */; };
-		CE4F9D1F244B066500968CFC /* SPEcommerce.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C85244B066500968CFC /* SPEcommerce.m */; };
-		CE4F9D20244B066500968CFC /* SPEcommerce.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C85244B066500968CFC /* SPEcommerce.m */; };
-		CE4F9D21244B066500968CFC /* SPEcommerce.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F9C85244B066500968CFC /* SPEcommerce.m */; };
-		D99BDC6D2834F89A00F6A14F /* TestLifecycleState.m in Sources */ = {isa = PBXBuildFile; fileRef = D99BDC6C2834F89A00F6A14F /* TestLifecycleState.m */; };
-		ED0EFE2D26E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0EFE2B26E240B0002CAA21 /* SPDeepLinkReceived.m */; };
-		ED0EFE2E26E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0EFE2B26E240B0002CAA21 /* SPDeepLinkReceived.m */; };
-		ED0EFE2F26E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0EFE2B26E240B0002CAA21 /* SPDeepLinkReceived.m */; };
-		ED0EFE3026E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0EFE2B26E240B0002CAA21 /* SPDeepLinkReceived.m */; };
-		ED0EFE3126E240B0002CAA21 /* SPDeepLinkReceived.h in Headers */ = {isa = PBXBuildFile; fileRef = ED0EFE2C26E240B0002CAA21 /* SPDeepLinkReceived.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED0EFE3226E240B0002CAA21 /* SPDeepLinkReceived.h in Headers */ = {isa = PBXBuildFile; fileRef = ED0EFE2C26E240B0002CAA21 /* SPDeepLinkReceived.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED0EFE3326E240B1002CAA21 /* SPDeepLinkReceived.h in Headers */ = {isa = PBXBuildFile; fileRef = ED0EFE2C26E240B0002CAA21 /* SPDeepLinkReceived.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED0EFE3426E240B1002CAA21 /* SPDeepLinkReceived.h in Headers */ = {isa = PBXBuildFile; fileRef = ED0EFE2C26E240B0002CAA21 /* SPDeepLinkReceived.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		ED26E6BA23842AB00096AF7C /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED26E6B923842AAF0096AF7C /* AdSupport.framework */; };
-		ED277BD22625F220002C7B6D /* SPConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BD02625F220002C7B6D /* SPConfigurationBundle.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED277BD32625F220002C7B6D /* SPConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BD02625F220002C7B6D /* SPConfigurationBundle.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED277BD42625F220002C7B6D /* SPConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BD02625F220002C7B6D /* SPConfigurationBundle.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED277BD52625F220002C7B6D /* SPConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BD02625F220002C7B6D /* SPConfigurationBundle.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED277BD62625F220002C7B6D /* SPConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BD12625F220002C7B6D /* SPConfigurationBundle.m */; };
-		ED277BD72625F220002C7B6D /* SPConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BD12625F220002C7B6D /* SPConfigurationBundle.m */; };
-		ED277BD82625F220002C7B6D /* SPConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BD12625F220002C7B6D /* SPConfigurationBundle.m */; };
-		ED277BD92625F220002C7B6D /* SPConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BD12625F220002C7B6D /* SPConfigurationBundle.m */; };
-		ED277BE22625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BE02625F5C5002C7B6D /* SPFetchedConfigurationBundle.h */; };
-		ED277BE32625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BE02625F5C5002C7B6D /* SPFetchedConfigurationBundle.h */; };
-		ED277BE42625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BE02625F5C5002C7B6D /* SPFetchedConfigurationBundle.h */; };
-		ED277BE52625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = ED277BE02625F5C5002C7B6D /* SPFetchedConfigurationBundle.h */; };
-		ED277BE62625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BE12625F5C5002C7B6D /* SPFetchedConfigurationBundle.m */; };
-		ED277BE72625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BE12625F5C5002C7B6D /* SPFetchedConfigurationBundle.m */; };
-		ED277BE82625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BE12625F5C5002C7B6D /* SPFetchedConfigurationBundle.m */; };
-		ED277BE92625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = ED277BE12625F5C5002C7B6D /* SPFetchedConfigurationBundle.m */; };
-		ED34672A26415C1D0018BA61 /* SPJSONSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = ED34672826415C1D0018BA61 /* SPJSONSerialization.h */; };
-		ED34672B26415C1D0018BA61 /* SPJSONSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = ED34672826415C1D0018BA61 /* SPJSONSerialization.h */; };
-		ED34672C26415C1D0018BA61 /* SPJSONSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = ED34672826415C1D0018BA61 /* SPJSONSerialization.h */; };
-		ED34672D26415C1D0018BA61 /* SPJSONSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = ED34672826415C1D0018BA61 /* SPJSONSerialization.h */; };
-		ED34672E26415C1D0018BA61 /* SPJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = ED34672926415C1D0018BA61 /* SPJSONSerialization.m */; };
-		ED34672F26415C1D0018BA61 /* SPJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = ED34672926415C1D0018BA61 /* SPJSONSerialization.m */; };
-		ED34673026415C1D0018BA61 /* SPJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = ED34672926415C1D0018BA61 /* SPJSONSerialization.m */; };
-		ED34673126415C1D0018BA61 /* SPJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = ED34672926415C1D0018BA61 /* SPJSONSerialization.m */; };
-		ED38D91F26EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D91D26EBCD58002AEC8E /* SPLifecycleEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED38D92026EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D91D26EBCD58002AEC8E /* SPLifecycleEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED38D92126EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D91D26EBCD58002AEC8E /* SPLifecycleEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED38D92226EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D91D26EBCD58002AEC8E /* SPLifecycleEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED38D92326EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D91E26EBCD59002AEC8E /* SPLifecycleEntity.m */; };
-		ED38D92426EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D91E26EBCD59002AEC8E /* SPLifecycleEntity.m */; };
-		ED38D92526EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D91E26EBCD59002AEC8E /* SPLifecycleEntity.m */; };
-		ED38D92626EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D91E26EBCD59002AEC8E /* SPLifecycleEntity.m */; };
-		ED38D92B26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92726EBCEBE002AEC8E /* SPLifecycleState.h */; };
-		ED38D92C26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92726EBCEBE002AEC8E /* SPLifecycleState.h */; };
-		ED38D92D26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92726EBCEBE002AEC8E /* SPLifecycleState.h */; };
-		ED38D92E26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92726EBCEBE002AEC8E /* SPLifecycleState.h */; };
-		ED38D92F26EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92826EBCEBE002AEC8E /* SPLifecycleStateMachine.m */; };
-		ED38D93026EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92826EBCEBE002AEC8E /* SPLifecycleStateMachine.m */; };
-		ED38D93126EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92826EBCEBE002AEC8E /* SPLifecycleStateMachine.m */; };
-		ED38D93226EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92826EBCEBE002AEC8E /* SPLifecycleStateMachine.m */; };
-		ED38D93326EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92926EBCEBE002AEC8E /* SPLifecycleStateMachine.h */; };
-		ED38D93426EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92926EBCEBE002AEC8E /* SPLifecycleStateMachine.h */; };
-		ED38D93526EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92926EBCEBE002AEC8E /* SPLifecycleStateMachine.h */; };
-		ED38D93626EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = ED38D92926EBCEBE002AEC8E /* SPLifecycleStateMachine.h */; };
-		ED38D93726EBCEBE002AEC8E /* SPLifecycleState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92A26EBCEBE002AEC8E /* SPLifecycleState.m */; };
-		ED38D93826EBCEBE002AEC8E /* SPLifecycleState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92A26EBCEBE002AEC8E /* SPLifecycleState.m */; };
-		ED38D93926EBCEBE002AEC8E /* SPLifecycleState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92A26EBCEBE002AEC8E /* SPLifecycleState.m */; };
-		ED38D93A26EBCEBE002AEC8E /* SPLifecycleState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED38D92A26EBCEBE002AEC8E /* SPLifecycleState.m */; };
-		ED49DF3C2757E4F500610843 /* SPSessionState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED49DF3A2757E4F400610843 /* SPSessionState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED49DF3D2757E4F500610843 /* SPSessionState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED49DF3A2757E4F400610843 /* SPSessionState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED49DF3E2757E4F500610843 /* SPSessionState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED49DF3A2757E4F400610843 /* SPSessionState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED49DF3F2757E4F500610843 /* SPSessionState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED49DF3A2757E4F400610843 /* SPSessionState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED49DF402757E4F500610843 /* SPSessionState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED49DF3B2757E4F400610843 /* SPSessionState.m */; };
-		ED49DF412757E4F500610843 /* SPSessionState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED49DF3B2757E4F400610843 /* SPSessionState.m */; };
-		ED49DF422757E4F500610843 /* SPSessionState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED49DF3B2757E4F400610843 /* SPSessionState.m */; };
-		ED49DF432757E4F500610843 /* SPSessionState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED49DF3B2757E4F400610843 /* SPSessionState.m */; };
-		ED6B0329271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = ED6B0327271094D700EFA12B /* SPMessageNotificationAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED6B032A271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = ED6B0327271094D700EFA12B /* SPMessageNotificationAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED6B032B271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = ED6B0327271094D700EFA12B /* SPMessageNotificationAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED6B032C271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = ED6B0327271094D700EFA12B /* SPMessageNotificationAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED6B032D271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6B0328271094D700EFA12B /* SPMessageNotificationAttachment.m */; };
-		ED6B032E271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6B0328271094D700EFA12B /* SPMessageNotificationAttachment.m */; };
-		ED6B032F271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6B0328271094D700EFA12B /* SPMessageNotificationAttachment.m */; };
-		ED6B0330271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6B0328271094D700EFA12B /* SPMessageNotificationAttachment.m */; };
-		ED7CE16626DE39510035C323 /* SPScreenStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB664F26D69D740067755F /* SPScreenStateMachine.h */; };
-		ED7CE16726DE39530035C323 /* SPScreenStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB664F26D69D740067755F /* SPScreenStateMachine.h */; };
-		ED7CE16A26DE43A00035C323 /* SPScreenStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB664E26D69D740067755F /* SPScreenStateMachine.m */; };
-		ED7CE16B26DE43A00035C323 /* SPScreenStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB664E26D69D740067755F /* SPScreenStateMachine.m */; };
-		ED7CE16F26DFB55C0035C323 /* SPTrackerState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE16D26DFB55C0035C323 /* SPTrackerState.h */; };
-		ED7CE17026DFB55C0035C323 /* SPTrackerState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE16D26DFB55C0035C323 /* SPTrackerState.h */; };
-		ED7CE17126DFB55C0035C323 /* SPTrackerState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE16D26DFB55C0035C323 /* SPTrackerState.h */; };
-		ED7CE17226DFB55C0035C323 /* SPTrackerState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE16D26DFB55C0035C323 /* SPTrackerState.h */; };
-		ED7CE17326DFB55C0035C323 /* SPTrackerState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7CE16E26DFB55C0035C323 /* SPTrackerState.m */; };
-		ED7CE17426DFB55C0035C323 /* SPTrackerState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7CE16E26DFB55C0035C323 /* SPTrackerState.m */; };
-		ED7CE17526DFB55C0035C323 /* SPTrackerState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7CE16E26DFB55C0035C323 /* SPTrackerState.m */; };
-		ED7CE17626DFB55C0035C323 /* SPTrackerState.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7CE16E26DFB55C0035C323 /* SPTrackerState.m */; };
-		ED7CE17826DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17726DFBFA30035C323 /* SPTrackerStateSnapshot.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7CE17926DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17726DFBFA30035C323 /* SPTrackerStateSnapshot.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7CE17A26DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17726DFBFA30035C323 /* SPTrackerStateSnapshot.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7CE17B26DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17726DFBFA30035C323 /* SPTrackerStateSnapshot.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7CE17D26DFC12C0035C323 /* SPState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17C26DFC12C0035C323 /* SPState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7CE17E26DFC12C0035C323 /* SPState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17C26DFC12C0035C323 /* SPState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7CE17F26DFC12C0035C323 /* SPState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17C26DFC12C0035C323 /* SPState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7CE18026DFC12C0035C323 /* SPState.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7CE17C26DFC12C0035C323 /* SPState.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7F080626190B5F005D377E /* TestRemoteConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F080526190B5F005D377E /* TestRemoteConfiguration.m */; };
-		ED7F081526190E00005D377E /* SPConfigurationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F081326190E00005D377E /* SPConfigurationProvider.h */; };
-		ED7F081626190E00005D377E /* SPConfigurationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F081326190E00005D377E /* SPConfigurationProvider.h */; };
-		ED7F081726190E00005D377E /* SPConfigurationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F081326190E00005D377E /* SPConfigurationProvider.h */; };
-		ED7F081826190E00005D377E /* SPConfigurationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F081426190E00005D377E /* SPConfigurationProvider.m */; };
-		ED7F081926190E00005D377E /* SPConfigurationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F081426190E00005D377E /* SPConfigurationProvider.m */; };
-		ED7F081A26190E00005D377E /* SPConfigurationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F081426190E00005D377E /* SPConfigurationProvider.m */; };
-		ED7F082126191108005D377E /* SPConfigurationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F081326190E00005D377E /* SPConfigurationProvider.h */; };
-		ED7F082A2619199D005D377E /* SPRemoteConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F08282619199D005D377E /* SPRemoteConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7F082B2619199D005D377E /* SPRemoteConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F08282619199D005D377E /* SPRemoteConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7F082C2619199D005D377E /* SPRemoteConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F08282619199D005D377E /* SPRemoteConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7F082D2619199D005D377E /* SPRemoteConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F08282619199D005D377E /* SPRemoteConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED7F082E2619199D005D377E /* SPRemoteConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F08292619199D005D377E /* SPRemoteConfiguration.m */; };
-		ED7F082F2619199D005D377E /* SPRemoteConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F08292619199D005D377E /* SPRemoteConfiguration.m */; };
-		ED7F08302619199D005D377E /* SPRemoteConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F08292619199D005D377E /* SPRemoteConfiguration.m */; };
-		ED7F08312619199D005D377E /* SPRemoteConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F08292619199D005D377E /* SPRemoteConfiguration.m */; };
-		ED7F0840261924BF005D377E /* SPConfigurationFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F083E261924BF005D377E /* SPConfigurationFetcher.h */; };
-		ED7F0841261924BF005D377E /* SPConfigurationFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F083E261924BF005D377E /* SPConfigurationFetcher.h */; };
-		ED7F0842261924BF005D377E /* SPConfigurationFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F083E261924BF005D377E /* SPConfigurationFetcher.h */; };
-		ED7F0843261924BF005D377E /* SPConfigurationFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F083E261924BF005D377E /* SPConfigurationFetcher.h */; };
-		ED7F0844261924BF005D377E /* SPConfigurationFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F083F261924BF005D377E /* SPConfigurationFetcher.m */; };
-		ED7F0845261924BF005D377E /* SPConfigurationFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F083F261924BF005D377E /* SPConfigurationFetcher.m */; };
-		ED7F0846261924BF005D377E /* SPConfigurationFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F083F261924BF005D377E /* SPConfigurationFetcher.m */; };
-		ED7F0847261924BF005D377E /* SPConfigurationFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = ED7F083F261924BF005D377E /* SPConfigurationFetcher.m */; };
-		ED8122AA25E9578500AE7FE8 /* SPSnowplow.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8122A825E9578500AE7FE8 /* SPSnowplow.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8122AB25E9578600AE7FE8 /* SPSnowplow.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8122A825E9578500AE7FE8 /* SPSnowplow.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8122AC25E9578600AE7FE8 /* SPSnowplow.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8122A825E9578500AE7FE8 /* SPSnowplow.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8122AD25E9578600AE7FE8 /* SPSnowplow.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8122A825E9578500AE7FE8 /* SPSnowplow.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8122AE25E9578600AE7FE8 /* SPSnowplow.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8122A925E9578500AE7FE8 /* SPSnowplow.m */; };
-		ED8122AF25E9578600AE7FE8 /* SPSnowplow.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8122A925E9578500AE7FE8 /* SPSnowplow.m */; };
-		ED8122B025E9578600AE7FE8 /* SPSnowplow.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8122A925E9578500AE7FE8 /* SPSnowplow.m */; };
-		ED8122B125E9578600AE7FE8 /* SPSnowplow.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8122A925E9578500AE7FE8 /* SPSnowplow.m */; };
-		ED852B2F23A0E90E00F2DF6B /* SNOWReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = ED852B2E23A0E90E00F2DF6B /* SNOWReachability.m */; };
-		ED852B3023A0E90E00F2DF6B /* SNOWReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = ED852B2E23A0E90E00F2DF6B /* SNOWReachability.m */; };
-		ED852B3123A0E90E00F2DF6B /* SNOWReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = ED852B2E23A0E90E00F2DF6B /* SNOWReachability.m */; };
-		ED852B3323A0EEC600F2DF6B /* SNOWReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = ED852B3223A0EEC600F2DF6B /* SNOWReachability.h */; };
-		ED852B3423A0EEC600F2DF6B /* SNOWReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = ED852B3223A0EEC600F2DF6B /* SNOWReachability.h */; };
-		ED852B3523A0EEC600F2DF6B /* SNOWReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = ED852B3223A0EEC600F2DF6B /* SNOWReachability.h */; };
-		ED87A3D925765DAE000C54EB /* SPSessionController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3D825765DAE000C54EB /* SPSessionController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A3DA25765DAE000C54EB /* SPSessionController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3D825765DAE000C54EB /* SPSessionController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A3DB25765DAE000C54EB /* SPSessionController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3D825765DAE000C54EB /* SPSessionController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A3DC25765DAE000C54EB /* SPSessionController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3D825765DAE000C54EB /* SPSessionController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A3EB25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3E925766BB4000C54EB /* SPSessionControllerImpl.h */; };
-		ED87A3EC25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3E925766BB4000C54EB /* SPSessionControllerImpl.h */; };
-		ED87A3ED25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3E925766BB4000C54EB /* SPSessionControllerImpl.h */; };
-		ED87A3EE25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A3E925766BB4000C54EB /* SPSessionControllerImpl.h */; };
-		ED87A3EF25766BB4000C54EB /* SPSessionControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A3EA25766BB4000C54EB /* SPSessionControllerImpl.m */; };
-		ED87A3F025766BB4000C54EB /* SPSessionControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A3EA25766BB4000C54EB /* SPSessionControllerImpl.m */; };
-		ED87A3F125766BB4000C54EB /* SPSessionControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A3EA25766BB4000C54EB /* SPSessionControllerImpl.m */; };
-		ED87A3F225766BB4000C54EB /* SPSessionControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A3EA25766BB4000C54EB /* SPSessionControllerImpl.m */; };
-		ED87A41C2577AC5B000C54EB /* SPTrackerController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A41B2577AC5B000C54EB /* SPTrackerController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A41D2577AC5B000C54EB /* SPTrackerController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A41B2577AC5B000C54EB /* SPTrackerController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A41E2577AC5B000C54EB /* SPTrackerController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A41B2577AC5B000C54EB /* SPTrackerController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A41F2577AC5B000C54EB /* SPTrackerController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A41B2577AC5B000C54EB /* SPTrackerController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED87A42E2577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A42C2577ADFF000C54EB /* SPTrackerControllerImpl.h */; };
-		ED87A42F2577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A42C2577ADFF000C54EB /* SPTrackerControllerImpl.h */; };
-		ED87A4302577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A42C2577ADFF000C54EB /* SPTrackerControllerImpl.h */; };
-		ED87A4312577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED87A42C2577ADFF000C54EB /* SPTrackerControllerImpl.h */; };
-		ED87A4322577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A42D2577ADFF000C54EB /* SPTrackerControllerImpl.m */; };
-		ED87A4332577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A42D2577ADFF000C54EB /* SPTrackerControllerImpl.m */; };
-		ED87A4342577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A42D2577ADFF000C54EB /* SPTrackerControllerImpl.m */; };
-		ED87A4352577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A42D2577ADFF000C54EB /* SPTrackerControllerImpl.m */; };
-		ED87A43D2577E441000C54EB /* TestTrackerController.m in Sources */ = {isa = PBXBuildFile; fileRef = ED87A43C2577E440000C54EB /* TestTrackerController.m */; };
-		ED8866BE25711EC000DB53BB /* SPLoggerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866BD25711EC000DB53BB /* SPLoggerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866BF25711EC000DB53BB /* SPLoggerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866BD25711EC000DB53BB /* SPLoggerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866C025711EC000DB53BB /* SPLoggerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866BD25711EC000DB53BB /* SPLoggerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866C125711EC000DB53BB /* SPLoggerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866BD25711EC000DB53BB /* SPLoggerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866E22571445300DB53BB /* SPSubjectConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866E02571445300DB53BB /* SPSubjectConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866E32571445300DB53BB /* SPSubjectConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866E02571445300DB53BB /* SPSubjectConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866E42571445300DB53BB /* SPSubjectConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866E02571445300DB53BB /* SPSubjectConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866E52571445300DB53BB /* SPSubjectConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866E02571445300DB53BB /* SPSubjectConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866E62571445300DB53BB /* SPSubjectConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866E12571445300DB53BB /* SPSubjectConfiguration.m */; };
-		ED8866E72571445300DB53BB /* SPSubjectConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866E12571445300DB53BB /* SPSubjectConfiguration.m */; };
-		ED8866E82571445300DB53BB /* SPSubjectConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866E12571445300DB53BB /* SPSubjectConfiguration.m */; };
-		ED8866E92571445300DB53BB /* SPSubjectConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866E12571445300DB53BB /* SPSubjectConfiguration.m */; };
-		ED8866FE25715DD600DB53BB /* SPConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866FC25715DD600DB53BB /* SPConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8866FF25715DD600DB53BB /* SPConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866FC25715DD600DB53BB /* SPConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88670025715DD600DB53BB /* SPConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866FC25715DD600DB53BB /* SPConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88670125715DD600DB53BB /* SPConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8866FC25715DD600DB53BB /* SPConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88670225715DD600DB53BB /* SPConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866FD25715DD600DB53BB /* SPConfiguration.m */; };
-		ED88670325715DD600DB53BB /* SPConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866FD25715DD600DB53BB /* SPConfiguration.m */; };
-		ED88670425715DD600DB53BB /* SPConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866FD25715DD600DB53BB /* SPConfiguration.m */; };
-		ED88670525715DD600DB53BB /* SPConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8866FD25715DD600DB53BB /* SPConfiguration.m */; };
-		ED88672B2573C1F100DB53BB /* SPSessionConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8867292573C1EF00DB53BB /* SPSessionConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88672C2573C1F100DB53BB /* SPSessionConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8867292573C1EF00DB53BB /* SPSessionConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88672D2573C1F200DB53BB /* SPSessionConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8867292573C1EF00DB53BB /* SPSessionConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88672E2573C1F200DB53BB /* SPSessionConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8867292573C1EF00DB53BB /* SPSessionConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88672F2573C1F200DB53BB /* SPSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88672A2573C1EF00DB53BB /* SPSessionConfiguration.m */; };
-		ED8867302573C1F200DB53BB /* SPSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88672A2573C1EF00DB53BB /* SPSessionConfiguration.m */; };
-		ED8867312573C1F200DB53BB /* SPSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88672A2573C1EF00DB53BB /* SPSessionConfiguration.m */; };
-		ED8867322573C1F200DB53BB /* SPSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88672A2573C1EF00DB53BB /* SPSessionConfiguration.m */; };
-		ED88B5692578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5672578F8820048FAD1 /* SPEmitterConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B56A2578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5672578F8820048FAD1 /* SPEmitterConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B56B2578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5672578F8820048FAD1 /* SPEmitterConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B56C2578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5672578F8820048FAD1 /* SPEmitterConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B56D2578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5682578F8820048FAD1 /* SPEmitterConfiguration.m */; };
-		ED88B56E2578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5682578F8820048FAD1 /* SPEmitterConfiguration.m */; };
-		ED88B56F2578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5682578F8820048FAD1 /* SPEmitterConfiguration.m */; };
-		ED88B5702578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5682578F8820048FAD1 /* SPEmitterConfiguration.m */; };
-		ED88B58F257922490048FAD1 /* SPEmitterController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B58E257922490048FAD1 /* SPEmitterController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B590257922490048FAD1 /* SPEmitterController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B58E257922490048FAD1 /* SPEmitterController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B591257922490048FAD1 /* SPEmitterController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B58E257922490048FAD1 /* SPEmitterController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B592257922490048FAD1 /* SPEmitterController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B58E257922490048FAD1 /* SPEmitterController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5A725792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5A525792C620048FAD1 /* SPEmitterControllerImpl.h */; };
-		ED88B5A825792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5A525792C620048FAD1 /* SPEmitterControllerImpl.h */; };
-		ED88B5A925792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5A525792C620048FAD1 /* SPEmitterControllerImpl.h */; };
-		ED88B5AA25792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5A525792C620048FAD1 /* SPEmitterControllerImpl.h */; };
-		ED88B5AB25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5A625792C620048FAD1 /* SPEmitterControllerImpl.m */; };
-		ED88B5AC25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5A625792C620048FAD1 /* SPEmitterControllerImpl.m */; };
-		ED88B5AD25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5A625792C620048FAD1 /* SPEmitterControllerImpl.m */; };
-		ED88B5AE25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5A625792C620048FAD1 /* SPEmitterControllerImpl.m */; };
-		ED88B5DF257950210048FAD1 /* SPGDPRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5DD257950210048FAD1 /* SPGDPRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5E0257950210048FAD1 /* SPGDPRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5DD257950210048FAD1 /* SPGDPRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5E1257950210048FAD1 /* SPGDPRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5DD257950210048FAD1 /* SPGDPRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5E2257950210048FAD1 /* SPGDPRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5DD257950210048FAD1 /* SPGDPRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5E3257950210048FAD1 /* SPGDPRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5DE257950210048FAD1 /* SPGDPRConfiguration.m */; };
-		ED88B5E4257950210048FAD1 /* SPGDPRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5DE257950210048FAD1 /* SPGDPRConfiguration.m */; };
-		ED88B5E5257950210048FAD1 /* SPGDPRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5DE257950210048FAD1 /* SPGDPRConfiguration.m */; };
-		ED88B5E6257950210048FAD1 /* SPGDPRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B5DE257950210048FAD1 /* SPGDPRConfiguration.m */; };
-		ED88B5FB257954370048FAD1 /* SPGDPRController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5FA257954370048FAD1 /* SPGDPRController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5FC257954370048FAD1 /* SPGDPRController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5FA257954370048FAD1 /* SPGDPRController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5FD257954370048FAD1 /* SPGDPRController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5FA257954370048FAD1 /* SPGDPRController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B5FE257954370048FAD1 /* SPGDPRController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B5FA257954370048FAD1 /* SPGDPRController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B60D257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B60B257956490048FAD1 /* SPGDPRControllerImpl.h */; };
-		ED88B60E257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B60B257956490048FAD1 /* SPGDPRControllerImpl.h */; };
-		ED88B60F257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B60B257956490048FAD1 /* SPGDPRControllerImpl.h */; };
-		ED88B610257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B60B257956490048FAD1 /* SPGDPRControllerImpl.h */; };
-		ED88B611257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B60C257956490048FAD1 /* SPGDPRControllerImpl.m */; };
-		ED88B612257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B60C257956490048FAD1 /* SPGDPRControllerImpl.m */; };
-		ED88B613257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B60C257956490048FAD1 /* SPGDPRControllerImpl.m */; };
-		ED88B614257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B60C257956490048FAD1 /* SPGDPRControllerImpl.m */; };
-		ED88B629257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B627257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B62A257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B627257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B62B257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B627257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B62C257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B627257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B62D257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B628257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m */; };
-		ED88B62E257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B628257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m */; };
-		ED88B62F257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B628257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m */; };
-		ED88B630257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B628257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m */; };
-		ED88B64A257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B649257A57F80048FAD1 /* SPGlobalContextsController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B64B257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B649257A57F80048FAD1 /* SPGlobalContextsController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B64C257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B649257A57F80048FAD1 /* SPGlobalContextsController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B64D257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B649257A57F80048FAD1 /* SPGlobalContextsController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B668257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B666257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h */; };
-		ED88B669257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B666257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h */; };
-		ED88B66A257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B666257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h */; };
-		ED88B66B257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B666257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h */; };
-		ED88B66C257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B667257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m */; };
-		ED88B66D257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B667257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m */; };
-		ED88B66E257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B667257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m */; };
-		ED88B66F257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B667257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m */; };
-		ED88B6B3258253F20048FAD1 /* TestEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B6B2258253F20048FAD1 /* TestEvents.m */; };
-		ED88B6DB2583DFC80048FAD1 /* SPServiceProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B6D92583DFC70048FAD1 /* SPServiceProvider.h */; };
-		ED88B6DC2583DFC80048FAD1 /* SPServiceProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B6D92583DFC70048FAD1 /* SPServiceProvider.h */; };
-		ED88B6DD2583DFC90048FAD1 /* SPServiceProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B6D92583DFC70048FAD1 /* SPServiceProvider.h */; };
-		ED88B6DE2583DFC90048FAD1 /* SPServiceProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B6D92583DFC70048FAD1 /* SPServiceProvider.h */; };
-		ED88B6DF2583DFC90048FAD1 /* SPServiceProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B6DA2583DFC70048FAD1 /* SPServiceProvider.m */; };
-		ED88B6E02583DFC90048FAD1 /* SPServiceProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B6DA2583DFC70048FAD1 /* SPServiceProvider.m */; };
-		ED88B6E12583DFC90048FAD1 /* SPServiceProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B6DA2583DFC70048FAD1 /* SPServiceProvider.m */; };
-		ED88B6E22583DFC90048FAD1 /* SPServiceProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B6DA2583DFC70048FAD1 /* SPServiceProvider.m */; };
-		ED88B7342587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B7332587777A0048FAD1 /* SPEmitterEventProcessing.h */; };
-		ED88B7352587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B7332587777A0048FAD1 /* SPEmitterEventProcessing.h */; };
-		ED88B7362587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B7332587777A0048FAD1 /* SPEmitterEventProcessing.h */; };
-		ED88B7372587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B7332587777A0048FAD1 /* SPEmitterEventProcessing.h */; };
-		ED88B76D2587B4EF0048FAD1 /* SPNetworkController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B76C2587B4EF0048FAD1 /* SPNetworkController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B76E2587B4F00048FAD1 /* SPNetworkController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B76C2587B4EF0048FAD1 /* SPNetworkController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B76F2587B4F00048FAD1 /* SPNetworkController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B76C2587B4EF0048FAD1 /* SPNetworkController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B7702587B4F00048FAD1 /* SPNetworkController.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B76C2587B4EF0048FAD1 /* SPNetworkController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED88B7912587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B78F2587B5620048FAD1 /* SPNetworkControllerImpl.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		ED88B7922587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B78F2587B5620048FAD1 /* SPNetworkControllerImpl.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		ED88B7932587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B78F2587B5620048FAD1 /* SPNetworkControllerImpl.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		ED88B7942587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88B78F2587B5620048FAD1 /* SPNetworkControllerImpl.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		ED88B7952587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B7902587B5620048FAD1 /* SPNetworkControllerImpl.m */; };
-		ED88B7962587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B7902587B5620048FAD1 /* SPNetworkControllerImpl.m */; };
-		ED88B7972587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B7902587B5620048FAD1 /* SPNetworkControllerImpl.m */; };
-		ED88B7982587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88B7902587B5620048FAD1 /* SPNetworkControllerImpl.m */; };
-		ED8A3EEC2371708C00E51827 /* SPInstallTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 75264A31224E5DD2000E0E9B /* SPInstallTracker.m */; };
-		ED8A3EED2371709100E51827 /* SPInstallTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 75264A2F224E5DBC000E0E9B /* SPInstallTracker.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		ED8BF8B325700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8B125700B3F001DFDD9 /* SPTrackerConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8B425700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8B125700B3F001DFDD9 /* SPTrackerConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8B525700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8B125700B3F001DFDD9 /* SPTrackerConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8B625700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8B125700B3F001DFDD9 /* SPTrackerConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8B725700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8B225700B3F001DFDD9 /* SPTrackerConfiguration.m */; };
-		ED8BF8B825700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8B225700B3F001DFDD9 /* SPTrackerConfiguration.m */; };
-		ED8BF8B925700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8B225700B3F001DFDD9 /* SPTrackerConfiguration.m */; };
-		ED8BF8BA25700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8B225700B3F001DFDD9 /* SPTrackerConfiguration.m */; };
-		ED8BF8CB25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8C925701853001DFDD9 /* SPNetworkConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8CC25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8C925701853001DFDD9 /* SPNetworkConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8CD25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8C925701853001DFDD9 /* SPNetworkConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8CE25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8BF8C925701853001DFDD9 /* SPNetworkConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED8BF8CF25701853001DFDD9 /* SPNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8CA25701853001DFDD9 /* SPNetworkConfiguration.m */; };
-		ED8BF8D025701853001DFDD9 /* SPNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8CA25701853001DFDD9 /* SPNetworkConfiguration.m */; };
-		ED8BF8D125701853001DFDD9 /* SPNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8CA25701853001DFDD9 /* SPNetworkConfiguration.m */; };
-		ED8BF8D225701853001DFDD9 /* SPNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8CA25701853001DFDD9 /* SPNetworkConfiguration.m */; };
-		ED8BF8ED2570FB2F001DFDD9 /* TestTrackerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8BF8EC2570FB2F001DFDD9 /* TestTrackerConfiguration.m */; };
-		ED9081B12703747C00EE9421 /* SPMessageNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9081AF2703747B00EE9421 /* SPMessageNotification.m */; };
-		ED9081B22703747C00EE9421 /* SPMessageNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9081AF2703747B00EE9421 /* SPMessageNotification.m */; };
-		ED9081B32703747C00EE9421 /* SPMessageNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9081AF2703747B00EE9421 /* SPMessageNotification.m */; };
-		ED9081B42703747C00EE9421 /* SPMessageNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9081AF2703747B00EE9421 /* SPMessageNotification.m */; };
-		ED9081B52703747C00EE9421 /* SPMessageNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9081B02703747C00EE9421 /* SPMessageNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED9081B62703747C00EE9421 /* SPMessageNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9081B02703747C00EE9421 /* SPMessageNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED9081B72703747C00EE9421 /* SPMessageNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9081B02703747C00EE9421 /* SPMessageNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED9081B82703747C00EE9421 /* SPMessageNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9081B02703747C00EE9421 /* SPMessageNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED914EB52431FEE70068DA0A /* TestGlobalContexts.m in Sources */ = {isa = PBXBuildFile; fileRef = ED914EB42431FEE70068DA0A /* TestGlobalContexts.m */; };
-		ED914EB72432081F0068DA0A /* TestSchemaRuleset.m in Sources */ = {isa = PBXBuildFile; fileRef = ED914EB62432081F0068DA0A /* TestSchemaRuleset.m */; };
-		ED914EBA24325AB40068DA0A /* SPGdprContext.h in Headers */ = {isa = PBXBuildFile; fileRef = ED914EB824325AB40068DA0A /* SPGdprContext.h */; };
-		ED914EBB24325AB40068DA0A /* SPGdprContext.h in Headers */ = {isa = PBXBuildFile; fileRef = ED914EB824325AB40068DA0A /* SPGdprContext.h */; };
-		ED914EBC24325AB40068DA0A /* SPGdprContext.h in Headers */ = {isa = PBXBuildFile; fileRef = ED914EB824325AB40068DA0A /* SPGdprContext.h */; };
-		ED914EBD24325AB40068DA0A /* SPGdprContext.h in Headers */ = {isa = PBXBuildFile; fileRef = ED914EB824325AB40068DA0A /* SPGdprContext.h */; };
-		ED914EBE24325AB40068DA0A /* SPGdprContext.m in Sources */ = {isa = PBXBuildFile; fileRef = ED914EB924325AB40068DA0A /* SPGdprContext.m */; };
-		ED914EBF24325AB40068DA0A /* SPGdprContext.m in Sources */ = {isa = PBXBuildFile; fileRef = ED914EB924325AB40068DA0A /* SPGdprContext.m */; };
-		ED914EC024325AB40068DA0A /* SPGdprContext.m in Sources */ = {isa = PBXBuildFile; fileRef = ED914EB924325AB40068DA0A /* SPGdprContext.m */; };
-		ED914EC124325AB40068DA0A /* SPGdprContext.m in Sources */ = {isa = PBXBuildFile; fileRef = ED914EB924325AB40068DA0A /* SPGdprContext.m */; };
-		ED91CB6C23AA715B0078E75F /* SPDevicePlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = ED91CB6B23AA715B0078E75F /* SPDevicePlatform.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED91CB6D23AA715B0078E75F /* SPDevicePlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = ED91CB6B23AA715B0078E75F /* SPDevicePlatform.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED91CB6E23AA715B0078E75F /* SPDevicePlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = ED91CB6B23AA715B0078E75F /* SPDevicePlatform.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED91CB6F23AA715B0078E75F /* SPDevicePlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = ED91CB6B23AA715B0078E75F /* SPDevicePlatform.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		ED91CB7123AA8AD50078E75F /* SPDevicePlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = ED91CB7023AA8AD50078E75F /* SPDevicePlatform.m */; };
-		ED91CB7223AA8AD50078E75F /* SPDevicePlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = ED91CB7023AA8AD50078E75F /* SPDevicePlatform.m */; };
-		ED91CB7323AA8AD50078E75F /* SPDevicePlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = ED91CB7023AA8AD50078E75F /* SPDevicePlatform.m */; };
-		ED91CB7423AA8AD50078E75F /* SPDevicePlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = ED91CB7023AA8AD50078E75F /* SPDevicePlatform.m */; };
-		ED9897162627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9897142627006F00145157 /* NSDictionary+SP_TypeMethods.h */; };
-		ED9897172627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9897142627006F00145157 /* NSDictionary+SP_TypeMethods.h */; };
-		ED9897182627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9897142627006F00145157 /* NSDictionary+SP_TypeMethods.h */; };
-		ED9897192627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = ED9897142627006F00145157 /* NSDictionary+SP_TypeMethods.h */; };
-		ED98971A2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9897152627006F00145157 /* NSDictionary+SP_TypeMethods.m */; };
-		ED98971B2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9897152627006F00145157 /* NSDictionary+SP_TypeMethods.m */; };
-		ED98971C2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9897152627006F00145157 /* NSDictionary+SP_TypeMethods.m */; };
-		ED98971D2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = ED9897152627006F00145157 /* NSDictionary+SP_TypeMethods.m */; };
-		ED98972626287F7A00145157 /* SPConfigurationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = ED98972426287F7900145157 /* SPConfigurationCache.h */; };
-		ED98972726287F7A00145157 /* SPConfigurationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = ED98972426287F7900145157 /* SPConfigurationCache.h */; };
-		ED98972826287F7A00145157 /* SPConfigurationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = ED98972426287F7900145157 /* SPConfigurationCache.h */; };
-		ED98972926287F7A00145157 /* SPConfigurationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = ED98972426287F7900145157 /* SPConfigurationCache.h */; };
-		ED98972A26287F7A00145157 /* SPConfigurationCache.m in Sources */ = {isa = PBXBuildFile; fileRef = ED98972526287F7900145157 /* SPConfigurationCache.m */; };
-		ED98972B26287F7A00145157 /* SPConfigurationCache.m in Sources */ = {isa = PBXBuildFile; fileRef = ED98972526287F7900145157 /* SPConfigurationCache.m */; };
-		ED98972C26287F7A00145157 /* SPConfigurationCache.m in Sources */ = {isa = PBXBuildFile; fileRef = ED98972526287F7900145157 /* SPConfigurationCache.m */; };
-		ED98972D26287F7A00145157 /* SPConfigurationCache.m in Sources */ = {isa = PBXBuildFile; fileRef = ED98972526287F7900145157 /* SPConfigurationCache.m */; };
-		EDA06FB62664CD2F007FA773 /* SPMockEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EDA06FB52664CD2F007FA773 /* SPMockEventStore.m */; };
-		EDAB65CE26CBD5150067755F /* SPDeepLinkEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB65CC26CBD5150067755F /* SPDeepLinkEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDAB65CF26CBD5150067755F /* SPDeepLinkEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB65CC26CBD5150067755F /* SPDeepLinkEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDAB65D026CBD5150067755F /* SPDeepLinkEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB65CC26CBD5150067755F /* SPDeepLinkEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDAB65D126CBD5150067755F /* SPDeepLinkEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB65CC26CBD5150067755F /* SPDeepLinkEntity.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDAB65D226CBD5150067755F /* SPDeepLinkEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB65CD26CBD5150067755F /* SPDeepLinkEntity.m */; };
-		EDAB65D326CBD5150067755F /* SPDeepLinkEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB65CD26CBD5150067755F /* SPDeepLinkEntity.m */; };
-		EDAB65D426CBD5150067755F /* SPDeepLinkEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB65CD26CBD5150067755F /* SPDeepLinkEntity.m */; };
-		EDAB65D526CBD5150067755F /* SPDeepLinkEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB65CD26CBD5150067755F /* SPDeepLinkEntity.m */; };
-		EDAB663426D699D90067755F /* SPStateFuture.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB662F26D699D80067755F /* SPStateFuture.m */; };
-		EDAB663526D699D90067755F /* SPStateFuture.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB662F26D699D80067755F /* SPStateFuture.m */; };
-		EDAB663626D699D90067755F /* SPStateFuture.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB662F26D699D80067755F /* SPStateFuture.m */; };
-		EDAB663726D699D90067755F /* SPStateFuture.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB662F26D699D80067755F /* SPStateFuture.m */; };
-		EDAB663826D699D90067755F /* SPStateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663026D699D90067755F /* SPStateManager.h */; };
-		EDAB663926D699D90067755F /* SPStateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663026D699D90067755F /* SPStateManager.h */; };
-		EDAB663A26D699D90067755F /* SPStateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663026D699D90067755F /* SPStateManager.h */; };
-		EDAB663B26D699D90067755F /* SPStateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663026D699D90067755F /* SPStateManager.h */; };
-		EDAB663C26D699D90067755F /* SPStateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB663126D699D90067755F /* SPStateManager.m */; };
-		EDAB663D26D699D90067755F /* SPStateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB663126D699D90067755F /* SPStateManager.m */; };
-		EDAB663E26D699D90067755F /* SPStateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB663126D699D90067755F /* SPStateManager.m */; };
-		EDAB663F26D699D90067755F /* SPStateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB663126D699D90067755F /* SPStateManager.m */; };
-		EDAB664026D699D90067755F /* SPStateFuture.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663226D699D90067755F /* SPStateFuture.h */; };
-		EDAB664126D699D90067755F /* SPStateFuture.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663226D699D90067755F /* SPStateFuture.h */; };
-		EDAB664226D699D90067755F /* SPStateFuture.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663226D699D90067755F /* SPStateFuture.h */; };
-		EDAB664326D699D90067755F /* SPStateFuture.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663226D699D90067755F /* SPStateFuture.h */; };
-		EDAB664426D699D90067755F /* SPStateMachineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663326D699D90067755F /* SPStateMachineProtocol.h */; };
-		EDAB664526D699D90067755F /* SPStateMachineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663326D699D90067755F /* SPStateMachineProtocol.h */; };
-		EDAB664626D699D90067755F /* SPStateMachineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663326D699D90067755F /* SPStateMachineProtocol.h */; };
-		EDAB664726D699D90067755F /* SPStateMachineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB663326D699D90067755F /* SPStateMachineProtocol.h */; };
-		EDAB664D26D69A7B0067755F /* TestStateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB664826D69A160067755F /* TestStateManager.m */; };
-		EDAB665026D69D740067755F /* SPScreenStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB664E26D69D740067755F /* SPScreenStateMachine.m */; };
-		EDAB665126D69D740067755F /* SPScreenStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB664E26D69D740067755F /* SPScreenStateMachine.m */; };
-		EDAB665226D69D740067755F /* SPScreenStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB664F26D69D740067755F /* SPScreenStateMachine.h */; };
-		EDAB665326D69D740067755F /* SPScreenStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB664F26D69D740067755F /* SPScreenStateMachine.h */; };
-		EDAB665626D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665426D6AA940067755F /* SPDeepLinkStateMachine.h */; };
-		EDAB665726D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665426D6AA940067755F /* SPDeepLinkStateMachine.h */; };
-		EDAB665826D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665426D6AA940067755F /* SPDeepLinkStateMachine.h */; };
-		EDAB665926D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665426D6AA940067755F /* SPDeepLinkStateMachine.h */; };
-		EDAB665A26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665526D6AA940067755F /* SPDeepLinkStateMachine.m */; };
-		EDAB665B26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665526D6AA940067755F /* SPDeepLinkStateMachine.m */; };
-		EDAB665C26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665526D6AA940067755F /* SPDeepLinkStateMachine.m */; };
-		EDAB665D26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665526D6AA940067755F /* SPDeepLinkStateMachine.m */; };
-		EDAB666026D6ACCB0067755F /* SPDeepLinkState.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665E26D6ACCB0067755F /* SPDeepLinkState.h */; };
-		EDAB666126D6ACCB0067755F /* SPDeepLinkState.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665E26D6ACCB0067755F /* SPDeepLinkState.h */; };
-		EDAB666226D6ACCB0067755F /* SPDeepLinkState.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665E26D6ACCB0067755F /* SPDeepLinkState.h */; };
-		EDAB666326D6ACCB0067755F /* SPDeepLinkState.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAB665E26D6ACCB0067755F /* SPDeepLinkState.h */; };
-		EDAB666426D6ACCB0067755F /* SPDeepLinkState.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665F26D6ACCB0067755F /* SPDeepLinkState.m */; };
-		EDAB666526D6ACCB0067755F /* SPDeepLinkState.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665F26D6ACCB0067755F /* SPDeepLinkState.m */; };
-		EDAB666626D6ACCB0067755F /* SPDeepLinkState.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665F26D6ACCB0067755F /* SPDeepLinkState.m */; };
-		EDAB666726D6ACCB0067755F /* SPDeepLinkState.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAB665F26D6ACCB0067755F /* SPDeepLinkState.m */; };
-		EDB2FD1926C130B80031B872 /* SPDataPersistence.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB2FD1726C130B80031B872 /* SPDataPersistence.h */; };
-		EDB2FD1A26C130B80031B872 /* SPDataPersistence.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB2FD1726C130B80031B872 /* SPDataPersistence.h */; };
-		EDB2FD1B26C130B80031B872 /* SPDataPersistence.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB2FD1726C130B80031B872 /* SPDataPersistence.h */; };
-		EDB2FD1C26C130B80031B872 /* SPDataPersistence.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB2FD1726C130B80031B872 /* SPDataPersistence.h */; };
-		EDB2FD1D26C130B80031B872 /* SPDataPersistence.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB2FD1826C130B80031B872 /* SPDataPersistence.m */; };
-		EDB2FD1E26C130B80031B872 /* SPDataPersistence.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB2FD1826C130B80031B872 /* SPDataPersistence.m */; };
-		EDB2FD1F26C130B80031B872 /* SPDataPersistence.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB2FD1826C130B80031B872 /* SPDataPersistence.m */; };
-		EDB2FD2026C130B80031B872 /* SPDataPersistence.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB2FD1826C130B80031B872 /* SPDataPersistence.m */; };
-		EDB2FD2226C57F6D0031B872 /* TestDataPersistence.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB2FD2126C57F6C0031B872 /* TestDataPersistence.m */; };
-		EDB693FA26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB693F826B7F61D00B76A79 /* SPMemoryEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDB693FB26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB693F826B7F61D00B76A79 /* SPMemoryEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDB693FC26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB693F826B7F61D00B76A79 /* SPMemoryEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDB693FD26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB693F826B7F61D00B76A79 /* SPMemoryEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDB693FE26B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB693F926B7F61D00B76A79 /* SPMemoryEventStore.m */; };
-		EDB693FF26B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB693F926B7F61D00B76A79 /* SPMemoryEventStore.m */; };
-		EDB6940026B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB693F926B7F61D00B76A79 /* SPMemoryEventStore.m */; };
-		EDB6940126B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB693F926B7F61D00B76A79 /* SPMemoryEventStore.m */; };
-		EDB6940326B83BF300B76A79 /* TestMemoryEventStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB6940226B83BF300B76A79 /* TestMemoryEventStore.m */; };
-		EDCBD0D824F5084900D39DD2 /* TestNetworkConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = EDCBD0D724F5084800D39DD2 /* TestNetworkConnection.m */; };
-		EDD8540C24EE786900661F6B /* SPEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8540A24EE786900661F6B /* SPEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8540D24EE786900661F6B /* SPEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8540A24EE786900661F6B /* SPEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8540E24EE786900661F6B /* SPEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8540A24EE786900661F6B /* SPEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8540F24EE786900661F6B /* SPEventStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8540A24EE786900661F6B /* SPEventStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8541624EEC25100661F6B /* SPEmitterEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541424EEC25000661F6B /* SPEmitterEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8541724EEC25100661F6B /* SPEmitterEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541424EEC25000661F6B /* SPEmitterEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8541824EEC25100661F6B /* SPEmitterEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541424EEC25000661F6B /* SPEmitterEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8541924EEC25100661F6B /* SPEmitterEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541424EEC25000661F6B /* SPEmitterEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8541A24EEC25100661F6B /* SPEmitterEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541524EEC25100661F6B /* SPEmitterEvent.m */; };
-		EDD8541B24EEC25100661F6B /* SPEmitterEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541524EEC25100661F6B /* SPEmitterEvent.m */; };
-		EDD8541C24EEC25100661F6B /* SPEmitterEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541524EEC25100661F6B /* SPEmitterEvent.m */; };
-		EDD8541D24EEC25100661F6B /* SPEmitterEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541524EEC25100661F6B /* SPEmitterEvent.m */; };
-		EDD8542024EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541E24EFEFB900661F6B /* SPDefaultNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8542124EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541E24EFEFB900661F6B /* SPDefaultNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8542224EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541E24EFEFB900661F6B /* SPDefaultNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8542324EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8541E24EFEFB900661F6B /* SPDefaultNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8542424EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541F24EFEFB900661F6B /* SPDefaultNetworkConnection.m */; };
-		EDD8542524EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541F24EFEFB900661F6B /* SPDefaultNetworkConnection.m */; };
-		EDD8542624EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541F24EFEFB900661F6B /* SPDefaultNetworkConnection.m */; };
-		EDD8542724EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8541F24EFEFB900661F6B /* SPDefaultNetworkConnection.m */; };
-		EDD8542A24EFEFE600661F6B /* SPNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8542824EFEFE600661F6B /* SPNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8542B24EFEFE600661F6B /* SPNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8542824EFEFE600661F6B /* SPNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8542C24EFEFE600661F6B /* SPNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8542824EFEFE600661F6B /* SPNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8542D24EFEFE600661F6B /* SPNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8542824EFEFE600661F6B /* SPNetworkConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8543424EFFFB300661F6B /* SPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8543224EFFFB300661F6B /* SPRequest.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8543524EFFFB300661F6B /* SPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8543224EFFFB300661F6B /* SPRequest.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8543624EFFFB300661F6B /* SPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8543224EFFFB300661F6B /* SPRequest.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8543724EFFFB300661F6B /* SPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD8543224EFFFB300661F6B /* SPRequest.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDD8543824EFFFB300661F6B /* SPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8543324EFFFB300661F6B /* SPRequest.m */; };
-		EDD8543924EFFFB300661F6B /* SPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8543324EFFFB300661F6B /* SPRequest.m */; };
-		EDD8543A24EFFFB300661F6B /* SPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8543324EFFFB300661F6B /* SPRequest.m */; };
-		EDD8543B24EFFFB300661F6B /* SPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD8543324EFFFB300661F6B /* SPRequest.m */; };
-		EDDD6FFD264E873B00259404 /* SPController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD6FFB264E873B00259404 /* SPController.h */; };
-		EDDD6FFE264E873B00259404 /* SPController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD6FFB264E873B00259404 /* SPController.h */; };
-		EDDD6FFF264E873B00259404 /* SPController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD6FFB264E873B00259404 /* SPController.h */; };
-		EDDD7000264E873B00259404 /* SPController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD6FFB264E873B00259404 /* SPController.h */; };
-		EDDD7001264E873B00259404 /* SPController.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD6FFC264E873B00259404 /* SPController.m */; };
-		EDDD7002264E873B00259404 /* SPController.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD6FFC264E873B00259404 /* SPController.m */; };
-		EDDD7003264E873B00259404 /* SPController.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD6FFC264E873B00259404 /* SPController.m */; };
-		EDDD7004264E873B00259404 /* SPController.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD6FFC264E873B00259404 /* SPController.m */; };
-		EDDD7007264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7005264E8ECE00259404 /* SPServiceProviderProtocol.h */; };
-		EDDD7008264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7005264E8ECE00259404 /* SPServiceProviderProtocol.h */; };
-		EDDD7009264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7005264E8ECE00259404 /* SPServiceProviderProtocol.h */; };
-		EDDD700A264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7005264E8ECE00259404 /* SPServiceProviderProtocol.h */; };
-		EDDD7011264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD700F264F1D2100259404 /* SPTrackerConfigurationUpdate.h */; };
-		EDDD7012264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD700F264F1D2100259404 /* SPTrackerConfigurationUpdate.h */; };
-		EDDD7013264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD700F264F1D2100259404 /* SPTrackerConfigurationUpdate.h */; };
-		EDDD7014264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD700F264F1D2100259404 /* SPTrackerConfigurationUpdate.h */; };
-		EDDD7015264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7010264F1D2100259404 /* SPTrackerConfigurationUpdate.m */; };
-		EDDD7016264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7010264F1D2100259404 /* SPTrackerConfigurationUpdate.m */; };
-		EDDD7017264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7010264F1D2100259404 /* SPTrackerConfigurationUpdate.m */; };
-		EDDD7018264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7010264F1D2100259404 /* SPTrackerConfigurationUpdate.m */; };
-		EDDD701B264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7019264F230400259404 /* SPSubjectConfigurationUpdate.h */; };
-		EDDD701C264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7019264F230400259404 /* SPSubjectConfigurationUpdate.h */; };
-		EDDD701D264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7019264F230400259404 /* SPSubjectConfigurationUpdate.h */; };
-		EDDD701E264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7019264F230400259404 /* SPSubjectConfigurationUpdate.h */; };
-		EDDD701F264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD701A264F230400259404 /* SPSubjectConfigurationUpdate.m */; };
-		EDDD7020264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD701A264F230400259404 /* SPSubjectConfigurationUpdate.m */; };
-		EDDD7021264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD701A264F230400259404 /* SPSubjectConfigurationUpdate.m */; };
-		EDDD7022264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD701A264F230400259404 /* SPSubjectConfigurationUpdate.m */; };
-		EDDD7025264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7023264F23C600259404 /* SPGDPRConfigurationUpdate.h */; };
-		EDDD7026264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7023264F23C600259404 /* SPGDPRConfigurationUpdate.h */; };
-		EDDD7027264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7023264F23C600259404 /* SPGDPRConfigurationUpdate.h */; };
-		EDDD7028264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7023264F23C600259404 /* SPGDPRConfigurationUpdate.h */; };
-		EDDD7029264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7024264F23C600259404 /* SPGDPRConfigurationUpdate.m */; };
-		EDDD702A264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7024264F23C600259404 /* SPGDPRConfigurationUpdate.m */; };
-		EDDD702B264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7024264F23C600259404 /* SPGDPRConfigurationUpdate.m */; };
-		EDDD702C264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7024264F23C600259404 /* SPGDPRConfigurationUpdate.m */; };
-		EDDD702F264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD702D264F25A200259404 /* SPSessionConfigurationUpdate.h */; };
-		EDDD7030264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD702D264F25A200259404 /* SPSessionConfigurationUpdate.h */; };
-		EDDD7031264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD702D264F25A200259404 /* SPSessionConfigurationUpdate.h */; };
-		EDDD7032264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD702D264F25A200259404 /* SPSessionConfigurationUpdate.h */; };
-		EDDD7033264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD702E264F25A200259404 /* SPSessionConfigurationUpdate.m */; };
-		EDDD7034264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD702E264F25A200259404 /* SPSessionConfigurationUpdate.m */; };
-		EDDD7035264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD702E264F25A200259404 /* SPSessionConfigurationUpdate.m */; };
-		EDDD7036264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD702E264F25A200259404 /* SPSessionConfigurationUpdate.m */; };
-		EDDD7039264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7037264F27E700259404 /* SPNetworkConfigurationUpdate.h */; };
-		EDDD703A264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7037264F27E700259404 /* SPNetworkConfigurationUpdate.h */; };
-		EDDD703B264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7037264F27E700259404 /* SPNetworkConfigurationUpdate.h */; };
-		EDDD703C264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7037264F27E700259404 /* SPNetworkConfigurationUpdate.h */; };
-		EDDD703D264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7038264F27E700259404 /* SPNetworkConfigurationUpdate.m */; };
-		EDDD703E264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7038264F27E700259404 /* SPNetworkConfigurationUpdate.m */; };
-		EDDD703F264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7038264F27E700259404 /* SPNetworkConfigurationUpdate.m */; };
-		EDDD7040264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7038264F27E700259404 /* SPNetworkConfigurationUpdate.m */; };
-		EDDD7043264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7041264F2A8800259404 /* SPEmitterConfigurationUpdate.h */; };
-		EDDD7044264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7041264F2A8800259404 /* SPEmitterConfigurationUpdate.h */; };
-		EDDD7045264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7041264F2A8800259404 /* SPEmitterConfigurationUpdate.h */; };
-		EDDD7046264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = EDDD7041264F2A8800259404 /* SPEmitterConfigurationUpdate.h */; };
-		EDDD7047264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7042264F2A8800259404 /* SPEmitterConfigurationUpdate.m */; };
-		EDDD7048264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7042264F2A8800259404 /* SPEmitterConfigurationUpdate.m */; };
-		EDDD7049264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7042264F2A8800259404 /* SPEmitterConfigurationUpdate.m */; };
-		EDDD704A264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDDD7042264F2A8800259404 /* SPEmitterConfigurationUpdate.m */; };
-		EDE54F4825EFA38D0073947D /* TestMultipleInstances.m in Sources */ = {isa = PBXBuildFile; fileRef = EDE54F4725EFA38D0073947D /* TestMultipleInstances.m */; };
-		EDEE834B24BDB326000B8530 /* SPTrackerError.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE834924BDB326000B8530 /* SPTrackerError.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		EDEE834C24BDB326000B8530 /* SPTrackerError.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE834924BDB326000B8530 /* SPTrackerError.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		EDEE834D24BDB326000B8530 /* SPTrackerError.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE834924BDB326000B8530 /* SPTrackerError.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		EDEE834E24BDB326000B8530 /* SPTrackerError.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE834924BDB326000B8530 /* SPTrackerError.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		EDEE834F24BDB326000B8530 /* SPTrackerError.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE834A24BDB326000B8530 /* SPTrackerError.m */; };
-		EDEE835024BDB326000B8530 /* SPTrackerError.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE834A24BDB326000B8530 /* SPTrackerError.m */; };
-		EDEE835124BDB326000B8530 /* SPTrackerError.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE834A24BDB326000B8530 /* SPTrackerError.m */; };
-		EDEE835224BDB326000B8530 /* SPTrackerError.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE834A24BDB326000B8530 /* SPTrackerError.m */; };
-		EDEE835A24BE0944000B8530 /* SPLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE835824BE0944000B8530 /* SPLogger.h */; };
-		EDEE835B24BE0944000B8530 /* SPLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE835824BE0944000B8530 /* SPLogger.h */; };
-		EDEE835C24BE0944000B8530 /* SPLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE835824BE0944000B8530 /* SPLogger.h */; };
-		EDEE835D24BE0944000B8530 /* SPLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEE835824BE0944000B8530 /* SPLogger.h */; };
-		EDEE835E24BE0944000B8530 /* SPLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE835924BE0944000B8530 /* SPLogger.m */; };
-		EDEE835F24BE0944000B8530 /* SPLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE835924BE0944000B8530 /* SPLogger.m */; };
-		EDEE836024BE0944000B8530 /* SPLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE835924BE0944000B8530 /* SPLogger.m */; };
-		EDEE836124BE0944000B8530 /* SPLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE835924BE0944000B8530 /* SPLogger.m */; };
-		EDEE836324C0C318000B8530 /* TestLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EDEE836224C0C317000B8530 /* TestLogger.m */; };
-		EDF2A1B426402D53009032AB /* SPSubjectController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B326402D52009032AB /* SPSubjectController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDF2A1B526402D53009032AB /* SPSubjectController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B326402D52009032AB /* SPSubjectController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDF2A1B626402D53009032AB /* SPSubjectController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B326402D52009032AB /* SPSubjectController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDF2A1B726402D53009032AB /* SPSubjectController.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B326402D52009032AB /* SPSubjectController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EDF2A1BA264032F9009032AB /* SPSubjectControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B8264032F9009032AB /* SPSubjectControllerImpl.h */; };
-		EDF2A1BB264032F9009032AB /* SPSubjectControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B8264032F9009032AB /* SPSubjectControllerImpl.h */; };
-		EDF2A1BC264032F9009032AB /* SPSubjectControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B8264032F9009032AB /* SPSubjectControllerImpl.h */; };
-		EDF2A1BD264032F9009032AB /* SPSubjectControllerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = EDF2A1B8264032F9009032AB /* SPSubjectControllerImpl.h */; };
-		EDF2A1BE264032F9009032AB /* SPSubjectControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF2A1B9264032F9009032AB /* SPSubjectControllerImpl.m */; };
-		EDF2A1BF264032F9009032AB /* SPSubjectControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF2A1B9264032F9009032AB /* SPSubjectControllerImpl.m */; };
-		EDF2A1C0264032F9009032AB /* SPSubjectControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF2A1B9264032F9009032AB /* SPSubjectControllerImpl.m */; };
-		EDF2A1C1264032F9009032AB /* SPSubjectControllerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF2A1B9264032F9009032AB /* SPSubjectControllerImpl.m */; };
-		EDFEEAC823A7CB2A001E6D03 /* SPInstallTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 75264A2F224E5DBC000E0E9B /* SPInstallTracker.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		EDFEEAC923A7CB3C001E6D03 /* SPInstallTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 75264A31224E5DD2000E0E9B /* SPInstallTracker.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -915,14 +559,15 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		75CAC42921F29E5900271FB3 /* CopyFiles */ = {
+		75CAC42921F29E5900271FB3 /* Copy Files */ = {
 			isa = PBXCopyFilesBuildPhase;
 			buildActionMask = 2147483647;
 			dstPath = Products;
 			dstSubfolderSpec = 7;
 			files = (
-				75CAC42A21F29E6C00271FB3 /* iglu_resolver.json in CopyFiles */,
+				75CAC42A21F29E6C00271FB3 /* iglu_resolver.json in Copy Files */,
 			);
+			name = "Copy Files";
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		75F9C5C621FA2E0F00A5B8FC /* CopyFiles */ = {
@@ -937,279 +582,185 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-		04062D741B8390710019B8D1 /* SPSubject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSubject.h; sourceTree = "<group>"; };
-		04062D751B8390870019B8D1 /* SPSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSubject.m; sourceTree = "<group>"; };
-		0413DD751B78D635000D2112 /* SPRequestResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPRequestResult.h; sourceTree = "<group>"; };
-		0413DD761B78D643000D2112 /* SPRequestResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPRequestResult.m; sourceTree = "<group>"; };
-		043EC5DD1B8F048500294081 /* SPSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSession.h; sourceTree = "<group>"; };
-		043EC5DF1B8F049200294081 /* SPSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSession.m; sourceTree = "<group>"; };
-		043EC5E61B8F224900294081 /* SPTrackerConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTrackerConstants.m; sourceTree = "<group>"; };
-		044CA88B1B94791E000EA3B1 /* SPWeakTimerTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPWeakTimerTarget.h; sourceTree = "<group>"; };
-		044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPWeakTimerTarget.m; sourceTree = "<group>"; };
-		0485CA141BAC658500214BC5 /* SPSelfDescribingJson.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSelfDescribingJson.h; sourceTree = "<group>"; };
-		0485CA151BAC65A300214BC5 /* SPSelfDescribingJson.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSelfDescribingJson.m; sourceTree = "<group>"; };
-		049B2BDA1B7A203200BD82FC /* SPRequestCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPRequestCallback.h; sourceTree = "<group>"; };
-		6B07CDAB287721C600E510D6 /* SPWebViewMessageHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPWebViewMessageHandler.h; sourceTree = "<group>"; };
-		6B07CDAC287721C600E510D6 /* SPWebViewMessageHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPWebViewMessageHandler.m; sourceTree = "<group>"; };
+		6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRControllerImpl.swift; sourceTree = "<group>"; };
+		6B0CCFB4292262640054954B /* GDPRController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRController.swift; sourceTree = "<group>"; };
+		6B0CCFBD292275580054954B /* Controller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Controller.swift; sourceTree = "<group>"; };
+		6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRConfigurationUpdate.swift; sourceTree = "<group>"; };
+		6B0CCFCD29236B0A0054954B /* GDPRContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRContext.swift; sourceTree = "<group>"; };
+		6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectControllerImpl.swift; sourceTree = "<group>"; };
+		6B0CCFD329236D4A0054954B /* SubjectController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectController.swift; sourceTree = "<group>"; };
 		6B4B3086287730D200B4C16B /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/iOSSupport/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
 		6B4B3088287730E000B4C16B /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
-		6B4B77D127C64F6000F4E878 /* TestServiceProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestServiceProvider.m; sourceTree = SOURCE_ROOT; };
-		6B871F6027C3913300BCF742 /* TestEmitterConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestEmitterConfiguration.m; sourceTree = "<group>"; };
-		6B871F6227C3928900BCF742 /* SPMockNetworkConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPMockNetworkConnection.h; sourceTree = "<group>"; };
-		6B871F6327C3928900BCF742 /* SPMockNetworkConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPMockNetworkConnection.m; sourceTree = "<group>"; };
-		6BA1492F29005EF700407200 /* SPMockLoggerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPMockLoggerDelegate.h; sourceTree = "<group>"; };
-		6BA1493029005EF700407200 /* SPMockLoggerDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPMockLoggerDelegate.m; sourceTree = "<group>"; };
-		6BACDF912897C2580013276E /* SPConfigurationState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPConfigurationState.h; sourceTree = "<group>"; };
-		6BBDCD4027019AF4001B547F /* SPPlatformContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPPlatformContext.h; path = Snowplow/Internal/Subject/SPPlatformContext.h; sourceTree = SOURCE_ROOT; };
-		6BBDCD4127019AF4001B547F /* SPPlatformContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPPlatformContext.m; path = Snowplow/Internal/Subject/SPPlatformContext.m; sourceTree = SOURCE_ROOT; };
-		6BD6A6A828871262002D6D40 /* TestWebViewMessageHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestWebViewMessageHandler.m; sourceTree = "<group>"; };
-		6BD6A6AA288719C7002D6D40 /* SPMockWKScriptMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPMockWKScriptMessage.h; sourceTree = "<group>"; };
-		6BD6A6AB288719C7002D6D40 /* SPMockWKScriptMessage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPMockWKScriptMessage.m; sourceTree = "<group>"; };
-		6BF08DA4270DEED6009C7E2B /* SPDeviceInfoMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPDeviceInfoMonitor.h; sourceTree = "<group>"; };
-		6BF08DA5270DEED6009C7E2B /* SPDeviceInfoMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPDeviceInfoMonitor.m; sourceTree = "<group>"; };
-		6BF08DAE270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPMockDeviceInfoMonitor.h; sourceTree = "<group>"; };
-		6BF08DAF270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPMockDeviceInfoMonitor.m; sourceTree = "<group>"; };
-		6BF15D0B2702ECD70048F376 /* TestPlatformContext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestPlatformContext.m; sourceTree = "<group>"; };
-		6BF15D1227035A480048F376 /* TestSubject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestSubject.m; sourceTree = "<group>"; };
+		6B8586F4292388C9006E4A5F /* SessionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionController.swift; sourceTree = "<group>"; };
+		6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionControllerImpl.swift; sourceTree = "<group>"; };
+		6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteEventStore.swift; sourceTree = "<group>"; };
+		6B85870029239A9D006E4A5F /* MemoryEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryEventStore.swift; sourceTree = "<group>"; };
+		6B85870D2923C18C006E4A5F /* EventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventStore.swift; sourceTree = "<group>"; };
+		6B85872B2923C5F0006E4A5F /* TrackerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerController.swift; sourceTree = "<group>"; };
+		6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerControllerImpl.swift; sourceTree = "<group>"; };
+		6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkConfigurationUpdate.swift; sourceTree = "<group>"; };
+		6B85873A2923E024006E4A5F /* EmitterController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterController.swift; sourceTree = "<group>"; };
+		6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterControllerImpl.swift; sourceTree = "<group>"; };
+		6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContextsController.swift; sourceTree = "<group>"; };
+		6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContextsControllerImpl.swift; sourceTree = "<group>"; };
+		6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkControllerImpl.swift; sourceTree = "<group>"; };
+		6B85874F2923E7CF006E4A5F /* NetworkController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkController.swift; sourceTree = "<group>"; };
+		6B8587582924CDB0006E4A5F /* Snowplow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Snowplow.swift; sourceTree = "<group>"; };
+		6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchedConfigurationBundle.swift; sourceTree = "<group>"; };
+		6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationProvider.swift; sourceTree = "<group>"; };
+		6B858767292509AC006E4A5F /* ConfigurationCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationCache.swift; sourceTree = "<group>"; };
+		6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationFetcher.swift; sourceTree = "<group>"; };
+		6B85877129251779006E4A5F /* ConfigurationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationState.swift; sourceTree = "<group>"; };
+		6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkConnection.swift; sourceTree = "<group>"; };
+		6B85877729251CBD006E4A5F /* NetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = "<group>"; };
+		6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfDescribingJson.swift; sourceTree = "<group>"; };
+		6B85878129251DF4006E4A5F /* Payload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Payload.swift; sourceTree = "<group>"; };
+		6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerConfiguration.swift; sourceTree = "<group>"; };
+		6B9DA53D29253951006D721A /* LifecycleEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifecycleEntity.swift; sourceTree = "<group>"; };
+		6B9DA53E29253951006D721A /* DeepLinkEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkEntity.swift; sourceTree = "<group>"; };
+		6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentWithdrawn.swift; sourceTree = "<group>"; };
+		6B9DA54829261E5A006D721A /* ConsentGranted.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentGranted.swift; sourceTree = "<group>"; };
+		6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkReceived.swift; sourceTree = "<group>"; };
+		6B9DA54A29261E5A006D721A /* ConsentDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentDocument.swift; sourceTree = "<group>"; };
+		6B9DA54B29261E5A006D721A /* EventBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventBase.swift; sourceTree = "<group>"; };
+		6B9DA54D29261E5A006D721A /* Foreground.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Foreground.swift; sourceTree = "<group>"; };
+		6B9DA54E29261E5B006D721A /* SelfDescribing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfDescribing.swift; sourceTree = "<group>"; };
+		6B9DA54F29261E5B006D721A /* Background.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Background.swift; sourceTree = "<group>"; };
+		6B9DA55029261E5B006D721A /* Timing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timing.swift; sourceTree = "<group>"; };
+		6B9DA55129261E5B006D721A /* Structured.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Structured.swift; sourceTree = "<group>"; };
+		6B9DA55229261E5B006D721A /* ScreenView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenView.swift; sourceTree = "<group>"; };
+		6B9DA5832926437F006D721A /* PushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotification.swift; sourceTree = "<group>"; };
+		6B9DA58829264EC2006D721A /* MessageNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNotification.swift; sourceTree = "<group>"; };
+		6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNotificationAttachment.swift; sourceTree = "<group>"; };
+		6B9DA59C29267B66006D721A /* Ecommerce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ecommerce.swift; sourceTree = "<group>"; };
+		6B9DA5A129268956006D721A /* EcommerceItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcommerceItem.swift; sourceTree = "<group>"; };
+		6B9DA5A629268B0E006D721A /* TrackerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerError.swift; sourceTree = "<group>"; };
+		6B9DA5AB29268E4E006D721A /* PageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageView.swift; sourceTree = "<group>"; };
+		6B9DA5B029269036006D721A /* SNOWError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SNOWError.swift; sourceTree = "<group>"; };
+		6B9DA5B529269755006D721A /* TrackerEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerEvent.swift; sourceTree = "<group>"; };
+		6B9DA5BF2927BCD5006D721A /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
+		6B9DA5C02927BCD5006D721A /* RequestCallback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestCallback.swift; sourceTree = "<group>"; };
+		6B9DA5C12927BCD6006D721A /* RequestResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestResult.swift; sourceTree = "<group>"; };
+		6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterEvent.swift; sourceTree = "<group>"; };
+		6B9DA5C32927BCD6006D721A /* Emitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Emitter.swift; sourceTree = "<group>"; };
+		6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmitterEventProcessing.swift; sourceTree = "<group>"; };
+		6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifecycleStateMachine.swift; sourceTree = "<group>"; };
+		6B9DA5E82927E635006D721A /* InstallTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstallTracker.swift; sourceTree = "<group>"; };
+		6B9DA5E92927E635006D721A /* DeepLinkState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkState.swift; sourceTree = "<group>"; };
+		6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkStateMachine.swift; sourceTree = "<group>"; };
+		6B9DA5EB2927E635006D721A /* LifecycleState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifecycleState.swift; sourceTree = "<group>"; };
+		6B9DA6002927E669006D721A /* State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = "<group>"; };
+		6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachineProtocol.swift; sourceTree = "<group>"; };
+		6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewMessageHandler.swift; sourceTree = "<group>"; };
+		6B9DA60B2927E99B006D721A /* StateFuture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateFuture.swift; sourceTree = "<group>"; };
+		6B9DA60C2927E99B006D721A /* StateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateManager.swift; sourceTree = "<group>"; };
+		6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerStateSnapshot.swift; sourceTree = "<group>"; };
+		6B9DA60E2927E99C006D721A /* TrackerState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerState.swift; sourceTree = "<group>"; };
+		6B9DA6232928B2FB006D721A /* SessionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionState.swift; sourceTree = "<group>"; };
+		6B9DA6242928B2FB006D721A /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; };
+		6B9DA62D2928C08D006D721A /* DataPersistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataPersistence.swift; sourceTree = "<group>"; };
+		6B9DA6322928CAA3006D721A /* Tracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tracker.swift; sourceTree = "<group>"; };
+		6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenStateMachine.swift; sourceTree = "<group>"; };
+		6B9DA63D292B5196006D721A /* ScreenState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenState.swift; sourceTree = "<group>"; };
+		6B9DA646292B5487006D721A /* DevicePlatform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DevicePlatform.swift; sourceTree = "<group>"; };
+		6B9DA647292B5487006D721A /* PlatformContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformContext.swift; sourceTree = "<group>"; };
+		6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceInfoMonitor.swift; sourceTree = "<group>"; };
+		6B9DA655292B60E1006D721A /* Subject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subject.swift; sourceTree = "<group>"; };
+		6B9DA65A292B6E68006D721A /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
+		6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceProviderProtocol.swift; sourceTree = "<group>"; };
+		6B9DA660292BAE5F006D721A /* ServiceProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceProvider.swift; sourceTree = "<group>"; };
+		6B9DA669292BBC83006D721A /* SPSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPSize.swift; sourceTree = "<group>"; };
+		6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SNOWReachability.swift; sourceTree = "<group>"; };
+		6B9DA673292BD2D6006D721A /* SchemaRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaRule.swift; sourceTree = "<group>"; };
+		6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaRuleset.swift; sourceTree = "<group>"; };
+		6B9DA675292BD2D7006D721A /* GlobalContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContext.swift; sourceTree = "<group>"; };
+		6B9DA682292BDD39006D721A /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
+		6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggerDelegate.swift; sourceTree = "<group>"; };
+		6B9DA68C292BF03B006D721A /* TrackerConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerConstants.swift; sourceTree = "<group>"; };
+		6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitScreenViewTracking.swift; sourceTree = "<group>"; };
+		6B9DA696292CB9A3006D721A /* MockNetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockNetworkConnection.swift; sourceTree = "<group>"; };
+		6B9DA697292CB9A3006D721A /* MockLoggerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLoggerDelegate.swift; sourceTree = "<group>"; };
+		6B9DA698292CB9A3006D721A /* MockEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockEventStore.swift; sourceTree = "<group>"; };
+		6B9DA699292CB9A3006D721A /* MockDeviceInfoMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDeviceInfoMonitor.swift; sourceTree = "<group>"; };
+		6B9DA69A292CB9A3006D721A /* MockWKScriptMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockWKScriptMessage.swift; sourceTree = "<group>"; };
+		6B9DA6A0292CBFEA006D721A /* TestEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEvents.swift; sourceTree = "<group>"; };
+		6B9DA6A2292CFF47006D721A /* TestPayload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestPayload.swift; sourceTree = "<group>"; };
+		6B9DA6A4292D051A006D721A /* TestRequestResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRequestResult.swift; sourceTree = "<group>"; };
+		6B9DA6A6292D060E006D721A /* TestTrackerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestTrackerConfiguration.swift; sourceTree = "<group>"; };
+		6B9DA6A7292D060E006D721A /* TestEmitterConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEmitterConfiguration.swift; sourceTree = "<group>"; };
+		6B9DA6A8292D060E006D721A /* TestMultipleInstances.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestMultipleInstances.swift; sourceTree = "<group>"; };
+		6B9DA6A9292D060F006D721A /* TestRemoteConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRemoteConfiguration.swift; sourceTree = "<group>"; };
+		6B9DA6AA292D060F006D721A /* TestTrackerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestTrackerController.swift; sourceTree = "<group>"; };
+		6B9DA6B0292D203E006D721A /* TestStateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestStateManager.swift; sourceTree = "<group>"; };
+		6B9DA6B1292D203E006D721A /* TestSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSession.swift; sourceTree = "<group>"; };
+		6B9DA6B4292E1181006D721A /* TestRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRequest.swift; sourceTree = "<group>"; };
+		6B9DA6B6292E15FE006D721A /* TestSchemaRuleset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSchemaRuleset.swift; sourceTree = "<group>"; };
+		6B9DA6B7292E15FF006D721A /* TestGlobalContexts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestGlobalContexts.swift; sourceTree = "<group>"; };
+		6B9DA6BA292E258A006D721A /* TestNetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNetworkConnection.swift; sourceTree = "<group>"; };
+		6B9DA6BC292E2923006D721A /* TestGeneratedJsons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestGeneratedJsons.swift; sourceTree = "<group>"; };
+		6B9DA6BE292E2B16006D721A /* Tests-bridging-header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-bridging-header.h"; sourceTree = "<group>"; };
+		6B9DA6BF292E4D74006D721A /* LegacyTestEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyTestEmitter.swift; sourceTree = "<group>"; };
+		6B9DA6C0292E4D74006D721A /* LegacyTestTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyTestTracker.swift; sourceTree = "<group>"; };
+		6B9DA6C3292E5760006D721A /* TestSQLiteEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSQLiteEventStore.swift; sourceTree = "<group>"; };
+		6B9DA6C5292E589E006D721A /* TestWebViewMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestWebViewMessageHandler.swift; sourceTree = "<group>"; };
+		6B9DA6C7292E5B2A006D721A /* TestPlatformContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestPlatformContext.swift; sourceTree = "<group>"; };
+		6B9DA6C9292E5DD9006D721A /* TestMemoryEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestMemoryEventStore.swift; sourceTree = "<group>"; };
+		6B9DA6CB292E5DF9006D721A /* TestSelfDescribingJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSelfDescribingJson.swift; sourceTree = "<group>"; };
+		6B9DA6CD292E5E39006D721A /* TestScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestScreenState.swift; sourceTree = "<group>"; };
+		6B9DA6CF292E5E7F006D721A /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = "<group>"; };
+		6B9DA6D1292E5F5E006D721A /* TestLifecycleState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestLifecycleState.swift; sourceTree = "<group>"; };
+		6B9DA6D3292E5F71006D721A /* TestLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestLogger.swift; sourceTree = "<group>"; };
+		6B9DA6D5292E5FB6006D721A /* TestDataPersistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDataPersistence.swift; sourceTree = "<group>"; };
+		6B9DA6D7292E5FE6006D721A /* TestSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSubject.swift; sourceTree = "<group>"; };
+		6B9DA6D9292E6010006D721A /* TestServiceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestServiceProvider.swift; sourceTree = "<group>"; };
+		6B9DA6DB292E6034006D721A /* LegacyTestSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTestSubject.swift; sourceTree = "<group>"; };
+		6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmitterDefaults.swift; sourceTree = "<group>"; };
+		6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerDefaults.swift; sourceTree = "<group>"; };
+		6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerConfigurationUpdate.swift; sourceTree = "<group>"; };
+		6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkConfiguration.swift; sourceTree = "<group>"; };
+		6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionConfiguration.swift; sourceTree = "<group>"; };
+		6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionConfigurationUpdate.swift; sourceTree = "<group>"; };
+		6BABED20291E3DB200F6798A /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
+		6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectConfiguration.swift; sourceTree = "<group>"; };
+		6BABED34291E596100F6798A /* EmitterConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterConfiguration.swift; sourceTree = "<group>"; };
+		6BABED39291E60F000F6798A /* GDPRConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRConfiguration.swift; sourceTree = "<group>"; };
+		6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterConfigurationUpdate.swift; sourceTree = "<group>"; };
+		6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectConfigurationUpdate.swift; sourceTree = "<group>"; };
+		6BABED48291E884900F6798A /* ConfigurationBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationBundle.swift; sourceTree = "<group>"; };
+		6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContextsConfiguration.swift; sourceTree = "<group>"; };
+		6BABED52291E91E000F6798A /* RemoteConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteConfiguration.swift; sourceTree = "<group>"; };
+		6BE70849292F64A700911E55 /* BufferOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BufferOption.swift; sourceTree = "<group>"; };
+		6BE7084C292F654D00911E55 /* ContextGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextGenerator.swift; sourceTree = "<group>"; };
+		6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpMethodOptions.swift; sourceTree = "<group>"; };
+		6BE70860292F66E800911E55 /* ProtocolOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolOptions.swift; sourceTree = "<group>"; };
+		6BE70867292F67C000911E55 /* InspectableEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectableEvent.swift; sourceTree = "<group>"; };
+		6BE7086C292F683600911E55 /* LogLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogLevel.swift; sourceTree = "<group>"; };
+		6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRProcessingBasis.swift; sourceTree = "<group>"; };
 		750E7F1121F2735C0050A993 /* Nocilla.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nocilla.framework; path = Carthage/Build/iOS/Nocilla.framework; sourceTree = "<group>"; };
-		75264A2F224E5DBC000E0E9B /* SPInstallTracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPInstallTracker.h; sourceTree = "<group>"; };
-		75264A31224E5DD2000E0E9B /* SPInstallTracker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPInstallTracker.m; sourceTree = "<group>"; };
 		752DABCC21CC38550065F874 /* SnowplowTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnowplowTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		752DABD421CC38560065F874 /* Snowplow-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Snowplow-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		752DABD421CC38560065F874 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		752DABE821CC3A090065F874 /* SnowplowTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnowplowTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		752DABF521CC3B380065F874 /* Snowplow_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Snowplow_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		752DABFD21CC3B380065F874 /* Snowplow-macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Snowplow-macOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
-		752DAC0221CC3B380065F874 /* Snowplow_macOSTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Snowplow_macOSTests.m; sourceTree = "<group>"; };
-		752DAC0421CC3B380065F874 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		752DAC0C21CC3EEA0065F874 /* FMDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FMDB.framework; path = Carthage/Build/iOS/FMDB.framework; sourceTree = "<group>"; };
 		752DAC1021CC3F010065F874 /* FMDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FMDB.framework; path = Carthage/Build/watchOS/FMDB.framework; sourceTree = "<group>"; };
 		752DAC1221CC3F140065F874 /* Reachability.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reachability.framework; path = Carthage/Build/Mac/Reachability.framework; sourceTree = "<group>"; };
 		752DAC1321CC3F140065F874 /* FMDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FMDB.framework; path = Carthage/Build/Mac/FMDB.framework; sourceTree = "<group>"; };
-		7534D20722569F3400904EE5 /* TestScreenState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestScreenState.m; sourceTree = "<group>"; };
 		753DDA6C21F803B10007C3AE /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
-		754774BB2225FBA60043B814 /* SPScreenState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPScreenState.m; sourceTree = "<group>"; };
-		754774BF2225FBB90043B814 /* SPScreenState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPScreenState.h; sourceTree = "<group>"; };
-		754774CB222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+SPScreenView_SWIZZLE.h"; sourceTree = "<group>"; };
-		754774CC222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+SPScreenView_SWIZZLE.m"; sourceTree = "<group>"; };
 		75CAC3B921F28BD600271FB3 /* SnowplowIgluClient.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SnowplowIgluClient.framework; path = Carthage/Build/iOS/SnowplowIgluClient.framework; sourceTree = "<group>"; };
 		75CAC3C021F2930100271FB3 /* VVJSONSchemaValidation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VVJSONSchemaValidation.framework; path = Carthage/Build/iOS/VVJSONSchemaValidation.framework; sourceTree = "<group>"; };
-		75CAC3F121F2955000271FB3 /* TestSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestSession.m; sourceTree = "<group>"; };
-		75CAC3F221F2955000271FB3 /* TestSQLiteEventStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestSQLiteEventStore.m; sourceTree = "<group>"; };
-		75CAC3F321F2955000271FB3 /* TestPayload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestPayload.m; sourceTree = "<group>"; };
-		75CAC3F421F2955000271FB3 /* LegacyTestSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LegacyTestSubject.m; sourceTree = "<group>"; };
-		75CAC3F521F2955000271FB3 /* TestSelfDescribingJson.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestSelfDescribingJson.m; sourceTree = "<group>"; };
-		75CAC3F621F2955000271FB3 /* TestGeneratedJsons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestGeneratedJsons.m; sourceTree = "<group>"; };
 		75CAC3F921F2955000271FB3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = InfoPlist.strings; sourceTree = "<group>"; };
-		75CAC3FA21F2955000271FB3 /* LegacyTestEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LegacyTestEvent.m; sourceTree = "<group>"; };
-		75CAC3FB21F2955100271FB3 /* LegacyTestEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LegacyTestEmitter.m; sourceTree = "<group>"; };
-		75CAC3FC21F2955100271FB3 /* TestRequestResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestRequestResult.m; sourceTree = "<group>"; };
 		75CAC3FF21F2955100271FB3 /* iglu_resolver.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iglu_resolver.json; sourceTree = "<group>"; };
-		75CAC40021F2955100271FB3 /* LegacyTestTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LegacyTestTracker.m; sourceTree = "<group>"; };
-		75CAC40121F2955100271FB3 /* TestRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestRequest.m; sourceTree = "<group>"; };
-		75CAC40221F2955100271FB3 /* TestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestUtils.m; sourceTree = "<group>"; };
 		75CAC40321F2955100271FB3 /* SnowplowTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SnowplowTests-Info.plist"; sourceTree = "<group>"; };
 		75CAC40421F2955100271FB3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		75D6061E21C9CA8A00C7B016 /* Snowplow-umbrella-header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Snowplow-umbrella-header.h"; sourceTree = "<group>"; };
 		75F9C5C821FA2E0F00A5B8FC /* libSnowplow-iOS-Static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSnowplow-iOS-Static.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		AB0C27C0191B408200018557 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
 		AB0C27C4191B408200018557 /* Snowplow-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Snowplow-Prefix.pch"; sourceTree = "<group>"; };
-		AB0C27C5191B408200018557 /* SPTrackerConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerConstants.h; sourceTree = "<group>"; };
-		AB0C27E9191B43D600018557 /* SPEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmitter.h; sourceTree = "<group>"; };
-		AB0C27EA191B43D600018557 /* SPEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmitter.m; sourceTree = "<group>"; };
-		AB0C27F3191C67CD00018557 /* SPPayload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPayload.h; sourceTree = "<group>"; };
-		AB0C27F4191C67CD00018557 /* SPPayload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPayload.m; sourceTree = "<group>"; };
-		AB9E8210192DD336006744C9 /* SPTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTracker.h; sourceTree = "<group>"; };
-		AB9E8211192DD336006744C9 /* SPTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTracker.m; sourceTree = "<group>"; };
 		AB9E8213192DEC38006744C9 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
 		ABB67D8C192D9552009A1ECE /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
-		ABB767AE194974D3006275D1 /* SPSQLiteEventStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLiteEventStore.h; sourceTree = "<group>"; };
-		ABB767AF194974D3006275D1 /* SPSQLiteEventStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSQLiteEventStore.m; sourceTree = "<group>"; };
-		ABFCC3741922984A00FAE8FE /* SPUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPUtilities.h; sourceTree = "<group>"; };
-		ABFCC3751922984A00FAE8FE /* SPUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPUtilities.m; sourceTree = "<group>"; };
 		B3D9BE0F237ACE0D009B310A /* watchos.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = watchos.modulemap; sourceTree = "<group>"; };
-		CE4F9C5F244B066400968CFC /* SPTiming.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTiming.m; sourceTree = "<group>"; };
-		CE4F9C60244B066400968CFC /* SPConsentDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPConsentDocument.m; sourceTree = "<group>"; };
-		CE4F9C61244B066400968CFC /* SPConsentWithdrawn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPConsentWithdrawn.m; sourceTree = "<group>"; };
-		CE4F9C62244B066400968CFC /* SPForeground.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPForeground.m; sourceTree = "<group>"; };
-		CE4F9C63244B066400968CFC /* SPEcommerceItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEcommerceItem.m; sourceTree = "<group>"; };
-		CE4F9C64244B066400968CFC /* SPEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEvent.h; sourceTree = "<group>"; };
-		CE4F9C65244B066400968CFC /* SPSchemaRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSchemaRule.h; sourceTree = "<group>"; };
-		CE4F9C66244B066400968CFC /* SPPageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPageView.h; sourceTree = "<group>"; };
-		CE4F9C67244B066400968CFC /* SPTiming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTiming.h; sourceTree = "<group>"; };
-		CE4F9C68244B066400968CFC /* SPTrackerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTrackerEvent.h; sourceTree = "<group>"; };
-		CE4F9C69244B066400968CFC /* SPConsentWithdrawn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPConsentWithdrawn.h; sourceTree = "<group>"; };
-		CE4F9C6A244B066400968CFC /* SNOWError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNOWError.h; sourceTree = "<group>"; };
-		CE4F9C6B244B066400968CFC /* SPSelfDescribing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSelfDescribing.h; sourceTree = "<group>"; };
-		CE4F9C6C244B066400968CFC /* SPSelfDescribing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSelfDescribing.m; sourceTree = "<group>"; };
-		CE4F9C6D244B066400968CFC /* SPForeground.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPForeground.h; sourceTree = "<group>"; };
-		CE4F9C6E244B066400968CFC /* SPConsentGranted.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPConsentGranted.m; sourceTree = "<group>"; };
-		CE4F9C6F244B066400968CFC /* SPSchemaRuleset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSchemaRuleset.m; sourceTree = "<group>"; };
-		CE4F9C70244B066400968CFC /* SPGlobalContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPGlobalContext.h; sourceTree = "<group>"; };
-		CE4F9C71244B066400968CFC /* SNOWError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNOWError.m; sourceTree = "<group>"; };
-		CE4F9C72244B066400968CFC /* SPTrackerEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTrackerEvent.m; sourceTree = "<group>"; };
-		CE4F9C73244B066400968CFC /* SPScreenView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPScreenView.h; sourceTree = "<group>"; };
-		CE4F9C74244B066400968CFC /* SPPageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPageView.m; sourceTree = "<group>"; };
-		CE4F9C75244B066400968CFC /* SPStructured.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPStructured.m; sourceTree = "<group>"; };
-		CE4F9C76244B066400968CFC /* SPConsentGranted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPConsentGranted.h; sourceTree = "<group>"; };
-		CE4F9C77244B066400968CFC /* SPPushNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPushNotification.h; sourceTree = "<group>"; };
-		CE4F9C78244B066400968CFC /* SPPushNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPushNotification.m; sourceTree = "<group>"; };
-		CE4F9C79244B066400968CFC /* SPSchemaRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSchemaRule.m; sourceTree = "<group>"; };
-		CE4F9C7A244B066400968CFC /* SPStructured.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPStructured.h; sourceTree = "<group>"; };
-		CE4F9C7B244B066400968CFC /* SPEventBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEventBase.m; sourceTree = "<group>"; };
-		CE4F9C7C244B066400968CFC /* SPEventBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEventBase.h; sourceTree = "<group>"; };
-		CE4F9C7D244B066500968CFC /* SPBackground.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPBackground.m; sourceTree = "<group>"; };
-		CE4F9C7E244B066500968CFC /* SPEcommerceItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEcommerceItem.h; sourceTree = "<group>"; };
-		CE4F9C7F244B066500968CFC /* SPSchemaRuleset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSchemaRuleset.h; sourceTree = "<group>"; };
-		CE4F9C80244B066500968CFC /* SPScreenView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPScreenView.m; sourceTree = "<group>"; };
-		CE4F9C81244B066500968CFC /* SPConsentDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPConsentDocument.h; sourceTree = "<group>"; };
-		CE4F9C82244B066500968CFC /* SPBackground.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPBackground.h; sourceTree = "<group>"; };
-		CE4F9C83244B066500968CFC /* SPGlobalContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPGlobalContext.m; sourceTree = "<group>"; };
-		CE4F9C84244B066500968CFC /* SPEcommerce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEcommerce.h; sourceTree = "<group>"; };
-		CE4F9C85244B066500968CFC /* SPEcommerce.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEcommerce.m; sourceTree = "<group>"; };
-		D99BDC6C2834F89A00F6A14F /* TestLifecycleState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestLifecycleState.m; sourceTree = "<group>"; };
-		ED0EFE2B26E240B0002CAA21 /* SPDeepLinkReceived.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDeepLinkReceived.m; sourceTree = "<group>"; };
-		ED0EFE2C26E240B0002CAA21 /* SPDeepLinkReceived.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDeepLinkReceived.h; sourceTree = "<group>"; };
 		ED26E6B923842AAF0096AF7C /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AdSupport.framework; sourceTree = DEVELOPER_DIR; };
-		ED277BD02625F220002C7B6D /* SPConfigurationBundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPConfigurationBundle.h; sourceTree = "<group>"; };
-		ED277BD12625F220002C7B6D /* SPConfigurationBundle.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPConfigurationBundle.m; sourceTree = "<group>"; };
-		ED277BE02625F5C5002C7B6D /* SPFetchedConfigurationBundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPFetchedConfigurationBundle.h; sourceTree = "<group>"; };
-		ED277BE12625F5C5002C7B6D /* SPFetchedConfigurationBundle.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPFetchedConfigurationBundle.m; sourceTree = "<group>"; };
-		ED34672826415C1D0018BA61 /* SPJSONSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPJSONSerialization.h; sourceTree = "<group>"; };
-		ED34672926415C1D0018BA61 /* SPJSONSerialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPJSONSerialization.m; sourceTree = "<group>"; };
-		ED38D91D26EBCD58002AEC8E /* SPLifecycleEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPLifecycleEntity.h; sourceTree = "<group>"; };
-		ED38D91E26EBCD59002AEC8E /* SPLifecycleEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPLifecycleEntity.m; sourceTree = "<group>"; };
-		ED38D92726EBCEBE002AEC8E /* SPLifecycleState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPLifecycleState.h; sourceTree = "<group>"; };
-		ED38D92826EBCEBE002AEC8E /* SPLifecycleStateMachine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPLifecycleStateMachine.m; sourceTree = "<group>"; };
-		ED38D92926EBCEBE002AEC8E /* SPLifecycleStateMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPLifecycleStateMachine.h; sourceTree = "<group>"; };
-		ED38D92A26EBCEBE002AEC8E /* SPLifecycleState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPLifecycleState.m; sourceTree = "<group>"; };
-		ED49DF3A2757E4F400610843 /* SPSessionState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSessionState.h; sourceTree = "<group>"; };
-		ED49DF3B2757E4F400610843 /* SPSessionState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSessionState.m; sourceTree = "<group>"; };
 		ED6AC5152369D42800A8F8A3 /* ios.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = ios.modulemap; sourceTree = "<group>"; };
-		ED6B0327271094D700EFA12B /* SPMessageNotificationAttachment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPMessageNotificationAttachment.h; sourceTree = "<group>"; };
-		ED6B0328271094D700EFA12B /* SPMessageNotificationAttachment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPMessageNotificationAttachment.m; sourceTree = "<group>"; };
-		ED7CE16D26DFB55C0035C323 /* SPTrackerState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerState.h; sourceTree = "<group>"; };
-		ED7CE16E26DFB55C0035C323 /* SPTrackerState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPTrackerState.m; sourceTree = "<group>"; };
-		ED7CE17726DFBFA30035C323 /* SPTrackerStateSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerStateSnapshot.h; sourceTree = "<group>"; };
-		ED7CE17C26DFC12C0035C323 /* SPState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPState.h; sourceTree = "<group>"; };
-		ED7F080526190B5F005D377E /* TestRemoteConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestRemoteConfiguration.m; sourceTree = "<group>"; };
-		ED7F081326190E00005D377E /* SPConfigurationProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPConfigurationProvider.h; sourceTree = "<group>"; };
-		ED7F081426190E00005D377E /* SPConfigurationProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPConfigurationProvider.m; sourceTree = "<group>"; };
-		ED7F08282619199D005D377E /* SPRemoteConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPRemoteConfiguration.h; sourceTree = "<group>"; };
-		ED7F08292619199D005D377E /* SPRemoteConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPRemoteConfiguration.m; sourceTree = "<group>"; };
-		ED7F083E261924BF005D377E /* SPConfigurationFetcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPConfigurationFetcher.h; sourceTree = "<group>"; };
-		ED7F083F261924BF005D377E /* SPConfigurationFetcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPConfigurationFetcher.m; sourceTree = "<group>"; };
-		ED8122A825E9578500AE7FE8 /* SPSnowplow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSnowplow.h; sourceTree = "<group>"; };
-		ED8122A925E9578500AE7FE8 /* SPSnowplow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSnowplow.m; sourceTree = "<group>"; };
-		ED852B2E23A0E90E00F2DF6B /* SNOWReachability.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNOWReachability.m; sourceTree = "<group>"; };
-		ED852B3223A0EEC600F2DF6B /* SNOWReachability.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNOWReachability.h; sourceTree = "<group>"; };
-		ED87A3D825765DAE000C54EB /* SPSessionController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSessionController.h; sourceTree = "<group>"; };
-		ED87A3E925766BB4000C54EB /* SPSessionControllerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSessionControllerImpl.h; sourceTree = "<group>"; };
-		ED87A3EA25766BB4000C54EB /* SPSessionControllerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSessionControllerImpl.m; sourceTree = "<group>"; };
-		ED87A41B2577AC5B000C54EB /* SPTrackerController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerController.h; sourceTree = "<group>"; };
-		ED87A42C2577ADFF000C54EB /* SPTrackerControllerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerControllerImpl.h; sourceTree = "<group>"; };
-		ED87A42D2577ADFF000C54EB /* SPTrackerControllerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPTrackerControllerImpl.m; sourceTree = "<group>"; };
-		ED87A43C2577E440000C54EB /* TestTrackerController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestTrackerController.m; sourceTree = "<group>"; };
-		ED8866BD25711EC000DB53BB /* SPLoggerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPLoggerDelegate.h; sourceTree = "<group>"; };
-		ED8866E02571445300DB53BB /* SPSubjectConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSubjectConfiguration.h; sourceTree = "<group>"; };
-		ED8866E12571445300DB53BB /* SPSubjectConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSubjectConfiguration.m; sourceTree = "<group>"; };
-		ED8866FC25715DD600DB53BB /* SPConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPConfiguration.h; sourceTree = "<group>"; };
-		ED8866FD25715DD600DB53BB /* SPConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPConfiguration.m; sourceTree = "<group>"; };
-		ED8867292573C1EF00DB53BB /* SPSessionConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSessionConfiguration.h; sourceTree = "<group>"; };
-		ED88672A2573C1EF00DB53BB /* SPSessionConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSessionConfiguration.m; sourceTree = "<group>"; };
-		ED88B5672578F8820048FAD1 /* SPEmitterConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPEmitterConfiguration.h; sourceTree = "<group>"; };
-		ED88B5682578F8820048FAD1 /* SPEmitterConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPEmitterConfiguration.m; sourceTree = "<group>"; };
-		ED88B58E257922490048FAD1 /* SPEmitterController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPEmitterController.h; sourceTree = "<group>"; };
-		ED88B5A525792C620048FAD1 /* SPEmitterControllerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPEmitterControllerImpl.h; sourceTree = "<group>"; };
-		ED88B5A625792C620048FAD1 /* SPEmitterControllerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPEmitterControllerImpl.m; sourceTree = "<group>"; };
-		ED88B5DD257950210048FAD1 /* SPGDPRConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGDPRConfiguration.h; sourceTree = "<group>"; };
-		ED88B5DE257950210048FAD1 /* SPGDPRConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPGDPRConfiguration.m; sourceTree = "<group>"; };
-		ED88B5FA257954370048FAD1 /* SPGDPRController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGDPRController.h; sourceTree = "<group>"; };
-		ED88B60B257956490048FAD1 /* SPGDPRControllerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGDPRControllerImpl.h; sourceTree = "<group>"; };
-		ED88B60C257956490048FAD1 /* SPGDPRControllerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPGDPRControllerImpl.m; sourceTree = "<group>"; };
-		ED88B627257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGlobalContextsConfiguration.h; sourceTree = "<group>"; };
-		ED88B628257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPGlobalContextsConfiguration.m; sourceTree = "<group>"; };
-		ED88B649257A57F80048FAD1 /* SPGlobalContextsController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGlobalContextsController.h; sourceTree = "<group>"; };
-		ED88B666257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGlobalContextsControllerImpl.h; sourceTree = "<group>"; };
-		ED88B667257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPGlobalContextsControllerImpl.m; sourceTree = "<group>"; };
-		ED88B6B2258253F20048FAD1 /* TestEvents.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestEvents.m; sourceTree = "<group>"; };
-		ED88B6D92583DFC70048FAD1 /* SPServiceProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPServiceProvider.h; sourceTree = "<group>"; };
-		ED88B6DA2583DFC70048FAD1 /* SPServiceProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPServiceProvider.m; sourceTree = "<group>"; };
-		ED88B7332587777A0048FAD1 /* SPEmitterEventProcessing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPEmitterEventProcessing.h; sourceTree = "<group>"; };
-		ED88B76C2587B4EF0048FAD1 /* SPNetworkController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPNetworkController.h; sourceTree = "<group>"; };
-		ED88B78F2587B5620048FAD1 /* SPNetworkControllerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPNetworkControllerImpl.h; sourceTree = "<group>"; };
-		ED88B7902587B5620048FAD1 /* SPNetworkControllerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPNetworkControllerImpl.m; sourceTree = "<group>"; };
-		ED8BF8B125700B3F001DFDD9 /* SPTrackerConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerConfiguration.h; sourceTree = "<group>"; };
-		ED8BF8B225700B3F001DFDD9 /* SPTrackerConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPTrackerConfiguration.m; sourceTree = "<group>"; };
-		ED8BF8C925701853001DFDD9 /* SPNetworkConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPNetworkConfiguration.h; sourceTree = "<group>"; };
-		ED8BF8CA25701853001DFDD9 /* SPNetworkConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPNetworkConfiguration.m; sourceTree = "<group>"; };
-		ED8BF8EC2570FB2F001DFDD9 /* TestTrackerConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestTrackerConfiguration.m; sourceTree = "<group>"; };
-		ED9081AF2703747B00EE9421 /* SPMessageNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMessageNotification.m; sourceTree = "<group>"; };
-		ED9081B02703747C00EE9421 /* SPMessageNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMessageNotification.h; sourceTree = "<group>"; };
-		ED914EB42431FEE70068DA0A /* TestGlobalContexts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestGlobalContexts.m; sourceTree = "<group>"; };
-		ED914EB62432081F0068DA0A /* TestSchemaRuleset.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestSchemaRuleset.m; sourceTree = "<group>"; };
-		ED914EB824325AB40068DA0A /* SPGdprContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGdprContext.h; sourceTree = "<group>"; };
-		ED914EB924325AB40068DA0A /* SPGdprContext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPGdprContext.m; sourceTree = "<group>"; };
-		ED91CB6B23AA715B0078E75F /* SPDevicePlatform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPDevicePlatform.h; sourceTree = "<group>"; };
-		ED91CB7023AA8AD50078E75F /* SPDevicePlatform.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPDevicePlatform.m; sourceTree = "<group>"; };
-		ED9897142627006F00145157 /* NSDictionary+SP_TypeMethods.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+SP_TypeMethods.h"; sourceTree = "<group>"; };
-		ED9897152627006F00145157 /* NSDictionary+SP_TypeMethods.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+SP_TypeMethods.m"; sourceTree = "<group>"; };
-		ED98972426287F7900145157 /* SPConfigurationCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPConfigurationCache.h; sourceTree = "<group>"; };
-		ED98972526287F7900145157 /* SPConfigurationCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPConfigurationCache.m; sourceTree = "<group>"; };
-		EDA06FB42664CD2F007FA773 /* SPMockEventStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPMockEventStore.h; sourceTree = "<group>"; };
-		EDA06FB52664CD2F007FA773 /* SPMockEventStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPMockEventStore.m; sourceTree = "<group>"; };
-		EDAB65CC26CBD5150067755F /* SPDeepLinkEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDeepLinkEntity.h; sourceTree = "<group>"; };
-		EDAB65CD26CBD5150067755F /* SPDeepLinkEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDeepLinkEntity.m; sourceTree = "<group>"; };
-		EDAB662F26D699D80067755F /* SPStateFuture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPStateFuture.m; sourceTree = "<group>"; };
-		EDAB663026D699D90067755F /* SPStateManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPStateManager.h; sourceTree = "<group>"; };
-		EDAB663126D699D90067755F /* SPStateManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPStateManager.m; sourceTree = "<group>"; };
-		EDAB663226D699D90067755F /* SPStateFuture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPStateFuture.h; sourceTree = "<group>"; };
-		EDAB663326D699D90067755F /* SPStateMachineProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPStateMachineProtocol.h; sourceTree = "<group>"; };
-		EDAB664826D69A160067755F /* TestStateManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestStateManager.m; sourceTree = "<group>"; };
-		EDAB664E26D69D740067755F /* SPScreenStateMachine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPScreenStateMachine.m; sourceTree = "<group>"; };
-		EDAB664F26D69D740067755F /* SPScreenStateMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPScreenStateMachine.h; sourceTree = "<group>"; };
-		EDAB665426D6AA940067755F /* SPDeepLinkStateMachine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPDeepLinkStateMachine.h; sourceTree = "<group>"; };
-		EDAB665526D6AA940067755F /* SPDeepLinkStateMachine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPDeepLinkStateMachine.m; sourceTree = "<group>"; };
-		EDAB665E26D6ACCB0067755F /* SPDeepLinkState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPDeepLinkState.h; sourceTree = "<group>"; };
-		EDAB665F26D6ACCB0067755F /* SPDeepLinkState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPDeepLinkState.m; sourceTree = "<group>"; };
-		EDB2FD1726C130B80031B872 /* SPDataPersistence.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPDataPersistence.h; sourceTree = "<group>"; };
-		EDB2FD1826C130B80031B872 /* SPDataPersistence.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPDataPersistence.m; sourceTree = "<group>"; };
-		EDB2FD2126C57F6C0031B872 /* TestDataPersistence.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestDataPersistence.m; sourceTree = "<group>"; };
-		EDB693F826B7F61D00B76A79 /* SPMemoryEventStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPMemoryEventStore.h; sourceTree = "<group>"; };
-		EDB693F926B7F61D00B76A79 /* SPMemoryEventStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPMemoryEventStore.m; sourceTree = "<group>"; };
-		EDB6940226B83BF300B76A79 /* TestMemoryEventStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestMemoryEventStore.m; sourceTree = "<group>"; };
-		EDCBD0D724F5084800D39DD2 /* TestNetworkConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestNetworkConnection.m; sourceTree = "<group>"; };
-		EDD8540A24EE786900661F6B /* SPEventStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPEventStore.h; sourceTree = "<group>"; };
-		EDD8541424EEC25000661F6B /* SPEmitterEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPEmitterEvent.h; sourceTree = "<group>"; };
-		EDD8541524EEC25100661F6B /* SPEmitterEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPEmitterEvent.m; sourceTree = "<group>"; };
-		EDD8541E24EFEFB900661F6B /* SPDefaultNetworkConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPDefaultNetworkConnection.h; sourceTree = "<group>"; };
-		EDD8541F24EFEFB900661F6B /* SPDefaultNetworkConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPDefaultNetworkConnection.m; sourceTree = "<group>"; };
-		EDD8542824EFEFE600661F6B /* SPNetworkConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPNetworkConnection.h; sourceTree = "<group>"; };
-		EDD8543224EFFFB300661F6B /* SPRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPRequest.h; sourceTree = "<group>"; };
-		EDD8543324EFFFB300661F6B /* SPRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPRequest.m; sourceTree = "<group>"; };
-		EDDD6FFB264E873B00259404 /* SPController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPController.h; sourceTree = "<group>"; };
-		EDDD6FFC264E873B00259404 /* SPController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPController.m; sourceTree = "<group>"; };
-		EDDD7005264E8ECE00259404 /* SPServiceProviderProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPServiceProviderProtocol.h; sourceTree = "<group>"; };
-		EDDD700F264F1D2100259404 /* SPTrackerConfigurationUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerConfigurationUpdate.h; sourceTree = "<group>"; };
-		EDDD7010264F1D2100259404 /* SPTrackerConfigurationUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPTrackerConfigurationUpdate.m; sourceTree = "<group>"; };
-		EDDD7019264F230400259404 /* SPSubjectConfigurationUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSubjectConfigurationUpdate.h; sourceTree = "<group>"; };
-		EDDD701A264F230400259404 /* SPSubjectConfigurationUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSubjectConfigurationUpdate.m; sourceTree = "<group>"; };
-		EDDD7023264F23C600259404 /* SPGDPRConfigurationUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPGDPRConfigurationUpdate.h; sourceTree = "<group>"; };
-		EDDD7024264F23C600259404 /* SPGDPRConfigurationUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPGDPRConfigurationUpdate.m; sourceTree = "<group>"; };
-		EDDD702D264F25A200259404 /* SPSessionConfigurationUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSessionConfigurationUpdate.h; sourceTree = "<group>"; };
-		EDDD702E264F25A200259404 /* SPSessionConfigurationUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSessionConfigurationUpdate.m; sourceTree = "<group>"; };
-		EDDD7037264F27E700259404 /* SPNetworkConfigurationUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPNetworkConfigurationUpdate.h; sourceTree = "<group>"; };
-		EDDD7038264F27E700259404 /* SPNetworkConfigurationUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPNetworkConfigurationUpdate.m; sourceTree = "<group>"; };
-		EDDD7041264F2A8800259404 /* SPEmitterConfigurationUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPEmitterConfigurationUpdate.h; sourceTree = "<group>"; };
-		EDDD7042264F2A8800259404 /* SPEmitterConfigurationUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPEmitterConfigurationUpdate.m; sourceTree = "<group>"; };
-		EDE54F4725EFA38D0073947D /* TestMultipleInstances.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestMultipleInstances.m; sourceTree = "<group>"; };
-		EDEE834924BDB326000B8530 /* SPTrackerError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPTrackerError.h; sourceTree = "<group>"; };
-		EDEE834A24BDB326000B8530 /* SPTrackerError.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPTrackerError.m; sourceTree = "<group>"; };
-		EDEE835824BE0944000B8530 /* SPLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPLogger.h; sourceTree = "<group>"; };
-		EDEE835924BE0944000B8530 /* SPLogger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPLogger.m; sourceTree = "<group>"; };
-		EDEE836224C0C317000B8530 /* TestLogger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestLogger.m; sourceTree = "<group>"; };
-		EDF2A1B326402D52009032AB /* SPSubjectController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSubjectController.h; sourceTree = "<group>"; };
-		EDF2A1B8264032F9009032AB /* SPSubjectControllerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPSubjectControllerImpl.h; sourceTree = "<group>"; };
-		EDF2A1B9264032F9009032AB /* SPSubjectControllerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPSubjectControllerImpl.m; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -1279,9 +830,125 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
-		752DABD821CC38560065F874 /* Snowplow iOSTests */ = {
+		6BE70847292F63D000911E55 /* Controllers */ = {
 			isa = PBXGroup;
 			children = (
+				6B0CCFBD292275580054954B /* Controller.swift */,
+				6B85873A2923E024006E4A5F /* EmitterController.swift */,
+				6B0CCFD329236D4A0054954B /* SubjectController.swift */,
+				6B0CCFB4292262640054954B /* GDPRController.swift */,
+				6B8586F4292388C9006E4A5F /* SessionController.swift */,
+				6B85874F2923E7CF006E4A5F /* NetworkController.swift */,
+				6B85872B2923C5F0006E4A5F /* TrackerController.swift */,
+				6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */,
+			);
+			path = Controllers;
+			sourceTree = "<group>";
+		};
+		6BE70848292F648000911E55 /* Emitter */ = {
+			isa = PBXGroup;
+			children = (
+				6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */,
+				6BE70849292F64A700911E55 /* BufferOption.swift */,
+				6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */,
+				6B85870D2923C18C006E4A5F /* EventStore.swift */,
+			);
+			path = Emitter;
+			sourceTree = "<group>";
+		};
+		6BE7084B292F653100911E55 /* GlobalContexts */ = {
+			isa = PBXGroup;
+			children = (
+				6BE7084C292F654D00911E55 /* ContextGenerator.swift */,
+				6B9DA675292BD2D7006D721A /* GlobalContext.swift */,
+				6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */,
+			);
+			path = GlobalContexts;
+			sourceTree = "<group>";
+		};
+		6BE7085A292F668F00911E55 /* Network */ = {
+			isa = PBXGroup;
+			children = (
+				6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */,
+				6BE70860292F66E800911E55 /* ProtocolOptions.swift */,
+				6B9DA5BF2927BCD5006D721A /* Request.swift */,
+				6B9DA5C02927BCD5006D721A /* RequestCallback.swift */,
+				6B9DA5C12927BCD6006D721A /* RequestResult.swift */,
+				6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */,
+				6B85877729251CBD006E4A5F /* NetworkConnection.swift */,
+			);
+			path = Network;
+			sourceTree = "<group>";
+		};
+		6BE70866292F678000911E55 /* Tracker */ = {
+			isa = PBXGroup;
+			children = (
+				6B9DA646292B5487006D721A /* DevicePlatform.swift */,
+				6BE70867292F67C000911E55 /* InspectableEvent.swift */,
+				6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */,
+				6BE7086C292F683600911E55 /* LogLevel.swift */,
+				6B9DA6232928B2FB006D721A /* SessionState.swift */,
+				6B9DA6002927E669006D721A /* State.swift */,
+				6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */,
+				6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */,
+			);
+			path = Tracker;
+			sourceTree = "<group>";
+		};
+		6BE70871292F686A00911E55 /* Utils */ = {
+			isa = PBXGroup;
+			children = (
+				6B9DA669292BBC83006D721A /* SPSize.swift */,
+				6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */,
+			);
+			path = Utils;
+			sourceTree = "<group>";
+		};
+		6BEBA0EF29195AF700B5737F /* Sources */ = {
+			isa = PBXGroup;
+			children = (
+				6BEBA0F029195B1700B5737F /* Core */,
+				AB0C27C2191B408200018557 /* Snowplow */,
+			);
+			path = Sources;
+			sourceTree = "<group>";
+		};
+		6BEBA0F029195B1700B5737F /* Core */ = {
+			isa = PBXGroup;
+			children = (
+				6B9DA68C292BF03B006D721A /* TrackerConstants.swift */,
+				ED7F080426190AF2005D377E /* RemoteConfiguration */,
+				EDC9B37B255C3DC600F4136D /* Subject */,
+				ED88B5F9257953EE0048FAD1 /* GDPR */,
+				EDC9B37A255C3D3700F4136D /* ScreenViewTracking */,
+				EDC9B36D255C3C8A00F4136D /* Session */,
+				EDC9B35A255C3B9700F4136D /* Utils */,
+				EDC9B353255B02AB00F4136D /* Storage */,
+				EDC9B352255B027E00F4136D /* NetworkConnection */,
+				EDC9B351255AEEBC00F4136D /* Tracker */,
+				EDC9B34F255AEC7E00F4136D /* Logger */,
+				EDC9B34E255AE66D00F4136D /* GlobalContexts */,
+				EDC9B34D255AE46300F4136D /* Emitter */,
+			);
+			path = Core;
+			sourceTree = "<group>";
+		};
+		752DABD821CC38560065F874 /* Tests */ = {
+			isa = PBXGroup;
+			children = (
+				6B9DA6D1292E5F5E006D721A /* TestLifecycleState.swift */,
+				6B9DA6C9292E5DD9006D721A /* TestMemoryEventStore.swift */,
+				6B9DA6C7292E5B2A006D721A /* TestPlatformContext.swift */,
+				6B9DA6C5292E589E006D721A /* TestWebViewMessageHandler.swift */,
+				6B9DA6C3292E5760006D721A /* TestSQLiteEventStore.swift */,
+				6B9DA6BC292E2923006D721A /* TestGeneratedJsons.swift */,
+				6B9DA6BA292E258A006D721A /* TestNetworkConnection.swift */,
+				6B9DA6B4292E1181006D721A /* TestRequest.swift */,
+				6B9DA6B1292D203E006D721A /* TestSession.swift */,
+				6B9DA6B0292D203E006D721A /* TestStateManager.swift */,
+				6B9DA6A4292D051A006D721A /* TestRequestResult.swift */,
+				6B9DA6A2292CFF47006D721A /* TestPayload.swift */,
+				6B9DA6A0292CBFEA006D721A /* TestEvents.swift */,
 				EDA06FB32664CD02007FA773 /* Utils */,
 				ED88B6C62583A3770048FAD1 /* Legacy Tests */,
 				ED8BF8EB2570FAFB001DFDD9 /* Configurations */,
@@ -1290,37 +957,16 @@
 				75CAC40421F2955100271FB3 /* Info.plist */,
 				75CAC3FE21F2955100271FB3 /* Products */,
 				75CAC40321F2955100271FB3 /* SnowplowTests-Info.plist */,
-				EDCBD0D724F5084800D39DD2 /* TestNetworkConnection.m */,
-				ED88B6B2258253F20048FAD1 /* TestEvents.m */,
-				EDB6940226B83BF300B76A79 /* TestMemoryEventStore.m */,
-				75CAC3F221F2955000271FB3 /* TestSQLiteEventStore.m */,
-				75CAC3F621F2955000271FB3 /* TestGeneratedJsons.m */,
-				75CAC3F321F2955000271FB3 /* TestPayload.m */,
-				75CAC40121F2955100271FB3 /* TestRequest.m */,
-				75CAC3FC21F2955100271FB3 /* TestRequestResult.m */,
-				75CAC3F521F2955000271FB3 /* TestSelfDescribingJson.m */,
-				75CAC3F121F2955000271FB3 /* TestSession.m */,
-				75CAC40221F2955100271FB3 /* TestUtils.m */,
-				7534D20722569F3400904EE5 /* TestScreenState.m */,
-				D99BDC6C2834F89A00F6A14F /* TestLifecycleState.m */,
-				EDEE836224C0C317000B8530 /* TestLogger.m */,
-				EDB2FD2126C57F6C0031B872 /* TestDataPersistence.m */,
-				EDAB664826D69A160067755F /* TestStateManager.m */,
-				6BF15D0B2702ECD70048F376 /* TestPlatformContext.m */,
-				6BF15D1227035A480048F376 /* TestSubject.m */,
-				6B4B77D127C64F6000F4E878 /* TestServiceProvider.m */,
-				6BD6A6A828871262002D6D40 /* TestWebViewMessageHandler.m */,
-			);
-			path = "Snowplow iOSTests";
-			sourceTree = "<group>";
-		};
-		752DAC0121CC3B380065F874 /* Snowplow macOSTests */ = {
-			isa = PBXGroup;
-			children = (
-				752DAC0221CC3B380065F874 /* Snowplow_macOSTests.m */,
-				752DAC0421CC3B380065F874 /* Info.plist */,
-			);
-			path = "Snowplow macOSTests";
+				6B9DA6BE292E2B16006D721A /* Tests-bridging-header.h */,
+				6B9DA6CB292E5DF9006D721A /* TestSelfDescribingJson.swift */,
+				6B9DA6CD292E5E39006D721A /* TestScreenState.swift */,
+				6B9DA6CF292E5E7F006D721A /* TestUtils.swift */,
+				6B9DA6D3292E5F71006D721A /* TestLogger.swift */,
+				6B9DA6D5292E5FB6006D721A /* TestDataPersistence.swift */,
+				6B9DA6D7292E5FE6006D721A /* TestSubject.swift */,
+				6B9DA6D9292E6010006D721A /* TestServiceProvider.swift */,
+			);
+			path = Tests;
 			sourceTree = "<group>";
 		};
 		75CAC3F721F2955000271FB3 /* en.lproj */ = {
@@ -1342,9 +988,8 @@
 		AB0C27B4191B408200018557 = {
 			isa = PBXGroup;
 			children = (
-				752DABD821CC38560065F874 /* Snowplow iOSTests */,
-				752DAC0121CC3B380065F874 /* Snowplow macOSTests */,
-				AB0C27C2191B408200018557 /* Snowplow */,
+				6BEBA0EF29195AF700B5737F /* Sources */,
+				752DABD821CC38560065F874 /* Tests */,
 				AB0C27BF191B408200018557 /* Frameworks */,
 				AB0C27BE191B408200018557 /* Products */,
 			);
@@ -1354,7 +999,7 @@
 			isa = PBXGroup;
 			children = (
 				752DABCC21CC38550065F874 /* SnowplowTracker.framework */,
-				752DABD421CC38560065F874 /* Snowplow-iOSTests.xctest */,
+				752DABD421CC38560065F874 /* Tests.xctest */,
 				752DABE821CC3A090065F874 /* SnowplowTracker.framework */,
 				752DABF521CC3B380065F874 /* Snowplow_macOS.framework */,
 				752DABFD21CC3B380065F874 /* Snowplow-macOSTests.xctest */,
@@ -1387,9 +1032,18 @@
 		AB0C27C2191B408200018557 /* Snowplow */ = {
 			isa = PBXGroup;
 			children = (
-				EDD53F7E25473C430000E57D /* Internal */,
-				75D6061E21C9CA8A00C7B016 /* Snowplow-umbrella-header.h */,
+				6B8587582924CDB0006E4A5F /* Snowplow.swift */,
+				ED8BF8B0256FFEA4001DFDD9 /* Configurations */,
+				6BE70847292F63D000911E55 /* Controllers */,
+				6BE70848292F648000911E55 /* Emitter */,
+				EDAB65D626CD2F970067755F /* Entities */,
+				EDC9B346255ACF5100F4136D /* Events */,
+				6BE7084B292F653100911E55 /* GlobalContexts */,
+				6BE7085A292F668F00911E55 /* Network */,
+				EDC9B350255AEDF000F4136D /* Payload */,
 				AB0C27C3191B408200018557 /* Supporting Files */,
+				6BE70866292F678000911E55 /* Tracker */,
+				6BE70871292F686A00911E55 /* Utils */,
 			);
 			path = Snowplow;
 			sourceTree = "<group>";
@@ -1407,15 +1061,10 @@
 		ED7F080426190AF2005D377E /* RemoteConfiguration */ = {
 			isa = PBXGroup;
 			children = (
-				ED7F081326190E00005D377E /* SPConfigurationProvider.h */,
-				ED7F081426190E00005D377E /* SPConfigurationProvider.m */,
-				ED7F083E261924BF005D377E /* SPConfigurationFetcher.h */,
-				ED7F083F261924BF005D377E /* SPConfigurationFetcher.m */,
-				ED277BE02625F5C5002C7B6D /* SPFetchedConfigurationBundle.h */,
-				ED277BE12625F5C5002C7B6D /* SPFetchedConfigurationBundle.m */,
-				ED98972426287F7900145157 /* SPConfigurationCache.h */,
-				ED98972526287F7900145157 /* SPConfigurationCache.m */,
-				6BACDF912897C2580013276E /* SPConfigurationState.h */,
+				6B858767292509AC006E4A5F /* ConfigurationCache.swift */,
+				6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */,
+				6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */,
+				6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */,
 			);
 			path = RemoteConfiguration;
 			sourceTree = "<group>";
@@ -1423,13 +1072,9 @@
 		ED88B5F9257953EE0048FAD1 /* GDPR */ = {
 			isa = PBXGroup;
 			children = (
-				ED88B5FA257954370048FAD1 /* SPGDPRController.h */,
-				ED88B60B257956490048FAD1 /* SPGDPRControllerImpl.h */,
-				ED88B60C257956490048FAD1 /* SPGDPRControllerImpl.m */,
-				EDDD7023264F23C600259404 /* SPGDPRConfigurationUpdate.h */,
-				EDDD7024264F23C600259404 /* SPGDPRConfigurationUpdate.m */,
-				ED914EB824325AB40068DA0A /* SPGdprContext.h */,
-				ED914EB924325AB40068DA0A /* SPGdprContext.m */,
+				6B0CCFCD29236B0A0054954B /* GDPRContext.swift */,
+				6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */,
+				6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */,
 			);
 			path = GDPR;
 			sourceTree = "<group>";
@@ -1437,10 +1082,9 @@
 		ED88B6C62583A3770048FAD1 /* Legacy Tests */ = {
 			isa = PBXGroup;
 			children = (
-				75CAC3FA21F2955000271FB3 /* LegacyTestEvent.m */,
-				75CAC3F421F2955000271FB3 /* LegacyTestSubject.m */,
-				75CAC40021F2955100271FB3 /* LegacyTestTracker.m */,
-				75CAC3FB21F2955100271FB3 /* LegacyTestEmitter.m */,
+				6B9DA6BF292E4D74006D721A /* LegacyTestEmitter.swift */,
+				6B9DA6C0292E4D74006D721A /* LegacyTestTracker.swift */,
+				6B9DA6DB292E6034006D721A /* LegacyTestSubject.swift */,
 			);
 			path = "Legacy Tests";
 			sourceTree = "<group>";
@@ -1448,26 +1092,17 @@
 		ED8BF8B0256FFEA4001DFDD9 /* Configurations */ = {
 			isa = PBXGroup;
 			children = (
-				ED8866FC25715DD600DB53BB /* SPConfiguration.h */,
-				ED8866FD25715DD600DB53BB /* SPConfiguration.m */,
-				ED277BD02625F220002C7B6D /* SPConfigurationBundle.h */,
-				ED277BD12625F220002C7B6D /* SPConfigurationBundle.m */,
-				ED8BF8B125700B3F001DFDD9 /* SPTrackerConfiguration.h */,
-				ED8BF8B225700B3F001DFDD9 /* SPTrackerConfiguration.m */,
-				ED8BF8C925701853001DFDD9 /* SPNetworkConfiguration.h */,
-				ED8BF8CA25701853001DFDD9 /* SPNetworkConfiguration.m */,
-				ED8866E02571445300DB53BB /* SPSubjectConfiguration.h */,
-				ED8866E12571445300DB53BB /* SPSubjectConfiguration.m */,
-				ED8867292573C1EF00DB53BB /* SPSessionConfiguration.h */,
-				ED88672A2573C1EF00DB53BB /* SPSessionConfiguration.m */,
-				ED88B5672578F8820048FAD1 /* SPEmitterConfiguration.h */,
-				ED88B5682578F8820048FAD1 /* SPEmitterConfiguration.m */,
-				ED88B5DD257950210048FAD1 /* SPGDPRConfiguration.h */,
-				ED88B5DE257950210048FAD1 /* SPGDPRConfiguration.m */,
-				ED88B627257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h */,
-				ED88B628257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m */,
-				ED7F08282619199D005D377E /* SPRemoteConfiguration.h */,
-				ED7F08292619199D005D377E /* SPRemoteConfiguration.m */,
+				6B85877129251779006E4A5F /* ConfigurationState.swift */,
+				6BABED52291E91E000F6798A /* RemoteConfiguration.swift */,
+				6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */,
+				6BABED48291E884900F6798A /* ConfigurationBundle.swift */,
+				6BABED39291E60F000F6798A /* GDPRConfiguration.swift */,
+				6BABED34291E596100F6798A /* EmitterConfiguration.swift */,
+				6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */,
+				6BABED20291E3DB200F6798A /* Configuration.swift */,
+				6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */,
+				6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */,
+				6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */,
 			);
 			path = Configurations;
 			sourceTree = "<group>";
@@ -1475,11 +1110,11 @@
 		ED8BF8EB2570FAFB001DFDD9 /* Configurations */ = {
 			isa = PBXGroup;
 			children = (
-				ED8BF8EC2570FB2F001DFDD9 /* TestTrackerConfiguration.m */,
-				ED87A43C2577E440000C54EB /* TestTrackerController.m */,
-				EDE54F4725EFA38D0073947D /* TestMultipleInstances.m */,
-				ED7F080526190B5F005D377E /* TestRemoteConfiguration.m */,
-				6B871F6027C3913300BCF742 /* TestEmitterConfiguration.m */,
+				6B9DA6A7292D060E006D721A /* TestEmitterConfiguration.swift */,
+				6B9DA6A8292D060E006D721A /* TestMultipleInstances.swift */,
+				6B9DA6A9292D060F006D721A /* TestRemoteConfiguration.swift */,
+				6B9DA6A6292D060E006D721A /* TestTrackerConfiguration.swift */,
+				6B9DA6AA292D060F006D721A /* TestTrackerController.swift */,
 			);
 			path = Configurations;
 			sourceTree = "<group>";
@@ -1487,8 +1122,8 @@
 		ED914EB02430D03A0068DA0A /* Global Contexts */ = {
 			isa = PBXGroup;
 			children = (
-				ED914EB42431FEE70068DA0A /* TestGlobalContexts.m */,
-				ED914EB62432081F0068DA0A /* TestSchemaRuleset.m */,
+				6B9DA6B7292E15FF006D721A /* TestGlobalContexts.swift */,
+				6B9DA6B6292E15FE006D721A /* TestSchemaRuleset.swift */,
 			);
 			path = "Global Contexts";
 			sourceTree = "<group>";
@@ -1496,16 +1131,11 @@
 		EDA06FB32664CD02007FA773 /* Utils */ = {
 			isa = PBXGroup;
 			children = (
-				EDA06FB42664CD2F007FA773 /* SPMockEventStore.h */,
-				EDA06FB52664CD2F007FA773 /* SPMockEventStore.m */,
-				6BF08DAE270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h */,
-				6BF08DAF270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m */,
-				6B871F6227C3928900BCF742 /* SPMockNetworkConnection.h */,
-				6B871F6327C3928900BCF742 /* SPMockNetworkConnection.m */,
-				6BD6A6AA288719C7002D6D40 /* SPMockWKScriptMessage.h */,
-				6BD6A6AB288719C7002D6D40 /* SPMockWKScriptMessage.m */,
-				6BA1492F29005EF700407200 /* SPMockLoggerDelegate.h */,
-				6BA1493029005EF700407200 /* SPMockLoggerDelegate.m */,
+				6B9DA699292CB9A3006D721A /* MockDeviceInfoMonitor.swift */,
+				6B9DA698292CB9A3006D721A /* MockEventStore.swift */,
+				6B9DA697292CB9A3006D721A /* MockLoggerDelegate.swift */,
+				6B9DA696292CB9A3006D721A /* MockNetworkConnection.swift */,
+				6B9DA69A292CB9A3006D721A /* MockWKScriptMessage.swift */,
 			);
 			path = Utils;
 			sourceTree = "<group>";
@@ -1513,10 +1143,8 @@
 		EDAB65D626CD2F970067755F /* Entities */ = {
 			isa = PBXGroup;
 			children = (
-				ED38D91D26EBCD58002AEC8E /* SPLifecycleEntity.h */,
-				ED38D91E26EBCD59002AEC8E /* SPLifecycleEntity.m */,
-				EDAB65CC26CBD5150067755F /* SPDeepLinkEntity.h */,
-				EDAB65CD26CBD5150067755F /* SPDeepLinkEntity.m */,
+				6B9DA53E29253951006D721A /* DeepLinkEntity.swift */,
+				6B9DA53D29253951006D721A /* LifecycleEntity.swift */,
 			);
 			path = Entities;
 			sourceTree = "<group>";
@@ -1524,45 +1152,25 @@
 		EDC9B346255ACF5100F4136D /* Events */ = {
 			isa = PBXGroup;
 			children = (
-				EDEE834924BDB326000B8530 /* SPTrackerError.h */,
-				EDEE834A24BDB326000B8530 /* SPTrackerError.m */,
-				CE4F9C6A244B066400968CFC /* SNOWError.h */,
-				CE4F9C71244B066400968CFC /* SNOWError.m */,
-				CE4F9C82244B066500968CFC /* SPBackground.h */,
-				CE4F9C7D244B066500968CFC /* SPBackground.m */,
-				CE4F9C81244B066500968CFC /* SPConsentDocument.h */,
-				CE4F9C60244B066400968CFC /* SPConsentDocument.m */,
-				CE4F9C76244B066400968CFC /* SPConsentGranted.h */,
-				CE4F9C6E244B066400968CFC /* SPConsentGranted.m */,
-				CE4F9C69244B066400968CFC /* SPConsentWithdrawn.h */,
-				CE4F9C61244B066400968CFC /* SPConsentWithdrawn.m */,
-				ED0EFE2C26E240B0002CAA21 /* SPDeepLinkReceived.h */,
-				ED0EFE2B26E240B0002CAA21 /* SPDeepLinkReceived.m */,
-				CE4F9C84244B066500968CFC /* SPEcommerce.h */,
-				CE4F9C85244B066500968CFC /* SPEcommerce.m */,
-				CE4F9C7E244B066500968CFC /* SPEcommerceItem.h */,
-				CE4F9C63244B066400968CFC /* SPEcommerceItem.m */,
-				CE4F9C64244B066400968CFC /* SPEvent.h */,
-				CE4F9C7C244B066400968CFC /* SPEventBase.h */,
-				CE4F9C7B244B066400968CFC /* SPEventBase.m */,
-				CE4F9C6D244B066400968CFC /* SPForeground.h */,
-				CE4F9C62244B066400968CFC /* SPForeground.m */,
-				ED9081B02703747C00EE9421 /* SPMessageNotification.h */,
-				ED9081AF2703747B00EE9421 /* SPMessageNotification.m */,
-				ED6B0327271094D700EFA12B /* SPMessageNotificationAttachment.h */,
-				ED6B0328271094D700EFA12B /* SPMessageNotificationAttachment.m */,
-				CE4F9C66244B066400968CFC /* SPPageView.h */,
-				CE4F9C74244B066400968CFC /* SPPageView.m */,
-				CE4F9C77244B066400968CFC /* SPPushNotification.h */,
-				CE4F9C78244B066400968CFC /* SPPushNotification.m */,
-				CE4F9C73244B066400968CFC /* SPScreenView.h */,
-				CE4F9C80244B066500968CFC /* SPScreenView.m */,
-				CE4F9C7A244B066400968CFC /* SPStructured.h */,
-				CE4F9C75244B066400968CFC /* SPStructured.m */,
-				CE4F9C67244B066400968CFC /* SPTiming.h */,
-				CE4F9C5F244B066400968CFC /* SPTiming.m */,
-				CE4F9C6B244B066400968CFC /* SPSelfDescribing.h */,
-				CE4F9C6C244B066400968CFC /* SPSelfDescribing.m */,
+				6B9DA54F29261E5B006D721A /* Background.swift */,
+				6B9DA54A29261E5A006D721A /* ConsentDocument.swift */,
+				6B9DA54829261E5A006D721A /* ConsentGranted.swift */,
+				6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */,
+				6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */,
+				6B9DA54B29261E5A006D721A /* EventBase.swift */,
+				6B9DA54D29261E5A006D721A /* Foreground.swift */,
+				6B9DA55229261E5B006D721A /* ScreenView.swift */,
+				6B9DA54E29261E5B006D721A /* SelfDescribing.swift */,
+				6B9DA55129261E5B006D721A /* Structured.swift */,
+				6B9DA55029261E5B006D721A /* Timing.swift */,
+				6B9DA5832926437F006D721A /* PushNotification.swift */,
+				6B9DA58829264EC2006D721A /* MessageNotification.swift */,
+				6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */,
+				6B9DA59C29267B66006D721A /* Ecommerce.swift */,
+				6B9DA5A129268956006D721A /* EcommerceItem.swift */,
+				6B9DA5A629268B0E006D721A /* TrackerError.swift */,
+				6B9DA5AB29268E4E006D721A /* PageView.swift */,
+				6B9DA5B029269036006D721A /* SNOWError.swift */,
 			);
 			path = Events;
 			sourceTree = "<group>";
@@ -1570,21 +1178,10 @@
 		EDC9B34D255AE46300F4136D /* Emitter */ = {
 			isa = PBXGroup;
 			children = (
-				ED88B58E257922490048FAD1 /* SPEmitterController.h */,
-				ED88B5A525792C620048FAD1 /* SPEmitterControllerImpl.h */,
-				ED88B5A625792C620048FAD1 /* SPEmitterControllerImpl.m */,
-				EDDD7041264F2A8800259404 /* SPEmitterConfigurationUpdate.h */,
-				EDDD7042264F2A8800259404 /* SPEmitterConfigurationUpdate.m */,
-				ED88B7332587777A0048FAD1 /* SPEmitterEventProcessing.h */,
-				AB0C27E9191B43D600018557 /* SPEmitter.h */,
-				AB0C27EA191B43D600018557 /* SPEmitter.m */,
-				EDD8543224EFFFB300661F6B /* SPRequest.h */,
-				EDD8543324EFFFB300661F6B /* SPRequest.m */,
-				EDD8541424EEC25000661F6B /* SPEmitterEvent.h */,
-				EDD8541524EEC25100661F6B /* SPEmitterEvent.m */,
-				049B2BDA1B7A203200BD82FC /* SPRequestCallback.h */,
-				0413DD751B78D635000D2112 /* SPRequestResult.h */,
-				0413DD761B78D643000D2112 /* SPRequestResult.m */,
+				6B9DA5C32927BCD6006D721A /* Emitter.swift */,
+				6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */,
+				6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */,
+				6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */,
 			);
 			path = Emitter;
 			sourceTree = "<group>";
@@ -1592,15 +1189,8 @@
 		EDC9B34E255AE66D00F4136D /* GlobalContexts */ = {
 			isa = PBXGroup;
 			children = (
-				ED88B649257A57F80048FAD1 /* SPGlobalContextsController.h */,
-				ED88B666257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h */,
-				ED88B667257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m */,
-				CE4F9C70244B066400968CFC /* SPGlobalContext.h */,
-				CE4F9C83244B066500968CFC /* SPGlobalContext.m */,
-				CE4F9C65244B066400968CFC /* SPSchemaRule.h */,
-				CE4F9C79244B066400968CFC /* SPSchemaRule.m */,
-				CE4F9C7F244B066500968CFC /* SPSchemaRuleset.h */,
-				CE4F9C6F244B066400968CFC /* SPSchemaRuleset.m */,
+				6B9DA673292BD2D6006D721A /* SchemaRule.swift */,
+				6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */,
 			);
 			path = GlobalContexts;
 			sourceTree = "<group>";
@@ -1608,9 +1198,7 @@
 		EDC9B34F255AEC7E00F4136D /* Logger */ = {
 			isa = PBXGroup;
 			children = (
-				ED8866BD25711EC000DB53BB /* SPLoggerDelegate.h */,
-				EDEE835824BE0944000B8530 /* SPLogger.h */,
-				EDEE835924BE0944000B8530 /* SPLogger.m */,
+				6B9DA682292BDD39006D721A /* Logger.swift */,
 			);
 			path = Logger;
 			sourceTree = "<group>";
@@ -1618,10 +1206,8 @@
 		EDC9B350255AEDF000F4136D /* Payload */ = {
 			isa = PBXGroup;
 			children = (
-				AB0C27F3191C67CD00018557 /* SPPayload.h */,
-				AB0C27F4191C67CD00018557 /* SPPayload.m */,
-				0485CA141BAC658500214BC5 /* SPSelfDescribingJson.h */,
-				0485CA151BAC65A300214BC5 /* SPSelfDescribingJson.m */,
+				6B85878129251DF4006E4A5F /* Payload.swift */,
+				6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */,
 			);
 			path = Payload;
 			sourceTree = "<group>";
@@ -1629,39 +1215,22 @@
 		EDC9B351255AEEBC00F4136D /* Tracker */ = {
 			isa = PBXGroup;
 			children = (
-				ED87A41B2577AC5B000C54EB /* SPTrackerController.h */,
-				ED87A42C2577ADFF000C54EB /* SPTrackerControllerImpl.h */,
-				ED87A42D2577ADFF000C54EB /* SPTrackerControllerImpl.m */,
-				EDDD700F264F1D2100259404 /* SPTrackerConfigurationUpdate.h */,
-				EDDD7010264F1D2100259404 /* SPTrackerConfigurationUpdate.m */,
-				EDDD7005264E8ECE00259404 /* SPServiceProviderProtocol.h */,
-				ED88B6D92583DFC70048FAD1 /* SPServiceProvider.h */,
-				ED88B6DA2583DFC70048FAD1 /* SPServiceProvider.m */,
-				AB9E8210192DD336006744C9 /* SPTracker.h */,
-				AB9E8211192DD336006744C9 /* SPTracker.m */,
-				75264A2F224E5DBC000E0E9B /* SPInstallTracker.h */,
-				75264A31224E5DD2000E0E9B /* SPInstallTracker.m */,
-				CE4F9C68244B066400968CFC /* SPTrackerEvent.h */,
-				CE4F9C72244B066400968CFC /* SPTrackerEvent.m */,
-				EDAB663326D699D90067755F /* SPStateMachineProtocol.h */,
-				EDAB663226D699D90067755F /* SPStateFuture.h */,
-				EDAB662F26D699D80067755F /* SPStateFuture.m */,
-				EDAB663026D699D90067755F /* SPStateManager.h */,
-				EDAB663126D699D90067755F /* SPStateManager.m */,
-				ED7CE16D26DFB55C0035C323 /* SPTrackerState.h */,
-				ED7CE16E26DFB55C0035C323 /* SPTrackerState.m */,
-				ED7CE17726DFBFA30035C323 /* SPTrackerStateSnapshot.h */,
-				ED7CE17C26DFC12C0035C323 /* SPState.h */,
-				EDAB665426D6AA940067755F /* SPDeepLinkStateMachine.h */,
-				EDAB665526D6AA940067755F /* SPDeepLinkStateMachine.m */,
-				EDAB665E26D6ACCB0067755F /* SPDeepLinkState.h */,
-				EDAB665F26D6ACCB0067755F /* SPDeepLinkState.m */,
-				ED38D92726EBCEBE002AEC8E /* SPLifecycleState.h */,
-				ED38D92A26EBCEBE002AEC8E /* SPLifecycleState.m */,
-				ED38D92926EBCEBE002AEC8E /* SPLifecycleStateMachine.h */,
-				ED38D92826EBCEBE002AEC8E /* SPLifecycleStateMachine.m */,
-				6B07CDAB287721C600E510D6 /* SPWebViewMessageHandler.h */,
-				6B07CDAC287721C600E510D6 /* SPWebViewMessageHandler.m */,
+				6B9DA5E92927E635006D721A /* DeepLinkState.swift */,
+				6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */,
+				6B9DA5E82927E635006D721A /* InstallTracker.swift */,
+				6B9DA5EB2927E635006D721A /* LifecycleState.swift */,
+				6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */,
+				6B9DA660292BAE5F006D721A /* ServiceProvider.swift */,
+				6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */,
+				6B9DA60B2927E99B006D721A /* StateFuture.swift */,
+				6B9DA60C2927E99B006D721A /* StateManager.swift */,
+				6B9DA6322928CAA3006D721A /* Tracker.swift */,
+				6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */,
+				6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */,
+				6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */,
+				6B9DA5B529269755006D721A /* TrackerEvent.swift */,
+				6B9DA60E2927E99C006D721A /* TrackerState.swift */,
+				6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */,
 			);
 			path = Tracker;
 			sourceTree = "<group>";
@@ -1669,14 +1238,8 @@
 		EDC9B352255B027E00F4136D /* NetworkConnection */ = {
 			isa = PBXGroup;
 			children = (
-				ED88B76C2587B4EF0048FAD1 /* SPNetworkController.h */,
-				ED88B78F2587B5620048FAD1 /* SPNetworkControllerImpl.h */,
-				ED88B7902587B5620048FAD1 /* SPNetworkControllerImpl.m */,
-				EDDD7037264F27E700259404 /* SPNetworkConfigurationUpdate.h */,
-				EDDD7038264F27E700259404 /* SPNetworkConfigurationUpdate.m */,
-				EDD8542824EFEFE600661F6B /* SPNetworkConnection.h */,
-				EDD8541E24EFEFB900661F6B /* SPDefaultNetworkConnection.h */,
-				EDD8541F24EFEFB900661F6B /* SPDefaultNetworkConnection.m */,
+				6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */,
+				6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */,
 			);
 			path = NetworkConnection;
 			sourceTree = "<group>";
@@ -1684,11 +1247,8 @@
 		EDC9B353255B02AB00F4136D /* Storage */ = {
 			isa = PBXGroup;
 			children = (
-				EDD8540A24EE786900661F6B /* SPEventStore.h */,
-				EDB693F826B7F61D00B76A79 /* SPMemoryEventStore.h */,
-				EDB693F926B7F61D00B76A79 /* SPMemoryEventStore.m */,
-				ABB767AE194974D3006275D1 /* SPSQLiteEventStore.h */,
-				ABB767AF194974D3006275D1 /* SPSQLiteEventStore.m */,
+				6B85870029239A9D006E4A5F /* MemoryEventStore.swift */,
+				6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */,
 			);
 			path = Storage;
 			sourceTree = "<group>";
@@ -1696,22 +1256,10 @@
 		EDC9B35A255C3B9700F4136D /* Utils */ = {
 			isa = PBXGroup;
 			children = (
-				6BBDCD4027019AF4001B547F /* SPPlatformContext.h */,
-				6BBDCD4127019AF4001B547F /* SPPlatformContext.m */,
-				ED852B3223A0EEC600F2DF6B /* SNOWReachability.h */,
-				ED852B2E23A0E90E00F2DF6B /* SNOWReachability.m */,
-				ABFCC3741922984A00FAE8FE /* SPUtilities.h */,
-				ABFCC3751922984A00FAE8FE /* SPUtilities.m */,
-				044CA88B1B94791E000EA3B1 /* SPWeakTimerTarget.h */,
-				044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */,
-				ED34672826415C1D0018BA61 /* SPJSONSerialization.h */,
-				ED34672926415C1D0018BA61 /* SPJSONSerialization.m */,
-				ED9897142627006F00145157 /* NSDictionary+SP_TypeMethods.h */,
-				ED9897152627006F00145157 /* NSDictionary+SP_TypeMethods.m */,
-				EDB2FD1726C130B80031B872 /* SPDataPersistence.h */,
-				EDB2FD1826C130B80031B872 /* SPDataPersistence.m */,
-				6BF08DA4270DEED6009C7E2B /* SPDeviceInfoMonitor.h */,
-				6BF08DA5270DEED6009C7E2B /* SPDeviceInfoMonitor.m */,
+				6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */,
+				6B9DA65A292B6E68006D721A /* Utilities.swift */,
+				6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */,
+				6B9DA62D2928C08D006D721A /* DataPersistence.swift */,
 			);
 			path = Utils;
 			sourceTree = "<group>";
@@ -1719,15 +1267,9 @@
 		EDC9B36D255C3C8A00F4136D /* Session */ = {
 			isa = PBXGroup;
 			children = (
-				ED87A3D825765DAE000C54EB /* SPSessionController.h */,
-				ED87A3E925766BB4000C54EB /* SPSessionControllerImpl.h */,
-				ED87A3EA25766BB4000C54EB /* SPSessionControllerImpl.m */,
-				EDDD702D264F25A200259404 /* SPSessionConfigurationUpdate.h */,
-				EDDD702E264F25A200259404 /* SPSessionConfigurationUpdate.m */,
-				043EC5DD1B8F048500294081 /* SPSession.h */,
-				043EC5DF1B8F049200294081 /* SPSession.m */,
-				ED49DF3A2757E4F400610843 /* SPSessionState.h */,
-				ED49DF3B2757E4F400610843 /* SPSessionState.m */,
+				6B9DA6242928B2FB006D721A /* Session.swift */,
+				6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */,
+				6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */,
 			);
 			path = Session;
 			sourceTree = "<group>";
@@ -1735,12 +1277,9 @@
 		EDC9B37A255C3D3700F4136D /* ScreenViewTracking */ = {
 			isa = PBXGroup;
 			children = (
-				EDAB664F26D69D740067755F /* SPScreenStateMachine.h */,
-				EDAB664E26D69D740067755F /* SPScreenStateMachine.m */,
-				754774BF2225FBB90043B814 /* SPScreenState.h */,
-				754774BB2225FBA60043B814 /* SPScreenState.m */,
-				754774CB222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.h */,
-				754774CC222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.m */,
+				6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */,
+				6B9DA63D292B5196006D721A /* ScreenState.swift */,
+				6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */,
 			);
 			path = ScreenViewTracking;
 			sourceTree = "<group>";
@@ -1748,48 +1287,14 @@
 		EDC9B37B255C3DC600F4136D /* Subject */ = {
 			isa = PBXGroup;
 			children = (
-				EDF2A1B326402D52009032AB /* SPSubjectController.h */,
-				EDF2A1B8264032F9009032AB /* SPSubjectControllerImpl.h */,
-				EDF2A1B9264032F9009032AB /* SPSubjectControllerImpl.m */,
-				EDDD7019264F230400259404 /* SPSubjectConfigurationUpdate.h */,
-				EDDD701A264F230400259404 /* SPSubjectConfigurationUpdate.m */,
-				ED91CB6B23AA715B0078E75F /* SPDevicePlatform.h */,
-				ED91CB7023AA8AD50078E75F /* SPDevicePlatform.m */,
-				04062D741B8390710019B8D1 /* SPSubject.h */,
-				04062D751B8390870019B8D1 /* SPSubject.m */,
+				6B9DA655292B60E1006D721A /* Subject.swift */,
+				6B9DA647292B5487006D721A /* PlatformContext.swift */,
+				6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */,
+				6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */,
 			);
 			path = Subject;
 			sourceTree = "<group>";
 		};
-		EDD53F7E25473C430000E57D /* Internal */ = {
-			isa = PBXGroup;
-			children = (
-				ED7F080426190AF2005D377E /* RemoteConfiguration */,
-				ED8BF8B0256FFEA4001DFDD9 /* Configurations */,
-				EDC9B37B255C3DC600F4136D /* Subject */,
-				ED88B5F9257953EE0048FAD1 /* GDPR */,
-				EDC9B37A255C3D3700F4136D /* ScreenViewTracking */,
-				EDC9B36D255C3C8A00F4136D /* Session */,
-				EDC9B35A255C3B9700F4136D /* Utils */,
-				EDC9B353255B02AB00F4136D /* Storage */,
-				EDC9B352255B027E00F4136D /* NetworkConnection */,
-				EDC9B351255AEEBC00F4136D /* Tracker */,
-				EDC9B350255AEDF000F4136D /* Payload */,
-				EDC9B34F255AEC7E00F4136D /* Logger */,
-				EDC9B34E255AE66D00F4136D /* GlobalContexts */,
-				EDC9B34D255AE46300F4136D /* Emitter */,
-				EDAB65D626CD2F970067755F /* Entities */,
-				EDC9B346255ACF5100F4136D /* Events */,
-				AB0C27C5191B408200018557 /* SPTrackerConstants.h */,
-				043EC5E61B8F224900294081 /* SPTrackerConstants.m */,
-				ED8122A825E9578500AE7FE8 /* SPSnowplow.h */,
-				ED8122A925E9578500AE7FE8 /* SPSnowplow.m */,
-				EDDD6FFB264E873B00259404 /* SPController.h */,
-				EDDD6FFC264E873B00259404 /* SPController.m */,
-			);
-			path = Internal;
-			sourceTree = "<group>";
-		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -1797,120 +1302,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				ED87A3D925765DAE000C54EB /* SPSessionController.h in Headers */,
-				EDEE834B24BDB326000B8530 /* SPTrackerError.h in Headers */,
-				CE4F9CBE244B066500968CFC /* SPForeground.h in Headers */,
-				ED88B629257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */,
-				EDAB664026D699D90067755F /* SPStateFuture.h in Headers */,
-				ED6B0329271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */,
-				ED38D91F26EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */,
-				ED7F082A2619199D005D377E /* SPRemoteConfiguration.h in Headers */,
-				ED88B7342587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */,
-				EDF2A1B426402D53009032AB /* SPSubjectController.h in Headers */,
-				ED88B64A257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */,
-				EDB693FA26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */,
-				CE4F9D02244B066500968CFC /* SPEcommerceItem.h in Headers */,
-				EDD8541624EEC25100661F6B /* SPEmitterEvent.h in Headers */,
-				6B07CDAD287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */,
-				752DAC3221CC43C60065F874 /* SPTrackerConstants.h in Headers */,
-				ED87A3EB25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */,
-				ED8866BE25711EC000DB53BB /* SPLoggerDelegate.h in Headers */,
-				CE4F9CB6244B066500968CFC /* SPSelfDescribing.h in Headers */,
-				ED88B5A725792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */,
-				752DAC3321CC43C70065F874 /* SPTracker.h in Headers */,
-				CE4F9D12244B066500968CFC /* SPBackground.h in Headers */,
-				ED34672A26415C1D0018BA61 /* SPJSONSerialization.h in Headers */,
-				ED91CB6C23AA715B0078E75F /* SPDevicePlatform.h in Headers */,
-				CE4F9CA6244B066500968CFC /* SPTiming.h in Headers */,
-				EDAB665626D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */,
-				ED87A41C2577AC5B000C54EB /* SPTrackerController.h in Headers */,
-				ED852B3323A0EEC600F2DF6B /* SNOWReachability.h in Headers */,
-				CE4F9CB2244B066500968CFC /* SNOWError.h in Headers */,
-				ED8866FE25715DD600DB53BB /* SPConfiguration.h in Headers */,
-				ED8122AA25E9578500AE7FE8 /* SPSnowplow.h in Headers */,
-				EDAB665226D69D740067755F /* SPScreenStateMachine.h in Headers */,
-				ED8BF8B325700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */,
-				752DAC3421CC43C70065F874 /* SPEmitter.h in Headers */,
-				752DAC3521CC43C70065F874 /* SPSubject.h in Headers */,
-				ED0EFE3126E240B0002CAA21 /* SPDeepLinkReceived.h in Headers */,
-				EDDD7007264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */,
-				ED277BE22625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */,
-				6BF08DA6270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */,
-				CE4F9CE2244B066500968CFC /* SPConsentGranted.h in Headers */,
-				CE4F9CFA244B066500968CFC /* SPEventBase.h in Headers */,
-				EDAB663826D699D90067755F /* SPStateManager.h in Headers */,
-				ED9897162627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */,
-				ED88B5FB257954370048FAD1 /* SPGDPRController.h in Headers */,
-				752DAC3721CC43C70065F874 /* SPPayload.h in Headers */,
-				752DAC3A21CC43C70065F874 /* SPUtilities.h in Headers */,
-				CE4F9CE6244B066500968CFC /* SPPushNotification.h in Headers */,
-				ED88B668257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */,
-				ED88B60D257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */,
-				EDD8542024EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */,
-				754774C02225FBB90043B814 /* SPScreenState.h in Headers */,
-				CE4F9D0E244B066500968CFC /* SPConsentDocument.h in Headers */,
-				ED7CE17826DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */,
-				CE4F9CAA244B066500968CFC /* SPTrackerEvent.h in Headers */,
-				ED277BD22625F220002C7B6D /* SPConfigurationBundle.h in Headers */,
-				EDDD6FFD264E873B00259404 /* SPController.h in Headers */,
-				CE4F9CF2244B066500968CFC /* SPStructured.h in Headers */,
-				ED49DF3C2757E4F500610843 /* SPSessionState.h in Headers */,
-				EDD8542A24EFEFE600661F6B /* SPNetworkConnection.h in Headers */,
-				ED88B7912587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */,
-				ED38D93326EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */,
-				ED8BF8CB25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */,
-				752DAC3E21CC43C70065F874 /* SPRequestCallback.h in Headers */,
-				ED87A42E2577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */,
-				CE4F9D06244B066500968CFC /* SPSchemaRuleset.h in Headers */,
-				752DAC3821CC43C70065F874 /* SPSelfDescribingJson.h in Headers */,
-				ED88672B2573C1F100DB53BB /* SPSessionConfiguration.h in Headers */,
-				EDD8543424EFFFB300661F6B /* SPRequest.h in Headers */,
-				ED88B5692578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */,
-				754774CD222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.h in Headers */,
-				75264A30224E5DBC000E0E9B /* SPInstallTracker.h in Headers */,
-				ED88B6DB2583DFC80048FAD1 /* SPServiceProvider.h in Headers */,
-				EDDD7039264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */,
-				6BACDF922897C2580013276E /* SPConfigurationState.h in Headers */,
-				ED38D92B26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */,
-				ED8866E22571445300DB53BB /* SPSubjectConfiguration.h in Headers */,
-				ED7CE17D26DFC12C0035C323 /* SPState.h in Headers */,
-				CE4F9CCA244B066500968CFC /* SPGlobalContext.h in Headers */,
-				CE4F9CD6244B066500968CFC /* SPScreenView.h in Headers */,
-				EDDD701B264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */,
-				ED7F081526190E00005D377E /* SPConfigurationProvider.h in Headers */,
-				EDDD702F264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */,
-				752DAC4221CC60F20065F874 /* Snowplow-umbrella-header.h in Headers */,
-				ED7CE16F26DFB55C0035C323 /* SPTrackerState.h in Headers */,
-				CE4F9C9A244B066500968CFC /* SPEvent.h in Headers */,
-				ED88B76D2587B4EF0048FAD1 /* SPNetworkController.h in Headers */,
-				ED7F0840261924BF005D377E /* SPConfigurationFetcher.h in Headers */,
-				EDB2FD1926C130B80031B872 /* SPDataPersistence.h in Headers */,
-				ED88B5DF257950210048FAD1 /* SPGDPRConfiguration.h in Headers */,
-				ED88B58F257922490048FAD1 /* SPEmitterController.h in Headers */,
-				752DAC3621CC43C70065F874 /* SPSession.h in Headers */,
-				752DAC3B21CC43C70065F874 /* SPRequestResult.h in Headers */,
-				EDAB65CE26CBD5150067755F /* SPDeepLinkEntity.h in Headers */,
-				ED914EBA24325AB40068DA0A /* SPGdprContext.h in Headers */,
-				6BF08DB0270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */,
-				752DAC3C21CC43C70065F874 /* SPWeakTimerTarget.h in Headers */,
-				6BBDCD4227019AF4001B547F /* SPPlatformContext.h in Headers */,
-				EDDD7011264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */,
-				EDF2A1BA264032F9009032AB /* SPSubjectControllerImpl.h in Headers */,
-				EDAB664426D699D90067755F /* SPStateMachineProtocol.h in Headers */,
-				EDDD7025264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */,
-				CE4F9CA2244B066500968CFC /* SPPageView.h in Headers */,
-				EDEE835A24BE0944000B8530 /* SPLogger.h in Headers */,
-				CE4F9C9E244B066500968CFC /* SPSchemaRule.h in Headers */,
-				752DAC3921CC43C70065F874 /* SPSQLiteEventStore.h in Headers */,
-				CE4F9D1A244B066500968CFC /* SPEcommerce.h in Headers */,
-				6B871F6627C3976B00BCF742 /* SPMockNetworkConnection.h in Headers */,
-				EDDD7043264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */,
-				ED98972626287F7A00145157 /* SPConfigurationCache.h in Headers */,
-				ED9081B52703747C00EE9421 /* SPMessageNotification.h in Headers */,
-				EDD8540C24EE786900661F6B /* SPEventStore.h in Headers */,
-				CE4F9CAE244B066500968CFC /* SPConsentWithdrawn.h in Headers */,
-				6BD6A6AC288719C7002D6D40 /* SPMockWKScriptMessage.h in Headers */,
-				EDAB666026D6ACCB0067755F /* SPDeepLinkState.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1918,117 +1309,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CE4F9CA3244B066500968CFC /* SPPageView.h in Headers */,
-				CE4F9D03244B066500968CFC /* SPEcommerceItem.h in Headers */,
-				EDEE834C24BDB326000B8530 /* SPTrackerError.h in Headers */,
-				EDB2FD1A26C130B80031B872 /* SPDataPersistence.h in Headers */,
-				75CAC45821F2A21B00271FB3 /* Snowplow-umbrella-header.h in Headers */,
-				75CAC45921F2A21B00271FB3 /* SPTrackerConstants.h in Headers */,
-				6BBDCD4327019AF4001B547F /* SPPlatformContext.h in Headers */,
-				ED8BF8B425700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */,
-				ED88B62A257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */,
-				ED88B7922587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */,
-				ED88B60E257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */,
-				75CAC45A21F2A21B00271FB3 /* SPTracker.h in Headers */,
-				CE4F9CFB244B066500968CFC /* SPEventBase.h in Headers */,
-				ED88B64B257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */,
-				ED87A3EC25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */,
-				ED88B76E2587B4F00048FAD1 /* SPNetworkController.h in Headers */,
-				ED91CB6D23AA715B0078E75F /* SPDevicePlatform.h in Headers */,
-				75CAC45B21F2A21B00271FB3 /* SPEmitter.h in Headers */,
-				CE4F9D1B244B066500968CFC /* SPEcommerce.h in Headers */,
-				EDDD7012264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */,
-				ED38D92C26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */,
-				ED7F0841261924BF005D377E /* SPConfigurationFetcher.h in Headers */,
-				ED6B032A271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */,
-				75CAC45C21F2A21B00271FB3 /* SPSubject.h in Headers */,
-				EDDD703A264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */,
-				ED88B669257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */,
-				CE4F9CB7244B066500968CFC /* SPSelfDescribing.h in Headers */,
-				6BF08DA7270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */,
-				EDAB664526D699D90067755F /* SPStateMachineProtocol.h in Headers */,
-				75CAC45E21F2A21B00271FB3 /* SPPayload.h in Headers */,
-				75CAC45F21F2A21B00271FB3 /* SPSelfDescribingJson.h in Headers */,
-				EDAB665726D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */,
-				EDFEEAC823A7CB2A001E6D03 /* SPInstallTracker.h in Headers */,
-				ED9081B62703747C00EE9421 /* SPMessageNotification.h in Headers */,
-				ED8866BF25711EC000DB53BB /* SPLoggerDelegate.h in Headers */,
-				ED88B6DC2583DFC80048FAD1 /* SPServiceProvider.h in Headers */,
-				6B871F6727C3976C00BCF742 /* SPMockNetworkConnection.h in Headers */,
-				ED277BD32625F220002C7B6D /* SPConfigurationBundle.h in Headers */,
-				EDD8542B24EFEFE600661F6B /* SPNetworkConnection.h in Headers */,
-				EDAB666126D6ACCB0067755F /* SPDeepLinkState.h in Headers */,
-				ED914EBB24325AB40068DA0A /* SPGdprContext.h in Headers */,
-				ED8866E32571445300DB53BB /* SPSubjectConfiguration.h in Headers */,
-				CE4F9CD7244B066500968CFC /* SPScreenView.h in Headers */,
-				CE4F9CCB244B066500968CFC /* SPGlobalContext.h in Headers */,
-				CE4F9CE3244B066500968CFC /* SPConsentGranted.h in Headers */,
-				EDDD701C264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */,
-				6B07CDAE287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */,
-				CE4F9CE7244B066500968CFC /* SPPushNotification.h in Headers */,
-				EDDD7008264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */,
-				CE4F9CA7244B066500968CFC /* SPTiming.h in Headers */,
-				ED38D93426EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */,
-				754774C12225FBB90043B814 /* SPScreenState.h in Headers */,
-				75CAC46121F2A21B00271FB3 /* SPUtilities.h in Headers */,
-				EDDD7026264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */,
-				CE4F9C9B244B066500968CFC /* SPEvent.h in Headers */,
-				CE4F9CAF244B066500968CFC /* SPConsentWithdrawn.h in Headers */,
-				ED88B5E0257950210048FAD1 /* SPGDPRConfiguration.h in Headers */,
-				EDAB664126D699D90067755F /* SPStateFuture.h in Headers */,
-				CE4F9D13244B066500968CFC /* SPBackground.h in Headers */,
-				75CAC46521F2A21B00271FB3 /* SPRequestCallback.h in Headers */,
-				ED7CE17E26DFC12C0035C323 /* SPState.h in Headers */,
-				EDDD7030264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */,
-				CE4F9CAB244B066500968CFC /* SPTrackerEvent.h in Headers */,
-				ED7F081626190E00005D377E /* SPConfigurationProvider.h in Headers */,
-				EDD8540D24EE786900661F6B /* SPEventStore.h in Headers */,
-				EDAB663926D699D90067755F /* SPStateManager.h in Headers */,
-				ED87A41D2577AC5B000C54EB /* SPTrackerController.h in Headers */,
-				ED0EFE3226E240B0002CAA21 /* SPDeepLinkReceived.h in Headers */,
-				CE4F9D07244B066500968CFC /* SPSchemaRuleset.h in Headers */,
-				75CAC45D21F2A21B00271FB3 /* SPSession.h in Headers */,
-				EDDD7044264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */,
-				EDEE835B24BE0944000B8530 /* SPLogger.h in Headers */,
-				ED88B7352587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */,
-				CE4F9CB3244B066500968CFC /* SNOWError.h in Headers */,
-				6BF08DB1270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */,
-				ED8BF8CC25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */,
-				ED98972726287F7A00145157 /* SPConfigurationCache.h in Headers */,
-				ED7F082B2619199D005D377E /* SPRemoteConfiguration.h in Headers */,
-				ED38D92026EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */,
-				CE4F9D0F244B066500968CFC /* SPConsentDocument.h in Headers */,
-				EDD8543524EFFFB300661F6B /* SPRequest.h in Headers */,
-				EDD8541724EEC25100661F6B /* SPEmitterEvent.h in Headers */,
-				CE4F9C9F244B066500968CFC /* SPSchemaRule.h in Headers */,
-				ED87A42F2577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */,
-				CE4F9CBF244B066500968CFC /* SPForeground.h in Headers */,
-				ED88B56A2578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */,
-				CE4F9CF3244B066500968CFC /* SPStructured.h in Headers */,
-				6BACDF932897C2580013276E /* SPConfigurationState.h in Headers */,
-				ED9897172627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */,
-				EDD8542124EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */,
-				ED34672B26415C1D0018BA61 /* SPJSONSerialization.h in Headers */,
-				ED7CE17926DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */,
-				ED8866FF25715DD600DB53BB /* SPConfiguration.h in Headers */,
-				ED7CE16626DE39510035C323 /* SPScreenStateMachine.h in Headers */,
-				ED88B5A825792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */,
-				75CAC46021F2A21B00271FB3 /* SPSQLiteEventStore.h in Headers */,
-				ED7CE17026DFB55C0035C323 /* SPTrackerState.h in Headers */,
-				ED87A3DA25765DAE000C54EB /* SPSessionController.h in Headers */,
-				ED88672C2573C1F100DB53BB /* SPSessionConfiguration.h in Headers */,
-				EDF2A1B526402D53009032AB /* SPSubjectController.h in Headers */,
-				EDB693FB26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */,
-				ED277BE32625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */,
-				75CAC46221F2A21B00271FB3 /* SPRequestResult.h in Headers */,
-				EDDD6FFE264E873B00259404 /* SPController.h in Headers */,
-				ED8122AB25E9578600AE7FE8 /* SPSnowplow.h in Headers */,
-				EDF2A1BB264032F9009032AB /* SPSubjectControllerImpl.h in Headers */,
-				EDAB65CF26CBD5150067755F /* SPDeepLinkEntity.h in Headers */,
-				ED88B590257922490048FAD1 /* SPEmitterController.h in Headers */,
-				75CAC46321F2A21B00271FB3 /* SPWeakTimerTarget.h in Headers */,
-				ED49DF3D2757E4F500610843 /* SPSessionState.h in Headers */,
-				ED88B5FC257954370048FAD1 /* SPGDPRController.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2036,118 +1316,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CE4F9CA4244B066500968CFC /* SPPageView.h in Headers */,
-				CE4F9D04244B066500968CFC /* SPEcommerceItem.h in Headers */,
-				EDEE834D24BDB326000B8530 /* SPTrackerError.h in Headers */,
-				ED7CE17F26DFC12C0035C323 /* SPState.h in Headers */,
-				75CAC42B21F2A0CC00271FB3 /* SPTrackerConstants.h in Headers */,
-				EDAB665826D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */,
-				75CAC42C21F2A0CC00271FB3 /* SPTracker.h in Headers */,
-				EDAB664626D699D90067755F /* SPStateMachineProtocol.h in Headers */,
-				ED8BF8B525700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */,
-				ED88B62B257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */,
-				ED88B7932587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */,
-				ED88B60F257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */,
-				ED9081B72703747C00EE9421 /* SPMessageNotification.h in Headers */,
-				ED38D92126EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */,
-				75CAC42D21F2A0CC00271FB3 /* SPEmitter.h in Headers */,
-				CE4F9CFC244B066500968CFC /* SPEventBase.h in Headers */,
-				ED88B64C257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */,
-				ED87A3ED25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */,
-				ED88B76F2587B4F00048FAD1 /* SPNetworkController.h in Headers */,
-				EDDD701D264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */,
-				75CAC42E21F2A0CC00271FB3 /* SPSubject.h in Headers */,
-				75CAC43021F2A0CC00271FB3 /* SPPayload.h in Headers */,
-				CE4F9D1C244B066500968CFC /* SPEcommerce.h in Headers */,
-				75CAC43121F2A0CC00271FB3 /* SPSelfDescribingJson.h in Headers */,
-				ED88B66A257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */,
-				CE4F9CB8244B066500968CFC /* SPSelfDescribing.h in Headers */,
-				ED7CE17126DFB55C0035C323 /* SPTrackerState.h in Headers */,
-				75CAC43321F2A0CC00271FB3 /* SPUtilities.h in Headers */,
-				75CAC43721F2A0CC00271FB3 /* SPRequestCallback.h in Headers */,
-				ED34672C26415C1D0018BA61 /* SPJSONSerialization.h in Headers */,
-				ED914EBC24325AB40068DA0A /* SPGdprContext.h in Headers */,
-				ED38D92D26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */,
-				6B871F6827C3976C00BCF742 /* SPMockNetworkConnection.h in Headers */,
-				ED7CE16726DE39530035C323 /* SPScreenStateMachine.h in Headers */,
-				ED8866C025711EC000DB53BB /* SPLoggerDelegate.h in Headers */,
-				ED88B6DD2583DFC90048FAD1 /* SPServiceProvider.h in Headers */,
-				EDD8542C24EFEFE600661F6B /* SPNetworkConnection.h in Headers */,
-				754774C22225FBB90043B814 /* SPScreenState.h in Headers */,
-				ED8866E42571445300DB53BB /* SPSubjectConfiguration.h in Headers */,
-				CE4F9CD8244B066500968CFC /* SPScreenView.h in Headers */,
-				CE4F9CCC244B066500968CFC /* SPGlobalContext.h in Headers */,
-				CE4F9CE4244B066500968CFC /* SPConsentGranted.h in Headers */,
-				6BF08DA8270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */,
-				CE4F9CE8244B066500968CFC /* SPPushNotification.h in Headers */,
-				CE4F9CA8244B066500968CFC /* SPTiming.h in Headers */,
-				ED91CB6E23AA715B0078E75F /* SPDevicePlatform.h in Headers */,
-				EDDD7031264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */,
-				75CAC43821F2A0CC00271FB3 /* Snowplow-umbrella-header.h in Headers */,
-				ED49DF3E2757E4F500610843 /* SPSessionState.h in Headers */,
-				CE4F9C9C244B066500968CFC /* SPEvent.h in Headers */,
-				EDAB664226D699D90067755F /* SPStateFuture.h in Headers */,
-				CE4F9CB0244B066500968CFC /* SPConsentWithdrawn.h in Headers */,
-				EDF2A1BC264032F9009032AB /* SPSubjectControllerImpl.h in Headers */,
-				ED88B5E1257950210048FAD1 /* SPGDPRConfiguration.h in Headers */,
-				CE4F9D14244B066500968CFC /* SPBackground.h in Headers */,
-				6BACDF942897C2580013276E /* SPConfigurationState.h in Headers */,
-				ED98972826287F7A00145157 /* SPConfigurationCache.h in Headers */,
-				ED7F082C2619199D005D377E /* SPRemoteConfiguration.h in Headers */,
-				EDDD6FFF264E873B00259404 /* SPController.h in Headers */,
-				75CAC42F21F2A0CC00271FB3 /* SPSession.h in Headers */,
-				ED277BD42625F220002C7B6D /* SPConfigurationBundle.h in Headers */,
-				EDDD703B264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */,
-				CE4F9CAC244B066500968CFC /* SPTrackerEvent.h in Headers */,
-				ED9897182627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */,
-				ED852B3423A0EEC600F2DF6B /* SNOWReachability.h in Headers */,
-				EDAB666226D6ACCB0067755F /* SPDeepLinkState.h in Headers */,
-				EDD8540E24EE786900661F6B /* SPEventStore.h in Headers */,
-				ED87A41E2577AC5B000C54EB /* SPTrackerController.h in Headers */,
-				ED7F081726190E00005D377E /* SPConfigurationProvider.h in Headers */,
-				CE4F9D08244B066500968CFC /* SPSchemaRuleset.h in Headers */,
-				EDB2FD1B26C130B80031B872 /* SPDataPersistence.h in Headers */,
-				EDAB663A26D699D90067755F /* SPStateManager.h in Headers */,
-				75CAC43221F2A0CC00271FB3 /* SPSQLiteEventStore.h in Headers */,
-				EDEE835C24BE0944000B8530 /* SPLogger.h in Headers */,
-				ED7CE17A26DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */,
-				ED88B7362587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */,
-				ED7F0842261924BF005D377E /* SPConfigurationFetcher.h in Headers */,
-				CE4F9CB4244B066500968CFC /* SNOWError.h in Headers */,
-				ED8BF8CD25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */,
-				EDDD7027264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */,
-				ED277BE42625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */,
-				ED0EFE3326E240B1002CAA21 /* SPDeepLinkReceived.h in Headers */,
-				CE4F9D10244B066500968CFC /* SPConsentDocument.h in Headers */,
-				EDD8543624EFFFB300661F6B /* SPRequest.h in Headers */,
-				EDAB65D026CBD5150067755F /* SPDeepLinkEntity.h in Headers */,
-				EDD8541824EEC25100661F6B /* SPEmitterEvent.h in Headers */,
-				CE4F9CA0244B066500968CFC /* SPSchemaRule.h in Headers */,
-				ED87A4302577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */,
-				6BF08DB2270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */,
-				EDB693FC26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */,
-				CE4F9CC0244B066500968CFC /* SPForeground.h in Headers */,
-				ED6B032B271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */,
-				EDDD7045264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */,
-				ED88B56B2578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */,
-				CE4F9CF4244B066500968CFC /* SPStructured.h in Headers */,
-				EDD8542224EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */,
-				ED88670025715DD600DB53BB /* SPConfiguration.h in Headers */,
-				EDF2A1B626402D53009032AB /* SPSubjectController.h in Headers */,
-				ED88B5A925792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */,
-				75CAC43421F2A0CC00271FB3 /* SPRequestResult.h in Headers */,
-				ED87A3DB25765DAE000C54EB /* SPSessionController.h in Headers */,
-				ED88672D2573C1F200DB53BB /* SPSessionConfiguration.h in Headers */,
-				ED38D93526EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */,
-				6BBDCD4427019AF4001B547F /* SPPlatformContext.h in Headers */,
-				EDDD7009264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */,
-				ED8A3EED2371709100E51827 /* SPInstallTracker.h in Headers */,
-				6B07CDAF287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */,
-				ED8122AC25E9578600AE7FE8 /* SPSnowplow.h in Headers */,
-				ED88B591257922490048FAD1 /* SPEmitterController.h in Headers */,
-				75CAC43521F2A0CC00271FB3 /* SPWeakTimerTarget.h in Headers */,
-				EDDD7013264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */,
-				ED88B5FD257954370048FAD1 /* SPGDPRController.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2155,119 +1323,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				ED87A3DC25765DAE000C54EB /* SPSessionController.h in Headers */,
-				EDEE834E24BDB326000B8530 /* SPTrackerError.h in Headers */,
-				CE4F9CC1244B066500968CFC /* SPForeground.h in Headers */,
-				ED88B62C257A3FE60048FAD1 /* SPGlobalContextsConfiguration.h in Headers */,
-				EDAB664326D699D90067755F /* SPStateFuture.h in Headers */,
-				ED6B032C271094D700EFA12B /* SPMessageNotificationAttachment.h in Headers */,
-				ED38D92226EBCD59002AEC8E /* SPLifecycleEntity.h in Headers */,
-				ED7F082D2619199D005D377E /* SPRemoteConfiguration.h in Headers */,
-				ED88B7372587777B0048FAD1 /* SPEmitterEventProcessing.h in Headers */,
-				EDF2A1B726402D53009032AB /* SPSubjectController.h in Headers */,
-				ED88B64D257A57F80048FAD1 /* SPGlobalContextsController.h in Headers */,
-				EDB693FD26B7F61D00B76A79 /* SPMemoryEventStore.h in Headers */,
-				CE4F9D05244B066500968CFC /* SPEcommerceItem.h in Headers */,
-				EDD8541924EEC25100661F6B /* SPEmitterEvent.h in Headers */,
-				6B07CDB0287721C600E510D6 /* SPWebViewMessageHandler.h in Headers */,
-				75F9C5E521FA35BC00A5B8FC /* Snowplow-umbrella-header.h in Headers */,
-				ED87A3EE25766BB4000C54EB /* SPSessionControllerImpl.h in Headers */,
-				ED8866C125711EC000DB53BB /* SPLoggerDelegate.h in Headers */,
-				CE4F9CB9244B066500968CFC /* SPSelfDescribing.h in Headers */,
-				ED88B5AA25792C620048FAD1 /* SPEmitterControllerImpl.h in Headers */,
-				75F9C5E621FA35BC00A5B8FC /* SPTrackerConstants.h in Headers */,
-				CE4F9D15244B066500968CFC /* SPBackground.h in Headers */,
-				ED34672D26415C1D0018BA61 /* SPJSONSerialization.h in Headers */,
-				ED91CB6F23AA715B0078E75F /* SPDevicePlatform.h in Headers */,
-				CE4F9CA9244B066500968CFC /* SPTiming.h in Headers */,
-				EDAB665926D6AA940067755F /* SPDeepLinkStateMachine.h in Headers */,
-				ED87A41F2577AC5B000C54EB /* SPTrackerController.h in Headers */,
-				ED852B3523A0EEC600F2DF6B /* SNOWReachability.h in Headers */,
-				CE4F9CB5244B066500968CFC /* SNOWError.h in Headers */,
-				ED88670125715DD600DB53BB /* SPConfiguration.h in Headers */,
-				ED8122AD25E9578600AE7FE8 /* SPSnowplow.h in Headers */,
-				EDAB665326D69D740067755F /* SPScreenStateMachine.h in Headers */,
-				ED8BF8B625700B40001DFDD9 /* SPTrackerConfiguration.h in Headers */,
-				75F9C5E721FA35BC00A5B8FC /* SPTracker.h in Headers */,
-				75F9C5E821FA35BC00A5B8FC /* SPEmitter.h in Headers */,
-				ED0EFE3426E240B1002CAA21 /* SPDeepLinkReceived.h in Headers */,
-				EDDD700A264E8ECE00259404 /* SPServiceProviderProtocol.h in Headers */,
-				ED277BE52625F5C5002C7B6D /* SPFetchedConfigurationBundle.h in Headers */,
-				6BF08DA9270DEED6009C7E2B /* SPDeviceInfoMonitor.h in Headers */,
-				CE4F9CE5244B066500968CFC /* SPConsentGranted.h in Headers */,
-				CE4F9CFD244B066500968CFC /* SPEventBase.h in Headers */,
-				EDAB663B26D699D90067755F /* SPStateManager.h in Headers */,
-				ED9897192627006F00145157 /* NSDictionary+SP_TypeMethods.h in Headers */,
-				ED88B5FE257954370048FAD1 /* SPGDPRController.h in Headers */,
-				75F9C5E921FA35BC00A5B8FC /* SPSubject.h in Headers */,
-				75F9C5EB21FA35BC00A5B8FC /* SPPayload.h in Headers */,
-				CE4F9CE9244B066500968CFC /* SPPushNotification.h in Headers */,
-				ED88B66B257A5A520048FAD1 /* SPGlobalContextsControllerImpl.h in Headers */,
-				ED88B610257956490048FAD1 /* SPGDPRControllerImpl.h in Headers */,
-				EDD8542324EFEFB900661F6B /* SPDefaultNetworkConnection.h in Headers */,
-				75F9C5EC21FA35BC00A5B8FC /* SPSelfDescribingJson.h in Headers */,
-				CE4F9D11244B066500968CFC /* SPConsentDocument.h in Headers */,
-				ED7CE17B26DFBFA30035C323 /* SPTrackerStateSnapshot.h in Headers */,
-				CE4F9CAD244B066500968CFC /* SPTrackerEvent.h in Headers */,
-				ED277BD52625F220002C7B6D /* SPConfigurationBundle.h in Headers */,
-				EDDD7000264E873B00259404 /* SPController.h in Headers */,
-				CE4F9CF5244B066500968CFC /* SPStructured.h in Headers */,
-				ED49DF3F2757E4F500610843 /* SPSessionState.h in Headers */,
-				EDD8542D24EFEFE600661F6B /* SPNetworkConnection.h in Headers */,
-				ED88B7942587B5620048FAD1 /* SPNetworkControllerImpl.h in Headers */,
-				ED38D93626EBCEBE002AEC8E /* SPLifecycleStateMachine.h in Headers */,
-				ED8BF8CE25701853001DFDD9 /* SPNetworkConfiguration.h in Headers */,
-				75F9C5EE21FA35BC00A5B8FC /* SPUtilities.h in Headers */,
-				ED87A4312577ADFF000C54EB /* SPTrackerControllerImpl.h in Headers */,
-				CE4F9D09244B066500968CFC /* SPSchemaRuleset.h in Headers */,
-				7534D20422569BFF00904EE5 /* SPScreenState.h in Headers */,
-				ED88672E2573C1F200DB53BB /* SPSessionConfiguration.h in Headers */,
-				EDD8543724EFFFB300661F6B /* SPRequest.h in Headers */,
-				ED88B56C2578F8820048FAD1 /* SPEmitterConfiguration.h in Headers */,
-				75F9C5F221FA35BC00A5B8FC /* SPRequestCallback.h in Headers */,
-				ED88B6DE2583DFC90048FAD1 /* SPServiceProvider.h in Headers */,
-				ED8866E52571445300DB53BB /* SPSubjectConfiguration.h in Headers */,
-				EDDD703C264F27E700259404 /* SPNetworkConfigurationUpdate.h in Headers */,
-				6BACDF952897C2630013276E /* SPConfigurationState.h in Headers */,
-				ED38D92E26EBCEBE002AEC8E /* SPLifecycleState.h in Headers */,
-				CE4F9CCD244B066500968CFC /* SPGlobalContext.h in Headers */,
-				ED7CE18026DFC12C0035C323 /* SPState.h in Headers */,
-				CE4F9CD9244B066500968CFC /* SPScreenView.h in Headers */,
-				7534D20622569BFF00904EE5 /* SPInstallTracker.h in Headers */,
-				EDDD701E264F230400259404 /* SPSubjectConfigurationUpdate.h in Headers */,
-				ED7F082126191108005D377E /* SPConfigurationProvider.h in Headers */,
-				EDDD7032264F25A200259404 /* SPSessionConfigurationUpdate.h in Headers */,
-				7534D20522569BFF00904EE5 /* UIViewController+SPScreenView_SWIZZLE.h in Headers */,
-				ED7CE17226DFB55C0035C323 /* SPTrackerState.h in Headers */,
-				CE4F9C9D244B066500968CFC /* SPEvent.h in Headers */,
-				ED88B7702587B4F00048FAD1 /* SPNetworkController.h in Headers */,
-				ED7F0843261924BF005D377E /* SPConfigurationFetcher.h in Headers */,
-				EDB2FD1C26C130B80031B872 /* SPDataPersistence.h in Headers */,
-				ED88B5E2257950210048FAD1 /* SPGDPRConfiguration.h in Headers */,
-				ED88B592257922490048FAD1 /* SPEmitterController.h in Headers */,
-				75F9C5ED21FA35BC00A5B8FC /* SPSQLiteEventStore.h in Headers */,
-				75F9C5F021FA35BC00A5B8FC /* SPWeakTimerTarget.h in Headers */,
-				EDAB65D126CBD5150067755F /* SPDeepLinkEntity.h in Headers */,
-				ED914EBD24325AB40068DA0A /* SPGdprContext.h in Headers */,
-				6BF08DB3270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.h in Headers */,
-				75F9C5EA21FA35BC00A5B8FC /* SPSession.h in Headers */,
-				6BBDCD4527019AF4001B547F /* SPPlatformContext.h in Headers */,
-				EDDD7014264F1D2100259404 /* SPTrackerConfigurationUpdate.h in Headers */,
-				EDF2A1BD264032F9009032AB /* SPSubjectControllerImpl.h in Headers */,
-				EDAB664726D699D90067755F /* SPStateMachineProtocol.h in Headers */,
-				EDDD7028264F23C600259404 /* SPGDPRConfigurationUpdate.h in Headers */,
-				CE4F9CA5244B066500968CFC /* SPPageView.h in Headers */,
-				EDEE835D24BE0944000B8530 /* SPLogger.h in Headers */,
-				CE4F9CA1244B066500968CFC /* SPSchemaRule.h in Headers */,
-				75F9C5EF21FA35BC00A5B8FC /* SPRequestResult.h in Headers */,
-				CE4F9D1D244B066500968CFC /* SPEcommerce.h in Headers */,
-				6B871F6927C3976D00BCF742 /* SPMockNetworkConnection.h in Headers */,
-				EDDD7046264F2A8800259404 /* SPEmitterConfigurationUpdate.h in Headers */,
-				ED98972926287F7A00145157 /* SPConfigurationCache.h in Headers */,
-				ED9081B82703747C00EE9421 /* SPMessageNotification.h in Headers */,
-				EDD8540F24EE786900661F6B /* SPEventStore.h in Headers */,
-				CE4F9CB1244B066500968CFC /* SPConsentWithdrawn.h in Headers */,
-				EDAB666326D6ACCB0067755F /* SPDeepLinkState.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2279,7 +1334,6 @@
 			buildConfigurationList = 752DABDD21CC38560065F874 /* Build configuration list for PBXNativeTarget "Snowplow-iOS" */;
 			buildPhases = (
 				752DABC721CC38550065F874 /* Headers */,
-				ED8867182571695B00DB53BB /* Run Script - Copy public headers */,
 				752DABC821CC38550065F874 /* Sources */,
 				752DABC921CC38550065F874 /* Frameworks */,
 				752DABCA21CC38550065F874 /* Resources */,
@@ -2293,14 +1347,14 @@
 			productReference = 752DABCC21CC38550065F874 /* SnowplowTracker.framework */;
 			productType = "com.apple.product-type.framework";
 		};
-		752DABD321CC38560065F874 /* Snowplow-iOSTests */ = {
+		752DABD321CC38560065F874 /* Tests */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 752DABE021CC38560065F874 /* Build configuration list for PBXNativeTarget "Snowplow-iOSTests" */;
+			buildConfigurationList = 752DABE021CC38560065F874 /* Build configuration list for PBXNativeTarget "Tests" */;
 			buildPhases = (
 				752DABD021CC38560065F874 /* Sources */,
 				752DABD121CC38560065F874 /* Frameworks */,
 				75CAC41C21F2960F00271FB3 /* CopyFiles */,
-				75CAC42921F29E5900271FB3 /* CopyFiles */,
+				75CAC42921F29E5900271FB3 /* Copy Files */,
 				ED8EA918258A7118008E292C /* Run Script - Copy Frameworks (Carthage) */,
 			);
 			buildRules = (
@@ -2308,9 +1362,9 @@
 			dependencies = (
 				EDB27D2525E0111200DF8CE7 /* PBXTargetDependency */,
 			);
-			name = "Snowplow-iOSTests";
+			name = Tests;
 			productName = "Snowplow iOSTests";
-			productReference = 752DABD421CC38560065F874 /* Snowplow-iOSTests.xctest */;
+			productReference = 752DABD421CC38560065F874 /* Tests.xctest */;
 			productType = "com.apple.product-type.bundle.unit-test";
 		};
 		752DABE721CC3A090065F874 /* Snowplow-watchOS */ = {
@@ -2318,7 +1372,6 @@
 			buildConfigurationList = 752DABED21CC3A090065F874 /* Build configuration list for PBXNativeTarget "Snowplow-watchOS" */;
 			buildPhases = (
 				752DABE321CC3A090065F874 /* Headers */,
-				ED8C8D0825E4FC4500BF6D14 /* Run Script - Copy public headers */,
 				752DABE421CC3A090065F874 /* Sources */,
 				752DABE521CC3A090065F874 /* Frameworks */,
 				752DABE621CC3A090065F874 /* Resources */,
@@ -2337,7 +1390,6 @@
 			buildConfigurationList = 752DAC0621CC3B380065F874 /* Build configuration list for PBXNativeTarget "Snowplow-macOS" */;
 			buildPhases = (
 				752DABF021CC3B380065F874 /* Headers */,
-				ED8C8D0925E4FCF000BF6D14 /* Run Script - Copy public headers */,
 				752DABF121CC3B380065F874 /* Sources */,
 				752DABF221CC3B380065F874 /* Frameworks */,
 				752DABF321CC3B380065F874 /* Resources */,
@@ -2374,7 +1426,6 @@
 			buildConfigurationList = 75F9C5CE21FA2E0F00A5B8FC /* Build configuration list for PBXNativeTarget "Snowplow-iOS-Static" */;
 			buildPhases = (
 				75F9C5E321FA359400A5B8FC /* Headers */,
-				ED8C8D0A25E4FD1100BF6D14 /* Run Script - Copy public headers */,
 				75F9C5C421FA2E0F00A5B8FC /* Sources */,
 				75F9C5C521FA2E0F00A5B8FC /* Frameworks */,
 				75F9C5C621FA2E0F00A5B8FC /* CopyFiles */,
@@ -2394,12 +1445,12 @@
 		AB0C27B5191B408200018557 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1230;
+				LastUpgradeCheck = 1410;
 				ORGANIZATIONNAME = "Snowplow Analytics";
 				TargetAttributes = {
 					752DABCB21CC38550065F874 = {
 						CreatedOnToolsVersion = 10.1;
-						LastSwiftMigration = 1020;
+						LastSwiftMigration = 1410;
 					};
 					752DABD321CC38560065F874 = {
 						CreatedOnToolsVersion = 10.1;
@@ -2407,15 +1458,18 @@
 					};
 					752DABE721CC3A090065F874 = {
 						CreatedOnToolsVersion = 10.1;
+						LastSwiftMigration = 1410;
 					};
 					752DABF421CC3B380065F874 = {
 						CreatedOnToolsVersion = 10.1;
+						LastSwiftMigration = 1410;
 					};
 					752DABFC21CC3B380065F874 = {
 						CreatedOnToolsVersion = 10.1;
 					};
 					75F9C5C721FA2E0F00A5B8FC = {
 						CreatedOnToolsVersion = 10.1;
+						LastSwiftMigration = 1410;
 					};
 				};
 			};
@@ -2433,7 +1487,7 @@
 			projectRoot = "";
 			targets = (
 				752DABCB21CC38550065F874 /* Snowplow-iOS */,
-				752DABD321CC38560065F874 /* Snowplow-iOSTests */,
+				752DABD321CC38560065F874 /* Tests */,
 				752DABE721CC3A090065F874 /* Snowplow-watchOS */,
 				752DABF421CC3B380065F874 /* Snowplow-macOS */,
 				752DABFC21CC3B380065F874 /* Snowplow-macOSTests */,
@@ -2474,78 +1528,6 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		ED8867182571695B00DB53BB /* Run Script - Copy public headers */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			name = "Run Script - Copy public headers";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "sh ./Snowplow/buildPhaseScript/copy_public_headers.sh\n";
-		};
-		ED8C8D0825E4FC4500BF6D14 /* Run Script - Copy public headers */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			name = "Run Script - Copy public headers";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "sh ./Snowplow/buildPhaseScript/copy_public_headers.sh\n";
-		};
-		ED8C8D0925E4FCF000BF6D14 /* Run Script - Copy public headers */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			name = "Run Script - Copy public headers";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "sh ./Snowplow/buildPhaseScript/copy_public_headers.sh\n";
-		};
-		ED8C8D0A25E4FD1100BF6D14 /* Run Script - Copy public headers */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			name = "Run Script - Copy public headers";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "sh ./Snowplow/buildPhaseScript/copy_public_headers.sh\n";
-		};
 		ED8EA918258A7118008E292C /* Run Script - Copy Frameworks (Carthage) */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -2579,100 +1561,122 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				ED98972A26287F7A00145157 /* SPConfigurationCache.m in Sources */,
-				ED88B5AB25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */,
-				CE4F9CC6244B066500968CFC /* SPSchemaRuleset.m in Sources */,
-				6B07CDB1287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */,
-				EDDD7047264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */,
-				754774D0222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.m in Sources */,
-				CE4F9CDA244B066500968CFC /* SPPageView.m in Sources */,
-				ED34672E26415C1D0018BA61 /* SPJSONSerialization.m in Sources */,
-				ED88B7952587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */,
-				EDF2A1BE264032F9009032AB /* SPSubjectControllerImpl.m in Sources */,
-				ED88670225715DD600DB53BB /* SPConfiguration.m in Sources */,
-				EDAB666426D6ACCB0067755F /* SPDeepLinkState.m in Sources */,
-				ED88B6DF2583DFC90048FAD1 /* SPServiceProvider.m in Sources */,
-				ED6B032D271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */,
-				EDB2FD1D26C130B80031B872 /* SPDataPersistence.m in Sources */,
-				EDAB665026D69D740067755F /* SPScreenStateMachine.m in Sources */,
-				ED7F081826190E00005D377E /* SPConfigurationProvider.m in Sources */,
-				CE4F9C96244B066500968CFC /* SPEcommerceItem.m in Sources */,
-				6BF08DB4270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */,
-				ED88B611257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */,
-				EDDD7033264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */,
-				752DAC1721CC42BC0065F874 /* SPTrackerConstants.m in Sources */,
-				ED277BE62625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */,
-				CE4F9C92244B066500968CFC /* SPForeground.m in Sources */,
-				ED49DF402757E4F500610843 /* SPSessionState.m in Sources */,
-				ED91CB7123AA8AD50078E75F /* SPDevicePlatform.m in Sources */,
-				CE4F9CBA244B066500968CFC /* SPSelfDescribing.m in Sources */,
-				ED277BD62625F220002C7B6D /* SPConfigurationBundle.m in Sources */,
-				ED88B62D257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */,
-				CE4F9D16244B066500968CFC /* SPGlobalContext.m in Sources */,
-				EDDD7029264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */,
-				ED87A3EF25766BB4000C54EB /* SPSessionControllerImpl.m in Sources */,
-				ED8BF8B725700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */,
-				ED7F082E2619199D005D377E /* SPRemoteConfiguration.m in Sources */,
-				EDEE835E24BE0944000B8530 /* SPLogger.m in Sources */,
-				6BD6A6AD288719C7002D6D40 /* SPMockWKScriptMessage.m in Sources */,
-				ED88B56D2578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */,
-				ED88B5E3257950210048FAD1 /* SPGDPRConfiguration.m in Sources */,
-				6BBDCD4627019AF4001B547F /* SPPlatformContext.m in Sources */,
-				CE4F9CDE244B066500968CFC /* SPStructured.m in Sources */,
-				ED914EBE24325AB40068DA0A /* SPGdprContext.m in Sources */,
-				EDAB665A26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */,
-				752DAC1921CC42BC0065F874 /* SPTracker.m in Sources */,
-				EDAB65D226CBD5150067755F /* SPDeepLinkEntity.m in Sources */,
-				ED38D92326EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */,
-				EDD8543824EFFFB300661F6B /* SPRequest.m in Sources */,
-				EDDD703D264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */,
-				CE4F9CC2244B066500968CFC /* SPConsentGranted.m in Sources */,
-				ED38D92F26EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */,
-				ED7F0844261924BF005D377E /* SPConfigurationFetcher.m in Sources */,
-				752DAC1B21CC42BC0065F874 /* SPEmitter.m in Sources */,
-				ED7CE17326DFB55C0035C323 /* SPTrackerState.m in Sources */,
-				75264A32224E5DD2000E0E9B /* SPInstallTracker.m in Sources */,
-				ED9081B12703747C00EE9421 /* SPMessageNotification.m in Sources */,
-				EDDD7001264E873B00259404 /* SPController.m in Sources */,
-				EDB693FE26B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */,
-				ED88B66C257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */,
-				ED8122AE25E9578600AE7FE8 /* SPSnowplow.m in Sources */,
-				ED8866E62571445300DB53BB /* SPSubjectConfiguration.m in Sources */,
-				CE4F9C86244B066500968CFC /* SPTiming.m in Sources */,
-				EDD8541A24EEC25100661F6B /* SPEmitterEvent.m in Sources */,
-				ED98971A2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */,
-				CE4F9CF6244B066500968CFC /* SPEventBase.m in Sources */,
-				CE4F9D0A244B066500968CFC /* SPScreenView.m in Sources */,
-				752DAC1D21CC42BC0065F874 /* SPSubject.m in Sources */,
-				ED0EFE2D26E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */,
-				EDDD701F264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */,
-				752DAC1F21CC42BC0065F874 /* SPSession.m in Sources */,
-				CE4F9D1E244B066500968CFC /* SPEcommerce.m in Sources */,
-				752DAC2121CC42BC0065F874 /* SPPayload.m in Sources */,
-				CE4F9CCE244B066500968CFC /* SNOWError.m in Sources */,
-				752DAC2321CC42BC0065F874 /* SPSelfDescribingJson.m in Sources */,
-				CE4F9CEE244B066500968CFC /* SPSchemaRule.m in Sources */,
-				CE4F9C8E244B066500968CFC /* SPConsentWithdrawn.m in Sources */,
-				CE4F9CD2244B066500968CFC /* SPTrackerEvent.m in Sources */,
-				752DAC2521CC42BC0065F874 /* SPSQLiteEventStore.m in Sources */,
-				752DAC2721CC42BC0065F874 /* SPUtilities.m in Sources */,
-				EDD8542424EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */,
-				CE4F9CEA244B066500968CFC /* SPPushNotification.m in Sources */,
-				ED852B2F23A0E90E00F2DF6B /* SNOWReachability.m in Sources */,
-				752DAC2921CC42BC0065F874 /* SPRequestResult.m in Sources */,
-				752DAC2B21CC42BC0065F874 /* SPWeakTimerTarget.m in Sources */,
-				ED38D93726EBCEBE002AEC8E /* SPLifecycleState.m in Sources */,
-				ED87A4322577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */,
-				CE4F9C8A244B066500968CFC /* SPConsentDocument.m in Sources */,
-				6BF08DAA270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */,
-				EDAB663C26D699D90067755F /* SPStateManager.m in Sources */,
-				CE4F9CFE244B066500968CFC /* SPBackground.m in Sources */,
-				EDDD7015264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */,
-				ED88672F2573C1F200DB53BB /* SPSessionConfiguration.m in Sources */,
-				EDEE834F24BDB326000B8530 /* SPTrackerError.m in Sources */,
-				EDAB663426D699D90067755F /* SPStateFuture.m in Sources */,
-				ED8BF8CF25701853001DFDD9 /* SPNetworkConfiguration.m in Sources */,
-				754774BC2225FBA60043B814 /* SPScreenState.m in Sources */,
+				6BE70873292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
+				6B9DA6332928CAA3006D721A /* Tracker.swift in Sources */,
+				6B9DA6E4292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
+				6B9DA62E2928C08D006D721A /* DataPersistence.swift in Sources */,
+				6B9DA6292928B2FC006D721A /* Session.swift in Sources */,
+				6B85876D292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
+				6B85873C2923E025006E4A5F /* EmitterController.swift in Sources */,
+				6B8F5605291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
+				6B8587462923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
+				6B9DA5F82927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
+				6B85877229251779006E4A5F /* ConfigurationState.swift in Sources */,
+				6B9DA676292BD2D7006D721A /* SchemaRule.swift in Sources */,
+				6B9DA6012927E669006D721A /* State.swift in Sources */,
+				6B9DA5AC29268E4E006D721A /* PageView.swift in Sources */,
+				6BABED49291E884900F6798A /* ConfigurationBundle.swift in Sources */,
+				6BABED35291E596100F6798A /* EmitterConfiguration.swift in Sources */,
+				6B9DA642292B5197006D721A /* ScreenState.swift in Sources */,
+				6B9DA5A729268B0E006D721A /* TrackerError.swift in Sources */,
+				6B9DA5CC2927BCD6006D721A /* RequestResult.swift in Sources */,
+				6B9DA61B2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
+				6B85870129239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
+				6B9DA61F2927E99C006D721A /* TrackerState.swift in Sources */,
+				6B85875E2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
+				6B0CCFCE29236B0B0054954B /* GDPRContext.swift in Sources */,
+				6B8587502923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
+				6BABED4E291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
+				6B9DA67E292BD2D7006D721A /* GlobalContext.swift in Sources */,
+				6B9DA60F2927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
+				6B9DA651292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
+				6B8587592924CDB0006E4A5F /* Snowplow.swift in Sources */,
+				6B9DA5842926437F006D721A /* PushNotification.swift in Sources */,
+				6BABED26291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
+				6B85874A2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
+				6B9DA64C292B5487006D721A /* PlatformContext.swift in Sources */,
+				6B0CCFD429236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
+				6B9DA55329261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
+				6B9DA648292B5487006D721A /* DevicePlatform.swift in Sources */,
+				6B9DA656292B60E2006D721A /* Subject.swift in Sources */,
+				6B8587542923E7D0006E4A5F /* NetworkController.swift in Sources */,
+				6BE7084A292F64A700911E55 /* BufferOption.swift in Sources */,
+				6B9DA55F29261E5B006D721A /* ConsentDocument.swift in Sources */,
+				6B8587632924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
+				6BABED44291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
+				6B9DA56B29261E5C006D721A /* Foreground.swift in Sources */,
+				6B85870E2923C18D006E4A5F /* EventStore.swift in Sources */,
+				6B85872D2923C5F0006E4A5F /* TrackerController.swift in Sources */,
+				6B9DA55B29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
+				6B858769292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
+				6B9DA56F29261E5C006D721A /* SelfDescribing.swift in Sources */,
+				6B9DA67A292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
+				6B9DA692292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
+				6B9DA5D02927BCD6006D721A /* EmitterEvent.swift in Sources */,
+				6B9DA665292BAE5F006D721A /* ServiceProvider.swift in Sources */,
+				6B9DA57B29261E5C006D721A /* Structured.swift in Sources */,
+				6BABED3F291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
+				6B9DA5F02927E635006D721A /* InstallTracker.swift in Sources */,
+				6B9DA56329261E5B006D721A /* EventBase.swift in Sources */,
+				6B9DA57729261E5C006D721A /* Timing.swift in Sources */,
+				6BE70868292F67C000911E55 /* InspectableEvent.swift in Sources */,
+				6B9DA5B629269755006D721A /* TrackerEvent.swift in Sources */,
+				6B9DA684292BDD3A006D721A /* Logger.swift in Sources */,
+				6B9DA688292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
+				6B9DA5C82927BCD6006D721A /* RequestCallback.swift in Sources */,
+				6BABEB6C291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
+				6BE70861292F66E800911E55 /* ProtocolOptions.swift in Sources */,
+				6B9DA5B129269036006D721A /* SNOWError.swift in Sources */,
+				6B9DA6062927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
+				6B8587362923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
+				6B9DA5D42927BCD6006D721A /* Emitter.swift in Sources */,
+				6B9DA5A229268956006D721A /* EcommerceItem.swift in Sources */,
+				6B9DA5EC2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
+				6B9DA53F29253951006D721A /* LifecycleEntity.swift in Sources */,
+				6B85878229251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
+				6BE7084D292F654D00911E55 /* ContextGenerator.swift in Sources */,
+				6BABED3A291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
+				6BABEB7C291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
+				6B8586FA292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
+				6B9DA5C42927BCD6006D721A /* Request.swift in Sources */,
+				6B8587402923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
+				6B9DA6172927E99C006D721A /* StateManager.swift in Sources */,
+				6B9DA6252928B2FC006D721A /* SessionState.swift in Sources */,
+				6B85870929239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
+				6BE7085C292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
+				6BABE8BD291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
+				6B9DA598292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
+				6BABEB71291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
+				6B85877829251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
+				6B9DA661292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
+				6B9DA5F42927E635006D721A /* DeepLinkState.swift in Sources */,
+				6B9DA59D29267B66006D721A /* Ecommerce.swift in Sources */,
+				6B8586F6292388C9006E4A5F /* SessionController.swift in Sources */,
+				6B9DA5E32927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
+				6B9DA55729261E5B006D721A /* ConsentGranted.swift in Sources */,
+				6B9DA6DF292F4A0C006D721A /* EmitterDefaults.swift in Sources */,
+				6B9DA66F292BBCDE006D721A /* SNOWReachability.swift in Sources */,
+				6B85877C29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
+				6B9DA68D292BF03C006D721A /* TrackerConstants.swift in Sources */,
+				6B0CCFD829236D4A0054954B /* SubjectController.swift in Sources */,
+				6B9DA63E292B5197006D721A /* ScreenStateMachine.swift in Sources */,
+				6BE7086D292F683600911E55 /* LogLevel.swift in Sources */,
+				6B8587312923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
+				6B9DA54329253952006D721A /* DeepLinkEntity.swift in Sources */,
+				6B9DA58929264EC2006D721A /* MessageNotification.swift in Sources */,
+				6B0CCFBE292275580054954B /* Controller.swift in Sources */,
+				6B85878629251DF4006E4A5F /* Payload.swift in Sources */,
+				6B0CCFB9292262650054954B /* GDPRController.swift in Sources */,
+				6B9DA66A292BBC83006D721A /* SPSize.swift in Sources */,
+				6B9DA5FC2927E635006D721A /* LifecycleState.swift in Sources */,
+				6BABED21291E3DB200F6798A /* Configuration.swift in Sources */,
+				6B9DA57329261E5C006D721A /* Background.swift in Sources */,
+				6B0CCFB5292262650054954B /* GDPRControllerImpl.swift in Sources */,
+				6BABED53291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
+				6B9DA57F29261E5C006D721A /* ScreenView.swift in Sources */,
+				6B9DA6132927E99C006D721A /* StateFuture.swift in Sources */,
+				6B0CCFC9292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
+				6B9DA65B292B6E68006D721A /* Utilities.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2680,40 +1684,41 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				D99BDC6D2834F89A00F6A14F /* TestLifecycleState.m in Sources */,
-				6BF15D0C2702ECD70048F376 /* TestPlatformContext.m in Sources */,
-				75CAC40821F2955100271FB3 /* LegacyTestSubject.m in Sources */,
-				6BA149392900607C00407200 /* SPMockLoggerDelegate.m in Sources */,
-				ED8BF8ED2570FB2F001DFDD9 /* TestTrackerConfiguration.m in Sources */,
-				75CAC41221F2955100271FB3 /* TestRequest.m in Sources */,
-				75CAC40621F2955100271FB3 /* TestSQLiteEventStore.m in Sources */,
-				ED914EB72432081F0068DA0A /* TestSchemaRuleset.m in Sources */,
-				6B4B77D227C64F6000F4E878 /* TestServiceProvider.m in Sources */,
-				6BD6A6A928871262002D6D40 /* TestWebViewMessageHandler.m in Sources */,
-				75CAC41121F2955100271FB3 /* LegacyTestTracker.m in Sources */,
-				75CAC40D21F2955100271FB3 /* LegacyTestEmitter.m in Sources */,
-				75CAC40721F2955100271FB3 /* TestPayload.m in Sources */,
-				75CAC40A21F2955100271FB3 /* TestGeneratedJsons.m in Sources */,
-				7534D20822569F3400904EE5 /* TestScreenState.m in Sources */,
-				6B871F6127C3913300BCF742 /* TestEmitterConfiguration.m in Sources */,
-				ED914EB52431FEE70068DA0A /* TestGlobalContexts.m in Sources */,
-				EDCBD0D824F5084900D39DD2 /* TestNetworkConnection.m in Sources */,
-				ED88B6B3258253F20048FAD1 /* TestEvents.m in Sources */,
-				EDA06FB62664CD2F007FA773 /* SPMockEventStore.m in Sources */,
-				75CAC40921F2955100271FB3 /* TestSelfDescribingJson.m in Sources */,
-				6B871F6427C3928900BCF742 /* SPMockNetworkConnection.m in Sources */,
-				ED87A43D2577E441000C54EB /* TestTrackerController.m in Sources */,
-				EDEE836324C0C318000B8530 /* TestLogger.m in Sources */,
-				EDB6940326B83BF300B76A79 /* TestMemoryEventStore.m in Sources */,
-				75CAC41321F2955100271FB3 /* TestUtils.m in Sources */,
-				75CAC40521F2955100271FB3 /* TestSession.m in Sources */,
-				EDB2FD2226C57F6D0031B872 /* TestDataPersistence.m in Sources */,
-				ED7F080626190B5F005D377E /* TestRemoteConfiguration.m in Sources */,
-				75CAC40E21F2955100271FB3 /* TestRequestResult.m in Sources */,
-				6BABC50E270B40450043BB5C /* TestSubject.m in Sources */,
-				75CAC40C21F2955100271FB3 /* LegacyTestEvent.m in Sources */,
-				EDE54F4825EFA38D0073947D /* TestMultipleInstances.m in Sources */,
-				EDAB664D26D69A7B0067755F /* TestStateManager.m in Sources */,
+				6B9DA6C2292E4D75006D721A /* LegacyTestTracker.swift in Sources */,
+				6B9DA6AC292D060F006D721A /* TestEmitterConfiguration.swift in Sources */,
+				6B9DA6B9292E15FF006D721A /* TestGlobalContexts.swift in Sources */,
+				6B9DA69C292CB9A3006D721A /* MockLoggerDelegate.swift in Sources */,
+				6B9DA69D292CB9A3006D721A /* MockEventStore.swift in Sources */,
+				6B9DA6D8292E5FE6006D721A /* TestSubject.swift in Sources */,
+				6B9DA6B5292E1181006D721A /* TestRequest.swift in Sources */,
+				6B9DA69F292CB9A3006D721A /* MockWKScriptMessage.swift in Sources */,
+				6B9DA6A3292CFF47006D721A /* TestPayload.swift in Sources */,
+				6B9DA6AD292D060F006D721A /* TestMultipleInstances.swift in Sources */,
+				6B9DA6AF292D060F006D721A /* TestTrackerController.swift in Sources */,
+				6B9DA6B8292E15FF006D721A /* TestSchemaRuleset.swift in Sources */,
+				6B9DA6B3292D203E006D721A /* TestSession.swift in Sources */,
+				6B9DA6AB292D060F006D721A /* TestTrackerConfiguration.swift in Sources */,
+				6B9DA6DA292E6010006D721A /* TestServiceProvider.swift in Sources */,
+				6B9DA69E292CB9A3006D721A /* MockDeviceInfoMonitor.swift in Sources */,
+				6B9DA6BB292E258A006D721A /* TestNetworkConnection.swift in Sources */,
+				6B9DA6C1292E4D75006D721A /* LegacyTestEmitter.swift in Sources */,
+				6B9DA6AE292D060F006D721A /* TestRemoteConfiguration.swift in Sources */,
+				6B9DA6DC292E6034006D721A /* LegacyTestSubject.swift in Sources */,
+				6B9DA6CC292E5DF9006D721A /* TestSelfDescribingJson.swift in Sources */,
+				6B9DA6CA292E5DD9006D721A /* TestMemoryEventStore.swift in Sources */,
+				6B9DA69B292CB9A3006D721A /* MockNetworkConnection.swift in Sources */,
+				6B9DA6D0292E5E7F006D721A /* TestUtils.swift in Sources */,
+				6B9DA6BD292E2923006D721A /* TestGeneratedJsons.swift in Sources */,
+				6B9DA6C6292E589F006D721A /* TestWebViewMessageHandler.swift in Sources */,
+				6B9DA6C8292E5B2B006D721A /* TestPlatformContext.swift in Sources */,
+				6B9DA6C4292E5761006D721A /* TestSQLiteEventStore.swift in Sources */,
+				6B9DA6A1292CBFEB006D721A /* TestEvents.swift in Sources */,
+				6B9DA6B2292D203E006D721A /* TestStateManager.swift in Sources */,
+				6B9DA6D2292E5F5E006D721A /* TestLifecycleState.swift in Sources */,
+				6B9DA6CE292E5E39006D721A /* TestScreenState.swift in Sources */,
+				6B9DA6D6292E5FB6006D721A /* TestDataPersistence.swift in Sources */,
+				6B9DA6A5292D051A006D721A /* TestRequestResult.swift in Sources */,
+				6B9DA6D4292E5F71006D721A /* TestLogger.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2721,98 +1726,122 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				ED88B612257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */,
-				ED87A4332577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */,
-				EDAB663D26D699D90067755F /* SPStateManager.m in Sources */,
-				EDAB65D326CBD5150067755F /* SPDeepLinkEntity.m in Sources */,
-				ED8BF8B825700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */,
-				ED0EFE2E26E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */,
-				75CAC43A21F2A17500271FB3 /* SPTrackerConstants.m in Sources */,
-				CE4F9CDF244B066500968CFC /* SPStructured.m in Sources */,
-				EDD8543924EFFFB300661F6B /* SPRequest.m in Sources */,
-				ED88B7962587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */,
-				EDDD7020264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */,
-				ED8866E72571445300DB53BB /* SPSubjectConfiguration.m in Sources */,
-				754774BD2225FBA60043B814 /* SPScreenState.m in Sources */,
-				75CAC43B21F2A17500271FB3 /* SPTracker.m in Sources */,
-				75CAC43C21F2A17500271FB3 /* SPEmitter.m in Sources */,
-				ED277BD72625F220002C7B6D /* SPConfigurationBundle.m in Sources */,
-				CE4F9CBB244B066500968CFC /* SPSelfDescribing.m in Sources */,
-				ED6B032E271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */,
-				EDD8542524EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */,
-				75CAC43D21F2A17500271FB3 /* SPSubject.m in Sources */,
-				ED8BF8D025701853001DFDD9 /* SPNetworkConfiguration.m in Sources */,
-				ED914EBF24325AB40068DA0A /* SPGdprContext.m in Sources */,
-				6BF08DB5270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */,
-				CE4F9C8F244B066500968CFC /* SPConsentWithdrawn.m in Sources */,
-				CE4F9D17244B066500968CFC /* SPGlobalContext.m in Sources */,
-				ED9081B22703747C00EE9421 /* SPMessageNotification.m in Sources */,
-				ED88B5E4257950210048FAD1 /* SPGDPRConfiguration.m in Sources */,
-				ED7CE16A26DE43A00035C323 /* SPScreenStateMachine.m in Sources */,
-				EDDD7034264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */,
-				ED7F0845261924BF005D377E /* SPConfigurationFetcher.m in Sources */,
-				ED88670325715DD600DB53BB /* SPConfiguration.m in Sources */,
-				ED8867302573C1F200DB53BB /* SPSessionConfiguration.m in Sources */,
-				ED38D93026EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */,
-				ED88B5AC25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */,
-				CE4F9CC3244B066500968CFC /* SPConsentGranted.m in Sources */,
-				CE4F9CF7244B066500968CFC /* SPEventBase.m in Sources */,
-				EDAB665B26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */,
-				ED98972B26287F7A00145157 /* SPConfigurationCache.m in Sources */,
-				EDEE835024BDB326000B8530 /* SPTrackerError.m in Sources */,
-				CE4F9C93244B066500968CFC /* SPForeground.m in Sources */,
-				CE4F9CFF244B066500968CFC /* SPBackground.m in Sources */,
-				ED8122AF25E9578600AE7FE8 /* SPSnowplow.m in Sources */,
-				EDEE835F24BE0944000B8530 /* SPLogger.m in Sources */,
-				CE4F9CEF244B066500968CFC /* SPSchemaRule.m in Sources */,
-				75CAC43E21F2A17500271FB3 /* SPSession.m in Sources */,
-				ED88B66D257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */,
-				ED7CE17426DFB55C0035C323 /* SPTrackerState.m in Sources */,
-				ED277BE72625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */,
-				ED7F081926190E00005D377E /* SPConfigurationProvider.m in Sources */,
-				CE4F9CEB244B066500968CFC /* SPPushNotification.m in Sources */,
-				EDAB666526D6ACCB0067755F /* SPDeepLinkState.m in Sources */,
-				75CAC43F21F2A17500271FB3 /* SPPayload.m in Sources */,
-				EDDD7048264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */,
-				CE4F9CC7244B066500968CFC /* SPSchemaRuleset.m in Sources */,
-				CE4F9C8B244B066500968CFC /* SPConsentDocument.m in Sources */,
-				EDB2FD1E26C130B80031B872 /* SPDataPersistence.m in Sources */,
-				EDD8541B24EEC25100661F6B /* SPEmitterEvent.m in Sources */,
-				EDDD7016264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */,
-				ED38D93826EBCEBE002AEC8E /* SPLifecycleState.m in Sources */,
-				ED34672F26415C1D0018BA61 /* SPJSONSerialization.m in Sources */,
-				75CAC44021F2A17500271FB3 /* SPSelfDescribingJson.m in Sources */,
-				CE4F9CCF244B066500968CFC /* SNOWError.m in Sources */,
-				CE4F9C87244B066500968CFC /* SPTiming.m in Sources */,
-				6B07CDB2287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */,
-				ED49DF412757E4F500610843 /* SPSessionState.m in Sources */,
-				CE4F9D1F244B066500968CFC /* SPEcommerce.m in Sources */,
-				EDDD703E264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */,
-				75CAC44121F2A17500271FB3 /* SPSQLiteEventStore.m in Sources */,
-				CE4F9D0B244B066500968CFC /* SPScreenView.m in Sources */,
-				75CAC44221F2A17500271FB3 /* SPUtilities.m in Sources */,
-				ED7F082F2619199D005D377E /* SPRemoteConfiguration.m in Sources */,
-				ED87A3F025766BB4000C54EB /* SPSessionControllerImpl.m in Sources */,
-				ED88B6E02583DFC90048FAD1 /* SPServiceProvider.m in Sources */,
-				CE4F9CDB244B066500968CFC /* SPPageView.m in Sources */,
-				ED88B56E2578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */,
-				EDAB663526D699D90067755F /* SPStateFuture.m in Sources */,
-				EDDD702A264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */,
-				75CAC44321F2A17500271FB3 /* SPRequestResult.m in Sources */,
-				EDDD7002264E873B00259404 /* SPController.m in Sources */,
-				EDFEEAC923A7CB3C001E6D03 /* SPInstallTracker.m in Sources */,
-				75CAC44421F2A17500271FB3 /* SPWeakTimerTarget.m in Sources */,
-				ED98971B2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */,
-				6BBDCD4727019AF4001B547F /* SPPlatformContext.m in Sources */,
-				EDB693FF26B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */,
-				CE4F9CD3244B066500968CFC /* SPTrackerEvent.m in Sources */,
-				EDF2A1BF264032F9009032AB /* SPSubjectControllerImpl.m in Sources */,
-				ED38D92426EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */,
-				ED91CB7223AA8AD50078E75F /* SPDevicePlatform.m in Sources */,
-				CE4F9C97244B066500968CFC /* SPEcommerceItem.m in Sources */,
-				ED88B62E257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */,
-				6BF08DAB270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */,
-				75CAC44721F2A17500271FB3 /* Snowplow-umbrella-header.h in Sources */,
+				6BE70874292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
+				6B9DA649292B5487006D721A /* DevicePlatform.swift in Sources */,
+				6B9DA6E5292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
+				6B85874B2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
+				6B0CCFCF29236B0B0054954B /* GDPRContext.swift in Sources */,
+				6B0CCFCA292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
+				6B9DA5852926437F006D721A /* PushNotification.swift in Sources */,
+				6BABED45291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
+				6B85872E2923C5F0006E4A5F /* TrackerController.swift in Sources */,
+				6B85877329251779006E4A5F /* ConfigurationState.swift in Sources */,
+				6B9DA5B729269755006D721A /* TrackerEvent.swift in Sources */,
+				6BABED3B291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
+				6B9DA5ED2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
+				6B9DA5E42927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
+				6B9DA63F292B5197006D721A /* ScreenStateMachine.swift in Sources */,
+				6B85875A2924CDB0006E4A5F /* Snowplow.swift in Sources */,
+				6B9DA599292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
+				6B85878329251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
+				6B9DA66B292BBC83006D721A /* SPSize.swift in Sources */,
+				6B9DA59E29267B66006D721A /* Ecommerce.swift in Sources */,
+				6B8587552923E7D0006E4A5F /* NetworkController.swift in Sources */,
+				6B8F5606291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
+				6B9DA670292BBCDE006D721A /* SNOWReachability.swift in Sources */,
+				6B9DA5F12927E635006D721A /* InstallTracker.swift in Sources */,
+				6B9DA57829261E5C006D721A /* Timing.swift in Sources */,
+				6B9DA5CD2927BCD6006D721A /* RequestResult.swift in Sources */,
+				6B9DA657292B60E2006D721A /* Subject.swift in Sources */,
+				6B9DA5FD2927E635006D721A /* LifecycleState.swift in Sources */,
+				6B9DA693292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
+				6B9DA6142927E99C006D721A /* StateFuture.swift in Sources */,
+				6B8587372923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
+				6BABE8BE291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
+				6B9DA643292B5197006D721A /* ScreenState.swift in Sources */,
+				6B9DA55829261E5B006D721A /* ConsentGranted.swift in Sources */,
+				6B85877929251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
+				6B9DA5D52927BCD6006D721A /* Emitter.swift in Sources */,
+				6B9DA5A829268B0E006D721A /* TrackerError.swift in Sources */,
+				6B0CCFD529236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
+				6BABED22291E3DB200F6798A /* Configuration.swift in Sources */,
+				6B8587472923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
+				6B9DA67B292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
+				6BE7084E292F656500911E55 /* BufferOption.swift in Sources */,
+				6B9DA685292BDD3A006D721A /* Logger.swift in Sources */,
+				6B9DA65C292B6E68006D721A /* Utilities.swift in Sources */,
+				6BABED27291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
+				6B9DA54429253952006D721A /* DeepLinkEntity.swift in Sources */,
+				6B9DA5F92927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
+				6BABEB72291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
+				6BABED40291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
+				6B9DA666292BAE5F006D721A /* ServiceProvider.swift in Sources */,
+				6B9DA57029261E5C006D721A /* SelfDescribing.swift in Sources */,
+				6B8587412923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
+				6B85877D29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
+				6B0CCFBF292275580054954B /* Controller.swift in Sources */,
+				6B9DA6072927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
+				6B9DA58029261E5C006D721A /* ScreenView.swift in Sources */,
+				6B9DA6102927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
+				6B0CCFD929236D4A0054954B /* SubjectController.swift in Sources */,
+				6BE70869292F67C000911E55 /* InspectableEvent.swift in Sources */,
+				6B9DA67F292BD2D7006D721A /* GlobalContext.swift in Sources */,
+				6B8586F7292388C9006E4A5F /* SessionController.swift in Sources */,
+				6B9DA54029253952006D721A /* LifecycleEntity.swift in Sources */,
+				6B9DA689292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
+				6B9DA5C52927BCD6006D721A /* Request.swift in Sources */,
+				6BE70862292F66E800911E55 /* ProtocolOptions.swift in Sources */,
+				6BABED36291E596100F6798A /* EmitterConfiguration.swift in Sources */,
+				6B8586FB292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
+				6B8587512923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
+				6B85878729251DF4006E4A5F /* Payload.swift in Sources */,
+				6B9DA57429261E5C006D721A /* Background.swift in Sources */,
+				6B9DA56C29261E5C006D721A /* Foreground.swift in Sources */,
+				6B9DA677292BD2D7006D721A /* SchemaRule.swift in Sources */,
+				6BABED54291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
+				6BE70857292F657E00911E55 /* ContextGenerator.swift in Sources */,
+				6B9DA6342928CAA3006D721A /* Tracker.swift in Sources */,
+				6B85870229239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
+				6B9DA652292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
+				6B9DA62A2928B2FC006D721A /* Session.swift in Sources */,
+				6B9DA62F2928C08D006D721A /* DataPersistence.swift in Sources */,
+				6B9DA6202927E99C006D721A /* TrackerState.swift in Sources */,
+				6B9DA64D292B5487006D721A /* PlatformContext.swift in Sources */,
+				6B9DA6262928B2FC006D721A /* SessionState.swift in Sources */,
+				6BE7085D292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
+				6B9DA6022927E669006D721A /* State.swift in Sources */,
+				6BABED4F291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
+				6B9DA5F52927E635006D721A /* DeepLinkState.swift in Sources */,
+				6B9DA57C29261E5C006D721A /* Structured.swift in Sources */,
+				6B9DA5A329268956006D721A /* EcommerceItem.swift in Sources */,
+				6B85875F2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
+				6BABED4A291E884900F6798A /* ConfigurationBundle.swift in Sources */,
+				6B85876A292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
+				6B9DA6E0292F4A0D006D721A /* EmitterDefaults.swift in Sources */,
+				6B9DA68E292BF03C006D721A /* TrackerConstants.swift in Sources */,
+				6BE7084F292F656500911E55 /* EventStore.swift in Sources */,
+				6B85870A29239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
+				6B9DA662292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
+				6B85873D2923E025006E4A5F /* EmitterController.swift in Sources */,
+				6B8587322923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
+				6BABEB7D291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
+				6BE7086E292F683600911E55 /* LogLevel.swift in Sources */,
+				6B9DA55C29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
+				6B9DA55429261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
+				6B9DA61C2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
+				6BE70850292F656500911E55 /* EmitterEvent.swift in Sources */,
+				6B9DA5B229269036006D721A /* SNOWError.swift in Sources */,
+				6B9DA5C92927BCD6006D721A /* RequestCallback.swift in Sources */,
+				6B9DA6182927E99C006D721A /* StateManager.swift in Sources */,
+				6B9DA58A29264EC2006D721A /* MessageNotification.swift in Sources */,
+				6B9DA5AD29268E4E006D721A /* PageView.swift in Sources */,
+				6B9DA56429261E5B006D721A /* EventBase.swift in Sources */,
+				6B0CCFBA292262650054954B /* GDPRController.swift in Sources */,
+				6B0CCFB6292262650054954B /* GDPRControllerImpl.swift in Sources */,
+				6B9DA56029261E5B006D721A /* ConsentDocument.swift in Sources */,
+				6B8587642924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
+				6B85876E292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
+				6BABEB6D291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2820,98 +1849,122 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				ED88B613257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */,
-				ED87A4342577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */,
-				EDAB663E26D699D90067755F /* SPStateManager.m in Sources */,
-				EDAB65D426CBD5150067755F /* SPDeepLinkEntity.m in Sources */,
-				ED8BF8B925700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */,
-				ED0EFE2F26E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */,
-				75CAC44821F2A19500271FB3 /* SPTrackerConstants.m in Sources */,
-				CE4F9CE0244B066500968CFC /* SPStructured.m in Sources */,
-				EDD8543A24EFFFB300661F6B /* SPRequest.m in Sources */,
-				ED88B7972587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */,
-				EDDD7021264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */,
-				ED8866E82571445300DB53BB /* SPSubjectConfiguration.m in Sources */,
-				754774BE2225FBA60043B814 /* SPScreenState.m in Sources */,
-				75CAC44921F2A19500271FB3 /* SPTracker.m in Sources */,
-				ED8A3EEC2371708C00E51827 /* SPInstallTracker.m in Sources */,
-				ED277BD82625F220002C7B6D /* SPConfigurationBundle.m in Sources */,
-				CE4F9CBC244B066500968CFC /* SPSelfDescribing.m in Sources */,
-				ED6B032F271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */,
-				EDD8542624EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */,
-				75CAC44A21F2A19500271FB3 /* SPEmitter.m in Sources */,
-				ED8BF8D125701853001DFDD9 /* SPNetworkConfiguration.m in Sources */,
-				ED914EC024325AB40068DA0A /* SPGdprContext.m in Sources */,
-				6BF08DB6270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */,
-				CE4F9C90244B066500968CFC /* SPConsentWithdrawn.m in Sources */,
-				CE4F9D18244B066500968CFC /* SPGlobalContext.m in Sources */,
-				ED9081B32703747C00EE9421 /* SPMessageNotification.m in Sources */,
-				ED88B5E5257950210048FAD1 /* SPGDPRConfiguration.m in Sources */,
-				ED7CE16B26DE43A00035C323 /* SPScreenStateMachine.m in Sources */,
-				EDDD7035264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */,
-				ED7F0846261924BF005D377E /* SPConfigurationFetcher.m in Sources */,
-				ED88670425715DD600DB53BB /* SPConfiguration.m in Sources */,
-				ED8867312573C1F200DB53BB /* SPSessionConfiguration.m in Sources */,
-				ED38D93126EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */,
-				ED88B5AD25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */,
-				CE4F9CC4244B066500968CFC /* SPConsentGranted.m in Sources */,
-				CE4F9CF8244B066500968CFC /* SPEventBase.m in Sources */,
-				EDAB665C26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */,
-				ED98972C26287F7A00145157 /* SPConfigurationCache.m in Sources */,
-				EDEE835124BDB326000B8530 /* SPTrackerError.m in Sources */,
-				CE4F9C94244B066500968CFC /* SPForeground.m in Sources */,
-				CE4F9D00244B066500968CFC /* SPBackground.m in Sources */,
-				ED8122B025E9578600AE7FE8 /* SPSnowplow.m in Sources */,
-				EDEE836024BE0944000B8530 /* SPLogger.m in Sources */,
-				CE4F9CF0244B066500968CFC /* SPSchemaRule.m in Sources */,
-				ED91CB7323AA8AD50078E75F /* SPDevicePlatform.m in Sources */,
-				ED88B66E257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */,
-				ED7CE17526DFB55C0035C323 /* SPTrackerState.m in Sources */,
-				ED277BE82625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */,
-				ED7F081A26190E00005D377E /* SPConfigurationProvider.m in Sources */,
-				CE4F9CEC244B066500968CFC /* SPPushNotification.m in Sources */,
-				EDAB666626D6ACCB0067755F /* SPDeepLinkState.m in Sources */,
-				75CAC44B21F2A19500271FB3 /* SPSubject.m in Sources */,
-				EDDD7049264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */,
-				CE4F9CC8244B066500968CFC /* SPSchemaRuleset.m in Sources */,
-				CE4F9C8C244B066500968CFC /* SPConsentDocument.m in Sources */,
-				EDB2FD1F26C130B80031B872 /* SPDataPersistence.m in Sources */,
-				EDD8541C24EEC25100661F6B /* SPEmitterEvent.m in Sources */,
-				EDDD7017264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */,
-				ED38D93926EBCEBE002AEC8E /* SPLifecycleState.m in Sources */,
-				ED34673026415C1D0018BA61 /* SPJSONSerialization.m in Sources */,
-				75CAC44C21F2A19500271FB3 /* SPSession.m in Sources */,
-				CE4F9CD0244B066500968CFC /* SNOWError.m in Sources */,
-				CE4F9C88244B066500968CFC /* SPTiming.m in Sources */,
-				6B07CDB3287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */,
-				ED49DF422757E4F500610843 /* SPSessionState.m in Sources */,
-				CE4F9D20244B066500968CFC /* SPEcommerce.m in Sources */,
-				EDDD703F264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */,
-				75CAC44D21F2A19500271FB3 /* SPPayload.m in Sources */,
-				CE4F9D0C244B066500968CFC /* SPScreenView.m in Sources */,
-				75CAC44E21F2A19500271FB3 /* SPSelfDescribingJson.m in Sources */,
-				ED7F08302619199D005D377E /* SPRemoteConfiguration.m in Sources */,
-				ED87A3F125766BB4000C54EB /* SPSessionControllerImpl.m in Sources */,
-				ED88B6E12583DFC90048FAD1 /* SPServiceProvider.m in Sources */,
-				CE4F9CDC244B066500968CFC /* SPPageView.m in Sources */,
-				ED88B56F2578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */,
-				EDAB663626D699D90067755F /* SPStateFuture.m in Sources */,
-				EDDD702B264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */,
-				75CAC44F21F2A19500271FB3 /* SPSQLiteEventStore.m in Sources */,
-				EDDD7003264E873B00259404 /* SPController.m in Sources */,
-				75CAC45021F2A19500271FB3 /* SPUtilities.m in Sources */,
-				75CAC45121F2A19500271FB3 /* SPRequestResult.m in Sources */,
-				ED98971C2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */,
-				6BBDCD4827019AF4001B547F /* SPPlatformContext.m in Sources */,
-				EDB6940026B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */,
-				CE4F9CD4244B066500968CFC /* SPTrackerEvent.m in Sources */,
-				EDF2A1C0264032F9009032AB /* SPSubjectControllerImpl.m in Sources */,
-				ED38D92526EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */,
-				ED852B3023A0E90E00F2DF6B /* SNOWReachability.m in Sources */,
-				CE4F9C98244B066500968CFC /* SPEcommerceItem.m in Sources */,
-				ED88B62F257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */,
-				6BF08DAC270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */,
-				75CAC45221F2A19500271FB3 /* SPWeakTimerTarget.m in Sources */,
+				6BE70875292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
+				6B9DA64A292B5487006D721A /* DevicePlatform.swift in Sources */,
+				6B9DA6E6292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
+				6B85874C2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
+				6B0CCFD029236B0B0054954B /* GDPRContext.swift in Sources */,
+				6B0CCFCB292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
+				6B9DA680292BD2D7006D721A /* GlobalContext.swift in Sources */,
+				6B9DA67C292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
+				6B9DA5862926437F006D721A /* PushNotification.swift in Sources */,
+				6BABED46291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
+				6B85872F2923C5F0006E4A5F /* TrackerController.swift in Sources */,
+				6B85877429251779006E4A5F /* ConfigurationState.swift in Sources */,
+				6B9DA5B829269755006D721A /* TrackerEvent.swift in Sources */,
+				6BABED3C291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
+				6B9DA5EE2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
+				6B9DA5E52927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
+				6B9DA640292B5197006D721A /* ScreenStateMachine.swift in Sources */,
+				6B85875B2924CDB0006E4A5F /* Snowplow.swift in Sources */,
+				6B9DA59A292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
+				6B85878429251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
+				6B9DA68F292BF03C006D721A /* TrackerConstants.swift in Sources */,
+				6B9DA66C292BBC83006D721A /* SPSize.swift in Sources */,
+				6B9DA59F29267B66006D721A /* Ecommerce.swift in Sources */,
+				6B8587562923E7D0006E4A5F /* NetworkController.swift in Sources */,
+				6B8F5607291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
+				6B9DA671292BBCDE006D721A /* SNOWReachability.swift in Sources */,
+				6B9DA5F22927E635006D721A /* InstallTracker.swift in Sources */,
+				6B9DA57929261E5C006D721A /* Timing.swift in Sources */,
+				6B9DA694292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
+				6B9DA5CE2927BCD6006D721A /* RequestResult.swift in Sources */,
+				6B9DA5FE2927E635006D721A /* LifecycleState.swift in Sources */,
+				6B9DA658292B60E2006D721A /* Subject.swift in Sources */,
+				6B8587382923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
+				6B9DA6152927E99C006D721A /* StateFuture.swift in Sources */,
+				6BABE8BF291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
+				6B9DA55929261E5B006D721A /* ConsentGranted.swift in Sources */,
+				6B9DA644292B5197006D721A /* ScreenState.swift in Sources */,
+				6B85877A29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
+				6B9DA5D62927BCD6006D721A /* Emitter.swift in Sources */,
+				6B9DA5A929268B0E006D721A /* TrackerError.swift in Sources */,
+				6B0CCFD629236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
+				6BE70851292F656500911E55 /* BufferOption.swift in Sources */,
+				6BABED23291E3DB200F6798A /* Configuration.swift in Sources */,
+				6B8587482923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
+				6B9DA68A292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
+				6B9DA65D292B6E68006D721A /* Utilities.swift in Sources */,
+				6BABED28291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
+				6B9DA54529253952006D721A /* DeepLinkEntity.swift in Sources */,
+				6B9DA5FA2927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
+				6BABEB73291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
+				6BABED41291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
+				6B9DA686292BDD3A006D721A /* Logger.swift in Sources */,
+				6B9DA667292BAE5F006D721A /* ServiceProvider.swift in Sources */,
+				6B9DA57129261E5C006D721A /* SelfDescribing.swift in Sources */,
+				6B8587422923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
+				6B85877E29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
+				6B0CCFC0292275580054954B /* Controller.swift in Sources */,
+				6B9DA6082927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
+				6BE7086A292F67C000911E55 /* InspectableEvent.swift in Sources */,
+				6B9DA58129261E5C006D721A /* ScreenView.swift in Sources */,
+				6B9DA6112927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
+				6B0CCFDA29236D4A0054954B /* SubjectController.swift in Sources */,
+				6B8586F8292388C9006E4A5F /* SessionController.swift in Sources */,
+				6B9DA5C62927BCD6006D721A /* Request.swift in Sources */,
+				6BE70863292F66E800911E55 /* ProtocolOptions.swift in Sources */,
+				6B9DA54129253952006D721A /* LifecycleEntity.swift in Sources */,
+				6BABED37291E596100F6798A /* EmitterConfiguration.swift in Sources */,
+				6B8586FC292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
+				6B8587522923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
+				6B9DA57529261E5C006D721A /* Background.swift in Sources */,
+				6B9DA56D29261E5C006D721A /* Foreground.swift in Sources */,
+				6B85878829251DF4006E4A5F /* Payload.swift in Sources */,
+				6B9DA6352928CAA3006D721A /* Tracker.swift in Sources */,
+				6BE70858292F657F00911E55 /* ContextGenerator.swift in Sources */,
+				6BABED55291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
+				6B85870329239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
+				6B9DA62B2928B2FC006D721A /* Session.swift in Sources */,
+				6B9DA653292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
+				6B9DA6032927E669006D721A /* State.swift in Sources */,
+				6B9DA6302928C08D006D721A /* DataPersistence.swift in Sources */,
+				6B9DA6212927E99C006D721A /* TrackerState.swift in Sources */,
+				6B9DA64E292B5487006D721A /* PlatformContext.swift in Sources */,
+				6BE7085E292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
+				6B9DA6272928B2FC006D721A /* SessionState.swift in Sources */,
+				6BABED50291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
+				6B9DA5F62927E635006D721A /* DeepLinkState.swift in Sources */,
+				6B9DA57D29261E5C006D721A /* Structured.swift in Sources */,
+				6B9DA5A429268956006D721A /* EcommerceItem.swift in Sources */,
+				6B8587602924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
+				6BABED4B291E884900F6798A /* ConfigurationBundle.swift in Sources */,
+				6B85876B292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
+				6B9DA6E1292F4A0D006D721A /* EmitterDefaults.swift in Sources */,
+				6B85870B29239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
+				6BE70852292F656500911E55 /* EventStore.swift in Sources */,
+				6B9DA663292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
+				6B85873E2923E025006E4A5F /* EmitterController.swift in Sources */,
+				6B8587332923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
+				6BABEB7E291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
+				6B9DA55D29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
+				6BE7086F292F683600911E55 /* LogLevel.swift in Sources */,
+				6B9DA678292BD2D7006D721A /* SchemaRule.swift in Sources */,
+				6B9DA61D2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
+				6B9DA55529261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
+				6BE70853292F656500911E55 /* EmitterEvent.swift in Sources */,
+				6B9DA5B329269036006D721A /* SNOWError.swift in Sources */,
+				6B9DA6192927E99C006D721A /* StateManager.swift in Sources */,
+				6B9DA5CA2927BCD6006D721A /* RequestCallback.swift in Sources */,
+				6B9DA58B29264EC2006D721A /* MessageNotification.swift in Sources */,
+				6B9DA5AE29268E4E006D721A /* PageView.swift in Sources */,
+				6B9DA56529261E5B006D721A /* EventBase.swift in Sources */,
+				6B0CCFBB292262650054954B /* GDPRController.swift in Sources */,
+				6B0CCFB7292262650054954B /* GDPRControllerImpl.swift in Sources */,
+				6B9DA56129261E5B006D721A /* ConsentDocument.swift in Sources */,
+				6B8587652924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
+				6B85876F292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
+				6BABEB6E291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2919,7 +1972,6 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				752DAC0321CC3B380065F874 /* Snowplow_macOSTests.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2927,98 +1979,122 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				ED88B5AE25792C620048FAD1 /* SPEmitterControllerImpl.m in Sources */,
-				CE4F9CC9244B066500968CFC /* SPSchemaRuleset.m in Sources */,
-				EDAB663F26D699D90067755F /* SPStateManager.m in Sources */,
-				EDAB65D526CBD5150067755F /* SPDeepLinkEntity.m in Sources */,
-				7534D20122569BED00904EE5 /* SPScreenState.m in Sources */,
-				ED0EFE3026E240B0002CAA21 /* SPDeepLinkReceived.m in Sources */,
-				CE4F9CDD244B066500968CFC /* SPPageView.m in Sources */,
-				ED34673126415C1D0018BA61 /* SPJSONSerialization.m in Sources */,
-				ED88B7982587B5620048FAD1 /* SPNetworkControllerImpl.m in Sources */,
-				EDF2A1C1264032F9009032AB /* SPSubjectControllerImpl.m in Sources */,
-				EDDD7022264F230400259404 /* SPSubjectConfigurationUpdate.m in Sources */,
-				ED88670525715DD600DB53BB /* SPConfiguration.m in Sources */,
-				ED88B6E22583DFC90048FAD1 /* SPServiceProvider.m in Sources */,
-				CE4F9C99244B066500968CFC /* SPEcommerceItem.m in Sources */,
-				ED88B614257956490048FAD1 /* SPGDPRControllerImpl.m in Sources */,
-				7534D20222569BED00904EE5 /* UIViewController+SPScreenView_SWIZZLE.m in Sources */,
-				7534D20322569BED00904EE5 /* SPInstallTracker.m in Sources */,
-				ED6B0330271094D700EFA12B /* SPMessageNotificationAttachment.m in Sources */,
-				ED277BD92625F220002C7B6D /* SPConfigurationBundle.m in Sources */,
-				CE4F9C95244B066500968CFC /* SPForeground.m in Sources */,
-				ED91CB7423AA8AD50078E75F /* SPDevicePlatform.m in Sources */,
-				CE4F9CBD244B066500968CFC /* SPSelfDescribing.m in Sources */,
-				6BF08DB7270DF2E8009C7E2B /* SPMockDeviceInfoMonitor.m in Sources */,
-				ED88B630257A3FE60048FAD1 /* SPGlobalContextsConfiguration.m in Sources */,
-				CE4F9D19244B066500968CFC /* SPGlobalContext.m in Sources */,
-				ED9081B42703747C00EE9421 /* SPMessageNotification.m in Sources */,
-				ED87A3F225766BB4000C54EB /* SPSessionControllerImpl.m in Sources */,
-				EDAB665D26D6AA940067755F /* SPDeepLinkStateMachine.m in Sources */,
-				EDAB666726D6ACCB0067755F /* SPDeepLinkState.m in Sources */,
-				EDAB665126D69D740067755F /* SPScreenStateMachine.m in Sources */,
-				EDDD7036264F25A200259404 /* SPSessionConfigurationUpdate.m in Sources */,
-				ED8BF8BA25700B40001DFDD9 /* SPTrackerConfiguration.m in Sources */,
-				ED38D93226EBCEBE002AEC8E /* SPLifecycleStateMachine.m in Sources */,
-				EDEE836124BE0944000B8530 /* SPLogger.m in Sources */,
-				ED7F0847261924BF005D377E /* SPConfigurationFetcher.m in Sources */,
-				ED88B5702578F8820048FAD1 /* SPEmitterConfiguration.m in Sources */,
-				ED88B5E6257950210048FAD1 /* SPGDPRConfiguration.m in Sources */,
-				CE4F9CE1244B066500968CFC /* SPStructured.m in Sources */,
-				ED914EC124325AB40068DA0A /* SPGdprContext.m in Sources */,
-				EDD8543B24EFFFB300661F6B /* SPRequest.m in Sources */,
-				ED98972D26287F7A00145157 /* SPConfigurationCache.m in Sources */,
-				CE4F9CC5244B066500968CFC /* SPConsentGranted.m in Sources */,
-				75F9C5D621FA357100A5B8FC /* SPTrackerConstants.m in Sources */,
-				75F9C5D721FA357100A5B8FC /* SPTracker.m in Sources */,
-				ED88B66F257A5A520048FAD1 /* SPGlobalContextsControllerImpl.m in Sources */,
-				ED8122B125E9578600AE7FE8 /* SPSnowplow.m in Sources */,
-				ED8866E92571445300DB53BB /* SPSubjectConfiguration.m in Sources */,
-				ED7CE17626DFB55C0035C323 /* SPTrackerState.m in Sources */,
-				CE4F9C89244B066500968CFC /* SPTiming.m in Sources */,
-				EDD8541D24EEC25100661F6B /* SPEmitterEvent.m in Sources */,
-				ED277BE92625F5C5002C7B6D /* SPFetchedConfigurationBundle.m in Sources */,
-				CE4F9CF9244B066500968CFC /* SPEventBase.m in Sources */,
-				EDDD704A264F2A8800259404 /* SPEmitterConfigurationUpdate.m in Sources */,
-				CE4F9D0D244B066500968CFC /* SPScreenView.m in Sources */,
-				75F9C5D821FA357100A5B8FC /* SPEmitter.m in Sources */,
-				EDB2FD2026C130B80031B872 /* SPDataPersistence.m in Sources */,
-				75F9C5D921FA357100A5B8FC /* SPSubject.m in Sources */,
-				EDDD7018264F1D2100259404 /* SPTrackerConfigurationUpdate.m in Sources */,
-				ED38D93A26EBCEBE002AEC8E /* SPLifecycleState.m in Sources */,
-				CE4F9D21244B066500968CFC /* SPEcommerce.m in Sources */,
-				75F9C5DA21FA357100A5B8FC /* SPSession.m in Sources */,
-				CE4F9CD1244B066500968CFC /* SNOWError.m in Sources */,
-				75F9C5DB21FA357100A5B8FC /* SPPayload.m in Sources */,
-				6B07CDB4287721C600E510D6 /* SPWebViewMessageHandler.m in Sources */,
-				ED49DF432757E4F500610843 /* SPSessionState.m in Sources */,
-				CE4F9CF1244B066500968CFC /* SPSchemaRule.m in Sources */,
-				EDDD7040264F27E700259404 /* SPNetworkConfigurationUpdate.m in Sources */,
-				CE4F9C91244B066500968CFC /* SPConsentWithdrawn.m in Sources */,
-				CE4F9CD5244B066500968CFC /* SPTrackerEvent.m in Sources */,
-				75F9C5DC21FA357100A5B8FC /* SPSelfDescribingJson.m in Sources */,
-				75F9C5DD21FA357100A5B8FC /* SPSQLiteEventStore.m in Sources */,
-				ED7F08312619199D005D377E /* SPRemoteConfiguration.m in Sources */,
-				EDD8542724EFEFB900661F6B /* SPDefaultNetworkConnection.m in Sources */,
-				CE4F9CED244B066500968CFC /* SPPushNotification.m in Sources */,
-				ED852B3123A0E90E00F2DF6B /* SNOWReachability.m in Sources */,
-				EDAB663726D699D90067755F /* SPStateFuture.m in Sources */,
-				EDDD702C264F23C600259404 /* SPGDPRConfigurationUpdate.m in Sources */,
-				75F9C5DE21FA357100A5B8FC /* SPUtilities.m in Sources */,
-				EDDD7004264E873B00259404 /* SPController.m in Sources */,
-				75F9C5DF21FA357100A5B8FC /* SPRequestResult.m in Sources */,
-				ED87A4352577ADFF000C54EB /* SPTrackerControllerImpl.m in Sources */,
-				CE4F9C8D244B066500968CFC /* SPConsentDocument.m in Sources */,
-				6BBDCD4927019AF4001B547F /* SPPlatformContext.m in Sources */,
-				EDB6940126B7F61D00B76A79 /* SPMemoryEventStore.m in Sources */,
-				ED98971D2627006F00145157 /* NSDictionary+SP_TypeMethods.m in Sources */,
-				CE4F9D01244B066500968CFC /* SPBackground.m in Sources */,
-				ED38D92626EBCD59002AEC8E /* SPLifecycleEntity.m in Sources */,
-				ED8867322573C1F200DB53BB /* SPSessionConfiguration.m in Sources */,
-				EDEE835224BDB326000B8530 /* SPTrackerError.m in Sources */,
-				ED8BF8D225701853001DFDD9 /* SPNetworkConfiguration.m in Sources */,
-				6BF08DAD270DEED6009C7E2B /* SPDeviceInfoMonitor.m in Sources */,
-				75F9C5E021FA357100A5B8FC /* SPWeakTimerTarget.m in Sources */,
+				6BE70876292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
+				6B9DA668292BAE5F006D721A /* ServiceProvider.swift in Sources */,
+				6B9DA6E7292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
+				6B9DA645292B5197006D721A /* ScreenState.swift in Sources */,
+				6B85874D2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
+				6B9DA6362928CAA3006D721A /* Tracker.swift in Sources */,
+				6B0CCFD129236B0B0054954B /* GDPRContext.swift in Sources */,
+				6B9DA55629261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
+				6B0CCFCC292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
+				6B9DA5D72927BCD6006D721A /* Emitter.swift in Sources */,
+				6B9DA5CB2927BCD6006D721A /* RequestCallback.swift in Sources */,
+				6B8587302923C5F0006E4A5F /* TrackerController.swift in Sources */,
+				6B9DA55E29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
+				6BABED47291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
+				6B9DA5F32927E635006D721A /* InstallTracker.swift in Sources */,
+				6BABED3D291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
+				6B9DA54229253952006D721A /* LifecycleEntity.swift in Sources */,
+				6B85877B29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
+				6B9DA659292B60E2006D721A /* Subject.swift in Sources */,
+				6B9DA58229261E5C006D721A /* ScreenView.swift in Sources */,
+				6B9DA65E292B6E68006D721A /* Utilities.swift in Sources */,
+				6B9DA54629253952006D721A /* DeepLinkEntity.swift in Sources */,
+				6B85875C2924CDB0006E4A5F /* Snowplow.swift in Sources */,
+				6B8587572923E7D0006E4A5F /* NetworkController.swift in Sources */,
+				6B9DA57229261E5C006D721A /* SelfDescribing.swift in Sources */,
+				6B9DA56229261E5B006D721A /* ConsentDocument.swift in Sources */,
+				6B85877F29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
+				6B8F5608291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
+				6B9DA641292B5197006D721A /* ScreenStateMachine.swift in Sources */,
+				6B9DA695292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
+				6B9DA5B429269036006D721A /* SNOWError.swift in Sources */,
+				6B9DA56E29261E5C006D721A /* Foreground.swift in Sources */,
+				6B9DA57A29261E5C006D721A /* Timing.swift in Sources */,
+				6B9DA59B292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
+				6B8587392923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
+				6B9DA5B929269755006D721A /* TrackerEvent.swift in Sources */,
+				6B9DA5EF2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
+				6BABE8C0291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
+				6B9DA64F292B5487006D721A /* PlatformContext.swift in Sources */,
+				6B9DA67D292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
+				6B9DA64B292B5487006D721A /* DevicePlatform.swift in Sources */,
+				6BE70854292F656700911E55 /* BufferOption.swift in Sources */,
+				6B9DA687292BDD3A006D721A /* Logger.swift in Sources */,
+				6B858770292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
+				6B0CCFD729236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
+				6B9DA66D292BBC83006D721A /* SPSize.swift in Sources */,
+				6B9DA6162927E99C006D721A /* StateFuture.swift in Sources */,
+				6B9DA5A029267B66006D721A /* Ecommerce.swift in Sources */,
+				6BABED24291E3DB200F6798A /* Configuration.swift in Sources */,
+				6BABED29291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
+				6B9DA6122927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
+				6B8587492923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
+				6B9DA62C2928B2FC006D721A /* Session.swift in Sources */,
+				6B9DA6282928B2FC006D721A /* SessionState.swift in Sources */,
+				6B9DA5A529268956006D721A /* EcommerceItem.swift in Sources */,
+				6B85876C292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
+				6B9DA664292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
+				6BABEB74291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
+				6BE7086B292F67C000911E55 /* InspectableEvent.swift in Sources */,
+				6B9DA61E2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
+				6B9DA681292BD2D7006D721A /* GlobalContext.swift in Sources */,
+				6BABED42291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
+				6B9DA68B292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
+				6B9DA57E29261E5C006D721A /* Structured.swift in Sources */,
+				6BE70864292F66E800911E55 /* ProtocolOptions.swift in Sources */,
+				6B8587432923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
+				6B9DA61A2927E99C006D721A /* StateManager.swift in Sources */,
+				6B0CCFC1292275580054954B /* Controller.swift in Sources */,
+				6B9DA6222927E99C006D721A /* TrackerState.swift in Sources */,
+				6B9DA5872926437F006D721A /* PushNotification.swift in Sources */,
+				6B9DA5AF29268E4E006D721A /* PageView.swift in Sources */,
+				6B9DA679292BD2D7006D721A /* SchemaRule.swift in Sources */,
+				6B9DA57629261E5C006D721A /* Background.swift in Sources */,
+				6BE70859292F657F00911E55 /* ContextGenerator.swift in Sources */,
+				6B85877529251779006E4A5F /* ConfigurationState.swift in Sources */,
+				6B9DA5E62927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
+				6B0CCFDB29236D4A0054954B /* SubjectController.swift in Sources */,
+				6B9DA58C29264EC2006D721A /* MessageNotification.swift in Sources */,
+				6B8586F9292388C9006E4A5F /* SessionController.swift in Sources */,
+				6B9DA6312928C08D006D721A /* DataPersistence.swift in Sources */,
+				6BABED38291E596100F6798A /* EmitterConfiguration.swift in Sources */,
+				6B8586FD292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
+				6BE7085F292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
+				6B9DA5C72927BCD6006D721A /* Request.swift in Sources */,
+				6B9DA55A29261E5B006D721A /* ConsentGranted.swift in Sources */,
+				6B8587532923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
+				6BABED56291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
+				6B85870429239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
+				6B9DA5AA29268B0E006D721A /* TrackerError.swift in Sources */,
+				6BABED51291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
+				6B9DA690292BF03C006D721A /* TrackerConstants.swift in Sources */,
+				6B9DA6E2292F4A0E006D721A /* EmitterDefaults.swift in Sources */,
+				6B9DA5FF2927E635006D721A /* LifecycleState.swift in Sources */,
+				6BE70855292F656700911E55 /* EventStore.swift in Sources */,
+				6B8587612924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
+				6BABED4C291E884900F6798A /* ConfigurationBundle.swift in Sources */,
+				6B9DA56629261E5B006D721A /* EventBase.swift in Sources */,
+				6B85878929251DF4006E4A5F /* Payload.swift in Sources */,
+				6B85870C29239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
+				6BE70870292F683600911E55 /* LogLevel.swift in Sources */,
+				6B9DA6092927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
+				6B85873F2923E025006E4A5F /* EmitterController.swift in Sources */,
+				6B8587342923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
+				6BE70856292F656700911E55 /* EmitterEvent.swift in Sources */,
+				6B9DA672292BBCDE006D721A /* SNOWReachability.swift in Sources */,
+				6B9DA5F72927E635006D721A /* DeepLinkState.swift in Sources */,
+				6B85878529251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
+				6B9DA6042927E669006D721A /* State.swift in Sources */,
+				6B9DA5FB2927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
+				6BABEB7F291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
+				6B9DA5CF2927BCD6006D721A /* RequestResult.swift in Sources */,
+				6B0CCFBC292262650054954B /* GDPRController.swift in Sources */,
+				6B0CCFB8292262650054954B /* GDPRControllerImpl.swift in Sources */,
+				6B9DA654292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
+				6B8587662924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
+				6BABEB6F291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -3055,6 +2131,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -3065,7 +2142,6 @@
 				CURRENT_PROJECT_VERSION = 1;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -3081,22 +2157,20 @@
 				);
 				INFOPLIST_FILE = "Snowplow iOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MODULEMAP_FILE = Snowplow/ios.modulemap;
+				MODULEMAP_FILE = Sources/Snowplow/ios.modulemap;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-iOS";
-				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
 				PRODUCT_NAME = SnowplowTracker;
-				PROVISIONING_PROFILE_SPECIFIER = "";
 				SKIP_INSTALL = YES;
-				SWIFT_VERSION = 4.2;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
@@ -3109,6 +2183,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -3120,7 +2195,6 @@
 				CURRENT_PROJECT_VERSION = 1;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -3135,22 +2209,19 @@
 				);
 				INFOPLIST_FILE = "Snowplow iOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MODULEMAP_FILE = Snowplow/ios.modulemap;
+				MODULEMAP_FILE = Sources/Snowplow/ios.modulemap;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-iOS";
-				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
 				PRODUCT_NAME = SnowplowTracker;
-				PROVISIONING_PROFILE_SPECIFIER = "";
 				SKIP_INSTALL = YES;
-				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
@@ -3160,6 +2231,7 @@
 		752DABE121CC38560065F874 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@@ -3186,8 +2258,8 @@
 					"$(inherited)",
 				);
 				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Snowplow\"";
-				INFOPLIST_FILE = "Snowplow iOSTests/Info.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 10.2;
+				INFOPLIST_FILE = Tests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					/usr/lib/swift,
 					"$(inherited)",
@@ -3206,9 +2278,8 @@
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_OBJC_BRIDGING_HEADER = "Snowplow iOSTests/Global Contexts/Snowplow-iOSTests-Bridging-Header.h";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Tests/Tests-bridging-header.h";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				USER_HEADER_SEARCH_PATHS = "";
 			};
@@ -3217,6 +2288,7 @@
 		752DABE221CC38560065F874 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@@ -3244,8 +2316,8 @@
 					"$(inherited)",
 				);
 				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Snowplow\"";
-				INFOPLIST_FILE = "Snowplow iOSTests/Info.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 10.2;
+				INFOPLIST_FILE = Tests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					/usr/lib/swift,
 					"$(inherited)",
@@ -3264,8 +2336,7 @@
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_OBJC_BRIDGING_HEADER = "Snowplow iOSTests/Global Contexts/Snowplow-iOSTests-Bridging-Header.h";
-				SWIFT_VERSION = 5.0;
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Tests/Tests-bridging-header.h";
 				TARGETED_DEVICE_FAMILY = "1,2";
 				USER_HEADER_SEARCH_PATHS = "";
 			};
@@ -3278,6 +2349,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -3309,7 +2381,7 @@
 					"@executable_path/Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MODULEMAP_FILE = Snowplow/watchos.modulemap;
+				MODULEMAP_FILE = Sources/Snowplow/watchos.modulemap;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3321,7 +2393,7 @@
 				TARGETED_DEVICE_FAMILY = 4;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 2.0;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Debug;
 		};
@@ -3332,6 +2404,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -3360,7 +2433,7 @@
 					"@executable_path/Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MODULEMAP_FILE = Snowplow/watchos.modulemap;
+				MODULEMAP_FILE = Sources/Snowplow/watchos.modulemap;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3372,7 +2445,7 @@
 				TARGETED_DEVICE_FAMILY = 4;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 2.0;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Release;
 		};
@@ -3382,6 +2455,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -3391,6 +2465,7 @@
 				CODE_SIGN_STYLE = Manual;
 				COMBINE_HIDPI_IMAGES = YES;
 				CURRENT_PROJECT_VERSION = 1;
+				DEAD_CODE_STRIPPING = YES;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEFINES_MODULE = YES;
 				DEVELOPMENT_TEAM = "";
@@ -3415,7 +2490,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.9;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3424,6 +2499,7 @@
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
 			};
@@ -3435,6 +2511,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -3445,6 +2522,7 @@
 				COMBINE_HIDPI_IMAGES = YES;
 				COPY_PHASE_STRIP = NO;
 				CURRENT_PROJECT_VERSION = 1;
+				DEAD_CODE_STRIPPING = YES;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEFINES_MODULE = YES;
 				DEVELOPMENT_TEAM = "";
@@ -3468,7 +2546,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.9;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3497,6 +2575,7 @@
 				CODE_SIGN_IDENTITY = "-";
 				CODE_SIGN_STYLE = Manual;
 				COMBINE_HIDPI_IMAGES = YES;
+				DEAD_CODE_STRIPPING = YES;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = "";
 				GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -3506,7 +2585,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/../Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.9;
+				MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3533,6 +2612,7 @@
 				CODE_SIGN_STYLE = Manual;
 				COMBINE_HIDPI_IMAGES = YES;
 				COPY_PHASE_STRIP = NO;
+				DEAD_CODE_STRIPPING = YES;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = "";
 				GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -3542,7 +2622,7 @@
 					"@executable_path/../Frameworks",
 					"@loader_path/../Frameworks",
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.9;
+				MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3559,6 +2639,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
@@ -3574,7 +2655,12 @@
 					"SNOWPLOW_IOS_STATIC=1",
 					"$(inherited)",
 				);
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3583,7 +2669,7 @@
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
-				SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME)-Swift.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -3595,6 +2681,7 @@
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_WEAK = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
@@ -3611,7 +2698,12 @@
 					"SNOWPLOW_IOS_STATIC=1",
 					"$(inherited)",
 				);
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				OTHER_CFLAGS = "";
@@ -3619,7 +2711,6 @@
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
-				SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME)-Swift.h";
 				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -3628,6 +2719,7 @@
 		AB0C27DE191B408200018557 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				APPLICATION_EXTENSION_API_ONLY = NO;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -3656,6 +2748,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
+				DEFINES_MODULE = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -3675,11 +2768,11 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Snowplow\"";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Sources\"";
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
-				SWIFT_VERSION = 4.2;
+				SWIFT_VERSION = 5.0;
 				VALID_ARCHS = "$(ARCHS_STANDARD)";
 			};
 			name = Debug;
@@ -3687,6 +2780,7 @@
 		AB0C27DF191B408200018557 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				APPLICATION_EXTENSION_API_ONLY = NO;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -3715,6 +2809,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = YES;
+				DEFINES_MODULE = YES;
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -3728,11 +2823,11 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Snowplow\"";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Sources\"";
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				SDKROOT = iphoneos;
 				SWIFT_COMPILATION_MODE = wholemodule;
-				SWIFT_VERSION = 4.2;
+				SWIFT_VERSION = 5.0;
 				VALIDATE_PRODUCT = YES;
 				VALID_ARCHS = "$(ARCHS_STANDARD)";
 			};
@@ -3750,7 +2845,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		752DABE021CC38560065F874 /* Build configuration list for PBXNativeTarget "Snowplow-iOSTests" */ = {
+		752DABE021CC38560065F874 /* Build configuration list for PBXNativeTarget "Tests" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				752DABE121CC38560065F874 /* Debug */,
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme
index 123820498..dc20d4299 100644
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme
+++ b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1230"
+   LastUpgradeVersion = "1410"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme
index 353614c7e..5e78a95d4 100644
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme
+++ b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1230"
+   LastUpgradeVersion = "1410"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -43,8 +43,8 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "752DABD321CC38560065F874"
-               BuildableName = "Snowplow-iOSTests.xctest"
-               BlueprintName = "Snowplow-iOSTests"
+               BuildableName = "Tests.xctest"
+               BlueprintName = "Tests"
                ReferencedContainer = "container:Snowplow.xcodeproj">
             </BuildableReference>
          </TestableReference>
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme
index ab3d232fb..569f454a8 100644
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme
+++ b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1230"
+   LastUpgradeVersion = "1410"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme
index 6e5b31e1d..76ebf923d 100644
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme
+++ b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1230"
+   LastUpgradeVersion = "1410"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Snowplow/Internal/Configurations/SPConfiguration.h b/Snowplow/Internal/Configurations/SPConfiguration.h
deleted file mode 100644
index 00f628682..000000000
--- a/Snowplow/Internal/Configurations/SPConfiguration.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPTrackerConstants.h"
-
-#ifndef SP_STR_PROP
-    #define SP_STR_PROP(prop) NSStringFromSelector(@selector(prop))
-#endif
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(Configuration)
-@interface SPConfiguration : NSObject <NSCopying, NSSecureCoding>
-
-- (instancetype)initWithDictionary:(NSDictionary<NSString *, NSObject *> *)dictionary;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPConfigurationBundle.h b/Snowplow/Internal/Configurations/SPConfigurationBundle.h
deleted file mode 100644
index 9b50dd4bf..000000000
--- a/Snowplow/Internal/Configurations/SPConfigurationBundle.h
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  SPConfigurationBundle.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConfiguration.h"
-#import "SPNetworkConfiguration.h"
-#import "SPTrackerConfiguration.h"
-#import "SPSubjectConfiguration.h"
-#import "SPSessionConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * This class represents the default configuration applied in place of the remote configuration.
- */
-NS_SWIFT_NAME(ConfigurationBundle)
-@interface SPConfigurationBundle : SPConfiguration
-
-@property (nonatomic, nonnull, readonly) NSString *namespace;
-@property (nonatomic, nullable) SPNetworkConfiguration *networkConfiguration;
-@property (nonatomic, nullable) SPTrackerConfiguration *trackerConfiguration;
-@property (nonatomic, nullable) SPSubjectConfiguration *subjectConfiguration;
-@property (nonatomic, nullable) SPSessionConfiguration *sessionConfiguration;
-
-@property (nonatomic, nonnull, readonly) NSArray<SPConfiguration *> *configurations;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-- (instancetype)initWithNamespace:(NSString *)namespace;
-- (instancetype)initWithNamespace:(NSString *)namespace networkConfiguration:(nullable SPNetworkConfiguration *)networkConfiguration;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPConfigurationBundle.m b/Snowplow/Internal/Configurations/SPConfigurationBundle.m
deleted file mode 100644
index 9e6475b0e..000000000
--- a/Snowplow/Internal/Configurations/SPConfigurationBundle.m
+++ /dev/null
@@ -1,114 +0,0 @@
-//
-//  SPConfigurationBundle.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConfigurationBundle.h"
-#import "NSDictionary+SP_TypeMethods.h"
-#import "SPLogger.h"
-
-@interface SPConfigurationBundle ()
-@property (nonatomic, nonnull) NSString *namespace;
-@end
-
-@implementation SPConfigurationBundle
-
-- (instancetype)initWithNamespace:(NSString *)namespace {
-    return [self initWithNamespace:namespace networkConfiguration:nil];
-}
-
-- (instancetype)initWithNamespace:(NSString *)namespace networkConfiguration:(nullable SPNetworkConfiguration *)networkConfiguration {
-    if (self = [super init]) {
-        self.namespace = namespace;
-        self.networkConfiguration = networkConfiguration;
-    }
-    return self;
-}
-
-- (NSArray<SPConfiguration *> *)configurations {
-    NSMutableArray *array = [NSMutableArray new];
-    if (self.networkConfiguration) {
-        [array addObject:self.networkConfiguration];
-    }
-    if (self.trackerConfiguration) {
-        [array addObject:self.trackerConfiguration];
-    }
-    if (self.subjectConfiguration) {
-        [array addObject:self.subjectConfiguration];
-    }
-    if (self.sessionConfiguration) {
-        [array addObject:self.sessionConfiguration];
-    }
-    return array;
-}
-
-- (instancetype)initWithDictionary:(NSDictionary<NSString *,NSObject *> *)dictionary {
-    if (self = [super init]) {
-        self.namespace = [dictionary sp_stringForKey:SP_STR_PROP(namespace) defaultValue:nil];
-        if (!self.namespace) {
-            SPLogDebug(@"Error assigning: namespace");
-            return nil;
-        }
-        self.networkConfiguration = (SPNetworkConfiguration *)[dictionary sp_configurationForKey:SP_STR_PROP(networkConfiguration) configurationClass:SPNetworkConfiguration.class defaultValue:nil];
-        self.trackerConfiguration = (SPTrackerConfiguration *)[dictionary sp_configurationForKey:SP_STR_PROP(trackerConfiguration) configurationClass:SPTrackerConfiguration.class defaultValue:nil];
-        self.subjectConfiguration = (SPSubjectConfiguration *)[dictionary sp_configurationForKey:SP_STR_PROP(subjectConfiguration) configurationClass:SPSubjectConfiguration.class defaultValue:nil];
-        self.sessionConfiguration = (SPSessionConfiguration *)[dictionary sp_configurationForKey:SP_STR_PROP(sessionConfiguration) configurationClass:SPSessionConfiguration.class defaultValue:nil];
-    }
-    return self;
-}
-
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPConfigurationBundle *copy;
-    copy.namespace = self.namespace;
-    copy.networkConfiguration = [self.networkConfiguration copyWithZone:zone];
-    copy.trackerConfiguration = [self.trackerConfiguration copyWithZone:zone];
-    copy.subjectConfiguration = [self.subjectConfiguration copyWithZone:zone];
-    copy.sessionConfiguration = [self.sessionConfiguration copyWithZone:zone];
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeObject:self.namespace forKey:SP_STR_PROP(namespace)];
-    [coder encodeObject:self.networkConfiguration forKey:SP_STR_PROP(networkConfiguration)];
-    [coder encodeObject:self.trackerConfiguration forKey:SP_STR_PROP(trackerConfiguration)];
-    [coder encodeObject:self.subjectConfiguration forKey:SP_STR_PROP(subjectConfiguration)];
-    [coder encodeObject:self.sessionConfiguration forKey:SP_STR_PROP(sessionConfiguration)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.namespace = [coder decodeObjectForKey:SP_STR_PROP(namespace)];
-        self.networkConfiguration = [coder decodeObjectForKey:SP_STR_PROP(networkConfiguration)];
-        self.trackerConfiguration = [coder decodeObjectForKey:SP_STR_PROP(trackerConfiguration)];
-        self.subjectConfiguration = [coder decodeObjectForKey:SP_STR_PROP(subjectConfiguration)];
-        self.sessionConfiguration = [coder decodeObjectForKey:SP_STR_PROP(sessionConfiguration)];
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/Configurations/SPEmitterConfiguration.h b/Snowplow/Internal/Configurations/SPEmitterConfiguration.h
deleted file mode 100644
index 62131fde5..000000000
--- a/Snowplow/Internal/Configurations/SPEmitterConfiguration.h
+++ /dev/null
@@ -1,161 +0,0 @@
-//
-//  SPEmitterConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-#import "SPEventStore.h"
-#import "SPRequestCallback.h"
-
-/*!
- @brief An enum for buffer options.
- */
-typedef NS_ENUM(NSUInteger, SPBufferOption) {
-    /**
-     * Sends both GET and POST requests with only a single event.  Can cause a spike in
-     * network traffic if used in correlation with a large amount of events.
-     */
-    SPBufferOptionSingle = 1,
-    /**
-     * Sends POST requests in groups of 10 events.  This is the default amount of events too
-     * package into a POST.  All GET requests will still emit one at a time.
-     */
-    SPBufferOptionDefaultGroup = 10,
-    /**
-     * Sends POST requests in groups of 25 events.  Useful for situations where many events
-     * need to be sent.  All GET requests will still emit one at a time.
-     */
-    SPBufferOptionLargeGroup = 25,
-    SPBufferOptionHeavyGroup __deprecated_enum_msg("Use BufferOption.largeGroup instead.") = SPBufferOptionLargeGroup
-} NS_SWIFT_NAME(BufferOption);
-
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(EmitterConfigurationProtocol)
-@protocol SPEmitterConfigurationProtocol
-
-/**
- * Sets whether the buffer should send events instantly or after the buffer
- * has reached it's limit. By default, this is set to BufferOption Default.
- */
-@property () SPBufferOption bufferOption;
-/**
- * Maximum number of events collected from the EventStore to be sent in a request.
- */
-@property () NSInteger emitRange;
-/**
- * Maximum number of threads working in parallel in the tracker to send requests.
- */
-@property () NSInteger threadPoolSize;
-/**
- * Maximum amount of bytes allowed to be sent in a payload in a GET request.
- */
-@property () NSInteger byteLimitGet;
-/**
- * Maximum amount of bytes allowed to be sent in a payload in a POST request.
- */
-@property () NSInteger byteLimitPost;
-/**
- * Callback called for each request performed by the tracker to the collector.
- */
-@property (nullable) id<SPRequestCallback> requestCallback;
-/**
- *  Custom retry rules for HTTP status codes returned from the Collector.
- *  The dictionary is a mapping of integers (status codes) to booleans (true for retry and false for not retry).
- */
-@property (nonatomic, nullable) NSDictionary<NSNumber *, NSNumber *> *customRetryForStatusCodes;
-/**
- * Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
- */
-@property () BOOL serverAnonymisation;
-
-@end
-
-/**
- * It allows the tracker configuration from the emission perspective.
- * The EmitterConfiguration can be used to setup details about how the tracker should treat the events
- * to emit to the collector.
- */
-NS_SWIFT_NAME(EmitterConfiguration)
-@interface SPEmitterConfiguration : SPConfiguration <SPEmitterConfigurationProtocol>
-
-/**
- * Custom component with full ownership for persisting events before to be sent to the collector.
- * If it's not set the tracker will use a SQLite database as default EventStore.
- */
-@property (nullable) id<SPEventStore> eventStore;
-
-/**
- * It sets a default EmitterConfiguration.
- * Default values:
- *         bufferOption = BufferOption.Single;
- *         emitRange = 150;
- *         threadPoolSize = 15;
- *         byteLimitGet = 40000;
- *         byteLimitPost = 40000;
- *         serverAnonymisation = false;
- */
-- (instancetype)init;
-
-/**
- * Sets whether the buffer should send events instantly or after the buffer
- * has reached it's limit. By default, this is set to BufferOption Default.
- */
-SP_BUILDER_DECLARE(SPBufferOption, bufferOption)
-/**
- * Maximum number of events collected from the EventStore to be sent in a request.
- */
-SP_BUILDER_DECLARE(NSInteger, emitRange)
-/**
- * Maximum number of threads working in parallel in the tracker to send requests.
- */
-SP_BUILDER_DECLARE(NSInteger, threadPoolSize)
-/**
- * Maximum amount of bytes allowed to be sent in a payload in a GET request.
- */
-SP_BUILDER_DECLARE(NSInteger, byteLimitGet)
-/**
- * Maximum amount of bytes allowed to be sent in a payload in a POST request.
- */
-SP_BUILDER_DECLARE(NSInteger, byteLimitPost)
-/**
- * Callback called for each request performed by the tracker to the collector.
- */
-SP_BUILDER_DECLARE_NULLABLE(id<SPRequestCallback>, requestCallback)
-
-/**
- * Custom component with full ownership for persisting events before to be sent to the collector.
- * If it's not set the tracker will use a SQLite database as default EventStore.
- */
-SP_BUILDER_DECLARE_NULLABLE(id<SPEventStore>, eventStore)
-/**
- * Custom retry rules for HTTP status codes returned from the Collector.
- * The dictionary is a mapping of integers (status codes) to booleans (true for retry and false for not retry).
- */
-SP_BUILDER_DECLARE_NULLABLE(NSDictionary *, customRetryForStatusCodes)
-/**
- * Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
- */
-SP_BUILDER_DECLARE(BOOL, serverAnonymisation)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPEmitterConfiguration.m b/Snowplow/Internal/Configurations/SPEmitterConfiguration.m
deleted file mode 100644
index 43accf82c..000000000
--- a/Snowplow/Internal/Configurations/SPEmitterConfiguration.m
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//  SPEmitterConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEmitterConfiguration.h"
-
-@implementation SPEmitterConfiguration
-
-@synthesize bufferOption;
-@synthesize byteLimitGet;
-@synthesize byteLimitPost;
-@synthesize emitRange;
-@synthesize threadPoolSize;
-@synthesize requestCallback;
-@synthesize customRetryForStatusCodes;
-@synthesize serverAnonymisation;
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.bufferOption = SPBufferOptionSingle;
-        self.emitRange = 150;
-        self.threadPoolSize = 15;
-        self.byteLimitGet = 40000;
-        self.byteLimitPost = 40000;
-        self.eventStore = nil;
-        self.requestCallback = nil;
-        self.serverAnonymisation = NO;
-    }
-    return self;
-}
-
-// MARK: - Builder
-
-SP_BUILDER_METHOD(SPBufferOption, bufferOption)
-SP_BUILDER_METHOD(NSInteger, emitRange)
-SP_BUILDER_METHOD(NSInteger, threadPoolSize)
-SP_BUILDER_METHOD(NSInteger, byteLimitGet)
-SP_BUILDER_METHOD(NSInteger, byteLimitPost)
-SP_BUILDER_METHOD(id<SPRequestCallback>, requestCallback)
-SP_BUILDER_METHOD(NSDictionary *, customRetryForStatusCodes)
-SP_BUILDER_METHOD(BOOL, serverAnonymisation)
-
-SP_BUILDER_METHOD(id<SPEventStore>, eventStore)
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPEmitterConfiguration *copy = [[SPEmitterConfiguration allocWithZone:zone] init];
-    copy.bufferOption = self.bufferOption;
-    copy.emitRange = self.emitRange;
-    copy.threadPoolSize = self.threadPoolSize;
-    copy.byteLimitGet = self.byteLimitGet;
-    copy.byteLimitPost = self.byteLimitPost;
-    copy.requestCallback = self.requestCallback;
-    copy.eventStore = self.eventStore;
-    copy.customRetryForStatusCodes = self.customRetryForStatusCodes;
-    copy.serverAnonymisation = self.serverAnonymisation;
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeInteger:self.bufferOption forKey:SP_STR_PROP(bufferOption)];
-    [coder encodeInteger:self.emitRange forKey:SP_STR_PROP(emitRange)];
-    [coder encodeInteger:self.threadPoolSize forKey:SP_STR_PROP(threadPoolSize)];
-    [coder encodeInteger:self.byteLimitGet forKey:SP_STR_PROP(byteLimitGet)];
-    [coder encodeInteger:self.byteLimitPost forKey:SP_STR_PROP(byteLimitPost)];
-    [coder encodeObject:self.customRetryForStatusCodes forKey:SP_STR_PROP(customRetryForStatusCodes)];
-    [coder encodeBool:self.serverAnonymisation forKey:SP_STR_PROP(serverAnonymisation)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.bufferOption = [coder decodeIntegerForKey:SP_STR_PROP(bufferOption)];
-        self.emitRange = [coder decodeIntegerForKey:SP_STR_PROP(emitRange)];
-        self.threadPoolSize = [coder decodeIntegerForKey:SP_STR_PROP(threadPoolSize)];
-        self.byteLimitGet = [coder decodeIntegerForKey:SP_STR_PROP(byteLimitGet)];
-        self.byteLimitPost = [coder decodeIntegerForKey:SP_STR_PROP(byteLimitPost)];
-        self.customRetryForStatusCodes = [coder decodeObjectForKey:SP_STR_PROP(customRetryForStatusCodes)];
-        self.serverAnonymisation = [coder decodeBoolForKey:SP_STR_PROP(serverAnonymisation)];
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/Configurations/SPGDPRConfiguration.h b/Snowplow/Internal/Configurations/SPGDPRConfiguration.h
deleted file mode 100644
index 9c9d11abd..000000000
--- a/Snowplow/Internal/Configurations/SPGDPRConfiguration.h
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-//  SPGDPRConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-
-
-typedef NS_ENUM(NSInteger, SPGdprProcessingBasis) {
-    SPGdprProcessingBasisConsent = 0,
-    SPGdprProcessingBasisContract = 1,
-    SPGdprProcessingBasisLegalObligation = 2,
-    SPGdprProcessingBasisVitalInterest = 3,
-    SPGdprProcessingBasisPublicTask = 4,
-    SPGdprProcessingBasisLegitimateInterests = 5
-} NS_SWIFT_NAME(GDPRProcessingBasis);
-
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(GDPRConfigurationProtocol)
-@protocol SPGDPRConfigurationProtocol
-
-/** Basis for processing. */
-@property (nonatomic, readonly) SPGdprProcessingBasis basisForProcessing;
-/** ID of a GDPR basis document. */
-@property (nonatomic, readonly, nullable) NSString *documentId;
-/** Version of the document. */
-@property (nonatomic, readonly, nullable) NSString *documentVersion;
-/** Description of the document. */
-@property (nonatomic, readonly, nullable) NSString *documentDescription;
-
-@end
-
-/**
- * This class allows the GDPR configuration of the tracker.
- */
-NS_SWIFT_NAME(GDPRConfiguration)
-@interface SPGDPRConfiguration : SPConfiguration <SPGDPRConfigurationProtocol>
-
-/**
- * Enables GDPR context to be sent with each event.
- * @param basisForProcessing GDPR Basis for processing.
- * @param documentId ID of a GDPR basis document.
- * @param documentVersion Version of the document.
- * @param documentDescription Description of the document.
- */
-- (instancetype)initWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                   documentId:(nullable NSString *)documentId
-              documentVersion:(nullable NSString *)documentVersion
-          documentDescription:(nullable NSString *)documentDescription
-NS_SWIFT_NAME(init(basis:documentId:documentVersion:documentDescription:));
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPGDPRConfiguration.m b/Snowplow/Internal/Configurations/SPGDPRConfiguration.m
deleted file mode 100644
index 1b7d608d1..000000000
--- a/Snowplow/Internal/Configurations/SPGDPRConfiguration.m
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-//  SPGDPRConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGDPRConfiguration.h"
-
-@interface SPGDPRConfiguration ()
-
-@property (nonatomic, readwrite) SPGdprProcessingBasis basisForProcessing;
-@property (nonatomic, readwrite) NSString *documentId;
-@property (nonatomic, readwrite) NSString *documentVersion;
-@property (nonatomic, readwrite) NSString *documentDescription;
-
-@end
-
-@implementation SPGDPRConfiguration
-
-- (instancetype)initWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                   documentId:(NSString *)documentId
-              documentVersion:(NSString *)documentVersion
-          documentDescription:(NSString *)documentDescription
-{
-    if (self = [super init]) {
-        self.basisForProcessing = basisForProcessing;
-        self.documentId = documentId;
-        self.documentVersion = documentVersion;
-        self.documentDescription = documentDescription;
-    }
-    return self;
-}
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPGDPRConfiguration *copy = [[SPGDPRConfiguration allocWithZone:zone] init];
-    copy.basisForProcessing = self.basisForProcessing;
-    copy.documentId = self.documentId;
-    copy.documentVersion = self.documentVersion;
-    copy.documentDescription = self.documentDescription;
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeInteger:self.basisForProcessing forKey:SP_STR_PROP(basisForProcessing)];
-    [coder encodeObject:self.documentId forKey:SP_STR_PROP(documentId)];
-    [coder encodeObject:self.documentVersion forKey:SP_STR_PROP(documentVersion)];
-    [coder encodeObject:self.documentDescription forKey:SP_STR_PROP(documentDescription)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.basisForProcessing = [coder decodeIntegerForKey:SP_STR_PROP(basisForProcessing)];
-        self.documentId = [coder decodeObjectForKey:SP_STR_PROP(documentId)];
-        self.documentVersion = [coder decodeObjectForKey:SP_STR_PROP(documentVersion)];
-        self.documentDescription = [coder decodeObjectForKey:SP_STR_PROP(documentDescription)];
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.h b/Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.h
deleted file mode 100644
index d0516239d..000000000
--- a/Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.h
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-//  SPGlobalContextsConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-#import "SPGlobalContext.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(GlobalContextsConfigurationProtocol)
-@protocol SPGlobalContextsConfigurationProtocol
-
-@property NSMutableDictionary<NSString *, SPGlobalContext *> *contextGenerators;
-
-/**
- * Add a GlobalContext generator to the configuration of the tracker.
- * @param tag The label identifying the generator in the tracker.
- * @param generator The GlobalContext generator.
- * @return Whether the adding operation has succeeded.
- */
-- (BOOL)addWithTag:(NSString *)tag contextGenerator:(SPGlobalContext *)generator NS_SWIFT_NAME(add(tag:contextGenerator:));
-/**
- * Remove a GlobalContext generator from the configuration of the tracker.
- * @param tag The label identifying the generator in the tracker.
- * @return Whether the removing operation has succeded.
- */
-- (nullable SPGlobalContext *)removeWithTag:(NSString *)tag NS_SWIFT_NAME(remove(tag:));
-
-@end
-
-/**
- * This class allows the setup of Global Contexts which are attached to selected events.
- */
-NS_SWIFT_NAME(GlobalContextsConfiguration)
-@interface SPGlobalContextsConfiguration : SPConfiguration <SPGlobalContextsConfigurationProtocol>
-
-SP_BUILDER_DECLARE(SP_ESCAPE(NSMutableDictionary<NSString *, SPGlobalContext *> *), contextGenerators)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.m b/Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.m
deleted file mode 100644
index 0046ef9fb..000000000
--- a/Snowplow/Internal/Configurations/SPGlobalContextsConfiguration.m
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-//  SPGlobalContextsConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGlobalContextsConfiguration.h"
-
-@implementation SPGlobalContextsConfiguration
-
-@synthesize contextGenerators;
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.contextGenerators = [NSMutableDictionary new];
-    }
-    return self;
-}
-
-- (BOOL)addWithTag:(nonnull NSString *)tag contextGenerator:(nonnull SPGlobalContext *)generator {
-    if ([self.contextGenerators objectForKey:tag]) {
-        return NO;
-    }
-    [self.contextGenerators setObject:generator forKey:tag];
-    return YES;
-}
-
-- (nullable SPGlobalContext *)removeWithTag:(nonnull NSString *)tag {
-    SPGlobalContext *toDelete = [self.contextGenerators objectForKey:tag];
-    if (toDelete) {
-        [self.contextGenerators removeObjectForKey:tag];
-    }
-    return toDelete;
-}
-
-// MARK: - Builder
-
-SP_BUILDER_METHOD(SP_ESCAPE(NSMutableDictionary<NSString *, SPGlobalContext *> *), contextGenerators)
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPGlobalContextsConfiguration *copy = [[SPGlobalContextsConfiguration allocWithZone:zone] init];
-    copy.contextGenerators = self.contextGenerators;
-    return copy;
-}
-
-// MARK: - NSCoding (No coding possible as we can't encode and decode the contextGenerators)
-
-@end
diff --git a/Snowplow/Internal/Configurations/SPNetworkConfiguration.h b/Snowplow/Internal/Configurations/SPNetworkConfiguration.h
deleted file mode 100644
index ea1fc9477..000000000
--- a/Snowplow/Internal/Configurations/SPNetworkConfiguration.h
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-//  SPNetworkConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-#import "SPNetworkConnection.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * Represents the network communication configuration
- * allowing the tracker to be able to send events to the Snowplow collector.
- */
-NS_SWIFT_NAME(NetworkConfiguration)
-@interface SPNetworkConfiguration : SPConfiguration
-
-/**
- * URL (without schema/protocol) used to send events to the collector.
- */
-@property (nonatomic, nullable, readonly) NSString *endpoint;
-
-/**
- * Method used to send events to the collector.
- */
-@property (nonatomic, readonly) SPHttpMethod method;
-
-/**
- * Protocol used to send events to the collector.
- */
-@property (nonatomic, readonly) SPProtocol protocol;
-
-/**
- * See `NetworkConfiguration(NetworkConnection)`
- */
-@property (nonatomic, nullable) id<SPNetworkConnection> networkConnection;
-
-/**
- * A custom path which will be added to the endpoint URL to specify the
- * complete URL of the collector when paired with the POST method.
- */
-@property (nonatomic, nullable) NSString *customPostPath;
-
-/**
- *  Custom headers for http requests.
- */
-@property (nonatomic, nullable) NSDictionary<NSString *, NSString *> *requestHeaders;
-
-// TODO: add -> @property () NSInteger timeout;
-
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-/**
- * @param endpoint URL of the collector that is going to receive the events tracked by the tracker.
- *                 The URL can include the schema/protocol (e.g.: `http://collector-url.com`).
- *                 In case the URL doesn't include the schema/protocol, the HTTPS protocol is
- *                 automatically selected.
- */
-- (instancetype)initWithEndpoint:(NSString *)endpoint;
-
-/**
- * @param endpoint URL of the collector that is going to receive the events tracked by the tracker.
- *                 The URL can include the schema/protocol (e.g.: `http://collector-url.com`).
- *                 In case the URL doesn't include the schema/protocol, the HTTPS protocol is
- *                 automatically selected.
- * @param method The method used to send the requests (GET or POST).
- */
-- (instancetype)initWithEndpoint:(NSString *)endpoint method:(SPHttpMethod)method;
-
-/**
- * @param networkConnection The NetworkConnection component which will control the
- *                          communication between the tracker and the collector.
- */
-- (instancetype)initWithNetworkConnection:(id<SPNetworkConnection>)networkConnection;
-
-SP_BUILDER_DECLARE_NULLABLE(NSString *, customPostPath)
-SP_BUILDER_DECLARE_NULLABLE(NSDictionary *, requestHeaders)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPNetworkConfiguration.m b/Snowplow/Internal/Configurations/SPNetworkConfiguration.m
deleted file mode 100644
index ed6211f47..000000000
--- a/Snowplow/Internal/Configurations/SPNetworkConfiguration.m
+++ /dev/null
@@ -1,127 +0,0 @@
-//
-//  SPNetworkConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPNetworkConfiguration.h"
-#import "NSDictionary+SP_TypeMethods.h"
-
-@interface SPNetworkConfiguration ()
-
-@property (nonatomic, nullable) NSString *endpoint;
-@property (nonatomic) SPHttpMethod method;
-@property (nonatomic) SPProtocol protocol;
-
-@end
-
-@implementation SPNetworkConfiguration
-
-@synthesize customPostPath;
-@synthesize requestHeaders;
-
-/// Allow endpoint and method only.
-- (instancetype)initWithDictionary:(NSDictionary<NSString *,NSObject *> *)dictionary {
-    NSString *endpoint = [dictionary sp_stringForKey:SP_STR_PROP(endpoint) defaultValue:nil];
-    NSString *method = [dictionary sp_stringForKey:SP_STR_PROP(method) defaultValue:nil];
-    if (!endpoint || !method) {
-        return nil;
-    }
-    SPHttpMethod httpMethod = [method isEqualToString:@"get"] ? SPHttpMethodGet : SPHttpMethodPost;
-    return [self initWithEndpoint:endpoint method:httpMethod];
-}
-
-- (instancetype)initWithEndpoint:(NSString *)endpoint method:(SPHttpMethod)method {
-    if (self = [super init]) {
-        NSURL *url = [[NSURL alloc] initWithString:endpoint];
-        if ([url.scheme isEqualToString:@"https"]) {
-            self.protocol = SPProtocolHttps;
-            self.endpoint = endpoint;
-        } else if ([url.scheme isEqualToString:@"http"]) {
-            self.protocol = SPProtocolHttp;
-            self.endpoint = endpoint;
-        } else {
-            self.protocol = SPProtocolHttps;
-            self.endpoint = [NSString stringWithFormat:@"https://%@", endpoint];
-        }
-        self.method = method;
-        self.networkConnection = nil;
-        self.customPostPath = nil;
-    }
-    return self;
-}
-
-- (instancetype)initWithEndpoint:(NSString *)endpoint {
-    return [self initWithEndpoint:endpoint method:SPHttpMethodPost];
-}
-
-- (instancetype)initWithNetworkConnection:(id<SPNetworkConnection>)networkConnection {
-    if (self = [super init]) {
-        self.endpoint = nil;
-        self.protocol = 0;
-        self.method = 0;
-        self.networkConnection = networkConnection;
-        self.customPostPath = nil;
-    }
-    return self;
-}
-
-// MARK: - Builder
-
-SP_BUILDER_METHOD(NSString *, customPostPath)
-SP_BUILDER_METHOD(NSDictionary *, requestHeaders)
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPNetworkConfiguration *copy;
-    if (self.networkConnection) {
-        copy = [[SPNetworkConfiguration alloc] initWithNetworkConnection:self.networkConnection];
-    } else {
-        copy = [[SPNetworkConfiguration allocWithZone:zone] initWithEndpoint:self.endpoint method:self.method];
-    }
-    copy.customPostPath = self.customPostPath;
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeObject:self.endpoint forKey:SP_STR_PROP(endpoint)];
-    [coder encodeInteger:self.protocol forKey:SP_STR_PROP(protocol)];
-    [coder encodeInteger:self.method forKey:SP_STR_PROP(method)];
-    [coder encodeObject:self.customPostPath forKey:SP_STR_PROP(customPostPath)];
-    [coder encodeObject:self.requestHeaders forKey:SP_STR_PROP(requestHeaders)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.endpoint = [coder decodeObjectForKey:SP_STR_PROP(endpoint)];
-        self.protocol = [coder decodeIntegerForKey:SP_STR_PROP(protocol)];
-        self.method = [coder decodeIntegerForKey:SP_STR_PROP(method)];
-        self.customPostPath = [coder decodeObjectForKey:SP_STR_PROP(customPostPath)];
-        self.requestHeaders = [coder decodeObjectForKey:SP_STR_PROP(requestHeaders)];
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/Configurations/SPRemoteConfiguration.h b/Snowplow/Internal/Configurations/SPRemoteConfiguration.h
deleted file mode 100644
index ca3857673..000000000
--- a/Snowplow/Internal/Configurations/SPRemoteConfiguration.h
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-//  SPRemoteConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-#import "SPNetworkConnection.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * Represents the configuration for fetching configurations from a remote source.
- * For details on the correct format of a remote configuration see the official documentation.
- */
-NS_SWIFT_NAME(RemoteConfiguration)
-@interface SPRemoteConfiguration : SPConfiguration
-
-/**
- * URL of the remote configuration.
- */
-@property (nonatomic, nullable, readonly) NSString *endpoint;
-
-/**
- * The method used to send the request.
- */
-@property (nonatomic, readonly) SPHttpMethod method;
-
-/**
- * @param endpoint URL of the remote configuration.
- *                 The URL can include the schema/protocol (e.g.: `http://remote-config-url.xyz`).
- *                 In case the URL doesn't include the schema/protocol, the HTTPS protocol is
- *                 automatically selected.
- * @param method The method used to send the requests (GET or POST).
- */
-- (instancetype)initWithEndpoint:(NSString *)endpoint method:(SPHttpMethod)method;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPRemoteConfiguration.m b/Snowplow/Internal/Configurations/SPRemoteConfiguration.m
deleted file mode 100644
index 63ed63269..000000000
--- a/Snowplow/Internal/Configurations/SPRemoteConfiguration.m
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-//  SPRemoteConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPRemoteConfiguration.h"
-
-@interface SPRemoteConfiguration ()
-
-@property (nonatomic) NSString *endpoint;
-@property (nonatomic) SPHttpMethod method;
-
-@end
-
-@implementation SPRemoteConfiguration
-
-- (instancetype)initWithEndpoint:(NSString *)endpoint method:(SPHttpMethod)method {
-    if (self = [super init]) {
-        NSURL *url = [[NSURL alloc] initWithString:endpoint];
-        if (url.scheme && [@[@"https", @"http"] containsObject:url.scheme]) {
-            self.endpoint = endpoint;
-        } else {
-            self.endpoint = [NSString stringWithFormat:@"https://%@", endpoint];
-        }
-        self.method = method;
-    }
-    return self;
-}
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    return [[SPRemoteConfiguration allocWithZone:zone] initWithEndpoint:self.endpoint method:self.method];
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeObject:self.endpoint forKey:SP_STR_PROP(endpoint)];
-    [coder encodeInteger:self.method forKey:SP_STR_PROP(method)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.endpoint = [coder decodeObjectForKey:SP_STR_PROP(endpoint)];
-        self.method = [coder decodeIntegerForKey:SP_STR_PROP(method)];
-    }
-    return self;
-}
-
-
-@end
diff --git a/Snowplow/Internal/Configurations/SPSessionConfiguration.h b/Snowplow/Internal/Configurations/SPSessionConfiguration.h
deleted file mode 100644
index 15b704017..000000000
--- a/Snowplow/Internal/Configurations/SPSessionConfiguration.h
+++ /dev/null
@@ -1,104 +0,0 @@
-//
-//  SPSessionConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-#import "SPSessionState.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-typedef void(^OnSessionStateUpdate)(SPSessionState * _Nonnull sessionState);
-
-NS_SWIFT_NAME(SessionConfigurationProtocol)
-@protocol SPSessionConfigurationProtocol
-
-/**
- * The amount of time that can elapse before the
- * session id is updated while the app is in the
- * foreground.
- */
-@property NSInteger foregroundTimeoutInSeconds;
-/**
- * The amount of time that can elapse before the
- * session id is updated while the app is in the
- * background.
- */
-@property NSInteger backgroundTimeoutInSeconds;
-
-/**
- * The amount of time that can elapse before the
- * session id is updated while the app is in the
- * foreground.
- */
-@property NSMeasurement<NSUnitDuration *> *foregroundTimeout API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0));
-/**
- * The amount of time that can elapse before the
- * session id is updated while the app is in the
- * background.
- */
-@property NSMeasurement<NSUnitDuration *> *backgroundTimeout API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0));
-
-/**
- * The callback called everytime the session is updated.
- */
-@property (nullable) OnSessionStateUpdate onSessionStateUpdate;
-
-@end
-
-/**
- * This class represents the configuration from of the applications session.
- * The SessionConfiguration can be used to setup the behaviour of sessions.
- *
- * A session is a context which is appended to each event sent.
- * The values it brings can change based on:
- * - the timeout set for the inactivity of app when in foreground;
- * - the timeout set for the inactivity of app when in background.
- *
- * Session data is maintained for the life of the application being installed on a device.
- * A new session will be created if the session information is not accessed within a configurable timeout.
- */
-NS_SWIFT_NAME(SessionConfiguration)
-@interface SPSessionConfiguration : SPConfiguration <SPSessionConfigurationProtocol>
-
-/**
- * This will setup the session behaviour of the tracker.
- * @param foregroundTimeout The timeout set for the inactivity of app when in foreground.
- * @param backgroundTimeout The timeout set for the inactivity of app when in background.
- */
-- (instancetype)initWithForegroundTimeoutInSeconds:(NSInteger)foregroundTimeout backgroundTimeoutInSeconds:(NSInteger)backgroundTimeout;
-
-/**
- * This will setup the session behaviour of the tracker.
- * @param foregroundTimeout The timeout set for the inactivity of app when in foreground.
- * @param backgroundTimeout The timeout set for the inactivity of app when in background.
- */
-- (instancetype)initWithForegroundTimeout:(NSMeasurement<NSUnitDuration *> *)foregroundTimeout
-                        backgroundTimeout:(NSMeasurement<NSUnitDuration *> *)backgroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0));
-
-/**
- * The callback called everytime the session is updated.
- */
-SP_BUILDER_DECLARE_NULLABLE(OnSessionStateUpdate, onSessionStateUpdate)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPSessionConfiguration.m b/Snowplow/Internal/Configurations/SPSessionConfiguration.m
deleted file mode 100644
index 18b9417f6..000000000
--- a/Snowplow/Internal/Configurations/SPSessionConfiguration.m
+++ /dev/null
@@ -1,117 +0,0 @@
-//
-//  SPSessionConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSessionConfiguration.h"
-#import "NSDictionary+SP_TypeMethods.h"
-
-@implementation SPSessionConfiguration
-
-@synthesize foregroundTimeoutInSeconds;
-@synthesize backgroundTimeoutInSeconds;
-@synthesize onSessionStateUpdate;
-
-- (instancetype)init {
-    return [self initWithForegroundTimeoutInSeconds:1800 backgroundTimeoutInSeconds:1800];
-}
-
-- (instancetype)initWithDictionary:(NSDictionary<NSString *,NSObject *> *)dictionary {
-    NSInteger foregroundTimeout = [dictionary sp_numberForKey:SP_STR_PROP(foregroundTimeout) defaultValue:@1800].integerValue;
-    NSInteger backgroundTimeout = [dictionary sp_numberForKey:SP_STR_PROP(backgroundTimeout) defaultValue:@1800].integerValue;
-    return [self initWithForegroundTimeoutInSeconds:foregroundTimeout backgroundTimeoutInSeconds:backgroundTimeout];
-}
-
-- (instancetype)initWithForegroundTimeout:(NSMeasurement<NSUnitDuration *> *)foregroundTimeout backgroundTimeout:(NSMeasurement<NSUnitDuration *> *)backgroundTimeout {
-    NSMeasurement<NSUnitDuration *> *foreground = [foregroundTimeout measurementByConvertingToUnit:NSUnitDuration.seconds];
-    NSInteger foregroundInSeconds = floor(foreground.doubleValue);
-    NSMeasurement<NSUnitDuration *> *background = [backgroundTimeout measurementByConvertingToUnit:NSUnitDuration.seconds];
-    NSInteger backgroundInSeconds = floor(background.doubleValue);
-    return [self initWithForegroundTimeoutInSeconds:foregroundInSeconds backgroundTimeoutInSeconds:backgroundInSeconds];
-}
-
-- (instancetype)initWithForegroundTimeoutInSeconds:(NSInteger)foregroundTimeout backgroundTimeoutInSeconds:(NSInteger)backgroundTimeout {
-    if (self = [super init]) {
-        self.backgroundTimeoutInSeconds = backgroundTimeout;
-        self.foregroundTimeoutInSeconds = foregroundTimeout;
-    }
-    return self;
-}
-
-- (void)setForegroundTimeout:(NSMeasurement<NSUnitDuration *> *)foregroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    NSMeasurement<NSUnitDuration *> *foreground = [foregroundTimeout measurementByConvertingToUnit:NSUnitDuration.seconds];
-    self.foregroundTimeoutInSeconds = floor(foreground.doubleValue);
-}
-
-- (void)setBackgroundTimeout:(NSMeasurement<NSUnitDuration *> *)backgroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    NSMeasurement<NSUnitDuration *> *background = [backgroundTimeout measurementByConvertingToUnit:NSUnitDuration.seconds];
-    self.backgroundTimeoutInSeconds = floor(background.doubleValue);
-}
-
-- (NSMeasurement<NSUnitDuration *> *)foregroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    return [[NSMeasurement alloc] initWithDoubleValue:self.foregroundTimeoutInSeconds unit:NSUnitDuration.seconds];
-}
-
-- (NSMeasurement<NSUnitDuration *> *)backgroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    return [[NSMeasurement alloc] initWithDoubleValue:self.backgroundTimeoutInSeconds unit:NSUnitDuration.seconds];
-}
-
-// MARK: - Builders
-
-SP_BUILDER_METHOD(OnSessionStateUpdate, onSessionStateUpdate)
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPSessionConfiguration *copy = [[SPSessionConfiguration allocWithZone:zone] init];
-    copy.backgroundTimeoutInSeconds = self.backgroundTimeoutInSeconds;
-    copy.foregroundTimeoutInSeconds = self.foregroundTimeoutInSeconds;
-    copy.onSessionStateUpdate = self.onSessionStateUpdate;
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeInteger:self.backgroundTimeoutInSeconds forKey:SP_STR_PROP(backgroundTimeoutInSeconds)];
-    [coder encodeInteger:self.foregroundTimeoutInSeconds forKey:SP_STR_PROP(foregroundTimeoutInSeconds)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.backgroundTimeoutInSeconds = [coder decodeIntegerForKey:SP_STR_PROP(backgroundTimeoutInSeconds)];
-        self.foregroundTimeoutInSeconds = [coder decodeIntegerForKey:SP_STR_PROP(foregroundTimeoutInSeconds)];
-    }
-    return self;
-}
-
-@end
-
diff --git a/Snowplow/Internal/Configurations/SPSubjectConfiguration.h b/Snowplow/Internal/Configurations/SPSubjectConfiguration.h
deleted file mode 100644
index 63cb5c752..000000000
--- a/Snowplow/Internal/Configurations/SPSubjectConfiguration.h
+++ /dev/null
@@ -1,218 +0,0 @@
-//
-//  SPSubjectConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import <CoreGraphics/CoreGraphics.h>
-#import "SPConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPSize : NSObject <NSCoding>
-
-@property (readonly) NSInteger width;
-@property (readonly) NSInteger height;
-
-- initWithWidth:(NSInteger)width height:(NSInteger)height;
-
-@end
-
-
-NS_SWIFT_NAME(SubjectConfigurationProtocol)
-@protocol SPSubjectConfigurationProtocol
-
-/**
- * The custom UserID.
- */
-@property (nullable) NSString *userId;
-/**
- * The network UserID.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-@property (nullable) NSString *networkUserId;
-/**
- * The domain UserID.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-@property (nullable) NSString *domainUserId;
-/**
- * The user-agent.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-@property (nullable) NSString *useragent;
-/**
- * The IP address.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-@property (nullable) NSString *ipAddress;
-
-/**
- * The current timezone.
- */
-@property (nullable) NSString *timezone;
-/**
- * The language set in the device.
- */
-@property (nullable) NSString *language;
-
-/**
- * The screen resolution.
- */
-@property (nullable) SPSize *screenResolution;
-/**
- * The screen viewport.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-@property (nullable) SPSize *screenViewPort;
-/**
- * The color depth.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-@property (nullable) NSNumber *colorDepth;
-
-// MARK: - GeoLocation
-
-/*!
- Latitude value for the geolocation context.
- */
-@property (nullable) NSNumber *geoLatitude;
-
-/*!
- Longitude value for the geo context.
- */
-@property (nullable) NSNumber *geoLongitude;
-
-/*!
- LatitudeLongitudeAccuracy value for the geolocation context.
- */
-@property (nullable) NSNumber *geoLatitudeLongitudeAccuracy;
-
-/*!
- Altitude value for the geolocation context.
- */
-@property (nullable) NSNumber *geoAltitude;
-
-/*!
- AltitudeAccuracy value for the geolocation context.
- */
-@property (nullable) NSNumber *geoAltitudeAccuracy;
-
-/*!
- Bearing value for the geolocation context.
- */
-@property (nullable) NSNumber *geoBearing;
-
-/*!
- Speed value for the geolocation context.
- */
-@property (nullable) NSNumber *geoSpeed;
-
-/*!
- Timestamp value for the geolocation context.
- */
-@property (nullable) NSNumber *geoTimestamp;
-
-@end
-
-/**
- * This class represents the configuration of the subject.
- * The SubjectConfiguration can be used to setup the tracker with the basic information about the
- * user and the app which will be attached on all the events as contexts.
- * The contexts to track can be enabled in the `TrackerConfiguration` class.
- */
-NS_SWIFT_NAME(SubjectConfiguration)
-@interface SPSubjectConfiguration : SPConfiguration <SPSubjectConfigurationProtocol>
-
-/**
- * The custom UserID.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSString *, userId)
-/**
- * The network UserID.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSString *, networkUserId)
-/**
- * The domain UserID.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSString *, domainUserId)
-/**
- * The user-agent.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSString *, useragent)
-/**
- * The IP address.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSString *, ipAddress)
-/**
- * The current timezone.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSString *, timezone)
-/**
- * The language set in the device.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSString *, language)
-/**
- * The screen resolution.
- */
-SP_BUILDER_DECLARE_NULLABLE(SPSize *, screenResolution)
-/**
- * The screen viewport.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-SP_BUILDER_DECLARE_NULLABLE(SPSize *, screenViewPort)
-/**
- * The color depth.
- * Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
- */
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, colorDepth)
-
-// GeoLocation builders
-
-/// Latitude value for the geolocation context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoLatitude)
-
-/// Longitude value for the geo context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoLongitude)
-
-/// LatitudeLongitudeAccuracy value for the geolocation context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoLatitudeLongitudeAccuracy)
-
-/// Altitude value for the geolocation context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoAltitude)
-
-/// AltitudeAccuracy value for the geolocation context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoAltitudeAccuracy)
-
-/// Bearing value for the geolocation context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoBearing)
-
-/// Speed value for the geolocation context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoSpeed)
-
-/// Timestamp value for the geolocation context.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, geoTimestamp)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPSubjectConfiguration.m b/Snowplow/Internal/Configurations/SPSubjectConfiguration.m
deleted file mode 100644
index 8a91976a9..000000000
--- a/Snowplow/Internal/Configurations/SPSubjectConfiguration.m
+++ /dev/null
@@ -1,198 +0,0 @@
-//
-//  SPSubjectConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSubjectConfiguration.h"
-#import "NSDictionary+SP_TypeMethods.h"
-
-
-@interface SPSize ()
-
-@property (readwrite) NSInteger width;
-@property (readwrite) NSInteger height;
-
-@end
-
-@implementation SPSize
-
-- initWithWidth:(NSInteger)width height:(NSInteger)height {
-    if (self = [super init]) {
-        self.width = width;
-        self.height = height;
-    }
-    return self;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeInteger:self.width forKey:SP_STR_PROP(width)];
-    [coder encodeInteger:self.height forKey:SP_STR_PROP(height)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.width = [coder decodeIntegerForKey:SP_STR_PROP(width)];
-        self.height = [coder decodeIntegerForKey:SP_STR_PROP(height)];
-    }
-    return self;
-}
-
-@end
-
-
-@implementation SPSubjectConfiguration
-
-@synthesize userId;
-@synthesize networkUserId;
-@synthesize domainUserId;
-@synthesize useragent;
-@synthesize ipAddress;
-@synthesize timezone;
-@synthesize language;
-@synthesize screenResolution;
-@synthesize screenViewPort;
-@synthesize colorDepth;
-
-@synthesize geoLatitude;
-@synthesize geoLongitude;
-@synthesize geoLatitudeLongitudeAccuracy;
-@synthesize geoAltitude;
-@synthesize geoAltitudeAccuracy;
-@synthesize geoSpeed;
-@synthesize geoBearing;
-@synthesize geoTimestamp;
-
-- (instancetype)initWithDictionary:(NSDictionary<NSString *,NSObject *> *)dictionary {
-    if (self = [self init]) {
-        self.userId = [dictionary sp_stringForKey:SP_STR_PROP(userId) defaultValue:self.userId];
-        self.networkUserId = [dictionary sp_stringForKey:SP_STR_PROP(networkUserId) defaultValue:self.networkUserId];
-        self.domainUserId = [dictionary sp_stringForKey:SP_STR_PROP(domainUserId) defaultValue:self.domainUserId];
-        self.useragent = [dictionary sp_stringForKey:SP_STR_PROP(useragent) defaultValue:self.useragent];
-        self.ipAddress = [dictionary sp_stringForKey:SP_STR_PROP(ipAddress) defaultValue:self.ipAddress];
-        self.timezone = [dictionary sp_stringForKey:SP_STR_PROP(timezone) defaultValue:self.timezone];
-        self.language = [dictionary sp_stringForKey:SP_STR_PROP(language) defaultValue:self.language];
-    }
-    return self;
-}
-
-// MARK: - Builder
-
-SP_BUILDER_METHOD(NSString *, userId)
-SP_BUILDER_METHOD(NSString *, networkUserId)
-SP_BUILDER_METHOD(NSString *, domainUserId)
-SP_BUILDER_METHOD(NSString *, useragent)
-SP_BUILDER_METHOD(NSString *, ipAddress)
-SP_BUILDER_METHOD(NSString *, timezone)
-SP_BUILDER_METHOD(NSString *, language)
-SP_BUILDER_METHOD(SPSize *, screenResolution)
-SP_BUILDER_METHOD(SPSize *, screenViewPort)
-SP_BUILDER_METHOD(NSNumber *, colorDepth)
-
-// geolocation
-SP_BUILDER_METHOD(NSNumber *, geoLatitude)
-SP_BUILDER_METHOD(NSNumber *, geoLongitude)
-SP_BUILDER_METHOD(NSNumber *, geoLatitudeLongitudeAccuracy)
-SP_BUILDER_METHOD(NSNumber *, geoAltitude)
-SP_BUILDER_METHOD(NSNumber *, geoAltitudeAccuracy)
-SP_BUILDER_METHOD(NSNumber *, geoBearing)
-SP_BUILDER_METHOD(NSNumber *, geoSpeed)
-SP_BUILDER_METHOD(NSNumber *, geoTimestamp)
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPSubjectConfiguration *copy = [[SPSubjectConfiguration allocWithZone:zone] init];
-    copy.userId = self.userId;
-    copy.networkUserId = self.networkUserId;
-    copy.domainUserId = self.domainUserId;
-    copy.useragent = self.useragent;
-    copy.ipAddress = self.ipAddress;
-    copy.timezone = self.timezone;
-    copy.language = self.language;
-    copy.screenResolution = self.screenResolution;
-    copy.screenViewPort = self.screenViewPort;
-    copy.colorDepth = self.colorDepth;
-
-    // geolocation
-    copy.geoLatitude = self.geoLatitude;
-    copy.geoLongitude = self.geoLongitude;
-    copy.geoLatitudeLongitudeAccuracy = self.geoLatitudeLongitudeAccuracy;
-    copy.geoAltitude = self.geoAltitude;
-    copy.geoAltitudeAccuracy = self.geoAltitudeAccuracy;
-    copy.geoSpeed = self.geoSpeed;
-    copy.geoBearing = self.geoBearing;
-    copy.geoTimestamp = self.geoTimestamp;
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeObject:self.userId forKey:SP_STR_PROP(userId)];
-    [coder encodeObject:self.networkUserId forKey:SP_STR_PROP(networkUserId)];
-    [coder encodeObject:self.domainUserId forKey:SP_STR_PROP(domainUserId)];
-    [coder encodeObject:self.useragent forKey:SP_STR_PROP(useragent)];
-    [coder encodeObject:self.ipAddress forKey:SP_STR_PROP(ipAddress)];
-    [coder encodeObject:self.timezone forKey:SP_STR_PROP(timezone)];
-    [coder encodeObject:self.language forKey:SP_STR_PROP(language)];
-    [coder encodeObject:self.screenResolution forKey:SP_STR_PROP(screenResolution)];
-    [coder encodeObject:self.screenViewPort forKey:SP_STR_PROP(screenViewPort)];
-    [coder encodeObject:self.colorDepth forKey:SP_STR_PROP(colorDepth)];
-    // geolocation
-    [coder encodeObject:self.geoLatitude forKey:SP_STR_PROP(geoLatitude)];
-    [coder encodeObject:self.geoLongitude forKey:SP_STR_PROP(geoLongitude)];
-    [coder encodeObject:self.geoLatitudeLongitudeAccuracy forKey:SP_STR_PROP(geoLatitudeLongitudeAccuracy)];
-    [coder encodeObject:self.geoAltitude forKey:SP_STR_PROP(geoAltitude)];
-    [coder encodeObject:self.geoAltitudeAccuracy forKey:SP_STR_PROP(geoAltitudeAccuracy)];
-    [coder encodeObject:self.geoSpeed forKey:SP_STR_PROP(geoSpeed)];
-    [coder encodeObject:self.geoBearing forKey:SP_STR_PROP(geoBearing)];
-    [coder encodeObject:self.geoTimestamp forKey:SP_STR_PROP(geoTimestamp)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.userId = [coder decodeObjectForKey:SP_STR_PROP(userId)];
-        self.networkUserId = [coder decodeObjectForKey:SP_STR_PROP(networkUserId)];
-        self.domainUserId = [coder decodeObjectForKey:SP_STR_PROP(domainUserId)];
-        self.useragent = [coder decodeObjectForKey:SP_STR_PROP(useragent)];
-        self.ipAddress = [coder decodeObjectForKey:SP_STR_PROP(ipAddress)];
-        self.timezone = [coder decodeObjectForKey:SP_STR_PROP(timezone)];
-        self.language = [coder decodeObjectForKey:SP_STR_PROP(language)];
-        self.screenResolution = [coder decodeObjectForKey:SP_STR_PROP(screenResolution)];
-        self.screenViewPort = [coder decodeObjectForKey:SP_STR_PROP(screenViewPort)];
-        self.colorDepth = [coder decodeObjectForKey:SP_STR_PROP(colorDepth)];
-        // geolocation
-        self.geoLatitude = [coder decodeObjectForKey:SP_STR_PROP(geoLatitude)];
-        self.geoLongitude = [coder decodeObjectForKey:SP_STR_PROP(geoLongitude)];
-        self.geoLatitudeLongitudeAccuracy = [coder decodeObjectForKey:SP_STR_PROP(geoLatitudeLongitudeAccuracy)];
-        self.geoAltitude = [coder decodeObjectForKey:SP_STR_PROP(geoAltitude)];
-        self.geoAltitudeAccuracy = [coder decodeObjectForKey:SP_STR_PROP(geoAltitudeAccuracy)];
-        self.geoSpeed = [coder decodeObjectForKey:SP_STR_PROP(geoSpeed)];
-        self.geoBearing = [coder decodeObjectForKey:SP_STR_PROP(geoBearing)];
-        self.geoTimestamp = [coder decodeObjectForKey:SP_STR_PROP(geoTimestamp)];
-    }
-    return self;
-}
-
-@end
-
diff --git a/Snowplow/Internal/Configurations/SPTrackerConfiguration.h b/Snowplow/Internal/Configurations/SPTrackerConfiguration.h
deleted file mode 100644
index a85775372..000000000
--- a/Snowplow/Internal/Configurations/SPTrackerConfiguration.h
+++ /dev/null
@@ -1,217 +0,0 @@
-//
-//  SPTrackerConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-#import "SPDevicePlatform.h"
-#import "SPLoggerDelegate.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(TrackerConfigurationProtocol)
-@protocol SPTrackerConfigurationProtocol
-
-/**
- * Identifer of the app.
- */
-@property () NSString *appId;
-
-/**
- * It sets the device platform the tracker is running on.
- */
-@property () SPDevicePlatform devicePlatform;
-/**
- * It indicates whether the JSON data in the payload should be base64 encoded.
- */
-@property () BOOL base64Encoding;
-
-/**
- * It sets the log level of tracker logs.
- */
-@property () SPLogLevel logLevel;
-/**
- * It sets the logger delegate that receive logs from the tracker.
- */
-@property (nullable) id<SPLoggerDelegate> loggerDelegate;
-
-/**
- * Whether application context is sent with all the tracked events.
- */
-@property () BOOL applicationContext;
-/**
- * Whether mobile/platform context is sent with all the tracked events.
- */
-@property () BOOL platformContext;
-/**
- * Whether geo-location context is sent with all the tracked events.
- */
-@property () BOOL geoLocationContext;
-/**
- * Whether session context is sent with all the tracked events.
- */
-@property () BOOL sessionContext;
-/**
- * Whether deepLink context is sent with all the ScreenView events.
- */
-@property () BOOL deepLinkContext;
-/**
- * Whether screen context is sent with all the tracked events.
- */
-@property () BOOL screenContext;
-/**
- * Whether enable automatic tracking of ScreenView events.
- */
-@property () BOOL screenViewAutotracking;
-/**
- * Whether enable automatic tracking of background and foreground transitions.
- */
-@property () BOOL lifecycleAutotracking;
-/**
- * Whether enable automatic tracking of install event.
- */
-@property () BOOL installAutotracking;
-/**
- * Whether enable crash reporting.
- */
-@property () BOOL exceptionAutotracking;
-/**
- * Whether enable diagnostic reporting.
- */
-@property () BOOL diagnosticAutotracking;
-/**
- * Decorate the v_tracker field in the tracker protocol.
- * @note Do not use. Internal use only.
- */
-@property (nonatomic, nullable) NSString *trackerVersionSuffix;
-/**
- * Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
- * Setting this property on a running tracker instance starts a new session (if sessions are tracked).
- */
-@property () BOOL userAnonymisation;
-
-@end
-
-/**
- * This class represents the configuration of the tracker and the core tracker properties.
- * The TrackerConfiguration can be used to setup the tracker behaviour indicating what should be
- * tracked in term of automatic tracking and contexts/entities to track with the events.
- */
-NS_SWIFT_NAME(TrackerConfiguration)
-@interface SPTrackerConfiguration : SPConfiguration <SPTrackerConfigurationProtocol>
-
-/**
- * It sets a default TrackerConfiguration.
- * Default values:
- *         devicePlatform = DevicePlatform.Mobile;
- *         base64encoding = true;
- *         logLevel = LogLevel.OFF;
- *         loggerDelegate = null;
- *         sessionContext = true;
- *         deepLinkContext = true;
- *         applicationContext = true;
- *         platformContext = true;
- *         geoLocationContext = false;
- *         screenContext = true;
- *         screenViewAutotracking = true;
- *         lifecycleAutotracking = true;
- *         installAutotracking = true;
- *         exceptionAutotracking = true;
- *         diagnosticAutotracking = false;
- *         userAnonymisation = false;
- */
-- (instancetype)init;
-
-/**
- * Identifer of the app.
- */
-SP_BUILDER_DECLARE(NSString *, appId)
-/**
- * It sets the device platform the tracker is running on.
- */
-SP_BUILDER_DECLARE(SPDevicePlatform, devicePlatform)
-/**
- * It indicates whether the JSON data in the payload should be base64 encoded.
- */
-SP_BUILDER_DECLARE(BOOL, base64Encoding)
-/**
- * It sets the log level of tracker logs.
- */
-SP_BUILDER_DECLARE(SPLogLevel, logLevel)
-/**
- * It sets the logger delegate that receive logs from the tracker.
- */
-SP_BUILDER_DECLARE_NULLABLE(id<SPLoggerDelegate>, loggerDelegate)
-/**
- * Whether application context is sent with all the tracked events.
- */
-SP_BUILDER_DECLARE(BOOL, applicationContext)
-/**
- * Whether mobile/platform context is sent with all the tracked events.
- */
-SP_BUILDER_DECLARE(BOOL, platformContext)
-/**
- * Whether geo-location context is sent with all the tracked events.
- */
-SP_BUILDER_DECLARE(BOOL, geoLocationContext)
-/**
- * Whether session context is sent with all the tracked events.
- */
-SP_BUILDER_DECLARE(BOOL, sessionContext)
-/**
- * Whether screen context is sent with all the tracked events.
- */
-SP_BUILDER_DECLARE(BOOL, screenContext)
-/**
- * Whether deepLink context is sent with all the ScreenView events.
- */
-SP_BUILDER_DECLARE(BOOL, deepLinkContext)
-/**
- * Whether enable automatic tracking of ScreenView events.
- */
-SP_BUILDER_DECLARE(BOOL, screenViewAutotracking)
-/**
- * Whether enable automatic tracking of background and foreground transitions.
- */
-SP_BUILDER_DECLARE(BOOL, lifecycleAutotracking)
-/**
- * Whether enable automatic tracking of install event.
- */
-SP_BUILDER_DECLARE(BOOL, installAutotracking)
-/**
- * Whether enable crash reporting.
- */
-SP_BUILDER_DECLARE(BOOL, exceptionAutotracking)
-/**
- * Whether enable diagnostic reporting.
- */
-SP_BUILDER_DECLARE(BOOL, diagnosticAutotracking)
-/**
- * Internal use only.
- */
-SP_BUILDER_DECLARE(NSString *, trackerVersionSuffix)
-/**
- * Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
- */
-SP_BUILDER_DECLARE(BOOL, userAnonymisation)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Configurations/SPTrackerConfiguration.m b/Snowplow/Internal/Configurations/SPTrackerConfiguration.m
deleted file mode 100644
index 3e395a228..000000000
--- a/Snowplow/Internal/Configurations/SPTrackerConfiguration.m
+++ /dev/null
@@ -1,199 +0,0 @@
-//
-//  SPTrackerConfiguration.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConfiguration.h"
-#import "NSDictionary+SP_TypeMethods.h"
-
-@implementation SPTrackerConfiguration
-
-@synthesize appId;
-@synthesize devicePlatform;
-@synthesize base64Encoding;
-@synthesize logLevel;
-@synthesize loggerDelegate;
-@synthesize applicationContext;
-@synthesize platformContext;
-@synthesize geoLocationContext;
-@synthesize sessionContext;
-@synthesize screenContext;
-@synthesize deepLinkContext;
-@synthesize screenViewAutotracking;
-@synthesize lifecycleAutotracking;
-@synthesize installAutotracking;
-@synthesize exceptionAutotracking;
-@synthesize diagnosticAutotracking;
-@synthesize trackerVersionSuffix;
-@synthesize userAnonymisation;
-
-- (instancetype)initWithDictionary:(NSDictionary<NSString *,NSObject *> *)dictionary {
-    if (self = [self init]) {
-        self.appId = [dictionary sp_stringForKey:SP_STR_PROP(appId) defaultValue:self.appId];
-        NSString *devicePlatform = [dictionary sp_stringForKey:SP_STR_PROP(appId) defaultValue:nil];
-        if (devicePlatform) {
-            self.devicePlatform = SPStringToDevicePlatform(devicePlatform);
-        }
-        // TODO: Uniform "base64encoding" string on both Android and iOS trackers
-        self.base64Encoding = [dictionary sp_boolForKey:@"base64encoding" defaultValue:self.base64Encoding];
-        NSString *logLevel = [dictionary sp_stringForKey:SP_STR_PROP(logLevel) defaultValue:nil];
-        if (logLevel) {
-            NSUInteger index = [@[@"off", @"error", @"debug", @"verbose"] indexOfObject:logLevel];
-            self.logLevel = index != NSNotFound ? index : SPLogLevelOff;
-        }
-        self.sessionContext = [dictionary sp_boolForKey:SP_STR_PROP(sessionContext) defaultValue:self.sessionContext];
-        self.applicationContext = [dictionary sp_boolForKey:SP_STR_PROP(applicationContext) defaultValue:self.applicationContext];
-        self.platformContext = [dictionary sp_boolForKey:SP_STR_PROP(platformContext) defaultValue:self.platformContext];
-        self.geoLocationContext = [dictionary sp_boolForKey:SP_STR_PROP(geoLocationContext) defaultValue:self.geoLocationContext];
-        self.deepLinkContext = [dictionary sp_boolForKey:SP_STR_PROP(deepLinkContext) defaultValue:self.deepLinkContext];
-        self.screenContext = [dictionary sp_boolForKey:SP_STR_PROP(screenContext) defaultValue:self.screenContext];
-        self.screenViewAutotracking = [dictionary sp_boolForKey:SP_STR_PROP(screenViewAutotracking) defaultValue:self.screenViewAutotracking];
-        self.lifecycleAutotracking = [dictionary sp_boolForKey:SP_STR_PROP(lifecycleAutotracking) defaultValue:self.lifecycleAutotracking];
-        self.installAutotracking = [dictionary sp_boolForKey:SP_STR_PROP(installAutotracking) defaultValue:self.installAutotracking];
-        self.exceptionAutotracking = [dictionary sp_boolForKey:SP_STR_PROP(exceptionAutotracking) defaultValue:self.exceptionAutotracking];
-        self.diagnosticAutotracking = [dictionary sp_boolForKey:SP_STR_PROP(diagnosticAutotracking) defaultValue:self.diagnosticAutotracking];
-        self.userAnonymisation = [dictionary sp_boolForKey:SP_STR_PROP(userAnonymisation) defaultValue:self.userAnonymisation];
-    }
-    return self;
-}
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.appId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
-
-        self.devicePlatform = SPDevicePlatformMobile;
-        self.base64Encoding = YES;
-        
-        self.logLevel = SPLogLevelOff;
-        self.loggerDelegate = nil;
-
-        self.sessionContext = YES;
-        self.applicationContext = YES;
-        self.platformContext = YES;
-        self.geoLocationContext = NO;
-        self.deepLinkContext = YES;
-        self.screenContext = YES;
-        self.screenViewAutotracking = YES;
-        self.lifecycleAutotracking = NO;
-        self.installAutotracking = YES;
-        self.exceptionAutotracking = YES;
-        self.diagnosticAutotracking = NO;
-        self.userAnonymisation = NO;
-    }
-    return self;
-}
-
-// MARK: - Builder
-
-SP_BUILDER_METHOD(NSString *, appId)
-SP_BUILDER_METHOD(SPDevicePlatform, devicePlatform)
-SP_BUILDER_METHOD(BOOL, base64Encoding)
-SP_BUILDER_METHOD(SPLogLevel, logLevel)
-SP_BUILDER_METHOD(id<SPLoggerDelegate>, loggerDelegate)
-SP_BUILDER_METHOD(BOOL, applicationContext)
-SP_BUILDER_METHOD(BOOL, platformContext)
-SP_BUILDER_METHOD(BOOL, geoLocationContext)
-SP_BUILDER_METHOD(BOOL, sessionContext)
-SP_BUILDER_METHOD(BOOL, deepLinkContext)
-SP_BUILDER_METHOD(BOOL, screenContext)
-SP_BUILDER_METHOD(BOOL, screenViewAutotracking)
-SP_BUILDER_METHOD(BOOL, lifecycleAutotracking)
-SP_BUILDER_METHOD(BOOL, installAutotracking)
-SP_BUILDER_METHOD(BOOL, exceptionAutotracking)
-SP_BUILDER_METHOD(BOOL, diagnosticAutotracking)
-SP_BUILDER_METHOD(BOOL, userAnonymisation)
-SP_BUILDER_METHOD(NSString *, trackerVersionSuffix)
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPTrackerConfiguration *copy = [[SPTrackerConfiguration allocWithZone:zone] init];
-    copy.appId = self.appId;
-    copy.devicePlatform = self.devicePlatform;
-    copy.base64Encoding = self.base64Encoding;
-    copy.logLevel = self.logLevel;
-    copy.loggerDelegate = self.loggerDelegate;
-    copy.sessionContext = self.sessionContext;
-    copy.applicationContext = self.applicationContext;
-    copy.platformContext = self.platformContext;
-    copy.geoLocationContext = self.geoLocationContext;
-    copy.deepLinkContext = self.deepLinkContext;
-    copy.screenContext = self.screenContext;
-    copy.screenViewAutotracking = self.screenViewAutotracking;
-    copy.lifecycleAutotracking = self.lifecycleAutotracking;
-    copy.installAutotracking = self.installAutotracking;
-    copy.exceptionAutotracking = self.exceptionAutotracking;
-    copy.diagnosticAutotracking = self.diagnosticAutotracking;
-    copy.trackerVersionSuffix = self.trackerVersionSuffix;
-    copy.userAnonymisation = self.userAnonymisation;
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeObject:self.appId forKey:SP_STR_PROP(appId)];
-    [coder encodeInteger:self.devicePlatform forKey:SP_STR_PROP(devicePlatform)];
-    [coder encodeBool:self.base64Encoding forKey:SP_STR_PROP(base64Encoding)];
-    [coder encodeInteger:self.logLevel forKey:SP_STR_PROP(logLevel)];
-    [coder encodeObject:self.loggerDelegate forKey:SP_STR_PROP(loggerDelegate)];
-    [coder encodeBool:self.sessionContext forKey:SP_STR_PROP(sessionContext)];
-    [coder encodeBool:self.applicationContext forKey:SP_STR_PROP(applicationContext)];
-    [coder encodeBool:self.platformContext forKey:SP_STR_PROP(platformContext)];
-    [coder encodeBool:self.geoLocationContext forKey:SP_STR_PROP(geoLocationContext)];
-    [coder encodeBool:self.deepLinkContext forKey:SP_STR_PROP(deepLinkContext)];
-    [coder encodeBool:self.screenContext forKey:SP_STR_PROP(screenContext)];
-    [coder encodeBool:self.screenViewAutotracking forKey:SP_STR_PROP(screenViewAutotracking)];
-    [coder encodeBool:self.lifecycleAutotracking forKey:SP_STR_PROP(lifecycleAutotracking)];
-    [coder encodeBool:self.installAutotracking forKey:SP_STR_PROP(installAutotracking)];
-    [coder encodeBool:self.exceptionAutotracking forKey:SP_STR_PROP(exceptionAutotracking)];
-    [coder encodeBool:self.diagnosticAutotracking forKey:SP_STR_PROP(diagnosticAutotracking)];
-    [coder encodeObject:self.trackerVersionSuffix forKey:SP_STR_PROP(trackerVersionSuffix)];
-    [coder encodeBool:self.userAnonymisation forKey:SP_STR_PROP(userAnonymisation)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.appId = [coder decodeObjectForKey:SP_STR_PROP(appId)];
-        self.devicePlatform = [coder decodeIntegerForKey:SP_STR_PROP(devicePlatform)];
-        self.base64Encoding = [coder decodeBoolForKey:SP_STR_PROP(base64Encoding)];
-        self.logLevel = [coder decodeIntegerForKey:SP_STR_PROP(logLevel)];
-        self.loggerDelegate = [coder decodeObjectForKey:SP_STR_PROP(loggerDelegate)];
-        self.sessionContext = [coder decodeBoolForKey:SP_STR_PROP(sessionContext)];
-        self.applicationContext = [coder decodeBoolForKey:SP_STR_PROP(applicationContext)];
-        self.platformContext = [coder decodeBoolForKey:SP_STR_PROP(platformContext)];
-        self.geoLocationContext = [coder decodeBoolForKey:SP_STR_PROP(geoLocationContext)];
-        self.deepLinkContext = [coder decodeBoolForKey:SP_STR_PROP(deepLinkContext)];
-        self.screenContext = [coder decodeBoolForKey:SP_STR_PROP(screenContext)];
-        self.screenViewAutotracking = [coder decodeBoolForKey:SP_STR_PROP(screenViewAutotracking)];
-        self.lifecycleAutotracking = [coder decodeBoolForKey:SP_STR_PROP(lifecycleAutotracking)];
-        self.installAutotracking = [coder decodeBoolForKey:SP_STR_PROP(installAutotracking)];
-        self.exceptionAutotracking = [coder decodeBoolForKey:SP_STR_PROP(exceptionAutotracking)];
-        self.diagnosticAutotracking = [coder decodeBoolForKey:SP_STR_PROP(diagnosticAutotracking)];
-        self.trackerVersionSuffix = [coder decodeObjectForKey:SP_STR_PROP(trackerVersionSuffix)];
-        self.userAnonymisation = [coder decodeBoolForKey:SP_STR_PROP(userAnonymisation)];
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/Emitter/SPEmitter.h b/Snowplow/Internal/Emitter/SPEmitter.h
deleted file mode 100644
index a07cccb3e..000000000
--- a/Snowplow/Internal/Emitter/SPEmitter.h
+++ /dev/null
@@ -1,219 +0,0 @@
-//
-//  SPEmitter.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPNetworkConnection.h"
-#import "SPEventStore.h"
-#import "SPEmitterConfiguration.h"
-#import "SPEmitterEventProcessing.h"
-
-@protocol SPRequestCallback;
-@class SPPayload;
-
-/*!
- @brief The builder for SPEmitter.
- */
-NS_SWIFT_NAME(EmitterBuilder)
-@protocol SPEmitterBuilder <NSObject>
-
-- (void) setNamespace:(NSString *)namespace;
-
-/*!
- @brief Emitter builder method to set the collector endpoint.
- @param urlEndpoint The collector endpoint.
- */
-- (void) setUrlEndpoint:(NSString *)urlEndpoint;
-
-/*!
- @brief Emitter builder method to set HTTP method.
- @param method Should be SPHttpMethodGet or SPHttpMethodPost.
- */
-- (void) setHttpMethod:(SPHttpMethod)method;
-
-/*!
- @brief Emitter builder method to set HTTP security.
- @param protocol Should be SPProtocolHttp or SPProtocolHttps.
- */
-- (void) setProtocol:(SPProtocol)protocol;
-
-/*!
- @brief Emitter builder method to set the buffer option.
- @param bufferOption the buffer option for the emitter.
- */
-- (void) setBufferOption:(SPBufferOption)bufferOption;
-
-/*!
- @brief Emitter builder method to set callbacks.
- @param callback Called on when events have sent.
- */
-- (void) setCallback:(id<SPRequestCallback>)callback;
-
-/*!
- @brief Emitter builder method to set emit range.
- @param emitRange Number of events to pull from database.
- */
-- (void) setEmitRange:(NSInteger)emitRange;
-
-/*!
- @brief Emitter builder method to set thread pool size.
- @param emitThreadPoolSize The number of threads used by the emitter.
- */
-- (void) setEmitThreadPoolSize:(NSInteger)emitThreadPoolSize;
-
-/*!
- @brief Emitter builder method to set byte limit for GET requests.
- @param byteLimitGet Maximum event size for a GET request.
- */
-- (void) setByteLimitGet:(NSInteger)byteLimitGet;
-
-/*!
- @brief Emitter builder method to set byte limit for POST requests.
- @param byteLimitPost Maximum event size for a POST request.
- */
-- (void) setByteLimitPost:(NSInteger)byteLimitPost;
-
-/*!
- @brief Emitter builder method to set a custom POST path.
- @param customPath A custom path that is used on the endpoint to send requests.
- */
-- (void) setCustomPostPath:(NSString *)customPath;
-
-/*!
- @brief Emitter builder method to set the server anonymisation flag.
- @param serverAnonymisation  Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
- */
-- (void) setServerAnonymisation:(BOOL)serverAnonymisation;
-
-/*!
- @brief Builder method to set request headers.
- @param requestHeadersKeyValue custom headers (key, value) for http requests.
- */
-- (void) setRequestHeaders:(NSDictionary<NSString *, NSString *> *)requestHeadersKeyValue;
-
-/*!
- @brief Emitter builder method to set SPNetworkConnection component.
- @param networkConnection The component in charge for sending events to the collector.
- */
-- (void) setNetworkConnection:(id<SPNetworkConnection>)networkConnection;
-
-/*!
- @brief Emitter builder method to set SPEventStore component.
- @param eventStore The component in charge for persisting events before sending.
- */
-- (void) setEventStore:(id<SPEventStore>)eventStore;
-
-/**
- @brief Set a custom retry rules for HTTP status codes received in emit responses from the Collector.
- 
- @param customRetryForStatusCodes Mapping of integers (status codes) to booleans (true for retry and false for not retry)
- */
-- (void) setCustomRetryForStatusCodes:(NSDictionary<NSNumber *, NSNumber *> *)customRetryForStatusCodes;
-
-@end
-
-/*!
- This class sends events to the collector.
- */
-NS_SWIFT_NAME(Emitter)
-@interface SPEmitter : NSObject <SPEmitterBuilder, SPEmitterEventProcessing>
-
-@property (readonly, nonatomic) NSString *namespace;
-/*! @brief Chosen HTTP method - SPHttpMethodGet or SPHttpMethodPost. */
-@property (readonly, nonatomic) SPHttpMethod httpMethod;
-/*! @brief Security of requests - SPProtocolHttp or SPProtocolHttps.  */
-@property (readonly, nonatomic) SPProtocol protocol;
-/*! @brief Buffer option */
-@property (readonly, nonatomic) SPBufferOption bufferOption;
-/*! @brief Collector endpoint. */
-@property (readonly, nonatomic, retain) NSURL *urlEndpoint;
-/*! @brief Number of events retrieved from the database when needed. */
-@property (readonly, nonatomic) NSInteger emitRange;
-/*! @brief Number of threads used for emitting events. */
-@property (readonly, nonatomic) NSInteger emitThreadPoolSize;
-/*! @brief Byte limit for GET requests. */
-@property (readonly, nonatomic) NSInteger byteLimitGet;
-/*! @brief Byte limit for POST requests. */
-@property (readonly, nonatomic) NSInteger byteLimitPost;
-/*! @brief Callbacks supplied with number of failures and successes of sent events. */
-@property (readonly, nonatomic, weak) id<SPRequestCallback> callback;
-/*! @brief Custom endpoint path for POST requests. */
-@property (readonly, nonatomic) NSString *customPostPath;
-/*! @brief Custom header requests. */
-@property (readonly, nonatomic) NSDictionary<NSString *, NSString *> *requestHeaders;
-/*! @brief Custom NetworkConnection istance to handle connection outside the emitter. */
-@property (readonly, nonatomic) id<SPNetworkConnection> networkConnection;
-/*! @brief Custom retry rules for HTTP status codes. */
-@property (readonly, nonatomic) NSDictionary<NSNumber *, NSNumber *> *customRetryForStatusCodes;
-/*! @brief Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`. */
-@property (readonly, nonatomic) BOOL serverAnonymisation;
-
-/*!
- @brief Builds the emitter using a build block of functions.
- */
-+ (instancetype) build:(void(^)(id<SPEmitterBuilder>builder))buildBlock;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-/*!
- @brief Insert a Payload object into the buffer to be sent to collector.
-
- This method will add the payload to the database and flush (send all events).
- @param eventPayload A Payload containing a completed event to be added into the buffer.
- */
-- (void)addPayloadToBuffer:(SPPayload *)eventPayload;
-
-/*!
- @brief Empties the buffer of events using the respective HTTP request method.
- */
-- (void)flush;
-
-/*!
- @brief Starts timer for periodically sending events to collector.
- */
-- (void)resumeTimer;
-
-/*!
- @brief Suspends timer for periodically sending events to collector.
- */
-- (void)pauseTimer;
-
-/*!
- @brief Allows sending events to collector.
- */
-- (void)resumeEmit;
-
-/*!
- @brief Suspends sending events to collector.
- */
-- (void)pauseEmit;
-
-/*!
- @brief Returns the number of events in the DB.
- */
-- (NSUInteger) getDbCount;
-
-/*!
- @brief Returns whether the emitter is currently sending.
- */
-- (BOOL) getSendingStatus;
-
-@end
diff --git a/Snowplow/Internal/Emitter/SPEmitter.m b/Snowplow/Internal/Emitter/SPEmitter.m
deleted file mode 100644
index 89bd91d00..000000000
--- a/Snowplow/Internal/Emitter/SPEmitter.m
+++ /dev/null
@@ -1,464 +0,0 @@
-//
-//  SPEmitter.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPEmitter.h"
-#import "SPSQLiteEventStore.h"
-#import "SPMemoryEventStore.h"
-#import "SPDefaultNetworkConnection.h"
-#import "SPEventStore.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "SPRequestResult.h"
-#import "SPWeakTimerTarget.h"
-#import "SPRequestCallback.h"
-#import "SPRequest.h"
-#import "SPLogger.h"
-
-@implementation SPEmitter {
-    id<SPEventStore> _eventStore;
-    id<SPNetworkConnection> _networkConnection;
-    SPBufferOption     _bufferOption;
-    NSString *         _url;
-    NSTimer *          _timer;
-    BOOL               _isSending;
-    NSOperationQueue * _dataOperationQueue;
-    BOOL               _builderFinished;
-    NSString *         _namespace;
-    BOOL               _pausedEmit;
-}
-
-const NSUInteger POST_WRAPPER_BYTES = 88;
-
-// SnowplowEmitter Builder
-
-+ (instancetype) build:(void(^)(id<SPEmitterBuilder>builder))buildBlock {
-    SPEmitter* emitter = [[SPEmitter alloc] initWithDefaultValues];
-    if (buildBlock) {
-        buildBlock(emitter);
-    }
-    [emitter setup];
-    return emitter;
-}
-
-- (instancetype) initWithDefaultValues {
-    self = [super init];
-    if (self) {
-        _namespace = nil;
-        _httpMethod = SPHttpMethodPost;
-        _protocol = SPProtocolHttps;
-        _bufferOption = SPBufferOptionDefaultGroup;
-        _callback = nil;
-        _emitRange = 150;
-        _emitThreadPoolSize = 15;
-        _byteLimitGet = 40000;
-        _byteLimitPost = 40000;
-        _isSending = NO;
-        _dataOperationQueue = [[NSOperationQueue alloc] init];
-        _builderFinished = NO;
-        _customPostPath = nil;
-        _requestHeaders = nil;
-        _eventStore = nil;
-        _networkConnection = nil;
-        _pausedEmit = NO;
-        _customRetryForStatusCodes = @{};
-        _serverAnonymisation = NO;
-    }
-    return self;
-}
-
-- (void) setup {
-    _dataOperationQueue.maxConcurrentOperationCount = _emitThreadPoolSize;
-    [self setupNetworkConnection];
-    [self resumeTimer];
-    _builderFinished = YES;
-}
-
-- (void)setupNetworkConnection {
-    if (!_builderFinished && _networkConnection) {
-        return;
-    }
-    __weak __typeof__(self) weakSelf = self;
-    _networkConnection = [SPDefaultNetworkConnection build:^(id<SPDefaultNetworkConnectionBuilder> builder) {
-        __typeof__(self) strongSelf = weakSelf;
-        if (!strongSelf) return;
-        NSString *endpoint = strongSelf->_url ? strongSelf->_url : @""; // use empty string in case nil to avoid crashing
-        if (![endpoint hasPrefix:@"http"]) {
-            NSString *protocol = strongSelf->_protocol == SPProtocolHttps ? @"https://" : @"http://";
-            endpoint = [protocol stringByAppendingString:endpoint];
-        }
-        [builder setUrlEndpoint:endpoint];
-        [builder setCustomPostPath:strongSelf->_customPostPath];
-        [builder setHttpMethod:strongSelf->_httpMethod];
-        [builder setRequestHeaders:strongSelf->_requestHeaders];
-        [builder setEmitThreadPoolSize:strongSelf->_emitThreadPoolSize];
-        [builder setByteLimitGet:strongSelf->_byteLimitGet];
-        [builder setByteLimitPost:strongSelf->_byteLimitPost];
-        [builder setServerAnonymisation:strongSelf->_serverAnonymisation];
-    }];
-}
-
-// MARK: - Builder methods
-
-- (void)setNamespace:(NSString *)namespace {
-    _namespace = namespace;
-    if (_builderFinished && !_eventStore) {
-#if TARGET_OS_TV || TARGET_OS_WATCH
-        _eventStore = [[SPMemoryEventStore alloc] init];
-#else
-        _eventStore = [[SPSQLiteEventStore alloc] initWithNamespace:_namespace];
-#endif
-    }
-}
-
-- (void) setUrlEndpoint:(NSString *)urlEndpoint {
-    _url = urlEndpoint;
-    if (_builderFinished) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void) setHttpMethod:(SPHttpMethod)method {
-    _httpMethod = method;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void) setProtocol:(SPProtocol)protocol {
-    _protocol = protocol;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void) setBufferOption:(SPBufferOption)bufferOption {
-    if (![self getSendingStatus]) {
-        _bufferOption = bufferOption;
-    }
-}
-
-- (void) setCallback:(id<SPRequestCallback>)callback {
-    _callback = callback;
-}
-
-- (void) setEmitRange:(NSInteger)emitRange {
-    if (emitRange > 0) {
-        _emitRange = emitRange;
-    }
-}
-
-- (void) setEmitThreadPoolSize:(NSInteger)emitThreadPoolSize {
-    if (emitThreadPoolSize > 0) {
-        _emitThreadPoolSize = emitThreadPoolSize;
-        if (_dataOperationQueue.maxConcurrentOperationCount != emitThreadPoolSize) {
-            _dataOperationQueue.maxConcurrentOperationCount = _emitThreadPoolSize;
-        }
-        if (_builderFinished && _networkConnection) {
-            [self setupNetworkConnection];
-        }
-    }
-}
-
-- (void) setByteLimitGet:(NSInteger)byteLimitGet {
-    _byteLimitGet = byteLimitGet;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void) setByteLimitPost:(NSInteger)byteLimitPost {
-    _byteLimitPost = byteLimitPost;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void) setServerAnonymisation:(BOOL)serverAnonymisation {
-    _serverAnonymisation = serverAnonymisation;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void) setCustomPostPath:(NSString *)customPath {
-    _customPostPath = customPath;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void) setRequestHeaders:(NSDictionary<NSString *, NSString *> *)requestHeaders {
-    _requestHeaders = requestHeaders;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void)setNetworkConnection:(id<SPNetworkConnection>)networkConnection {
-    _networkConnection = networkConnection;
-    if (_builderFinished && _networkConnection) {
-        [self setupNetworkConnection];
-    }
-}
-
-- (void)setEventStore:(id<SPEventStore>)eventStore {
-    if (!_builderFinished || !_eventStore || [_eventStore count] == 0 ) {
-        _eventStore = eventStore;
-    }
-}
-
-- (void)setCustomRetryForStatusCodes:(NSDictionary<NSNumber *, NSNumber *> *)customRetryForStatusCodes {
-    _customRetryForStatusCodes = customRetryForStatusCodes ?: @{};
-}
-
-// MARK: - Pause/Resume methods
-
-- (void)resumeTimer {
-    __weak __typeof__(self) weakSelf = self;
-    
-    if (_timer != nil) {
-        [self pauseTimer];
-    }
-    
-    dispatch_async(dispatch_get_main_queue(), ^{
-        __typeof__(self) strongSelf = weakSelf;
-        if (strongSelf == nil) return;
-        
-        strongSelf->_timer = [NSTimer scheduledTimerWithTimeInterval:kSPDefaultBufferTimeout
-                                                              target:[[SPWeakTimerTarget alloc] initWithTarget:strongSelf andSelector:@selector(flush)]
-                                                            selector:@selector(timerFired:)
-                                                            userInfo:nil
-                                                             repeats:YES];
-    });
-}
-
-- (void)pauseTimer {
-    [_timer invalidate];
-    _timer = nil;
-}
-
-- (void)resumeEmit {
-    _pausedEmit = NO;
-    [self flush];
-}
-
-- (void)pauseEmit {
-    _pausedEmit = YES;
-}
-
-- (void)addPayloadToBuffer:(SPPayload *)eventPayload {
-    __weak __typeof__(self) weakSelf = self;
-    
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        __typeof__(self) strongSelf = weakSelf;
-        if (strongSelf == nil) return;
-        
-        [strongSelf->_eventStore addEvent:eventPayload];
-        [strongSelf flush];
-    });
-}
-
-- (void)flush {
-    if ([NSThread isMainThread]) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-            [self sendGuard];
-        });
-    } else {
-        [self sendGuard];
-    }
-}
-
-// MARK: - Control methods
-
-- (void) sendGuard {
-    if (_isSending || _pausedEmit) {
-        return;
-    }
-    @synchronized (self) {
-        if (!_isSending && !_pausedEmit) {
-            _isSending = YES;
-            @try {
-                [self attemptEmit];
-            } @catch (NSException *exception) {
-                SPLogError(@"Received exception during emission process: %@", exception);
-                _isSending = NO;
-            }
-        }
-    }
-}
-
-- (void)attemptEmit {
-    if (!_eventStore.count) {
-        SPLogDebug(@"Database empty. Returning.", nil);
-        _isSending = NO;
-        return;
-    }
-    
-    NSArray<SPEmitterEvent *> *events = [_eventStore emittableEventsWithQueryLimit:_emitRange];
-    NSArray<SPRequest *> *requests = [self buildRequestsFromEvents:events];
-    NSArray<SPRequestResult *> *sendResults = [_networkConnection sendRequests:requests];
-    
-    SPLogVerbose(@"Processing emitter results.");
-    
-    NSInteger successCount = 0;
-    NSInteger failedWillRetryCount = 0;
-    NSInteger failedWontRetryCount = 0;
-    NSMutableArray<NSNumber *> *removableEvents = [NSMutableArray new];
-    
-    for (SPRequestResult *result in sendResults) {
-        NSArray<NSNumber *> *resultIndexArray = result.storeIds;
-        if (result.isSuccessful) {
-            successCount += resultIndexArray.count;
-            [removableEvents addObjectsFromArray:resultIndexArray];
-        } else if ([result shouldRetry:_customRetryForStatusCodes]) {
-            failedWillRetryCount += resultIndexArray.count;
-        } else {
-            failedWontRetryCount += resultIndexArray.count;
-            [removableEvents addObjectsFromArray:resultIndexArray];
-            SPLogError(@"Sending events to Collector failed with status %ld. Events will be dropped.", (long)[result statusCode]);
-        }
-    }
-    NSInteger allFailureCount = failedWillRetryCount + failedWontRetryCount;
-    
-    [_eventStore removeEventsWithIds:removableEvents];
-    
-    SPLogDebug(@"Success Count: %@", [@(successCount) stringValue]);
-    SPLogDebug(@"Failure Count: %@", [@(allFailureCount) stringValue]);
-    
-    if (_callback != nil) {
-        if (allFailureCount == 0) {
-            [_callback onSuccessWithCount:successCount];
-        } else {
-            [_callback onFailureWithCount:allFailureCount successCount:successCount];
-        }
-    }
-    
-    if (failedWillRetryCount > 0 && successCount == 0) {
-        SPLogDebug(@"Ending emitter run as all requests failed.", nil);
-        [NSThread sleepForTimeInterval:5];
-        _isSending = NO;
-        return;
-    } else {
-        [self attemptEmit];
-    }
-}
-
-- (NSArray<SPRequest *> *)buildRequestsFromEvents:(NSArray<SPEmitterEvent *> *)events {
-    NSMutableArray<SPRequest *> *requests = [NSMutableArray new];
-    NSNumber *sendingTime = [SPUtilities getTimestamp];
-    SPHttpMethod httpMethod = _networkConnection.httpMethod;
-    
-    if (httpMethod == SPHttpMethodGet) {
-        for (SPEmitterEvent *event in events) {
-            SPPayload *payload = event.payload;
-            [self addSendingTimeToPayload:payload timestamp:sendingTime];
-            BOOL oversize = [self isOversize:payload];
-            SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:event.storeId oversize:oversize];
-            [requests addObject:request];
-        }
-    } else {
-        for (int i = 0; i < events.count; i += _bufferOption) {
-            NSMutableArray<SPPayload *> *eventArray = [NSMutableArray new];
-            NSMutableArray<NSNumber *> *indexArray = [NSMutableArray new];
-            
-            for (int j = i; j < (i + _bufferOption) && j < events.count; j++) {
-                SPEmitterEvent *event = events[j];
-                
-                SPPayload *payload = event.payload;
-                NSNumber *emitterEventId = @(event.storeId);
-                [self addSendingTimeToPayload:payload timestamp:sendingTime];
-
-                if ([self isOversize:payload]) {
-                    SPRequest *request = [[SPRequest alloc] initWithPayload:payload emitterEventId:emitterEventId.longLongValue oversize:YES];
-                    [requests addObject:request];
-
-                } else if ([self isOversize:payload previousPayloads:eventArray]) {
-                    SPRequest *request = [[SPRequest alloc] initWithPayloads:eventArray emitterEventIds:indexArray];
-                    [requests addObject:request];
-
-                    // Clear collection and build a new POST
-                    eventArray = [NSMutableArray new];
-                    indexArray = [NSMutableArray new];
-                    
-                    // Build and store the request
-                    [eventArray addObject:payload];
-                    [indexArray addObject:emitterEventId];
-                    
-                } else {
-                    // Add event to collections
-                    [eventArray addObject:payload];
-                    [indexArray addObject:emitterEventId];
-                }
-            }
-            
-            // Check if all payloads have been processed
-            if (eventArray.count) {
-                SPRequest *request = [[SPRequest alloc] initWithPayloads:eventArray emitterEventIds:indexArray];
-                [requests addObject:request];
-            }
-        }
-    }
-    return requests;
-}
-
-- (BOOL)isOversize:(SPPayload *)payload {
-    return [self isOversize:payload previousPayloads:[NSArray array]];
-}
-
-- (BOOL)isOversize:(SPPayload *)payload previousPayloads:(NSArray<SPPayload *> *)previousPayloads {
-    NSUInteger byteLimit = _networkConnection.httpMethod == SPHttpMethodGet ? _byteLimitGet : _byteLimitPost;
-    return [self isOversize:payload byteLimit:byteLimit previousPayloads:previousPayloads];
-}
-
-- (BOOL)isOversize:(SPPayload *)payload byteLimit:(NSUInteger)byteLimit previousPayloads:(NSArray<SPPayload *> *)previousPayloads {
-    NSUInteger totalByteSize = payload.byteSize;
-    for (SPPayload *previousPayload in previousPayloads) {
-        totalByteSize += previousPayload.byteSize;
-    }
-    NSUInteger wrapperBytes = previousPayloads.count > 0 ? (previousPayloads.count + POST_WRAPPER_BYTES) : 0;
-    return totalByteSize + wrapperBytes > byteLimit;
-}
-
-- (void)addSendingTimeToPayload:(SPPayload *)payload timestamp:(NSNumber *)timestamp {
-    [payload addValueToPayload:[NSString stringWithFormat:@"%lld", timestamp.longLongValue] forKey:kSPSentTimestamp];
-}
-
-// MARK: - Getters
-
-- (NSURL *)urlEndpoint {
-    return _networkConnection.url;
-}
-
-- (NSUInteger) getDbCount {
-    return [_eventStore count];
-}
-
-- (BOOL) getSendingStatus {
-    return _isSending;
-}
-
-- (void) dealloc {
-    [self pauseTimer];
-}
-
-@end
diff --git a/Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.h b/Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.h
deleted file mode 100644
index bef557f47..000000000
--- a/Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.h
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-//  SPEmitterConfigurationUpdate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEmitterConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPEmitterConfigurationUpdate : SPEmitterConfiguration
-
-@property (nonatomic, nullable) SPEmitterConfiguration *sourceConfig;
-
-@property (nonatomic) BOOL isPaused;
-
-SP_DIRTYFLAG(bufferOption)
-SP_DIRTYFLAG(byteLimitGet)
-SP_DIRTYFLAG(byteLimitPost)
-SP_DIRTYFLAG(emitRange)
-SP_DIRTYFLAG(threadPoolSize)
-SP_DIRTYFLAG(customRetryForStatusCodes)
-SP_DIRTYFLAG(serverAnonymisation)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.m b/Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.m
deleted file mode 100644
index 52d017fba..000000000
--- a/Snowplow/Internal/Emitter/SPEmitterConfigurationUpdate.m
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  SPEmitterConfigurationUpdate.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEmitterConfigurationUpdate.h"
-
-@implementation SPEmitterConfigurationUpdate
-
-- (id<SPEventStore>)eventStore { return self.sourceConfig.eventStore; }
-- (id<SPRequestCallback>)requestCallback { return self.sourceConfig.requestCallback; }
-
-SP_DIRTY_GETTER(SPBufferOption, bufferOption)
-SP_DIRTY_GETTER(NSInteger, emitRange)
-SP_DIRTY_GETTER(NSInteger, threadPoolSize)
-SP_DIRTY_GETTER(NSInteger, byteLimitGet)
-SP_DIRTY_GETTER(NSInteger, byteLimitPost)
-SP_DIRTY_GETTER(NSDictionary *, customRetryForStatusCodes)
-SP_DIRTY_GETTER(BOOL, serverAnonymisation)
-
-@end
diff --git a/Snowplow/Internal/Emitter/SPEmitterController.h b/Snowplow/Internal/Emitter/SPEmitterController.h
deleted file mode 100644
index 73d523459..000000000
--- a/Snowplow/Internal/Emitter/SPEmitterController.h
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-//  SPEmitterController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPEmitterConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(EmitterController)
-@protocol SPEmitterController <SPEmitterConfigurationProtocol>
-
-/**
- * Number of events recorded in the EventStore.
- */
-@property (nonatomic, readonly) NSInteger dbCount;
-
-/**
- * Whether the emitter is currently sending events.
- */
-@property (nonatomic, readonly) BOOL isSending;
-
-- (void)flush;
-
-/**
- * Pause emitting events.
- * Emitting events will be suspended until resumed again.
- * Suitable for low bandwidth situations.
- */
-- (void)pause;
-
-/**
- * Resume emitting events if previously paused.
- * The emitter will resume emitting events again.
- */
-- (void)resume;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Emitter/SPEmitterControllerImpl.m b/Snowplow/Internal/Emitter/SPEmitterControllerImpl.m
deleted file mode 100644
index 369abb55c..000000000
--- a/Snowplow/Internal/Emitter/SPEmitterControllerImpl.m
+++ /dev/null
@@ -1,144 +0,0 @@
-//
-//  SPEmitterControllerImpl.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEmitterControllerImpl.h"
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPEmitterConfigurationUpdate.h"
-
-
-@implementation SPEmitterControllerImpl {
-    id<SPRequestCallback> _requestCallback;
-}
-
-@synthesize bufferOption;
-@synthesize byteLimitGet;
-@synthesize byteLimitPost;
-@synthesize emitRange;
-@synthesize threadPoolSize;
-@synthesize customRetryForStatusCodes;
-
-// MARK: - Properties
-
-- (void)setBufferOption:(SPBufferOption)bufferOption {
-    self.dirtyConfig.bufferOption = bufferOption;
-    self.dirtyConfig.bufferOptionUpdated = YES;
-    [self.emitter setBufferOption:bufferOption];
-}
-
-- (SPBufferOption)bufferOption {
-    return [self.emitter bufferOption];
-}
-
-- (void)setByteLimitGet:(NSInteger)byteLimitGet {
-    self.dirtyConfig.byteLimitGet = byteLimitGet;
-    self.dirtyConfig.byteLimitGetUpdated = YES;
-    [self.emitter setByteLimitGet:byteLimitGet];
-}
-
-- (NSInteger)byteLimitGet {
-    return [self.emitter byteLimitGet];
-}
-
-- (void)setByteLimitPost:(NSInteger)byteLimitPost {
-    self.dirtyConfig.byteLimitPost = byteLimitPost;
-    self.dirtyConfig.byteLimitPostUpdated = YES;
-    [self.emitter setByteLimitPost:byteLimitPost];
-}
-
-- (NSInteger)byteLimitPost {
-    return [self.emitter byteLimitPost];
-}
-
-- (void)setServerAnonymisation:(BOOL)serverAnonymisation {
-    self.dirtyConfig.serverAnonymisation = serverAnonymisation;
-    self.dirtyConfig.serverAnonymisationUpdated = YES;
-    [self.emitter setServerAnonymisation:serverAnonymisation];
-}
-
-- (BOOL)serverAnonymisation {
-    return [self.emitter serverAnonymisation];
-}
-
-- (void)setEmitRange:(NSInteger)emitRange {
-    self.dirtyConfig.emitRange = emitRange;
-    self.dirtyConfig.emitRangeUpdated = YES;
-    [self.emitter setEmitRange:emitRange];
-}
-
-- (NSInteger)emitRange {
-    return [self.emitter emitRange];
-}
-
-- (void)setThreadPoolSize:(NSInteger)emitThreadPoolSize {
-    self.dirtyConfig.threadPoolSize = emitThreadPoolSize;
-    self.dirtyConfig.threadPoolSizeUpdated = YES;
-    [self.emitter setEmitThreadPoolSize:emitThreadPoolSize];
-}
-
-- (NSInteger)threadPoolSize {
-    return [self.emitter emitThreadPoolSize];
-}
-
-- (void)setRequestCallback:(id<SPRequestCallback>)requestCallback {
-    _requestCallback = requestCallback;
-    [self.emitter setCallback:requestCallback];
-}
-
-- (id<SPRequestCallback>)requestCallback {
-    return _requestCallback;
-}
-
-// MARK: - Methods
-
-- (void)flush {
-    [self.emitter flush];
-}
-
-- (NSInteger)dbCount {
-    return [self.emitter getDbCount];
-}
-
-- (BOOL)isSending {
-    return [self.emitter getSendingStatus];
-}
-
-- (void)pause {
-    self.dirtyConfig.isPaused = YES;
-    [self.emitter pauseEmit];
-}
-
-- (void)resume {
-    self.dirtyConfig.isPaused = NO;
-    [self.emitter resumeEmit];
-}
-
-// MARK: - Private methods
-
-- (SPEmitter *)emitter {
-    return self.serviceProvider.tracker.emitter;
-}
-
-- (SPEmitterConfigurationUpdate *)dirtyConfig {
-    return self.serviceProvider.emitterConfigurationUpdate;
-}
-
-@end
diff --git a/Snowplow/Internal/Emitter/SPEmitterEvent.h b/Snowplow/Internal/Emitter/SPEmitterEvent.h
deleted file mode 100644
index bc28092a2..000000000
--- a/Snowplow/Internal/Emitter/SPEmitterEvent.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPEmitterEvent.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-#import "SPPayload.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(EmitterEvent)
-@interface SPEmitterEvent : NSObject
-
-@property (nonatomic, readonly) SPPayload *payload;
-@property (nonatomic, readonly) long long storeId;
-
-- (instancetype)initWithPayload:(SPPayload *)payload storeId:(long long)storeId;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Emitter/SPRequest.h b/Snowplow/Internal/Emitter/SPRequest.h
deleted file mode 100644
index d2566196a..000000000
--- a/Snowplow/Internal/Emitter/SPRequest.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  SPRequest.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPPayload.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(Request)
-@interface SPRequest : NSObject
-
-@property (nonatomic,readonly) SPPayload *payload;
-@property (nonatomic,readonly) NSArray<NSNumber *> *emitterEventIds;
-@property (nonatomic,readonly) BOOL oversize;
-@property (nonatomic,readonly) NSString *customUserAgent;
-
-- (instancetype)initWithPayload:(SPPayload *)payload emitterEventId:(long long)emitterEventId;
-
-- (instancetype)initWithPayload:(SPPayload *)payload emitterEventId:(long long)emitterEventId oversize:(BOOL)oversize;
-
-- (instancetype)initWithPayloads:(NSArray<SPPayload *> *)payloads emitterEventIds:(NSArray<NSNumber *> *)emitterEventIds;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Emitter/SPRequest.m b/Snowplow/Internal/Emitter/SPRequest.m
deleted file mode 100644
index 486b6d593..000000000
--- a/Snowplow/Internal/Emitter/SPRequest.m
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-//  SPRequest.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPRequest.h"
-#import "SPTrackerConstants.h"
-#import "SPSelfDescribingJson.h"
-
-@interface SPRequest ()
-
-@property (nonatomic,readwrite) SPPayload *payload;
-@property (nonatomic,readwrite) NSArray<NSNumber *> *emitterEventIds;
-@property (nonatomic,readwrite) BOOL oversize;
-@property (nonatomic,readwrite) NSString *customUserAgent;
-
-@end
-
-@implementation SPRequest
-
-- (instancetype)initWithPayload:(SPPayload *)payload emitterEventId:(long long)emitterEventId {
-    return [self initWithPayload:payload emitterEventId:emitterEventId oversize:NO];
-}
-
-- (instancetype)initWithPayload:(SPPayload *)payload emitterEventId:(long long)emitterEventId oversize:(BOOL)oversize {
-    if (self = [super init]) {
-        self.payload = payload;
-        self.emitterEventIds = @[[NSNumber numberWithLongLong:emitterEventId]];
-        self.customUserAgent = [self userAgentFromPayload:payload];
-        self.oversize = oversize;
-    }
-    return self;
-}
-
-- (instancetype)initWithPayloads:(NSArray<SPPayload *> *)payloads emitterEventIds:(NSArray<NSNumber *> *)emitterEventIds {
-    if (self = [super init]) {
-        NSString *tempUserAgent = nil;
-        NSMutableArray<NSDictionary<NSString *, NSObject *> *> *payloadData = [NSMutableArray new];
-        for (SPPayload *payload in payloads) {
-            [payloadData addObject:[payload getAsDictionary]];
-            tempUserAgent = [self userAgentFromPayload:payload];
-        }
-        SPSelfDescribingJson *payloadBundle = [[SPSelfDescribingJson alloc] initWithSchema:kSPPayloadDataSchema andData:payloadData];
-        self.payload = [[SPPayload alloc] initWithNSDictionary:[payloadBundle getAsDictionary]];
-        self.emitterEventIds = emitterEventIds;
-        self.customUserAgent = tempUserAgent;
-        self.oversize = NO;
-    }
-    return self;
-}
-
-// MARK: Private methods
-
-- (NSString *)userAgentFromPayload:(SPPayload *)payload {
-    return (NSString *)[[payload getAsDictionary] valueForKey:kSPUseragent];
-}
-
-@end
diff --git a/Snowplow/Internal/Emitter/SPRequestResult.h b/Snowplow/Internal/Emitter/SPRequestResult.h
deleted file mode 100644
index fa8a0b8d3..000000000
--- a/Snowplow/Internal/Emitter/SPRequestResult.h
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  SPRequestResult.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_SWIFT_NAME(RequestResult)
-@interface SPRequestResult : NSObject
-
-/// Returns the HTTP status code from Collector.
-@property (nonatomic, readonly) NSInteger statusCode;
-/// Was the request oversize
-@property (nonatomic, readonly) BOOL isOversize;
-/// Returns the stored index array, needed to remove the events after sending.
-@property (nonatomic, readonly) NSArray<NSNumber *> *storeIds;
-
-/**
- * Creates a request result object
- * @param statusCode HTTP status code from collector response
- * @param storeIds the event indexes in the database
- */
-- (instancetype)initWithStatusCode:(NSInteger)statusCode oversize:(BOOL)isOversize storeIds:(NSArray<NSNumber *> *)storeIds;
-
-/**
- * @return Whether the events were successfuly sent to the Collector.
- */
-- (BOOL)isSuccessful;
-
-/**
- * @param customRetryForStatusCodes mapping of custom retry rules for HTTP status codes in Collector response.
- * @return Whether sending the events to the Collector should be retried.
- */
-- (BOOL)shouldRetry:(NSDictionary<NSNumber *, NSNumber *> *)customRetryForStatusCodes;
-
-@end
diff --git a/Snowplow/Internal/Emitter/SPRequestResult.m b/Snowplow/Internal/Emitter/SPRequestResult.m
deleted file mode 100644
index c86f41db2..000000000
--- a/Snowplow/Internal/Emitter/SPRequestResult.m
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-//  SPRequestResult.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPRequestResult.h"
-
-@interface SPRequestResult ()
-
-@property (nonatomic, readwrite) NSInteger statusCode;
-@property (nonatomic, readwrite) BOOL isOversize;
-@property (nonatomic, readwrite) NSArray<NSNumber *> *storeIds;
-
-@end
-
-@implementation SPRequestResult
-
-- (instancetype)init {
-    return [self initWithStatusCode:-1 oversize:NO storeIds:@[]];
-}
-
-- (instancetype)initWithStatusCode:(NSInteger)statusCode oversize:(BOOL)isOversize storeIds:(NSArray<NSNumber *> *)storeIds {
-    if (self = [super init]) {
-        self.statusCode = statusCode;
-        self.isOversize = isOversize;
-        self.storeIds = storeIds;
-    }
-    return self;
-}
-
-- (BOOL)isSuccessful {
-    return _statusCode >= 200 && _statusCode < 300;
-}
-
-- (BOOL)shouldRetry:(NSDictionary<NSNumber *, NSNumber *> *)customRetryForStatusCodes {
-    // don't retry if successful
-    if ([self isSuccessful]) {
-        return false;
-    }
-
-    // don't retry if request is larger than max byte limit
-    if ([self isOversize]) {
-        return false;
-    }
-
-    // status code has a custom retry rule
-    NSNumber *code = [NSNumber numberWithInteger:_statusCode];
-    if ([customRetryForStatusCodes objectForKey:code]) {
-        return [[customRetryForStatusCodes objectForKey:code] boolValue];
-    }
-
-    // retry if status code is not in the list of no-retry status codes
-    NSArray *dontRetryStatusCodes = @[@400, @401, @403, @410, @422];
-    return ![dontRetryStatusCodes containsObject:code];
-}
-
-@end
diff --git a/Snowplow/Internal/Entities/SPDeepLinkEntity.h b/Snowplow/Internal/Entities/SPDeepLinkEntity.h
deleted file mode 100644
index 137e6b789..000000000
--- a/Snowplow/Internal/Entities/SPDeepLinkEntity.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// SPDeepLinkEntity.h
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- Entity that indicates a deep-link has been received and processed.
- */
-NS_SWIFT_NAME(DeepLinkEntity)
-@interface SPDeepLinkEntity : SPSelfDescribingJson
-
-extern NSString * const kSPDeepLinkSchema;
-extern NSString * const kSPDeepLinkParamReferrer;
-extern NSString * const kSPDeepLinkParamUrl;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithUrl:(NSString *)url;
-
-SP_BUILDER_DECLARE_NULLABLE(NSString *, referrer)
-
-@end
-
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Entities/SPDeepLinkEntity.m b/Snowplow/Internal/Entities/SPDeepLinkEntity.m
deleted file mode 100644
index 3ba7dba74..000000000
--- a/Snowplow/Internal/Entities/SPDeepLinkEntity.m
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// SPDeepLinkEntity.m
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPDeepLinkEntity.h"
-
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-@interface SPDeepLinkEntity ()
-@property (nonatomic, nonnull) NSMutableDictionary<NSString *, NSObject *> *parameters;
-@end
-
-@implementation SPDeepLinkEntity
-
-NSString * const kSPDeepLinkSchema = @"iglu:com.snowplowanalytics.mobile/deep_link/jsonschema/1-0-0";
-NSString * const kSPDeepLinkParamReferrer = @"referrer";
-NSString * const kSPDeepLinkParamUrl = @"url";
-
-- (instancetype)initWithUrl:(NSString *)url {
-    NSMutableDictionary<NSString *, NSObject *> *parameters = [NSMutableDictionary new];
-    if (self = [super initWithSchema:kSPDeepLinkSchema andDictionary:parameters]) {
-        self.parameters = parameters;
-        [self.parameters setValue:url forKey:kSPDeepLinkParamUrl];
-        // Set here further checks about the arguments.
-        // e.g.: [SPUtilities checkArgument:([_name length] != 0) withMessage:@"Name cannot be empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-- (SPDeepLinkEntity *)referrer:(NSString *)referrer {
-    [self.parameters setValue:referrer forKey:kSPDeepLinkParamReferrer];
-    return self;
-}
-
-@end
-
diff --git a/Snowplow/Internal/Entities/SPLifecycleEntity.h b/Snowplow/Internal/Entities/SPLifecycleEntity.h
deleted file mode 100644
index b48a633e1..000000000
--- a/Snowplow/Internal/Entities/SPLifecycleEntity.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// SPLifecycleEntity.h
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- Entity that indicates the state of the app is visible (foreground) when the event is tracked.
- */
-NS_SWIFT_NAME(LifecycleEntity)
-@interface SPLifecycleEntity : SPSelfDescribingJson
-
-extern NSString * const kSPLifecycleEntitySchema;
-extern NSString * const kSPLifecycleEntityParamIndex;
-extern NSString * const kSPLifecycleEntityParamIsVisible;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithIsVisible:(BOOL)isVisible;
-
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, index)
-
-@end
-
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Entities/SPLifecycleEntity.m b/Snowplow/Internal/Entities/SPLifecycleEntity.m
deleted file mode 100644
index fd8dab6c5..000000000
--- a/Snowplow/Internal/Entities/SPLifecycleEntity.m
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// SPLifecycleEntity.m
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPLifecycleEntity.h"
-
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-@interface SPLifecycleEntity ()
-@property (nonatomic, nonnull) NSMutableDictionary<NSString *, NSObject *> *parameters;
-@end
-
-@implementation SPLifecycleEntity
-
-NSString * const kSPLifecycleEntitySchema = @"iglu:com.snowplowanalytics.mobile/application_lifecycle/jsonschema/1-0-0";
-NSString * const kSPLifecycleEntityParamIndex = @"index";
-NSString * const kSPLifecycleEntityParamIsVisible = @"isVisible";
-
-- (instancetype)initWithIsVisible:(BOOL)isVisible {
-    NSMutableDictionary<NSString *, NSObject *> *parameters = [NSMutableDictionary new];
-    if (self = [super initWithSchema:kSPLifecycleEntitySchema andDictionary:parameters]) {
-        self.parameters = parameters;
-        [self.parameters setValue:@(isVisible) forKey:kSPLifecycleEntityParamIsVisible];
-        // Set here further checks about the arguments.
-        // e.g.: [SPUtilities checkArgument:([_name length] != 0) withMessage:@"Name cannot be empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-- (SPLifecycleEntity *)index:(NSNumber *)index {
-    [self.parameters setValue:index forKey:kSPLifecycleEntityParamIndex];
-    return self;
-}
-
-@end
-
diff --git a/Snowplow/Internal/Events/SNOWError.h b/Snowplow/Internal/Events/SNOWError.h
deleted file mode 100644
index 346bbacc5..000000000
--- a/Snowplow/Internal/Events/SNOWError.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-//  SNOWError.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// An error event representing an exception, error or warning message in the app
-@interface SNOWError : SPSelfDescribingAbstract
-
-@property (nonatomic, nullable) NSString *name;
-@property (nonatomic, nullable) NSString *stackTrace;
-@property (nonatomic, readonly) NSString *message;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithMessage:(NSString *)message NS_SWIFT_NAME(init(message:));
-
-SP_BUILDER_DECLARE_NULLABLE(NSString *, name)
-SP_BUILDER_DECLARE_NULLABLE(NSString *, stackTrace)
-
-@end
-
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SNOWError.m b/Snowplow/Internal/Events/SNOWError.m
deleted file mode 100644
index d6b10dfc7..000000000
--- a/Snowplow/Internal/Events/SNOWError.m
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-//  SNOWError.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SNOWError.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-
-@interface SNOWError ()
-
-@property (nonatomic, readwrite) NSString *message;
-
-@end
-
-@implementation SNOWError
-
-- (instancetype)init {
-    self = [super init];
-    return self;
-}
-
-- (instancetype)initWithMessage:(NSString *)message {
-    if (self = [super init]) {
-        _message = message;
-        [SPUtilities checkArgument:(_message != nil) withMessage:@"Message cannot be nil or empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, name)
-SP_BUILDER_METHOD(NSString *, stackTrace)
-
-// --- Public Methods
-
-- (NSString *)schema {
-    return kSPErrorSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_message forKey:kSPErrorMessage];
-    [payload setValue:_stackTrace forKey:kSPErrorStackTrace];
-    [payload setValue:_name forKey:kSPErrorName];
-    [payload setValue:@"OBJECTIVEC" forKey:kSPErrorLanguage];
-    return payload;
-}
-
-@synthesize schema;
-
-@end
diff --git a/Snowplow/Internal/Events/SPConsentDocument.h b/Snowplow/Internal/Events/SPConsentDocument.h
deleted file mode 100644
index 3400ecb59..000000000
--- a/Snowplow/Internal/Events/SPConsentDocument.h
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-//  SPConsentDocument.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A consent document event.
-NS_SWIFT_NAME(ConsentDocument)
-@interface SPConsentDocument : NSObject
-
-/// Identifier of the document.
-@property (nonatomic, readonly) NSString *documentId;
-/// Version of the document.
-@property (nonatomic, readonly) NSString *version;
-/// Name of the document.
-@property (nonatomic, nullable) NSString *name;
-/// Description of the document.
-@property (nonatomic, nullable) NSString *documentDescription;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Create a consent document event.
- @param documentId identifier of the document.
- @param version version of the document.
- */
-- (instancetype)initWithDocumentId:(NSString *)documentId version:(NSString *)version NS_SWIFT_NAME(init(documentId:version:));
-
-/// Returns the payload.
-- (SPSelfDescribingJson *)getPayload;
-
-/// Name of the document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, name)
-/// Description of the document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, documentDescription)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPConsentDocument.m b/Snowplow/Internal/Events/SPConsentDocument.m
deleted file mode 100644
index 3649dd7a0..000000000
--- a/Snowplow/Internal/Events/SPConsentDocument.m
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-//  SPConsentDocument.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConsentDocument.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-@interface SPConsentDocument ()
-
-@property (nonatomic, readwrite) NSString *documentId;
-@property (nonatomic, readwrite) NSString *version;
-
-@end
-
-@implementation SPConsentDocument
-
-- (instancetype)initWithDocumentId:(NSString *)documentId version:(NSString *)version {
-    if (self = [super init]) {
-        _documentId = documentId;
-        _version = version;
-        [SPUtilities checkArgument:(_documentId != nil) withMessage:@"Document ID cannot be nil."];
-        [SPUtilities checkArgument:(_version != nil) withMessage:@"Version cannot be nil."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, name)
-SP_BUILDER_METHOD(NSString *, documentDescription)
-
-// --- Public Methods
-
-- (SPSelfDescribingJson *)getPayload {
-    NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
-    [event setObject:_documentId forKey:kSPCdId];
-    [event setObject:_version forKey:kSPCdVersion];
-    if ([_name length] != 0) {
-        [event setObject:_name forKey:kSPCdName];
-    }
-    if ([_documentDescription length] != 0) {
-        [event setObject:_documentDescription forKey:KSPCdDescription];
-    }
-    return [[SPSelfDescribingJson alloc] initWithSchema:kSPConsentDocumentSchema
-                                                andData:event];
-}
-
-@end
-
diff --git a/Snowplow/Internal/Events/SPConsentGranted.h b/Snowplow/Internal/Events/SPConsentGranted.h
deleted file mode 100644
index 267716f39..000000000
--- a/Snowplow/Internal/Events/SPConsentGranted.h
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-//  SPConsentGranted.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A consent granted event.
-NS_SWIFT_NAME(ConsentGranted)
-@interface SPConsentGranted : SPSelfDescribingAbstract
-
-/// Expiration of the consent.
-@property (nonatomic, readonly) NSString *expiry;
-/// Identifier of the first document.
-@property (nonatomic, readonly) NSString *documentId;
-/// Version of the first document.
-@property (nonatomic, readonly) NSString *version;
-/// Name of the first document.
-@property (nonatomic, nullable) NSString *name;
-/// Description of the first document.
-@property (nonatomic, nullable) NSString *documentDescription;
-/// Other attached documents.
-@property (nonatomic, nullable) NSArray<SPSelfDescribingJson *> *documents;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Creates a consent granted event with a first document.
- @param expiry consent expiration.
- @param documentId identifier of the first document.
- @param version version of the first document.
- */
-- (instancetype)initWithExpiry:(NSString *)expiry documentId:(NSString *)documentId version:(NSString *)version NS_SWIFT_NAME(init(expiry:documentId:version:));
-
-/// Retuns the full list of attached documents.
-- (NSArray<SPSelfDescribingJson *> *)getDocuments;
-
-/// Name of the first document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, name)
-/// Description of the first document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, documentDescription)
-/// Other attached documents.
-SP_BUILDER_DECLARE_NULLABLE(NSArray<SPSelfDescribingJson *> *, documents)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPConsentGranted.m b/Snowplow/Internal/Events/SPConsentGranted.m
deleted file mode 100644
index a81830d17..000000000
--- a/Snowplow/Internal/Events/SPConsentGranted.m
+++ /dev/null
@@ -1,95 +0,0 @@
-//
-//  SPConsentGranted.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConsentGranted.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "SPConsentDocument.h"
-
-@interface SPConsentGranted ()
-
-@property (nonatomic, readwrite) NSString *expiry;
-@property (nonatomic, readwrite) NSString *documentId;
-@property (nonatomic, readwrite) NSString *version;
-
-@end
-
-@implementation SPConsentGranted
-
-- (instancetype)initWithExpiry:(NSString *)expiry documentId:(NSString *)documentId version:(NSString *)version {
-    if (self = [super init]) {
-        _expiry = expiry;
-        _documentId = documentId;
-        _version = version;
-        [SPUtilities checkArgument:(_expiry != nil) withMessage:@"Expiry cannot be nil."];
-        [SPUtilities checkArgument:(_documentId != nil) withMessage:@"Document ID cannot be nil."];
-        [SPUtilities checkArgument:(_version != nil) withMessage:@"Version cannot be nil."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, name)
-SP_BUILDER_METHOD(NSString *, documentDescription)
-SP_BUILDER_METHOD(NSArray<SPSelfDescribingJson *> *, documents)
-
-// --- Public Methods
-
-- (NSString *)schema {
-    return kSPConsentGrantedSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_expiry forKey:KSPCgExpiry];
-    return payload;
-}
-
-- (NSArray<SPSelfDescribingJson *> *)getDocuments {
-    NSMutableArray<SPSelfDescribingJson *> *documents = [NSMutableArray<SPSelfDescribingJson *> new];
-
-    SPConsentDocument *document = [[SPConsentDocument alloc] initWithDocumentId:_documentId version:_version];
-    if (_name.length != 0) {
-        document.name = _name;
-    }
-    if (_documentDescription != 0) {
-        document.documentDescription = _documentDescription;
-    }
-
-    [documents addObject:[document getPayload]];
-    if (_documents.count > 0) {
-        [documents addObjectsFromArray:_documents];
-    }
-    return documents;
-}
-
-- (void)beginProcessingWithTracker:(SPTracker *)tracker {
-    NSArray<SPSelfDescribingJson *> *documents = [self getDocuments];
-    if (documents) {
-        [self.contexts addObjectsFromArray:documents];  // TODO: Only the user should modify the public contexts property
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPConsentWithdrawn.h b/Snowplow/Internal/Events/SPConsentWithdrawn.h
deleted file mode 100644
index 021aea172..000000000
--- a/Snowplow/Internal/Events/SPConsentWithdrawn.h
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-//  SPConsentWithdrawn.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A consent withdrawn event.
-NS_SWIFT_NAME(ConsentWithdrawn)
-@interface SPConsentWithdrawn : SPSelfDescribingAbstract
-
-/// Consent to all.
-@property (nonatomic) BOOL all;
-/// Identifier of the first document.
-@property (nonatomic, nullable) NSString *documentId;
-/// Version of the first document.
-@property (nonatomic, nullable) NSString *version;
-/// Name of the first document.
-@property (nonatomic, nullable) NSString *name;
-/// Description of the first document.
-@property (nonatomic, nullable) NSString *documentDescription;
-/// Other documents.
-@property (nonatomic, nullable) NSArray<SPSelfDescribingJson *> *documents;
-
-/// Retuns the full list of attached documents.
-- (NSArray<SPSelfDescribingJson *> *)getDocuments;
-
-/// Consent to all.
-SP_BUILDER_DECLARE(BOOL, all)
-/// Identifier of the first document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, documentId)
-/// Version of the first document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, version)
-/// Name of the first document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, name)
-/// Description of the first document.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, documentDescription)
-/// Other documents.
-SP_BUILDER_DECLARE_NULLABLE(NSArray<SPSelfDescribingJson *> *, documents)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPConsentWithdrawn.m b/Snowplow/Internal/Events/SPConsentWithdrawn.m
deleted file mode 100644
index 40a3c7ae8..000000000
--- a/Snowplow/Internal/Events/SPConsentWithdrawn.m
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-//  SPConsentWithdrawn.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConsentWithdrawn.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPSelfDescribingJson.h"
-#import "SPConsentDocument.h"
-
-@implementation SPConsentWithdrawn
-
-- (instancetype)init {
-    self = [super init];
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(BOOL, all)
-SP_BUILDER_METHOD(NSString *, documentId)
-SP_BUILDER_METHOD(NSString *, version)
-SP_BUILDER_METHOD(NSString *, name)
-SP_BUILDER_METHOD(NSString *, documentDescription)
-SP_BUILDER_METHOD(NSArray<SPSelfDescribingJson *> *, documents)
-
-// --- Public Methods
-
-- (NSString *)schema {
-    return kSPConsentWithdrawnSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    return @{
-        KSPCwAll: (_all ? @YES: @NO),
-    };
-}
-
-- (NSArray<SPSelfDescribingJson *> *)getDocuments {
-    NSMutableArray<SPSelfDescribingJson *> *documents = [NSMutableArray<SPSelfDescribingJson *> new];
-
-    SPConsentDocument *document = [[SPConsentDocument alloc] initWithDocumentId:_documentId version:_version];
-    if (_name.length != 0) {
-        document.name = _name;
-    }
-    if (_documentDescription != 0) {
-        document.documentDescription = _documentDescription;
-    }
-
-    [documents addObject:[document getPayload]];
-    if (_documents.count > 0) {
-        [documents addObjectsFromArray:_documents];
-    }
-    return documents;
-}
-
-- (void)beginProcessingWithTracker:(SPTracker *)tracker {
-    NSArray<SPSelfDescribingJson *> *documents = [self getDocuments];
-    if (documents) {
-        [self.contexts addObjectsFromArray:documents];
-    }
-}
-
-@end
-
diff --git a/Snowplow/Internal/Events/SPDeepLinkReceived.h b/Snowplow/Internal/Events/SPDeepLinkReceived.h
deleted file mode 100644
index 9fb42275e..000000000
--- a/Snowplow/Internal/Events/SPDeepLinkReceived.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-// SPDeepLinkReceived.h
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A deep-link received in the app.
-NS_SWIFT_NAME(DeepLinkReceived)
-@interface SPDeepLinkReceived : SPSelfDescribingAbstract
-
-extern NSString * const kSPDeepLinkReceivedSchema;
-extern NSString * const kSPDeepLinkReceivedParamReferrer;
-extern NSString * const kSPDeepLinkReceivedParamUrl;
-
-/// Referrer URL, source of this deep-link.
-@property (nonatomic, nullable) NSString *referrer;
-/// URL in the received deep-link.
-@property (nonatomic, nonnull, readonly) NSString *url;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/// Creates a deep-link received event.
-/// @param url URL in the received deep-link.
-- (instancetype)initWithUrl:(NSString *)url;
-
-/// Referrer URL, source of this deep-link.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, referrer)
-
-@end
-
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPDeepLinkReceived.m b/Snowplow/Internal/Events/SPDeepLinkReceived.m
deleted file mode 100644
index 9d349b5b1..000000000
--- a/Snowplow/Internal/Events/SPDeepLinkReceived.m
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// SPDeepLinkReceived.m
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPDeepLinkReceived.h"
-
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-@interface SPDeepLinkReceived ()
-
-@property (nonatomic, nonnull, readwrite) NSString *url;
-
-@end
-
-@implementation SPDeepLinkReceived
-
-NSString * const kSPDeepLinkReceivedSchema = @"iglu:com.snowplowanalytics.mobile/deep_link_received/jsonschema/1-0-0";
-NSString * const kSPDeepLinkReceivedParamReferrer = @"referrer";
-NSString * const kSPDeepLinkReceivedParamUrl = @"url";
-
-- (instancetype)initWithUrl:(NSString *)url {
-    if (self = [super init]) {
-        _url = url;
-        // Set here further checks about the arguments.
-        // e.g.: [SPUtilities checkArgument:([_name length] != 0) withMessage:@"Name cannot be empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, referrer)
-
-// --- Tracker Methods
-
-- (NSString *)schema {
-    return kSPDeepLinkReceivedSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_referrer forKey:kSPDeepLinkReceivedParamReferrer];
-    [payload setValue:_url forKey:kSPDeepLinkReceivedParamUrl];
-    return payload;
-}
-
-@end
-
diff --git a/Snowplow/Internal/Events/SPEcommerce.h b/Snowplow/Internal/Events/SPEcommerce.h
deleted file mode 100644
index 254480c30..000000000
--- a/Snowplow/Internal/Events/SPEcommerce.h
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  SPEcommerce.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPEcommerceItem.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// An ecommerce event.
-NS_SWIFT_NAME(Ecommerce)
-@interface SPEcommerce : SPPrimitiveAbstract
-
-/// Identifier of the order.
-@property (nonatomic, readonly) NSString *orderId;
-/// Total amount of the order.
-@property (nonatomic, readonly) NSNumber *totalValue;
-/// Items purchased.
-@property (nonatomic, readonly) NSArray<SPEcommerceItem *> *items;
-/// Identifies an affiliation.
-@property (nonatomic, nullable) NSString *affiliation;
-/// Taxes applied to the purchase.
-@property (nonatomic, nullable) NSNumber *taxValue;
-/// Shipping number.
-@property (nonatomic, nullable) NSNumber *shipping;
-/// City for shipping.
-@property (nonatomic, nullable) NSString *city;
-/// State for shipping.
-@property (nonatomic, nullable) NSString *state;
-/// Country for shipping.
-@property (nonatomic, nullable) NSString *country;
-/// Currency used for totalValue and taxValue.
-@property (nonatomic, nullable) NSString *currency;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Creates an ecommerce event.
- @param orderId Identifier of the order.
- @param totalValue Total amount of the order.
- @param items Items purchased.
- */
-- (instancetype)initWithOrderId:(NSString *)orderId totalValue:(NSNumber *)totalValue items:(NSArray<SPEcommerceItem *> *)items NS_SWIFT_NAME(init(orderId:totalValue:items:));
-
-/// List of the items purchased.
-- (NSArray<SPEcommerceItem *> *)getItems;
-
-/// Identifies an affiliation.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, affiliation)
-/// Taxes applied to the purchase.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, taxValue)
-/// Shipping number.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, shipping)
-/// City for shipping.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, city)
-/// State for shipping.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, state)
-/// Country for shipping.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, country)
-/// Currency used for totalValue and taxValue.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, currency)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPEcommerce.m b/Snowplow/Internal/Events/SPEcommerce.m
deleted file mode 100644
index 2e59b1e4b..000000000
--- a/Snowplow/Internal/Events/SPEcommerce.m
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-//  SPEcommerce.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEcommerce.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPTracker.h"
-
-@interface SPEcommerce ()
-
-@property (nonatomic, readwrite) NSString *orderId;
-@property (nonatomic, readwrite) NSNumber *totalValue;
-@property (nonatomic, readwrite) NSArray<SPEcommerceItem *> *items;
-
-@end
-
-@implementation SPEcommerce
-
-- (instancetype)initWithOrderId:(NSString *)orderId totalValue:(NSNumber *)totalValue items:(NSArray<SPEcommerceItem *> *)items {
-    if (self = [super init]) {
-        _orderId = orderId;
-        _totalValue = totalValue;
-        _items = items.copy;
-        [SPUtilities checkArgument:([_orderId length] != 0) withMessage:@"OrderId cannot be nil or empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, affiliation)
-SP_BUILDER_METHOD(NSNumber *, taxValue)
-SP_BUILDER_METHOD(NSNumber *, shipping)
-SP_BUILDER_METHOD(NSString *, city)
-SP_BUILDER_METHOD(NSString *, state)
-SP_BUILDER_METHOD(NSString *, country)
-SP_BUILDER_METHOD(NSString *, currency)
-
-// --- Public Methods
-
-- (NSString *)eventName {
-    return kSPEventEcomm;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    if (_totalValue) [payload setObject:[NSString stringWithFormat:@"%.02f", [_totalValue doubleValue]] forKey:kSPEcommTotal];
-    if (_taxValue) [payload setObject:[NSString stringWithFormat:@"%.02f", [_taxValue doubleValue]] forKey:kSPEcommTax];
-    if (_shipping) [payload setObject:[NSString stringWithFormat:@"%.02f", [_shipping doubleValue]] forKey:kSPEcommShipping];
-    [payload setValue:_orderId forKey:kSPEcommId];
-    [payload setValue:_affiliation forKey:kSPEcommAffiliation];
-    [payload setValue:_city forKey:kSPEcommCity];
-    [payload setValue:_state forKey:kSPEcommState];
-    [payload setValue:_country forKey:kSPEcommCountry];
-    [payload setValue:_currency forKey:kSPEcommCurrency];
-    return payload;
-}
-
-- (NSArray<SPEcommerceItem *> *)getItems {
-    return _items;
-}
-
-- (void)endProcessingWithTracker:(SPTracker *)tracker {
-    for (SPEcommerceItem *item in _items) {
-        item.orderId  = _orderId;
-        [tracker track:item];
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPEcommerceItem.h b/Snowplow/Internal/Events/SPEcommerceItem.h
deleted file mode 100644
index d2fd60e7d..000000000
--- a/Snowplow/Internal/Events/SPEcommerceItem.h
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-//  SPEcommerceItem.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// An ecommerce item event.
-NS_SWIFT_NAME(EcommerceItem)
-@interface SPEcommerceItem : SPPrimitiveAbstract
-
-/// Stock Keeping Unit of the item.
-@property (nonatomic, readonly) NSString *sku;
-/// Price of the item.
-@property (nonatomic, readonly) NSNumber *price;
-/// Quantity of the item.
-@property (nonatomic, readonly) NSNumber *quantity;
-/// Name of the item.
-@property (nonatomic, nullable) NSString *name;
-/// Category of the item.
-@property (nonatomic, nullable) NSString *category;
-/// Currency used for the price of the item.
-@property (nonatomic, nullable) NSString *currency;
-/// OrderID of the order that contains this item.
-@property (nonatomic, nullable) NSString *orderId;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Creates an ecommerce item event.
- @param sku Stock Keeping Unit of the item.
- @param price Price of the item.
- @param quantity Quantity of the item.
- */
-- (instancetype)initWithSku:(NSString *)sku price:(NSNumber *)price quantity:(NSNumber *)quantity NS_SWIFT_NAME(init(sku:price:quantity:));
-
-/// Name of the item.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, name)
-/// Category of the item.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, category)
-/// Currency used for the price of the item.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, currency)
-/// OrderID of the order that contains this item.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, orderId)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPEcommerceItem.m b/Snowplow/Internal/Events/SPEcommerceItem.m
deleted file mode 100644
index df4d297c9..000000000
--- a/Snowplow/Internal/Events/SPEcommerceItem.m
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-//  SPEcommerceItem.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEcommerceItem.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-
-@interface SPEcommerceItem ()
-
-@property (nonatomic, readwrite) NSString *sku;
-@property (nonatomic, readwrite) NSNumber *price;
-@property (nonatomic, readwrite) NSNumber *quantity;
-
-@end
-
-@implementation SPEcommerceItem
-
-- (instancetype)initWithSku:(NSString *)sku price:(NSNumber *)price quantity:(NSNumber *)quantity {
-    if (self = [super init]) {
-        _sku = sku;
-        _price = price;
-        _quantity = quantity;
-        [SPUtilities checkArgument:([_sku length] != 0) withMessage:@"SKU cannot be nil or empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, name)
-SP_BUILDER_METHOD(NSString *, category)
-SP_BUILDER_METHOD(NSString *, currency)
-SP_BUILDER_METHOD(NSString *, orderId)
-
-// --- Public Methods
-
-- (NSString *)eventName {
-    return kSPEventEcommItem;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_orderId forKey:kSPEcommItemId];
-    [payload setValue:_sku forKey:kSPEcommItemSku];
-    [payload setValue:_name forKey:kSPEcommItemName];
-    [payload setValue:_category forKey:kSPEcommItemCategory];
-    [payload setValue:_currency forKey:kSPEcommItemCurrency];
-    if (_price) [payload setObject:[NSString stringWithFormat:@"%.02f", [_price doubleValue]] forKey:kSPEcommItemPrice];
-    if (_quantity) [payload setObject:[NSString stringWithFormat:@"%ld", [_quantity longValue]] forKey:kSPEcommItemQuantity];
-    return payload;
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPEvent.h b/Snowplow/Internal/Events/SPEvent.h
deleted file mode 100644
index 5d79f3d2e..000000000
--- a/Snowplow/Internal/Events/SPEvent.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  SPEvent.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-// #ifndef SPEvent_h
-// #define SPEvent_h
-
-#import "SPPageView.h"
-#import "SPStructured.h"
-#import "SPSelfDescribing.h"
-#import "SPScreenView.h"
-#import "SPConsentWithdrawn.h"
-#import "SPConsentDocument.h"
-#import "SPConsentGranted.h"
-#import "SPTiming.h"
-#import "SPEcommerce.h"
-#import "SPEcommerceItem.h"
-#import "SPPushNotification.h"
-#import "SPForeground.h"
-#import "SPBackground.h"
-#import "SNOWError.h"
-#import "SPDeepLinkReceived.h"
-#import "SPMessageNotification.h"
-#import "SPMessageNotificationAttachment.h"
-
-// #endif /* SPEvent_h */
diff --git a/Snowplow/Internal/Events/SPEventBase.h b/Snowplow/Internal/Events/SPEventBase.h
deleted file mode 100644
index 0f415b9a8..000000000
--- a/Snowplow/Internal/Events/SPEventBase.h
+++ /dev/null
@@ -1,116 +0,0 @@
-//
-//  SPEventBase.h
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPSelfDescribingJson.h"
-#import "SPTrackerConstants.h"
-#import "SPTrackerStateSnapshot.h"
-
-@class SPPayload;
-@class SPTracker;
-
-/// An enum for screen types.
-typedef NS_ENUM(NSInteger, SPScreenType) {
-    // sourced from `View Controller Catalog for iOS`
-    SPScreenTypeDefault,
-    SPScreenTypeNavigation,
-    SPScreenTypeTabBar,
-    SPScreenTypePageView,
-    SPScreenTypeSplitView,
-    SPScreenTypePopoverPresentation,
-    SPScreenTypeModal,
-    SPScreenTypeCombined
-} NS_SWIFT_NAME(ScreenType);
-
-NSString * _Nullable stringWithSPScreenType(SPScreenType screenType);
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// The inspectable properties of the event used to generate contexts.
-NS_SWIFT_NAME(InspectableEvent)
-@protocol SPInspectableEvent <NSObject>
-
-/// The schema of the event
-@property (nonatomic, readonly, nullable) NSString *schema;
-/// The name of the event
-@property (nonatomic, readonly, nullable) NSString *eventName;
-/// The payload of the event
-@property (nonatomic, readonly) NSDictionary<NSString *, NSObject *> *payload;
-
-/// The tracker state at the time the event was sent.
-@property (nonatomic, readonly) id<SPTrackerStateSnapshot> state;
-
-/// Add payload values to the event.
-/// @param payload Map of values to add to the event payload.
-/// @return Whether or not the values have been successfully added to the event payload.
-- (BOOL)addPayloadValues:(NSDictionary<NSString *, NSObject *> *)payload;
-
-@end
-
-
-/// This class has the basic functionality needed to represent all events
-NS_SWIFT_NAME(Event)
-@interface SPEvent : NSObject
-
-/// The user event timestamp in milliseconds (epoch time).
-@property (nonatomic, nullable) NSDate *trueTimestamp;
-
-/// The contexts attached to the event.
-@property (nonatomic) NSMutableArray<SPSelfDescribingJson *> *contexts;
-
-/// The payload of the event.
-@property (nonatomic, readonly) NSDictionary<NSString *, NSObject *> *payload;
-
-SP_BUILDER_DECLARE_NULLABLE(NSDate *, trueTimestamp)
-SP_BUILDER_DECLARE(NSMutableArray<SPSelfDescribingJson *> *, contexts)
-
-/**
- * Hook method called just before the event processing in order to execute special operations.
- * @note Internal use only - Don't use in production, it can change without notice.
- */
-- (void)beginProcessingWithTracker:(SPTracker *)tracker;
-
-/**
- * Hook method called just after the event processing in order to execute special operations.
- * @note Internal use only - Don't use in production, it can change without notice.
- */
-- (void)endProcessingWithTracker:(SPTracker *)tracker;
-@end
-
-/// The properties for all the self-describing events.
-NS_SWIFT_NAME(SelfDescribingAbstract)
-@interface SPSelfDescribingAbstract : SPEvent
-
-/// The schema of the event.
-@property (nonatomic, readonly) NSString *schema;
-
-@end
-
-/// The properties for all the self-describing events.
-NS_SWIFT_NAME(PrimitiveAbstract)
-@interface SPPrimitiveAbstract : SPEvent
-
-/// The name of the event.
-@property (nonatomic, readonly) NSString *eventName;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPEventBase.m b/Snowplow/Internal/Events/SPEventBase.m
deleted file mode 100644
index efffb9450..000000000
--- a/Snowplow/Internal/Events/SPEventBase.m
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-//  SPEventBase.m
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPEventBase.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPScreenState.h"
-#import "SPTracker.h"
-
-NSString * stringWithSPScreenType(SPScreenType screenType) {
-    NSArray * arr = @[
-                      @"Default",
-                      @"Navigation",
-                      @"TabBar",
-                      @"PageView",
-                      @"SplitView",
-                      @"PopoverPresentation",
-                      @"Modal",
-                      @"Combined"
-                      ];
-    return (NSString *)[arr objectAtIndex:screenType];
-}
-
-// Base Event
-
-@implementation SPEvent
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.contexts = [[NSMutableArray alloc] init];
-    }
-    return self;
-}
-
-SP_BUILDER_METHOD(NSDate *, trueTimestamp)
-SP_BUILDER_METHOD(NSMutableArray<SPSelfDescribingJson *> *, contexts)
-
-// --- Public Methods
-
-- (NSDictionary<NSString *,NSObject *> *)payload {
-    @throw [NSException exceptionWithName:NSInternalInconsistencyException
-                                   reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
-                                 userInfo:nil];
-}
-
-- (void)beginProcessingWithTracker:(SPTracker *)tracker {}
-- (void)endProcessingWithTracker:(SPTracker *)tracker {}
-
-@end
-
-// SelfDescribing base class
-
-@implementation SPSelfDescribingAbstract
-
-- (NSString *)schema {
-    @throw [NSException exceptionWithName:NSInternalInconsistencyException
-                                   reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
-                                 userInfo:nil];
-}
-
-@end
-
-// Primitive base class
-
-@implementation SPPrimitiveAbstract
-
-- (NSString *)eventName {
-    @throw [NSException exceptionWithName:NSInternalInconsistencyException
-                                   reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
-                                 userInfo:nil];
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPMessageNotification.h b/Snowplow/Internal/Events/SPMessageNotification.h
deleted file mode 100644
index ba9a0b0c4..000000000
--- a/Snowplow/Internal/Events/SPMessageNotification.h
+++ /dev/null
@@ -1,161 +0,0 @@
-//
-// SPMessageNotification.h
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-#import "SPMessageNotificationAttachment.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(MessageNotificationTrigger)
-typedef NS_ENUM(NSUInteger, SPMessageNotificationTrigger) {
-    SPMessageNotificationTriggerPush = 0,
-    SPMessageNotificationTriggerLocation,
-    SPMessageNotificationTriggerCalendar,
-    SPMessageNotificationTriggerTimeInterval,
-    SPMessageNotificationTriggerOther
-};
-
-
-/// An event that represents the reception of a push notification (or a locally generated one).
-NS_SWIFT_NAME(MessageNotification)
-@interface SPMessageNotification : SPSelfDescribingAbstract
-
-extern NSString * const kSPMessageNotificationSchema;
-extern NSString * const kSPMessageNotificationParamAction;
-extern NSString * const kSPMessageNotificationParamMessageNotificationAttachments;
-extern NSString * const kSPMessageNotificationParamBody;
-extern NSString * const kSPMessageNotificationParamBodyLocArgs;
-extern NSString * const kSPMessageNotificationParamBodyLocKey;
-extern NSString * const kSPMessageNotificationParamCategory;
-extern NSString * const kSPMessageNotificationParamContentAvailable;
-extern NSString * const kSPMessageNotificationParamGroup;
-extern NSString * const kSPMessageNotificationParamIcon;
-extern NSString * const kSPMessageNotificationParamNotificationCount;
-extern NSString * const kSPMessageNotificationParamNotificationTimestamp;
-extern NSString * const kSPMessageNotificationParamSound;
-extern NSString * const kSPMessageNotificationParamSubtitle;
-extern NSString * const kSPMessageNotificationParamTag;
-extern NSString * const kSPMessageNotificationParamThreadIdentifier;
-extern NSString * const kSPMessageNotificationParamTitle;
-extern NSString * const kSPMessageNotificationParamTitleLocArgs;
-extern NSString * const kSPMessageNotificationParamTitleLocKey;
-extern NSString * const kSPMessageNotificationParamTrigger;
-
-/// The action associated with the notification.
-@property (nonatomic, nullable) NSString *action;
-/// Attachments added to the notification (they can be part of the data object).
-@property (nonatomic, nullable) NSArray<SPMessageNotificationAttachment *> *attachments;
-/// The notification's body.
-@property (nonatomic, nonnull, readonly) NSString *body;
-/// Variable string values to be used in place of the format specifiers in bodyLocArgs to use to localize the body text to the user's current localization.
-@property (nonatomic, nullable) NSArray<NSString *> *bodyLocArgs;
-/// The key to the body string in the app's string resources to use to localize the body text to the user's current localization.
-@property (nonatomic, nullable) NSString *bodyLocKey;
-/// The category associated to the notification.
-@property (nonatomic, nullable) NSString *category;
-/// The application is notified of the delivery of the notification if it's in the foreground or background, the app will be woken up (iOS only).
-@property (nonatomic, nullable) NSNumber *contentAvailable;
-/// The group which this notification is part of.
-@property (nonatomic, nullable) NSString *group;
-/// The icon associated to the notification (Android only).
-@property (nonatomic, nullable) NSString *icon;
-/// The number of items this notification represents. It's the badge number on iOS.
-@property (nonatomic, nullable) NSNumber *notificationCount;
-/// The time when the event of the notification occurred.
-@property (nonatomic, nullable) NSString *notificationTimestamp;
-/// The sound played when the device receives the notification.
-@property (nonatomic, nullable) NSString *sound;
-/// The notification's subtitle. (iOS only)
-@property (nonatomic, nullable) NSString *subtitle;
-/// An identifier similar to 'group' but usable for different purposes (Android only).
-@property (nonatomic, nullable) NSString *tag;
-/// An identifier similar to 'group' but usable for different purposes (iOS only).
-@property (nonatomic, nullable) NSString *threadIdentifier;
-/// The notification's title.
-@property (nonatomic, nonnull, readonly) NSString *title;
-/// Variable string values to be used in place of the format specifiers in titleLocArgs to use to localize the title text to the user's current localization.
-@property (nonatomic, nullable) NSArray<NSString *> *titleLocArgs;
-/// The key to the title string in the app's string resources to use to localize the title text to the user's current localization.
-@property (nonatomic, nullable) NSString *titleLocKey;
-/// The trigger that raised the notification message.
-@property (nonatomic, readonly) SPMessageNotificationTrigger trigger;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Creates a Message Notification event that represents a push notification or a local notification.
- @note The custom data of the push notification have to be tracked separately in custom entities that can be attached to this event.
- @param title Title of message notification.
- @param body Body content of the message notification.
- @param trigger The trigger that raised this notification: remote notification (push), position related (location), date-time related (calendar, timeInterval) or app generated (other).
- */
-- (instancetype)initWithTitle:(NSString *)title
-                         body:(NSString *)body
-                      trigger:(SPMessageNotificationTrigger)trigger;
-
-/// The action associated with the notification.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, action)
-/// Attachments added to the notification (they can be part of the data object).
-SP_BUILDER_DECLARE_NULLABLE(NSArray<SPMessageNotificationAttachment *> *, attachments)
-/// Variable string values to be used in place of the format specifiers in bodyLocArgs to use to localize the body text to the user's current localization.
-SP_BUILDER_DECLARE_NULLABLE(NSArray<NSString *> *, bodyLocArgs)
-/// The key to the body string in the app's string resources to use to localize the body text to the user's current localization.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, bodyLocKey)
-/// The category associated to the notification.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, category)
-/// The application is notified of the delivery of the notification if it's in the foreground or background, the app will be woken up (iOS only).
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, contentAvailable)
-/// The group which this notification is part of.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, group)
-/// The icon associated to the notification (Android only).
-SP_BUILDER_DECLARE_NULLABLE(NSString *, icon)
-/// The number of items this notification represents. It's the badge number on iOS.
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, notificationCount)
-/// The time when the event of the notification occurred.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, notificationTimestamp)
-/// The sound played when the device receives the notification.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, sound)
-/// The notification's subtitle. (iOS only)
-SP_BUILDER_DECLARE_NULLABLE(NSString *, subtitle)
-/// An identifier similar to 'group' but usable for different purposes (Android only).
-SP_BUILDER_DECLARE_NULLABLE(NSString *, tag)
-/// An identifier similar to 'group' but usable for different purposes (iOS only).
-SP_BUILDER_DECLARE_NULLABLE(NSString *, threadIdentifier)
-/// Variable string values to be used in place of the format specifiers in titleLocArgs to use to localize the title text to the user's current localization.
-SP_BUILDER_DECLARE_NULLABLE(NSArray<NSString *> *, titleLocArgs)
-/// The key to the title string in the app's string resources to use to localize the title text to the user's current localization.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, titleLocKey)
-
-// Convenient methods
-
-/**
- Creates a Message Notification event from a user info object containing data from a push notification.
- @note The custom data of the push notification have to be tracked separately in custom entities that can be attached to this event.
- @param userInfo Dictionary with "aps" values got with the push notification.
- @param defaultTitle Title to set in the message notification event if the remote push notification is purely data and without title.
- @param defaultBody Body to set in the message notification event if the remote push notification is purely data and without body.
- @return A new MessageNotification event if the `userInfo` data were well formed.
- */
-+ (nullable SPMessageNotification *)messageNotificationWithUserInfo:(NSDictionary *)userInfo defaultTitle:(nullable NSString *)defaultTitle defaultBody:(nullable NSString *)defaultBody;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPMessageNotification.m b/Snowplow/Internal/Events/SPMessageNotification.m
deleted file mode 100644
index 4f0972ebb..000000000
--- a/Snowplow/Internal/Events/SPMessageNotification.m
+++ /dev/null
@@ -1,170 +0,0 @@
-//
-// SPMessageNotification.m
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPMessageNotification.h"
-
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "NSDictionary+SP_TypeMethods.h"
-
-#define SPMessageNotificationTriggerString(enum) [@[@"push", @"location", @"calendar", @"timeInterval", @"other"] objectAtIndex:enum]
-
-@implementation SPMessageNotification
-
-NSString * const kSPMessageNotificationSchema = @"iglu:com.snowplowanalytics.mobile/message_notification/jsonschema/1-0-0";
-NSString * const kSPMessageNotificationParamAction = @"action";
-NSString * const kSPMessageNotificationParamMessageNotificationAttachments = @"attachments";
-NSString * const kSPMessageNotificationParamBody = @"body";
-NSString * const kSPMessageNotificationParamBodyLocArgs = @"bodyLocArgs";
-NSString * const kSPMessageNotificationParamBodyLocKey = @"bodyLocKey";
-NSString * const kSPMessageNotificationParamCategory = @"category";
-NSString * const kSPMessageNotificationParamContentAvailable = @"contentAvailable";
-NSString * const kSPMessageNotificationParamGroup = @"group";
-NSString * const kSPMessageNotificationParamIcon = @"icon";
-NSString * const kSPMessageNotificationParamNotificationCount = @"notificationCount";
-NSString * const kSPMessageNotificationParamNotificationTimestamp = @"notificationTimestamp";
-NSString * const kSPMessageNotificationParamSound = @"sound";
-NSString * const kSPMessageNotificationParamSubtitle = @"subtitle";
-NSString * const kSPMessageNotificationParamTag = @"tag";
-NSString * const kSPMessageNotificationParamThreadIdentifier = @"threadIdentifier";
-NSString * const kSPMessageNotificationParamTitle = @"title";
-NSString * const kSPMessageNotificationParamTitleLocArgs = @"titleLocArgs";
-NSString * const kSPMessageNotificationParamTitleLocKey = @"titleLocKey";
-NSString * const kSPMessageNotificationParamTrigger = @"trigger";
-
-- (instancetype)initWithTitle:(NSString *)title
-                         body:(NSString *)body
-                      trigger:(SPMessageNotificationTrigger)trigger {
-    if (self = [super init]) {
-        _title = title;
-        _body = body;
-        _trigger = trigger;
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, action)
-SP_BUILDER_METHOD(NSArray<SPMessageNotificationAttachment *> *, attachments)
-SP_BUILDER_METHOD(NSArray<NSString *> *, bodyLocArgs)
-SP_BUILDER_METHOD(NSString *, bodyLocKey)
-SP_BUILDER_METHOD(NSString *, category)
-SP_BUILDER_METHOD(NSNumber *, contentAvailable)
-SP_BUILDER_METHOD(NSString *, group)
-SP_BUILDER_METHOD(NSString *, icon)
-SP_BUILDER_METHOD(NSNumber *, notificationCount)
-SP_BUILDER_METHOD(NSString *, notificationTimestamp)
-SP_BUILDER_METHOD(NSString *, sound)
-SP_BUILDER_METHOD(NSString *, subtitle)
-SP_BUILDER_METHOD(NSString *, tag)
-SP_BUILDER_METHOD(NSString *, threadIdentifier)
-SP_BUILDER_METHOD(NSArray<NSString *> *, titleLocArgs)
-SP_BUILDER_METHOD(NSString *, titleLocKey)
-
-// --- Tracker Methods
-
-- (NSString *)schema {
-    return kSPMessageNotificationSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_action forKey:kSPMessageNotificationParamAction];
-    if (_attachments.count > 0) {
-        NSMutableArray<NSDictionary<NSString *, NSObject *> *> *dictAttachments = [NSMutableArray array];
-        for (SPMessageNotificationAttachment *attachment in _attachments) {
-            [dictAttachments addObject:attachment.data];
-        }
-        [payload setValue:dictAttachments forKey:kSPMessageNotificationParamMessageNotificationAttachments];
-    }
-    [payload setValue:_body forKey:kSPMessageNotificationParamBody];
-    if (_bodyLocArgs.count > 0) {
-        [payload setValue:_bodyLocArgs forKey:kSPMessageNotificationParamBodyLocArgs];
-    }
-    [payload setValue:_bodyLocKey forKey:kSPMessageNotificationParamBodyLocKey];
-    [payload setValue:_category forKey:kSPMessageNotificationParamCategory];
-    if (_contentAvailable) {
-        NSNumber *contentAvailableBoolTyped = @(_contentAvailable.boolValue);
-        [payload setValue:contentAvailableBoolTyped forKey:kSPMessageNotificationParamContentAvailable];
-    }
-    [payload setValue:_group forKey:kSPMessageNotificationParamGroup];
-    [payload setValue:_icon forKey:kSPMessageNotificationParamIcon];
-    [payload setValue:_notificationCount forKey:kSPMessageNotificationParamNotificationCount];
-    [payload setValue:_notificationTimestamp forKey:kSPMessageNotificationParamNotificationTimestamp];
-    [payload setValue:_sound forKey:kSPMessageNotificationParamSound];
-    [payload setValue:_subtitle forKey:kSPMessageNotificationParamSubtitle];
-    [payload setValue:_tag forKey:kSPMessageNotificationParamTag];
-    [payload setValue:_threadIdentifier forKey:kSPMessageNotificationParamThreadIdentifier];
-    [payload setValue:_title forKey:kSPMessageNotificationParamTitle];
-    if (_titleLocArgs.count > 0) {
-        [payload setValue:_titleLocArgs forKey:kSPMessageNotificationParamTitleLocArgs];
-    }
-    [payload setValue:_titleLocKey forKey:kSPMessageNotificationParamTitleLocKey];
-    [payload setValue:SPMessageNotificationTriggerString(_trigger) forKey:kSPMessageNotificationParamTrigger];
-    return payload;
-}
-
-// Convenient methods
-
-+ (SPMessageNotification *)messageNotificationWithUserInfo:(NSDictionary *)userInfo defaultTitle:(NSString *)defaultTitle defaultBody:(NSString *)defaultBody {
-    NSDictionary *aps = [userInfo sp_dictionaryForKey:@"aps" defaultValue:nil];
-    if (!aps) {
-        return nil;
-    }
-    NSDictionary *alert = [aps sp_dictionaryForKey:@"alert" defaultValue:nil];
-    if (!alert) {
-        return nil;
-    }
-    // alert fields
-    NSString *title = [alert sp_stringForKey:@"title" defaultValue:defaultTitle];
-    NSString *body = [alert sp_stringForKey:@"body" defaultValue:defaultBody];
-    if (!title || !body) {
-        return nil;
-    }
-    NSString *subtitle = [alert sp_stringForKey:@"subtitle" defaultValue:nil];
-    NSString *launchImage = [alert sp_stringForKey:@"launch-image" defaultValue:nil];
-    NSString *titleLocKey = [alert sp_stringForKey:@"title-loc-key" defaultValue:nil];
-    NSArray *titleLocArgs = [alert sp_arrayForKey:@"title-loc-args" itemClass:NSString.class defaultValue:nil];
-    NSString *locKey = [alert sp_stringForKey:@"loc-key" defaultValue:nil];
-    NSArray *locArgs = [alert sp_arrayForKey:@"loc-args" itemClass:NSString.class defaultValue:nil];
-    // aps fields
-    NSNumber *badge = [aps sp_numberForKey:@"badge" defaultValue:nil];
-    NSString *sound = [aps sp_stringForKey:@"sound" defaultValue:nil];
-    NSNumber *contentAvailable = [aps sp_numberForKey:@"content-available" defaultValue:nil];
-    NSString *category = [aps sp_stringForKey:@"category" defaultValue:nil];
-    NSString *threadId = [aps sp_stringForKey:@"thread-id" defaultValue:nil];
-    SPMessageNotification *event = [[SPMessageNotification alloc] initWithTitle:title body:body trigger:SPMessageNotificationTriggerPush];
-    event.subtitle = subtitle;
-    event.icon = launchImage;
-    event.titleLocKey = titleLocKey;
-    event.titleLocArgs = titleLocArgs;
-    event.bodyLocKey = locKey;
-    event.bodyLocArgs = locArgs;
-    event.notificationCount = badge;
-    event.sound = sound;
-    event.contentAvailable = contentAvailable;
-    event.category = category;
-    event.threadIdentifier = threadId;
-    return event;
-}
-        
-@end
diff --git a/Snowplow/Internal/Events/SPMessageNotificationAttachment.h b/Snowplow/Internal/Events/SPMessageNotificationAttachment.h
deleted file mode 100644
index 04e20d325..000000000
--- a/Snowplow/Internal/Events/SPMessageNotificationAttachment.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// SPMessageNotificationAttachment.h
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// Attachment object that identify an attachment in the MessageNotification
-NS_SWIFT_NAME(MessageNotificationAttachment)
-@interface SPMessageNotificationAttachment : NSObject
-
-extern NSString * const kSPMessageNotificationAttachmentParamIdentifier;
-extern NSString * const kSPMessageNotificationAttachmentParamType;
-extern NSString * const kSPMessageNotificationAttachmentParamUrl;
-
-@property (readonly) NSDictionary<NSString *, NSObject *> *data;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/// Attachments added to the notification (they can be part of the data object).
-- (instancetype)initWithIdentifier:(NSString *)identifier
-                              type:(NSString *)type
-                               url:(NSString *)url;
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPMessageNotificationAttachment.m b/Snowplow/Internal/Events/SPMessageNotificationAttachment.m
deleted file mode 100644
index f6e1a1359..000000000
--- a/Snowplow/Internal/Events/SPMessageNotificationAttachment.m
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// SPMessageNotificationAttachment.m
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPMessageNotificationAttachment.h"
-
-
-@interface SPMessageNotificationAttachment ()
-
-@property NSMutableDictionary<NSString *, NSObject *> *dictionary;
-
-@end
-
-@implementation SPMessageNotificationAttachment
-
-NSString * const kSPMessageNotificationAttachmentParamIdentifier = @"identifier";
-NSString * const kSPMessageNotificationAttachmentParamType = @"type";
-NSString * const kSPMessageNotificationAttachmentParamUrl = @"url";
-
-- (instancetype)initWithIdentifier:(NSString *)identifier
-                              type:(NSString *)type
-                               url:(NSString *)url {
-    if (self = [super init]) {
-        self.dictionary = [[NSMutableDictionary alloc] init];
-        [self.dictionary setObject:identifier forKey:kSPMessageNotificationAttachmentParamIdentifier];
-        [self.dictionary setObject:type forKey:kSPMessageNotificationAttachmentParamType];
-        [self.dictionary setObject:url forKey:kSPMessageNotificationAttachmentParamUrl];
-    }
-    return self;
-}
-
-- (NSDictionary<NSString *,NSObject *> *)data {
-    return [self.dictionary copy];
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPPageView.h b/Snowplow/Internal/Events/SPPageView.h
deleted file mode 100644
index 7f0aa6d47..000000000
--- a/Snowplow/Internal/Events/SPPageView.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-//  SPPageView.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/*!
- A pageview event.
- @deprecated This event has been designed for web trackers, not suitable for mobile apps. Use DeepLinkReceived event to track deep link received in the app.
- */
-NS_SWIFT_NAME(PageView)
-@interface SPPageView : SPPrimitiveAbstract
-
-/// Page url.
-@property (nonatomic, readonly) NSString *pageUrl;
-/// Page title.
-@property (nonatomic, nullable) NSString *pageTitle;
-/// Page referrer url.
-@property (nonatomic, nullable) NSString *referrer;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/// Creates a pageview event.
-/// @param pageUrl The page url.
-- (instancetype)initWithPageUrl:(NSString *)pageUrl NS_SWIFT_NAME(init(pageUrl:));
-
-/// Page title.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, pageTitle)
-/// Page referrer url.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, referrer)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPPageView.m b/Snowplow/Internal/Events/SPPageView.m
deleted file mode 100644
index 45a43e191..000000000
--- a/Snowplow/Internal/Events/SPPageView.m
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-//  SPPageView.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPPageView.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-
-@interface SPPageView ()
-
-@property (nonatomic, readwrite) NSString *pageUrl;
-
-@end
-
-@implementation SPPageView
-
-- (instancetype)initWithPageUrl:(NSString *)pageUrl {
-    if (self = [super init]) {
-        _pageUrl = pageUrl;
-        [SPUtilities checkArgument:([_pageUrl length] != 0) withMessage:@"PageURL cannot be nil or empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, pageTitle)
-SP_BUILDER_METHOD(NSString *, referrer)
-
-// --- Public Methods
-
-- (NSString *)eventName {
-    return kSPEventPageView;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_pageUrl forKey:kSPPageUrl];
-    [payload setValue:_pageTitle forKey:kSPPageTitle];
-    [payload setValue:_referrer forKey:kSPPageRefr];
-    return payload;
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPPushNotification.h b/Snowplow/Internal/Events/SPPushNotification.h
deleted file mode 100644
index a12d09cd5..000000000
--- a/Snowplow/Internal/Events/SPPushNotification.h
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-//  SPPushNotification.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-#if SNOWPLOW_TARGET_IOS
-#import <UserNotifications/UserNotifications.h>
-#endif
-
-@class SPNotificationContent;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/*!
- A push notification event.
- @deprecated This is available only on iOS. Please, use MessageNotification instead, which is available for both iOS and Android trackers.
- */
-NS_SWIFT_NAME(PushNotification)
-@interface SPPushNotification : SPSelfDescribingAbstract
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithDate:(NSString *)date action:(NSString *)action trigger:(NSString *)trigger category:(NSString *)category thread:(NSString *)thread notification:(SPNotificationContent *)notification NS_SWIFT_NAME(init(date:action:trigger:category:thread:notification:));
-
-#if SNOWPLOW_TARGET_IOS
-- (instancetype)initWithDate:(NSString *)date action:(NSString *)action notificationTrigger:(nullable UNNotificationTrigger *)trigger category:(NSString *)category thread:(NSString *)thread notification:(SPNotificationContent *)notification NS_SWIFT_NAME(init(date:action:notificationTrigger:category:thread:notification:)) NS_AVAILABLE_IOS(10.0);
-#endif
-
-@end
-
-/*!
- A notification content event. This object is used to store information that supplements a push notification event.
- @deprecated This is available only on iOS. Please, use MessageNotification instead, which is available for both iOS and Android trackers.
- */
-NS_SWIFT_NAME(NotificationContent)
-@interface SPNotificationContent : NSObject
-
-@property (nonatomic, readonly) NSString *title;
-@property (nonatomic, readonly) NSString *body;
-@property (nonatomic, readonly) NSNumber *badge;
-@property (nonatomic, nullable) NSString *subtitle;
-@property (nonatomic, nullable) NSString *sound;
-@property (nonatomic, nullable) NSString *launchImageName;
-@property (nonatomic, nullable) NSDictionary *userInfo;
-@property (nonatomic, nullable) NSArray *attachments;
-
-@property (nonatomic) NSDictionary *payload;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body badge:(NSNumber *)badge NS_SWIFT_NAME(init(title:body:badge:));
-
-SP_BUILDER_DECLARE_NULLABLE(NSString *, subtitle)
-SP_BUILDER_DECLARE_NULLABLE(NSString *, sound)
-SP_BUILDER_DECLARE_NULLABLE(NSString *, launchImageName)
-SP_BUILDER_DECLARE_NULLABLE(NSDictionary *, userInfo)
-SP_BUILDER_DECLARE_NULLABLE(NSArray *, attachments)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPPushNotification.m b/Snowplow/Internal/Events/SPPushNotification.m
deleted file mode 100644
index 00a46d399..000000000
--- a/Snowplow/Internal/Events/SPPushNotification.m
+++ /dev/null
@@ -1,197 +0,0 @@
-//
-//  SPPushNotification.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPPushNotification.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-
-
-@interface SPPushNotification ()
-
-@property NSString *date;
-@property NSString *action;
-@property NSString *trigger;
-@property NSString *category;
-@property NSString *thread;
-@property SPNotificationContent *notification;
-
-@end
-
-@implementation SPPushNotification
-
-- (instancetype)initWithDate:(NSString *)date action:(NSString *)action trigger:(NSString *)trigger category:(NSString *)category thread:(NSString *)thread notification:(SPNotificationContent *)notification {
-    if (self = [super init]) {
-        _date = date;
-        _action = action;
-        _trigger = trigger;
-        _category = category;
-        _thread = thread;
-        _notification = notification;
-        [SPUtilities checkArgument:([_date length] != 0) withMessage:@"Delivery date cannot be nil or empty."];
-        [SPUtilities checkArgument:([_action length] != 0) withMessage:@"Action cannot be nil or empty."];
-        [SPUtilities checkArgument:([_trigger length] != 0) withMessage:@"Trigger cannot be nil or empty."];
-        [SPUtilities checkArgument:([_category length] != 0) withMessage:@"Category identifier cannot be nil or empty."];
-        [SPUtilities checkArgument:([_thread length] != 0) withMessage:@"Thread identifier cannot be nil or empty."];
-    }
-    return self;
-}
-
-#if SNOWPLOW_TARGET_IOS
-- (instancetype)initWithDate:(NSString *)date action:(NSString *)action notificationTrigger:(nullable UNNotificationTrigger *)trigger category:(NSString *)category thread:(NSString *)thread notification:(SPNotificationContent *)notification {
-    if (self = [super init]) {
-        _date = date;
-        _action = action;
-        _trigger = [SPPushNotification stringFromNotificationTrigger:trigger];
-        _category = category;
-        _thread = thread;
-        _notification = notification;
-        [SPUtilities checkArgument:([_date length] != 0) withMessage:@"Delivery date cannot be nil or empty."];
-        [SPUtilities checkArgument:([_action length] != 0) withMessage:@"Action cannot be nil or empty."];
-        [SPUtilities checkArgument:([_trigger length] != 0) withMessage:@"Trigger cannot be nil or empty."];
-        [SPUtilities checkArgument:([_category length] != 0) withMessage:@"Category identifier cannot be nil or empty."];
-        [SPUtilities checkArgument:([_thread length] != 0) withMessage:@"Thread identifier cannot be nil or empty."];
-    }
-    return self;
-}
-
-
-+ (NSString *)stringFromNotificationTrigger:(nullable UNNotificationTrigger *)trigger  API_AVAILABLE(ios(10.0)) {
-    NSMutableString * triggerType = [[NSMutableString alloc] initWithString:@"UNKNOWN"];
-    NSString * triggerClass = NSStringFromClass([trigger class]);
-    if ([triggerClass isEqualToString:@"UNTimeIntervalNotificationTrigger"]) {
-        [triggerType setString:@"TIME_INTERVAL"];
-    } else if ([triggerClass isEqualToString:@"UNCalendarNotificationTrigger"]) {
-        [triggerType setString:@"CALENDAR"];
-    } else if ([triggerClass isEqualToString:@"UNLocationNotificationTrigger"]) {
-        [triggerType setString:@"LOCATION"];
-    } else if ([triggerClass isEqualToString:@"UNPushNotificationTrigger"]) {
-        [triggerType setString:@"PUSH"];
-    }
-    return (NSString *)triggerType;
-}
-#endif
-
-// MARK: - Public Methods
-
-- (NSString *)schema {
-    return kSPPushNotificationSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    return @{
-        kSPPushNotification: _notification.payload,
-        kSPPushTrigger: _trigger,
-        kSPPushAction: _action,
-        kSPPushDeliveryDate: _date,
-        kSPPushCategoryId: _category,
-        kSPPushThreadId: _thread,
-    };
-}
-
-@end
-
-
-// MARK:- SPNotificationContent
-
-@interface SPNotificationContent ()
-
-@property (nonatomic, readwrite) NSString *title;
-@property (nonatomic, readwrite) NSString *body;
-@property (nonatomic, readwrite) NSNumber *badge;
-
-@end
-
-@implementation SPNotificationContent
-
-- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body badge:(NSNumber *)badge {
-    if (self = [super init]) {
-        _title = title;
-        _body = body;
-        _badge = badge;
-        [SPUtilities checkArgument:([_title length] != 0) withMessage:@"Title cannot be nil or empty."];
-        [SPUtilities checkArgument:([_body length] != 0) withMessage:@"Body cannot be nil or empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, subtitle)
-SP_BUILDER_METHOD(NSString *, sound)
-SP_BUILDER_METHOD(NSString *, launchImageName)
-SP_BUILDER_METHOD(NSDictionary *, userInfo)
-SP_BUILDER_METHOD(NSArray *, attachments)
-
-// --- Public Methods
-
-- (NSDictionary *)payload {
-    NSMutableDictionary * event = [[NSMutableDictionary alloc] init];
-    [event setObject:_title forKey:kSPPnTitle];
-    [event setObject:_body forKey:kSPPnBody];
-    [event setValue:_badge forKey:kSPPnBadge];
-    if (_subtitle != nil) {
-        [event setObject:_subtitle forKey:kSPPnSubtitle];
-    }
-    if (_subtitle != nil) {
-        [event setObject:_subtitle forKey:kSPPnSubtitle];
-    }
-    if (_sound != nil) {
-        [event setObject:_sound forKey:kSPPnSound];
-    }
-    if (_launchImageName != nil) {
-        [event setObject:_launchImageName forKey:kSPPnLaunchImageName];
-    }
-    if (_userInfo != nil) {
-        NSMutableDictionary * aps = nil;
-        NSMutableDictionary * newUserInfo = nil;
-
-        // modify contentAvailable value "1" and "0" to @YES and @NO to comply with schema
-        if (![[_userInfo valueForKeyPath:@"aps.contentAvailable"] isEqual:nil] &&
-            [[_userInfo objectForKey:@"aps"] isKindOfClass:[NSDictionary class]]) {
-            aps = [[NSMutableDictionary alloc] initWithDictionary:_userInfo[@"aps"]];
-
-            if ([[_userInfo valueForKeyPath:@"aps.contentAvailable"] isEqual:@1]) {
-                [aps setObject:@YES forKey:@"contentAvailable"];
-            } else if ([[_userInfo valueForKeyPath:@"aps.contentAvailable"] isEqual:@0]) {
-                [aps setObject:@NO forKey:@"contentAvailable"];
-            }
-            newUserInfo = [[NSMutableDictionary alloc] initWithDictionary:_userInfo];
-            [newUserInfo setObject:aps forKey:@"aps"];
-        }
-        [event setObject:[[NSDictionary alloc] initWithDictionary:newUserInfo] forKey:kSPPnUserInfo];
-    }
-    if (_attachments.count) {
-        NSMutableArray<NSDictionary *> * converting = [[NSMutableArray alloc] init];
-        NSMutableDictionary * newAttachment = [[NSMutableDictionary alloc] init];
-        for (id attachment in _attachments) {
-            newAttachment[kSPPnAttachmentId] = [attachment valueForKey:@"identifier"];
-            newAttachment[kSPPnAttachmentUrl] = [attachment valueForKey:@"URL"];
-            newAttachment[kSPPnAttachmentType] = [attachment valueForKey:@"type"];
-            [converting addObject: (NSDictionary *)[newAttachment copy]];
-            [newAttachment removeAllObjects];
-        }
-        [event setObject:[NSArray arrayWithArray:converting] forKey:kSPPnAttachments];
-    }
-    return [[NSDictionary alloc] initWithDictionary:event copyItems:YES];
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPScreenView.h b/Snowplow/Internal/Events/SPScreenView.h
deleted file mode 100644
index dfe2ec11a..000000000
--- a/Snowplow/Internal/Events/SPScreenView.h
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-//  SPScreenView.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-@class SPScreenState;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A screenview event.
-NS_SWIFT_NAME(ScreenView)
-@interface SPScreenView : SPSelfDescribingAbstract
-
-/// Name of the screen.
-@property (nonatomic, readonly) NSString *name;
-/// Identifier of the screen.
-@property (nonatomic, readonly) NSString *screenId;
-/// Type of screen.
-@property (nonatomic, nullable) NSString *type;
-/// Name of the previous screen.
-@property (nonatomic, nullable) NSString *previousName;
-/// Identifier of the previous screen.
-@property (nonatomic, nullable) NSString *previousId;
-/// Type of the previous screen.
-@property (nonatomic, nullable) NSString *previousType;
-/// Type of transition between previous and current screen,
-@property (nonatomic, nullable) NSString *transitionType;
-/// Name of the ViewController subclass.
-@property (nonatomic, nullable) NSString *viewControllerClassName;
-/// Name of the top ViewController subclass.
-@property (nonatomic, nullable) NSString *topViewControllerClassName;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/// Creates a screenview event.
-/// @param name Name of the screen.
-/// @param screenId Identifier of the screen.
-- (instancetype)initWithName:(NSString *)name screenId:(nullable NSUUID *)screenId NS_SWIFT_NAME(init(name:screenId:));
-
-/// Type of screen.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, type)
-/// Name of the previous screen.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, previousName)
-/// Identifier of the previous screen.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, previousId)
-/// Type of the previous screen.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, previousType)
-/// Type of transition between previous and current screen,
-SP_BUILDER_DECLARE_NULLABLE(NSString *, transitionType)
-/// Name of the ViewController subclass.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, viewControllerClassName)
-/// Name of the top ViewController subclass.
-SP_BUILDER_DECLARE_NULLABLE(NSString *, topViewControllerClassName)
-
-@end
-
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPScreenView.m b/Snowplow/Internal/Events/SPScreenView.m
deleted file mode 100644
index 95c8b4b93..000000000
--- a/Snowplow/Internal/Events/SPScreenView.m
+++ /dev/null
@@ -1,78 +0,0 @@
-//
-//  SPScreenView.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPScreenView.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "SPScreenState.h"
-
-
-@interface SPScreenView ()
-
-@property (nonatomic, readwrite) NSString *name;
-@property (nonatomic, readwrite) NSString *screenId;
-
-@end
-
-@implementation SPScreenView
-
-- (instancetype)initWithName:(NSString *)name screenId:(nullable NSUUID *)screenId {
-    if (self = [super init]) {
-        _screenId = [(screenId ?: [NSUUID UUID]) UUIDString];
-        _name = name;
-        [SPUtilities checkArgument:([_name length] != 0) withMessage:@"Name cannot be empty."];
-        [SPUtilities checkArgument:([SPUtilities isUUIDString:_screenId]) withMessage:@"ScreenID has to be a valid UUID string."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, type)
-SP_BUILDER_METHOD(NSString *, previousName)
-SP_BUILDER_METHOD(NSString *, previousId)
-SP_BUILDER_METHOD(NSString *, previousType)
-SP_BUILDER_METHOD(NSString *, transitionType)
-SP_BUILDER_METHOD(NSString *, viewControllerClassName)
-SP_BUILDER_METHOD(NSString *, topViewControllerClassName)
-
-// --- Public Methods
-
-- (NSString *)schema {
-    return kSPScreenViewSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_name forKey:kSPSvName];
-    [payload setValue:_screenId forKey:kSPSvScreenId];
-    [payload setValue:_type forKey:kSPSvType];
-    [payload setValue:_previousName forKey:kSPSvPreviousName];
-    [payload setValue:_previousType forKey:kSPSvPreviousType];
-    [payload setValue:_previousId forKey:kSPSvPreviousScreenId];
-    [payload setValue:_transitionType forKey:kSPSvTransitionType];
-    return payload;
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPSelfDescribing.h b/Snowplow/Internal/Events/SPSelfDescribing.h
deleted file mode 100644
index 07950b438..000000000
--- a/Snowplow/Internal/Events/SPSelfDescribing.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-//  SPSelfDescribing.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A self-describing event.
-NS_SWIFT_NAME(SelfDescribing)
-@interface SPSelfDescribing : SPSelfDescribingAbstract
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithEventData:(SPSelfDescribingJson *)eventData NS_SWIFT_NAME(init(eventData:));
-
-- (instancetype)initWithSchema:(NSString *)schema payload:(NSDictionary<NSString *, NSObject *> *)payload NS_SWIFT_NAME(init(schema:payload:));
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPSelfDescribing.m b/Snowplow/Internal/Events/SPSelfDescribing.m
deleted file mode 100644
index 94bb818fb..000000000
--- a/Snowplow/Internal/Events/SPSelfDescribing.m
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-//  SPUnstructured.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSelfDescribing.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-@implementation SPSelfDescribing {
-    SPSelfDescribingJson * _eventData;
-    NSString * _schema;
-    NSDictionary<NSString *, NSObject *> * _payload;
-}
-
-- (instancetype)initWithEventData:(SPSelfDescribingJson *)eventData {
-    if (self = [super init]) {
-        [SPUtilities checkArgument:(eventData != nil) withMessage:@"EventData cannot be nil."];
-        _schema = eventData.schema;
-        [SPUtilities checkArgument:(_schema != nil) withMessage:@"EventData schema cannot be nil."];
-        [SPUtilities checkArgument:([eventData.data isKindOfClass:[NSDictionary<NSString *, NSObject *> class]]) withMessage:@"EventData payload is not correctly formatted."];
-        _payload = (NSDictionary<NSString *, NSObject *> *)eventData.data;
-        [SPUtilities checkArgument:[NSJSONSerialization isValidJSONObject:_payload] withMessage:@"EventData payload has to be JSON serializable."];
-        _eventData = eventData;
-    }
-    return self;
-}
-
-- (instancetype)initWithSchema:(NSString *)schema payload:(NSDictionary<NSString *,NSObject *> *)payload {
-    if (self = [super init]) {
-        _schema = schema;
-        [SPUtilities checkArgument:(_schema != nil) withMessage:@"EventData schema cannot be nil."];
-        _payload = payload;
-        [SPUtilities checkArgument:[NSJSONSerialization isValidJSONObject:_payload] withMessage:@"EventData payload has to be JSON serializable."];
-    }
-    return self;
-}
-
-// --- Public Methods
-
-- (NSString *)schema {
-    return _schema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    return _payload;
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPStructured.h b/Snowplow/Internal/Events/SPStructured.h
deleted file mode 100644
index dac55a3c5..000000000
--- a/Snowplow/Internal/Events/SPStructured.h
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-//  SPStructured.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A structured event.
-NS_SWIFT_NAME(Structured)
-@interface SPStructured : SPPrimitiveAbstract
-
-@property (nonatomic, readonly) NSString *category;
-@property (nonatomic, readonly) NSString *action;
-@property (nonatomic, nullable) NSString *label;
-@property (nonatomic, nullable) NSString *property;
-@property (nonatomic, nullable) NSNumber *value;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithCategory:(NSString *)category action:(NSString *)action NS_SWIFT_NAME(init(category:action:));
-
-SP_BUILDER_DECLARE_NULLABLE(NSString *, label)
-SP_BUILDER_DECLARE_NULLABLE(NSString *, property)
-SP_BUILDER_DECLARE_NULLABLE(NSNumber *, value)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPStructured.m b/Snowplow/Internal/Events/SPStructured.m
deleted file mode 100644
index 306f98096..000000000
--- a/Snowplow/Internal/Events/SPStructured.m
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-//  SPStructured.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPStructured.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-
-@interface SPStructured ()
-
-@property (nonatomic, readwrite) NSString *category;
-@property (nonatomic, readwrite) NSString *action;
-
-@end
-
-@implementation SPStructured
-
-- (instancetype)initWithCategory:(NSString *)category action:(NSString *)action {
-    if (self = [super init]) {
-        _category = category;
-        _action = action;
-        [SPUtilities checkArgument:([_category length] != 0) withMessage:@"Category cannot be nil or empty."];
-        [SPUtilities checkArgument:([_action length] != 0) withMessage:@"Action cannot be nil or empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, label)
-SP_BUILDER_METHOD(NSString *, property)
-SP_BUILDER_METHOD(NSNumber *, value)
-
-// --- Public Methods
-
-- (NSString *)eventName {
-    return kSPEventStructured;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_category forKey:kSPStuctCategory];
-    [payload setValue:_action forKey:kSPStuctAction];
-    [payload setValue:_label forKey:kSPStuctLabel];
-    [payload setValue:_property forKey:kSPStuctProperty];
-    if (_value) [payload setObject:[NSString stringWithFormat:@"%.17g", [_value doubleValue]] forKey:kSPStuctValue];
-    return payload;
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPTiming.h b/Snowplow/Internal/Events/SPTiming.h
deleted file mode 100644
index a5736d58c..000000000
--- a/Snowplow/Internal/Events/SPTiming.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-//  SPTiming.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A timing event.
-NS_SWIFT_NAME(Timing)
-@interface SPTiming : SPSelfDescribingAbstract
-
-@property (nonatomic, readonly) NSString *category;
-@property (nonatomic, readonly) NSString *variable;
-@property (nonatomic, readonly) NSNumber *timing;
-@property (nonatomic, nullable) NSString *label;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithCategory:(NSString *)category variable:(NSString *)variable timing:(NSNumber *)timing NS_SWIFT_NAME(init(category:variable:timing:));
-
-SP_BUILDER_DECLARE_NULLABLE(NSString *, label)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPTiming.m b/Snowplow/Internal/Events/SPTiming.m
deleted file mode 100644
index 20bb3849c..000000000
--- a/Snowplow/Internal/Events/SPTiming.m
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-//  SPTiming.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPTiming.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPSelfDescribingJson.h"
-
-
-@interface SPTiming ()
-
-@property (nonatomic, readwrite) NSString *category;
-@property (nonatomic, readwrite) NSString *variable;
-@property (nonatomic, readwrite) NSNumber *timing;
-
-@end
-
-@implementation SPTiming
-
-- (instancetype)initWithCategory:(NSString *)category variable:(NSString *)variable timing:(NSNumber *)timing {
-    if (self = [super init]) {
-        _category = category;
-        _variable = variable;
-        _timing = timing;
-        [SPUtilities checkArgument:([_category length] != 0) withMessage:@"Category cannot be nil or empty."];
-        [SPUtilities checkArgument:([_variable length] != 0) withMessage:@"Variable cannot be nil or empty."];
-    }
-    return self;
-}
-
-// --- Builder Methods
-
-SP_BUILDER_METHOD(NSString *, label)
-
-// --- Public Methods
-
-- (NSString *)schema {
-    return kSPUserTimingsSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_category forKey:kSPUtCategory];
-    [payload setValue:_variable forKey:kSPUtVariable];
-    [payload setValue:_timing forKey:kSPUtTiming];
-    [payload setValue:_label forKey:kSPUtLabel];
-    return payload;
-}
-
-@end
diff --git a/Snowplow/Internal/Events/SPTrackerError.h b/Snowplow/Internal/Events/SPTrackerError.h
deleted file mode 100644
index b140aaa69..000000000
--- a/Snowplow/Internal/Events/SPTrackerError.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-//  SPTrackerError.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(TrackerError)
-@interface SPTrackerError : SPSelfDescribingAbstract
-
-- (instancetype)initWithSource:(NSString *)source message:(NSString *)message;
-
-- (instancetype)initWithSource:(NSString *)source message:(NSString *)message error:(nullable NSError *)error exception:(NSException *)exception;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Events/SPTrackerError.m b/Snowplow/Internal/Events/SPTrackerError.m
deleted file mode 100644
index 71f5f0eaf..000000000
--- a/Snowplow/Internal/Events/SPTrackerError.m
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-//  SPTrackerError.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerError.h"
-#import "SPTrackerConstants.h"
-
-const int kMaxMessageLength = 2048;
-const int kMaxStackLength = 8192;
-const int kMaxExceptionNameLength = 1024;
-
-@interface SPTrackerError ()
-
-@property (nonatomic) NSString *source;
-@property (nonatomic) NSString *message;
-@property (nonatomic) NSError *error;
-@property (nonatomic) NSException *exception;
-
-@end
-
-@implementation SPTrackerError
-
-- (instancetype)initWithSource:(NSString *)source message:(NSString *)message {
-    return [self initWithSource:source message:message];
-}
-
-- (instancetype)initWithSource:(NSString *)source message:(NSString *)message error:(NSError *)error exception:(NSException *)exception {
-    if (self = [super init]) {
-        self.source = source;
-        self.message = message;
-        self.error = error;
-        self.exception = exception;
-    }
-    return self;
-}
-
-// -- Public methods
-
-- (NSString *)schema {
-    return kSPDiagnosticErrorSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:self.source forKey:kSPDiagnosticErrorClassName];
-    [payload setValue:[self truncate:self.message maxLength:kMaxMessageLength] forKey:kSPDiagnosticErrorMessage];
-    if (self.error) {
-        [payload setValue:self.error forKey:kSPDiagnosticErrorExceptionName];
-    }
-    if (self.exception) {
-        [payload setValue:[self truncate:self.exception.name maxLength:kMaxExceptionNameLength] forKey:kSPDiagnosticErrorExceptionName];
-        NSArray<NSString *> *symbols = [self.exception callStackSymbols];
-        if ([symbols count]) {
-            NSString *stackTrace = [NSString stringWithFormat:@"Stacktrace:\n%@", symbols];
-            [payload setValue:[self truncate:stackTrace maxLength:kMaxStackLength] forKey:kSPDiagnosticErrorStack];
-        }
-    }
-    return payload;
-}
-
-// -- Private methods
-
-- (NSString *)truncate:(NSString *)s maxLength:(int)maxLength {
-    return [s substringToIndex:MIN(s.length, maxLength)];
-}
-
-@end
diff --git a/Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.h b/Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.h
deleted file mode 100644
index 939d48c20..000000000
--- a/Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPGDPRConfigurationUpdate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGDPRConfiguration.h"
-#import "SPGdprContext.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPGDPRConfigurationUpdate : SPGDPRConfiguration
-
-@property (nonatomic, nullable) SPGDPRConfiguration *sourceConfig;
-
-@property (nonatomic) SPGdprContext *gdpr;
-
-@property (nonatomic) BOOL isEnabled;
-@property (nonatomic) BOOL gdprUpdated;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.m b/Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.m
deleted file mode 100644
index 2f9cfc24b..000000000
--- a/Snowplow/Internal/GDPR/SPGDPRConfigurationUpdate.m
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPGDPRConfigurationUpdate.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGDPRConfigurationUpdate.h"
-
-@implementation SPGDPRConfigurationUpdate
-
-SP_DIRTY_GETTER(SPGdprProcessingBasis, basisForProcessing)
-SP_DIRTY_GETTER(NSString *, documentId)
-SP_DIRTY_GETTER(NSString *, documentVersion)
-SP_DIRTY_GETTER(NSString *, documentDescription)
-
-// Private methods
-
-- (BOOL)basisForProcessingUpdated { return self.gdprUpdated; }
-- (BOOL)documentIdUpdated { return self.gdprUpdated; }
-- (BOOL)documentVersionUpdated { return self.gdprUpdated; }
-- (BOOL)documentDescriptionUpdated { return self.gdprUpdated; }
-
-@end
diff --git a/Snowplow/Internal/GDPR/SPGDPRController.h b/Snowplow/Internal/GDPR/SPGDPRController.h
deleted file mode 100644
index 80f54f114..000000000
--- a/Snowplow/Internal/GDPR/SPGDPRController.h
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-//  SPGDPRController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPGDPRConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(GDPRController)
-@protocol SPGDPRController <SPGDPRConfigurationProtocol>
-
-/**
- * Whether the recorded GDPR context is enabled and will be attached as context.
- */
-@property (nonatomic, readonly) BOOL isEnabled;
-
-/**
- * Reset GDPR context to be sent with each event.
- * @param basisForProcessing GDPR Basis for processing.
- * @param documentId ID of a GDPR basis document.
- * @param documentVersion Version of the document.
- * @param documentDescription Description of the document.
- */
-- (void)resetWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                             documentId:(nullable NSString *)documentId
-                        documentVersion:(nullable NSString *)documentVersion
-                    documentDescription:(nullable NSString *)documentDescription
-NS_SWIFT_NAME(reset(basis:documentId:documentVersion:documentDescription:));
-
-/**
- * Enable the GDPR context recorded.
- */
-- (BOOL)enable;
-
-/**
- * Disable the GDPR context recorded.
- */
-- (void)disable;
-
-@end
-
-NS_ASSUME_NONNULL_END
-
diff --git a/Snowplow/Internal/GDPR/SPGDPRControllerImpl.m b/Snowplow/Internal/GDPR/SPGDPRControllerImpl.m
deleted file mode 100644
index 000214599..000000000
--- a/Snowplow/Internal/GDPR/SPGDPRControllerImpl.m
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-//  SPGDPRControllerImpl.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGDPRControllerImpl.h"
-#import "SPGdprContext.h"
-#import "SPTracker.h"
-#import "SPGDPRConfigurationUpdate.h"
-
-@interface SPGDPRControllerImpl ()
-
-@property (nonatomic, nullable) SPGdprContext *gdpr;
-
-@end
-
-@implementation SPGDPRControllerImpl
-
-// MARK: - Methods
-
-- (void)resetWithBasis:(SPGdprProcessingBasis)basisForProcessing
-            documentId:(nullable NSString *)documentId
-       documentVersion:(nullable NSString *)documentVersion
-   documentDescription:(nullable NSString *)documentDescription
-{
-    [self.tracker setGdprContextWithBasis:basisForProcessing
-                               documentId:documentId
-                          documentVersion:documentVersion
-                      documentDescription:documentDescription];
-    self.gdpr = self.tracker.gdprContext;
-    self.dirtyConfig.gdpr = self.gdpr;
-    self.dirtyConfig.gdprUpdated = YES;
-}
-
-- (void)disable {
-    self.dirtyConfig.isEnabled = NO;
-    [self.tracker disableGdprContext];
-}
-
-- (BOOL)isEnabled {
-    return self.tracker.gdprContext != nil;
-}
-
-- (BOOL)enable {
-    if (!self.gdpr) {
-        return NO;
-    }
-    [self.tracker enableGdprContextWithBasis:self.gdpr.basis
-                                  documentId:self.gdpr.documentId
-                             documentVersion:self.gdpr.documentVersion
-                         documentDescription:self.gdpr.documentDescription];
-    self.dirtyConfig.isEnabled = YES;
-    return YES;
-}
-
-- (SPGdprProcessingBasis)basisForProcessing {
-    return [self.gdpr basis];
-}
-
-- (NSString *)documentId {
-    return [self.gdpr documentId];
-}
-
-- (NSString *)documentVersion {
-    return [self.gdpr documentVersion];
-}
-
-- (NSString *)documentDescription {
-    return [self.gdpr documentDescription];
-}
-
-// MARK: - Private methods
-
-- (SPTracker *)tracker {
-    return self.serviceProvider.tracker;
-}
-
-- (SPGDPRConfigurationUpdate *)dirtyConfig {
-    return self.serviceProvider.gdprConfigurationUpdate;
-}
-
-@end
diff --git a/Snowplow/Internal/GDPR/SPGdprContext.h b/Snowplow/Internal/GDPR/SPGdprContext.h
deleted file mode 100644
index 5644c86df..000000000
--- a/Snowplow/Internal/GDPR/SPGdprContext.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-//  SPGdprContext.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPGDPRConfiguration.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(GDPRContext)
-@interface SPGdprContext : NSObject
-
-@property (nonatomic, readonly) SPGdprProcessingBasis basis;
-@property (nonatomic, readonly, nullable) NSString *documentId;
-@property (nonatomic, readonly, nullable) NSString *documentVersion;
-@property (nonatomic, readonly, nullable) NSString *documentDescription;
-
-/*!
- @brief Set a GDPR context for the tracker
- @param basisForProcessing Enum one of valid legal bases for processing.
- @param documentId Document ID.
- @param documentVersion Version of the document.
- @param documentDescription Description of the document.
- */
-- (nullable instancetype)initWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                            documentId:(nullable NSString *)documentId
-                       documentVersion:(nullable NSString *)documentVersion
-                   documentDescription:(nullable NSString *)documentDescription;
-
-/// Return context with value stored about GDPR processing.
-- (SPSelfDescribingJson *)context;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GDPR/SPGdprContext.m b/Snowplow/Internal/GDPR/SPGdprContext.m
deleted file mode 100644
index 4a6203a64..000000000
--- a/Snowplow/Internal/GDPR/SPGdprContext.m
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-//  SPGdprContext.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGdprContext.h"
-#import "SPTrackerConstants.h"
-
-@interface SPGdprContext ()
-
-@property (nonatomic) NSString *basisString;
-@property (nonatomic) SPGdprProcessingBasis basis;
-@property (nonatomic) NSString *documentId;
-@property (nonatomic) NSString *documentVersion;
-@property (nonatomic) NSString *documentDescription;
-
-@end
-
-@implementation SPGdprContext
-
-- (instancetype)initWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                   documentId:(NSString *)documentId
-              documentVersion:(NSString *)documentVersion
-          documentDescription:(NSString *)documentDescription
-{
-    if (self = [super init]) {
-        self.basisString = [self stringFromProcessingBasis:basisForProcessing];
-        if (!self.basisString) {
-            return nil;
-        }
-        self.basis = basisForProcessing;
-        self.documentId = documentId;
-        self.documentVersion = documentVersion;
-        self.documentDescription = documentDescription;
-    }
-    return self;
-}
-
-- (SPSelfDescribingJson *)context {
-    NSMutableDictionary<NSString *, NSString *> *data = [NSMutableDictionary dictionary];
-    [data setValue:self.basisString forKey:kSPBasisForProcessing];
-    [data setValue:self.documentId forKey:kSPDocumentId];
-    [data setValue:self.documentVersion forKey:kSPDocumentVersion];
-    [data setValue:self.documentDescription forKey:kSPDocumentDescription];
-    return [[SPSelfDescribingJson alloc] initWithSchema:kSPGdprContextSchema andData:data];
-}
-
-#pragma mark Private methods
-
-- (NSString *)stringFromProcessingBasis:(SPGdprProcessingBasis)basis {
-    switch (basis) {
-        case SPGdprProcessingBasisConsent:
-            return @"consent";
-        case SPGdprProcessingBasisContract:
-            return @"contract";
-        case SPGdprProcessingBasisLegalObligation:
-            return @"legal_obligation";
-        case SPGdprProcessingBasisVitalInterest:
-            return @"vital_interests";
-        case SPGdprProcessingBasisPublicTask:
-            return @"public_task";
-        case SPGdprProcessingBasisLegitimateInterests:
-            return @"legitimate_interests";
-        default:
-            return nil;
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/GlobalContexts/SPGlobalContext.h b/Snowplow/Internal/GlobalContexts/SPGlobalContext.h
deleted file mode 100644
index 22663cf51..000000000
--- a/Snowplow/Internal/GlobalContexts/SPGlobalContext.h
+++ /dev/null
@@ -1,129 +0,0 @@
-//
-//  SPContextGenerator.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPEventBase.h"
-
-@class SPSelfDescribingJson;
-@class SPSchemaRuleset;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/*!
- @brief Block signature for context generators, takes event information and generates a context.
- @param event informations about the event to process.
- @return a user-generated self-describing JSON.
- */
-typedef NSArray<SPSelfDescribingJson *> * _Nullable (^SPGeneratorBlock)(id<SPInspectableEvent> event);
-
-/*!
- @brief Block signature for context filtering, takes event information and decide if the context needs to be generated.
- @param event informations about the event to process.
- @return weather the context has to be generated.
-*/
-typedef BOOL (^SPFilterBlock)(id<SPInspectableEvent> event);
-
-#pragma mark - SPContextGenerator
-
-/*!
- @protocol SPContextGenerator
- @brief A context generator used to generate global contexts.
- */
-NS_SWIFT_NAME(ContextGenerator)
-@protocol SPContextGenerator <NSObject>
-
-/*!
- @brief Takes event information and decide if the context needs to be generated.
- @param event informations about the event to process.
- @return weather the context has to be generated.
- */
-- (BOOL)filterFromEvent:(id<SPInspectableEvent>)event;
-
-/*!
- @brief Takes event information and generates a context.
- @param event informations about the event to process.
- @return a user-generated self-describing JSON.
- */
-- (nullable NSArray<SPSelfDescribingJson *> *)generatorFromEvent:(id<SPInspectableEvent>)event;
-
-@end
-
-#pragma mark - SPGlobalContext
-
-NS_SWIFT_NAME(GlobalContext)
-@interface SPGlobalContext : NSObject
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-/*!
- Initialize a Global Context generator with a custom SPContextGenerator.
- @param generator Implementation of SPContextGenerator protocol.
- */
-- (instancetype)initWithContextGenerator:(id<SPContextGenerator>)generator;
-
-/*!
- Initialize a Global Context generator with static contexts.
- @param staticContexts Static contexts added to all the events.
- */
-- (instancetype)initWithStaticContexts:(NSArray<SPSelfDescribingJson *> *)staticContexts;
-/*!
- Initialize a Global Context generator with a generator block.
- @param generator Generator block able to generate multiple contexts.
- */
-- (instancetype)initWithGenerator:(SPGeneratorBlock)generator;
-
-/*!
- Initialize a Global Context generator with static contexts and a ruleset filter.
- @param staticContexts Static contexts added to all the events conforming with `ruleset`.
- @param ruleset Rule set to apply to events to check weather or not the contexts have to be added.
- */
-- (instancetype)initWithStaticContexts:(NSArray<SPSelfDescribingJson *> *)staticContexts ruleset:(nullable SPSchemaRuleset *)ruleset;
-/*!
- Initialize a Global Context generator with static contexts and a ruleset filter.
- @param generator Generator block able to generate multiple contexts.
- @param ruleset Rule set to apply to events to check weather or not the contexts have to be added.
- */
-- (instancetype)initWithGenerator:(SPGeneratorBlock)generator ruleset:(nullable SPSchemaRuleset *)ruleset;
-
-/*!
- Initialize a Global Context generator with static contexts and a ruleset filter.
- @param staticContexts Static contexts added to all the events conforming with `ruleset`.
- @param filter Filter to apply to events to check weather or not the contexts have to be added.
- */
-- (instancetype)initWithStaticContexts:(NSArray<SPSelfDescribingJson *> *)staticContexts filter:(nullable SPFilterBlock)filter;
-/*!
- Initialize a Global Context generator with static contexts and a ruleset filter.
- @param generator Generator block able to generate multiple contexts.
- @param filter Filter to apply to events to check weather or not the contexts have to be added.
- */
-- (instancetype)initWithGenerator:(SPGeneratorBlock)generator filter:(nullable SPFilterBlock)filter NS_DESIGNATED_INITIALIZER;
-
-/*!
- Generate contexts based on event details and internal filter and generator.
- @param event Event details used to filter and generate contexts.
- @return Generated contexts.
- */
-- (NSArray<SPSelfDescribingJson *> *)contextsFromEvent:(id<SPInspectableEvent>)event;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GlobalContexts/SPGlobalContext.m b/Snowplow/Internal/GlobalContexts/SPGlobalContext.m
deleted file mode 100644
index 9c2f250e7..000000000
--- a/Snowplow/Internal/GlobalContexts/SPGlobalContext.m
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-//  SPContextGenerator.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGlobalContext.h"
-#import "SPSchemaRuleset.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPGlobalContext ()
-
-@property (nonatomic) SPGeneratorBlock generator;
-@property (nonatomic, nullable) SPFilterBlock filter;
-
-@end
-
-NS_ASSUME_NONNULL_END
-
-@implementation SPGlobalContext
-
-- (instancetype)initWithContextGenerator:(id<SPContextGenerator>)generator {
-    return [self initWithGenerator:^NSArray<SPSelfDescribingJson *> *(id<SPInspectableEvent> event) {
-        return [generator generatorFromEvent:event];
-    } filter:^BOOL(id<SPInspectableEvent> event) {
-        return [generator filterFromEvent:event];
-    }];
-}
-
-- (instancetype)initWithStaticContexts:(NSArray<SPSelfDescribingJson *> *)staticContexts {
-    return [self initWithGenerator:^NSArray<SPSelfDescribingJson *> *(id<SPInspectableEvent> event) {
-        return staticContexts;
-    }];
-}
-
-- (instancetype)initWithGenerator:(SPGeneratorBlock)generator {
-    return [self initWithGenerator:generator filter:nil];
-}
-
-- (instancetype)initWithStaticContexts:(NSArray<SPSelfDescribingJson *> *)staticContexts ruleset:(SPSchemaRuleset *)ruleset {
-    return [self initWithGenerator:^NSArray<SPSelfDescribingJson *> *(id<SPInspectableEvent> event) {
-        return staticContexts;
-    } filter:ruleset.filterBlock];
-}
-
-- (instancetype)initWithGenerator:(SPGeneratorBlock)generator ruleset:(SPSchemaRuleset *)ruleset {
-    return [self initWithGenerator:generator filter:ruleset.filterBlock];
-}
-
-- (instancetype)initWithStaticContexts:(NSArray<SPSelfDescribingJson *> *)staticContexts filter:(SPFilterBlock)filter {
-    return [self initWithGenerator:^NSArray<SPSelfDescribingJson *> *(id<SPInspectableEvent> event) {
-        return staticContexts;
-    } filter:filter];
-}
-
-- (instancetype)initWithGenerator:(SPGeneratorBlock)generator filter:(SPFilterBlock)filter {
-    if (self = [super init]) {
-        self.generator = generator;
-        self.filter = filter;
-    }
-    return self;
-}
-
-- (NSArray<SPSelfDescribingJson *> *)contextsFromEvent:(id<SPInspectableEvent>)event {
-    if (!event) {
-        return @[];
-    }
-    if (!self.generator) {
-        return @[];
-    }
-    if (self.filter && !self.filter(event)) {
-        return @[];
-    }
-    return self.generator(event) ?: @[];
-}
-
-@end
diff --git a/Snowplow/Internal/GlobalContexts/SPGlobalContextsController.h b/Snowplow/Internal/GlobalContexts/SPGlobalContextsController.h
deleted file mode 100644
index ccdabd033..000000000
--- a/Snowplow/Internal/GlobalContexts/SPGlobalContextsController.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-//  SPGlobalContextsController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPGlobalContextsConfiguration.h"
-#import "SPGlobalContext.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(GlobalContextsController)
-@protocol SPGlobalContextsController <SPGlobalContextsConfigurationProtocol>
-
-@property (nonatomic, nonnull, readonly) NSArray<NSString *> *tags;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.h b/Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.h
deleted file mode 100644
index f9d731ef7..000000000
--- a/Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-//  SPGlobalContextsControllerImpl.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPGlobalContextsController.h"
-#import "SPController.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(GlobalContextsControllerImpl)
-@interface SPGlobalContextsControllerImpl : SPController <SPGlobalContextsController>
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.m b/Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.m
deleted file mode 100644
index adcd697b7..000000000
--- a/Snowplow/Internal/GlobalContexts/SPGlobalContextsControllerImpl.m
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-//  SPGlobalContextsControllerImpl.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPGlobalContextsControllerImpl.h"
-#import "SPTracker.h"
-
-
-@implementation SPGlobalContextsControllerImpl
-
-- (void)setContextGenerators:(NSMutableDictionary<NSString *,SPGlobalContext *> *)contextGenerators {
-    [self.tracker setGlobalContextGenerators:contextGenerators];
-}
-
-- (NSMutableDictionary<NSString *,SPGlobalContext *> *)contextGenerators {
-    return [self.tracker globalContextGenerators];
-}
-
-- (BOOL)addWithTag:(nonnull NSString *)tag contextGenerator:(nonnull SPGlobalContext *)generator {
-    return [self.tracker addGlobalContext:generator tag:tag];
-}
-
-- (nullable SPGlobalContext *)removeWithTag:(nonnull NSString *)tag {
-    return [self.tracker removeGlobalContext:tag];
-}
-
-- (NSArray<NSString *> *)tags {
-    return [self.tracker globalContextTags];
-}
-
-// MARK: - Private methods
-
-- (SPTracker *)tracker {
-    return self.serviceProvider.tracker;
-}
-
-@end
diff --git a/Snowplow/Internal/GlobalContexts/SPSchemaRule.h b/Snowplow/Internal/GlobalContexts/SPSchemaRule.h
deleted file mode 100644
index 6b6e826a7..000000000
--- a/Snowplow/Internal/GlobalContexts/SPSchemaRule.h
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-//  SNOWSchemaRule.h
-//  Snowplow-iOS
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(SchemaRule)
-@interface SPSchemaRule: NSObject <NSCopying>
-
-@property (nonatomic, copy, readonly) NSString *rule;
-@property (nonatomic, copy, readonly) NSArray<NSString *> *ruleParts;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-- (id)initWithRule:(NSString *)rule NS_DESIGNATED_INITIALIZER;
-
-/*!
- Weather the `uri` match the stored rule.
- @param uri URI to check.
- @return Weather the uri is allowed.
- */
-- (BOOL)matchWithUri:(NSString *)uri;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GlobalContexts/SPSchemaRule.m b/Snowplow/Internal/GlobalContexts/SPSchemaRule.m
deleted file mode 100644
index f2dce2bf7..000000000
--- a/Snowplow/Internal/GlobalContexts/SPSchemaRule.m
+++ /dev/null
@@ -1,155 +0,0 @@
-//
-//  SNOWSchemaRule.m
-//  Snowplow-iOS
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSchemaRule.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPSchemaRule ()
-
-@property (nonatomic, copy, readwrite) NSString *rule;
-@property (nonatomic, copy, readwrite) NSArray<NSString *> *ruleParts;
-
-@end
-
-@implementation SPSchemaRule
-
-static NSString * const kRulePattern = @"^iglu:((?:(?:[a-zA-Z0-9-_]+|\\*)\\.)+(?:[a-zA-Z0-9-_]+|\\*))\\/([a-zA-Z0-9-_\\.]+|\\*)\\/([a-zA-Z0-9-_\\.]+|\\*)\\/([1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)$";
-static NSString * const kUriPattern = @"^iglu:((?:(?:[a-zA-Z0-9-_]+)\\.)+(?:[a-zA-Z0-9-_]+))\\/([a-zA-Z0-9-_]+)\\/([a-zA-Z0-9-_]+)\\/([1-9][0-9]*)\\-(0|[1-9][0-9]*)\\-(0|[1-9][0-9]*)$";
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    return [[SPSchemaRule alloc] initWithRule:self.rule];
-}
-
-- (id)initWithRule:(NSString *)rule {
-    if (self = [super init]) {
-        if (!rule || rule.length == 0) {
-            return nil;
-        }
-        _rule = rule;
-        NSArray<NSString *> *parts = [self partsFromUri:rule regexPattern:kRulePattern];
-        // reject rule if vendor format isn't valid
-        if (!parts.count || ![self validateVendor:parts[0]]) {
-            return nil;
-        }
-        _ruleParts = parts;
-    }
-    return self;
-}
-
-- (BOOL)matchWithUri:(NSString *)uri {
-    if (!uri) {
-        return NO;
-    }
-    NSArray<NSString *> *uriParts = [self partsFromUri:uri regexPattern:kUriPattern];
-    if (uriParts.count < _ruleParts.count) {
-        return NO;
-    }
-    // Check vendor part
-    NSArray<NSString *> *ruleVendor = [_ruleParts[0] componentsSeparatedByString:@"."];
-    NSArray<NSString *> *uriVendor = [uriParts[0] componentsSeparatedByString:@"."];
-    if (uriVendor.count != ruleVendor.count) {
-        return NO;
-    }
-    NSUInteger index = 0;
-    for (NSString *ruleVendorPart in ruleVendor) {
-        if (![@"*" isEqualToString:ruleVendorPart] && ![uriVendor[index] isEqualToString:ruleVendorPart]) {
-            return NO;
-        }
-        index++;
-    }
-    // Check the rest of the rule
-    index = 1;
-    for (NSString *rulePart in [_ruleParts subarrayWithRange:NSMakeRange(1, _ruleParts.count-1)]) {
-        if (![@"*" isEqualToString:rulePart] && ![uriParts[index] isEqualToString:rulePart]) {
-            return NO;
-        }
-        index++;
-    }
-    return YES;
-}
-
-#pragma mark - Private methods
-
-- (nullable NSArray<NSString *> *)partsFromUri:(NSString *)uri regexPattern:(NSString *)pattern {
-    NSError *error = NULL;
-    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
-    NSTextCheckingResult *match = [regex firstMatchInString:uri options:0 range:NSMakeRange(0, uri.length)];
-    NSMutableArray *parts = [NSMutableArray arrayWithCapacity:6];
-    if (!match) {
-        return nil;
-    }
-    for (int i = 1; i < match.numberOfRanges; i++) {
-        if (i > 6) {
-            return nil;
-        }
-        NSString *part = [uri substringWithRange:[match rangeAtIndex:i]];
-        [parts setObject:part.copy atIndexedSubscript:i-1];
-    }
-    return parts;
-}
-
-- (BOOL)validateVendor:(NSString *)vendor {
-    // the components array will be generated like this from vendor:
-    // "com.acme.marketing" => ["com", "acme", "marketing"]
-    NSArray<NSString *> *components = [vendor componentsSeparatedByString:@"."];
-    // check that vendor doesn't begin or end with period
-    // e.g. ".snowplowanalytics.snowplow." => ["", "snowplowanalytics", "snowplow", ""]
-    if (components.count > 1 && (!components[0].length || !components[components.count-1].length)) {
-        return NO;
-    }
-    // reject vendors with criteria that are too broad & don't make sense, i.e. "*.*.marketing"
-    if ([@"*" isEqualToString:components[0]] || [@"*" isEqualToString:components[1]]) {
-        return NO;
-    }
-    // now validate the remaining parts, vendors should follow matching that never breaks trailing specificity
-    // in other words, once we use an asterisk, we must continue using asterisks for parts or stop
-    // e.g. "com.acme.marketing.*.*" is allowed, but "com.acme.*.marketing.*" or "com.acme.*.marketing" is forbidden
-    if (components.count <= 2) return YES;
-    // trailingComponents are the remaining parts after the first two
-    NSArray<NSString *> *trailingComponents = [components subarrayWithRange:NSMakeRange(2, components.count-2)];
-    BOOL asterisk = NO;
-    for (NSString *part in trailingComponents) {
-        if ([@"*" isEqualToString:part]) { // mark when we've found a wildcard
-            asterisk = true;
-        } else if (asterisk) { // invalid when alpha parts come after wildcard
-            return NO;
-        }
-    }
-    return YES;
-}
-
-#pragma mark - Overriden methods
-
-- (BOOL)isEqual:(id)object {
-    if (self == object) {
-        return YES;
-    }
-    return [object isKindOfClass:SPSchemaRule.class] && [self.rule isEqualToString:[(SPSchemaRule *)object rule]];
-}
-
-- (NSUInteger)hash {
-    return [_rule hash];
-}
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GlobalContexts/SPSchemaRuleset.h b/Snowplow/Internal/GlobalContexts/SPSchemaRuleset.h
deleted file mode 100644
index 65b80c572..000000000
--- a/Snowplow/Internal/GlobalContexts/SPSchemaRuleset.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-//  SNOWSchemaRuleset.h
-//  Snowplow-iOS
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPSchemaRule.h"
-#import "SPGlobalContext.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(SchemaRuleset)
-@interface SPSchemaRuleset : NSObject <NSCopying>
-
-@property (readonly, copy) NSArray<NSString *> *denied;
-@property (readonly, copy) NSArray<NSString *> *allowed;
-
-@property (nonatomic, readonly) SPFilterBlock filterBlock;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-/*!
- Generate a set of rules based on allowed and denied event schemas.
- @param allowed Rules of allowed schemas.
- */
-+ (SPSchemaRuleset *)rulesetWithAllowedList:(NSArray<NSString *> *)allowed;
-/*!
- Generate a set of rules based on allowed and denied event schemas.
- @param denied Rules of denied schemas.
- */
-+ (SPSchemaRuleset *)rulesetWithDeniedList:(NSArray<NSString *> *)denied;
-/*!
- Generate a set of rules based on allowed and denied event schemas.
- @param allowed Rules of allowed schemas.
- @param denied Rules of denied schemas.
- */
-+ (SPSchemaRuleset *)rulesetWithAllowedList:(NSArray<NSString *> *)allowed andDeniedList:(NSArray<NSString *> *)denied;
-
-/*!
- Weather the `uri` match the stored rules.
- @param uri URI to check.
- @return Weather the uri is allowed.
- */
-- (BOOL)matchWithUri:(NSString *)uri;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/GlobalContexts/SPSchemaRuleset.m b/Snowplow/Internal/GlobalContexts/SPSchemaRuleset.m
deleted file mode 100644
index 4c3c0b355..000000000
--- a/Snowplow/Internal/GlobalContexts/SPSchemaRuleset.m
+++ /dev/null
@@ -1,123 +0,0 @@
-//
-//  SNOWSchemaRuleset.m
-//  Snowplow-iOS
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSchemaRuleset.h"
-#import "SPSchemaRule.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPSchemaRuleset ()
-
-@property (nonatomic, copy) NSMutableArray<SPSchemaRule *> *rulesAllowed;
-@property (nonatomic, copy) NSMutableArray<SPSchemaRule *> *rulesDenied;
-
-@end
-
-@implementation SPSchemaRuleset: NSObject
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    return [SPSchemaRuleset rulesetWithAllowedList:self.allowed andDeniedList:self.denied];
-}
-
-- (instancetype)initWithAllowedList:(NSArray<NSString *> *)allowed andDeniedList:(NSArray<NSString *> *)denied {
-    self = [super init];
-    if (self) {
-        NSMutableArray<SPSchemaRule *> *rulesAllowed = [NSMutableArray array];
-        for (NSString *rule in allowed) {
-            SPSchemaRule *schemaRule = [[SPSchemaRule alloc] initWithRule:rule];
-            if (schemaRule) {
-                [rulesAllowed addObject:schemaRule];
-            }
-        }
-        self.rulesAllowed = rulesAllowed;
-        NSMutableArray<SPSchemaRule *> *rulesDenied = [NSMutableArray array];
-        for (NSString *rule in denied) {
-            SPSchemaRule *schemaRule = [[SPSchemaRule alloc] initWithRule:rule];
-            if (schemaRule) {
-                [rulesDenied addObject:schemaRule];
-            }
-        }
-        self.rulesDenied = rulesDenied;
-    }
-    return self;
-}
-
-+ (SPSchemaRuleset *)rulesetWithAllowedList:(NSArray<NSString *> *)allowed andDeniedList:(NSArray<NSString *> *)denied {
-    return [[SPSchemaRuleset alloc] initWithAllowedList:allowed andDeniedList:denied];
-}
-
-+ (SPSchemaRuleset *)rulesetWithAllowedList:(NSArray<NSString *> *)allowed {
-    return [SPSchemaRuleset rulesetWithAllowedList:allowed andDeniedList:@[]];
-}
-
-+ (SPSchemaRuleset *)rulesetWithDeniedList:(NSArray<NSString *> *)denied {
-    return [SPSchemaRuleset rulesetWithAllowedList:@[] andDeniedList:denied];
-}
-
-- (BOOL)matchWithUri:(NSString *)uri {
-    if (!uri) {
-        return NO;
-    }
-    for (SPSchemaRule *rule in self.rulesDenied) {
-        if ([rule matchWithUri:uri]) {
-            return NO;
-        }
-    }
-    if (!self.rulesAllowed.count) {
-        return YES;
-    }
-    for (SPSchemaRule *rule in self.rulesAllowed) {
-        if ([rule matchWithUri:uri]) {
-            return YES;
-        }
-    }
-    return NO;
-}
-
-- (NSArray<NSString *> *)allowed {
-    NSMutableArray<NSString *> *result = [NSMutableArray<NSString *> array];
-    for (SPSchemaRule *schemaRule in self.rulesAllowed) {
-        [result addObject:schemaRule.rule.copy];
-    }
-    return result;
-}
-
-- (NSArray<NSString *> *)denied {
-    NSMutableArray<NSString *> *result = [NSMutableArray<NSString *> array];
-    for (SPSchemaRule *schemaRule in self.rulesDenied) {
-        [result addObject:schemaRule.rule.copy];
-    }
-    return result;
-}
-
-- (SPFilterBlock)filterBlock {
-    return ^BOOL(id<SPInspectableEvent> event) {
-        return [self matchWithUri:event.schema];
-    };
-}
-
-- (NSString *)description {
-    return [NSString stringWithFormat:@"SchemaRuleset:\r\n  allowed:%@\r\n  denied:%@\r\n", self.allowed, self.denied];
-}
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Logger/SPLogger.h b/Snowplow/Internal/Logger/SPLogger.h
deleted file mode 100644
index 574417601..000000000
--- a/Snowplow/Internal/Logger/SPLogger.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-//  SPLogger.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPLoggerDelegate.h"
-
-#define SPLogTrack(optionalErrorOrException, format, ...) [SPLogger diagnostic:NSStringFromClass(self.class) message:[[NSString alloc] initWithFormat:format, ##__VA_ARGS__] errorOrException:optionalErrorOrException]
-#define SPLogError(format, ...) [SPLogger error:NSStringFromClass(self.class) message:[[NSString alloc] initWithFormat:format, ##__VA_ARGS__]]
-#define SPLogDebug(format, ...) [SPLogger debug:NSStringFromClass(self.class) message:[[NSString alloc] initWithFormat:format, ##__VA_ARGS__]]
-#define SPLogVerbose(format, ...) [SPLogger verbose:NSStringFromClass(self.class) message:[[NSString alloc] initWithFormat:format, ##__VA_ARGS__]]
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(Logger)
-@interface SPLogger : NSObject
-
-@property (class, nonatomic) SPLogLevel logLevel;
-@property (class, nonatomic, nullable) id<SPLoggerDelegate> delegate;
-
-+ (void)diagnostic:(NSString *)tag message:(NSString *)message errorOrException:(nullable id)errorOrException;
-+ (void)error:(NSString *)tag message:(NSString *)message;
-+ (void)debug:(NSString *)tag message:(NSString *)message;
-+ (void)verbose:(NSString *)tag message:(NSString *)message;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Logger/SPLogger.m b/Snowplow/Internal/Logger/SPLogger.m
deleted file mode 100644
index ad447fd23..000000000
--- a/Snowplow/Internal/Logger/SPLogger.m
+++ /dev/null
@@ -1,145 +0,0 @@
-//
-//  SPLogger.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPLogger.h"
-
-@interface SPLogger ()
-@property (nonatomic, weak) id<SPLoggerDelegate> delegate;
-@property (nonatomic) SPLogLevel logLevel;
-@end
-
-@implementation SPLogger
-
-+ (void)setDelegate:(id<SPLoggerDelegate>)delegate {
-    SPLogger *logger = [SPLogger shared];
-    logger.delegate = delegate;
-}
-
-+ (id<SPLoggerDelegate>)delegate {
-    SPLogger *logger = [SPLogger shared];
-    return logger.delegate;
-}
-
-+ (void)setLogLevel:(SPLogLevel)logLevel {
-    SPLogger *logger = [SPLogger shared];
-    logger.logLevel = logLevel;
-    if (logLevel == SPLogLevelOff) {
-        #ifdef SNOWPLOW_DEBUG
-            logger.logLevel = SPLogLevelDebug;
-        #elif DEBUG
-            logger.logLevel = SPLogLevelError;
-        #else
-            logger.logLevel = SPLogLevelOff;
-        #endif
-    }
-}
-
-+ (SPLogLevel)logLevel {
-    SPLogger *logger = [SPLogger shared];
-    return logger.logLevel ?: SPLogLevelOff;
-}
-
-+ (void)diagnostic:(NSString *)tag message:(NSString *)message errorOrException:(id)errorOrException {
-    SPLogger *logger = [SPLogger shared];
-    [logger log:SPLogLevelError tag:tag message:message];
-    [logger trackErrorWithTag:tag message:message errorOrException:errorOrException];
-}
-
-+ (void)error:(NSString *)tag message:(NSString *)message {
-    [[SPLogger shared] log:SPLogLevelError tag:tag message:message];
-}
-
-+ (void)debug:(NSString *)tag message:(NSString *)message {
-    [[SPLogger shared] log:SPLogLevelDebug tag:tag message:message];
-}
-
-+ (void)verbose:(NSString *)tag message:(NSString *)message {
-    [[SPLogger shared] log:SPLogLevelVerbose tag:tag message:message];
-}
-
-#pragma mark - Private methods
-
-+ (SPLogger *)shared {
-    static SPLogger *sharedLogger = nil;
-    @synchronized(self) {
-        if (!sharedLogger) {
-            sharedLogger = [[self alloc] init];
-            sharedLogger.logLevel = SPLogLevelOff;
-        }
-    }
-    return sharedLogger;
-}
-
-- (void)log:(SPLogLevel)level tag:(NSString *)tag message:(NSString *)message {
-    if (level > self.logLevel) {
-        return;
-    }
-    if (self.delegate) {
-        switch (level) {
-            case SPLogLevelOff:
-                // do nothing.
-                break;
-            case SPLogLevelError:
-                [self.delegate error:tag message:message];
-                break;
-            case SPLogLevelDebug:
-                [self.delegate debug:tag message:message];
-                break;
-            case SPLogLevelVerbose:
-                [self.delegate verbose:tag message:message];
-                break;
-        }
-        return;
-    }
-    #if SNOWPLOW_TEST
-        // NSLog doesn't work on test target
-        NSString *output = [NSString stringWithFormat:@"[%@] %@: %@", @[@"Off", @"Error", @"Error", @"Debug", @"Verbose"][level], tag, message];
-        printf("%s", [output UTF8String]);
-    #elif DEBUG
-        // Log should be printed only during debugging
-        NSLog(@"[%@] %@: %@", @[@"Off", @"Error", @"Debug", @"Verbose"][level], tag, message);
-    #endif
-}
-
-- (void)trackErrorWithTag:(NSString *)tag message:(NSString *)message errorOrException:(id)errorOrException {
-    NSError *error;
-    NSException *exception;
-    if ([errorOrException isKindOfClass:NSError.class]) {
-        error = (NSError *)errorOrException;
-    } else if ([errorOrException isKindOfClass:NSException.class]) {
-        exception = (NSException *)errorOrException;
-    }
-    
-    // Construct userInfo
-    NSMutableDictionary<NSString *, NSObject *> *userInfo = [NSMutableDictionary new];
-    userInfo[@"tag"] = tag;
-    userInfo[@"message"] = message;
-    userInfo[@"error"] = error;
-    userInfo[@"exception"] = exception;
-
-    // Send notification to tracker
-    [[NSNotificationCenter defaultCenter]
-     postNotificationName:@"SPTrackerDiagnostic"
-     object:self
-     userInfo:userInfo];
-}
-
-@end
diff --git a/Snowplow/Internal/Logger/SPLoggerDelegate.h b/Snowplow/Internal/Logger/SPLoggerDelegate.h
deleted file mode 100644
index d81e3ba8d..000000000
--- a/Snowplow/Internal/Logger/SPLoggerDelegate.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  SPLoggerDelegate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-NS_ASSUME_NONNULL_BEGIN
-
-typedef NS_ENUM(NSInteger, SPLogLevel) {
-    SPLogLevelOff = 0,
-    SPLogLevelError,
-    SPLogLevelDebug,
-    SPLogLevelVerbose,
-} NS_SWIFT_NAME(LogLevel);
-
-/*!
- @brief Logger delegate to implement in oder to receive logs from the tracker.
-*/
-NS_SWIFT_NAME(LoggerDelegate)
-@protocol SPLoggerDelegate <NSObject>
-- (void)error:(NSString *)tag message:(NSString *)message;
-- (void)debug:(NSString *)tag message:(NSString *)message;
-- (void)verbose:(NSString *)tag message:(NSString *)message;
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.h b/Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.h
deleted file mode 100644
index b8bef5939..000000000
--- a/Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.h
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-//  SPDefaultNetworkConnection.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPNetworkConnection.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(DefaultNetworkConnectionBuilder)
-@protocol SPDefaultNetworkConnectionBuilder <NSObject>
-
-/*!
- @brief Builder method to set the collector endpoint.
- @param urlEndpoint The collector endpoint.
- */
-- (void) setUrlEndpoint:(NSString *)urlEndpoint;
-
-/*!
- @brief Builder method to set HTTP method.
- @param method Should be SPHttpMethodGet or SPHttpMethodPost.
- */
-- (void) setHttpMethod:(SPHttpMethod)method;
-
-/*!
- @brief Builder method to set thread pool size.
- @param emitThreadPoolSize The number of threads used by the emitter.
- */
-- (void) setEmitThreadPoolSize:(NSUInteger)emitThreadPoolSize;
-
-/*!
- @brief Builder method to set byte limit for GET requests.
- @param byteLimitGet Maximum event size for a GET request.
- */
-- (void) setByteLimitGet:(NSUInteger)byteLimitGet;
-
-/*!
- @brief Builder method to set byte limit for POST requests.
- @param byteLimitPost Maximum event size for a POST request.
- */
-- (void) setByteLimitPost:(NSUInteger)byteLimitPost;
-
-/*!
- @brief Builder method to set a custom POST path.
- @param customPath A custom path that is used on the endpoint to send requests.
- */
-- (void) setCustomPostPath:(NSString *)customPath;
-
-/*!
- @brief Builder method to set request headers.
- @param requestHeadersKeyValue custom headers (key, value) for http requests.
- */
-- (void) setRequestHeaders:(NSDictionary<NSString *, NSString *> *)requestHeadersKeyValue;
-
-/*!
- @brief Builder method to set the server anonymisation flag.
- @param serverAnonymisation Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
- */
-- (void) setServerAnonymisation:(BOOL)serverAnonymisation;
-
-@end
-
-NS_SWIFT_NAME(DefaultNetworkConnection)
-@interface SPDefaultNetworkConnection : NSObject <SPNetworkConnection, SPDefaultNetworkConnectionBuilder>
-
-/*!
- @brief Builds the DefaultNetworkConnection using a build block of functions.
- */
-+ (instancetype) build:(void(^)(id<SPDefaultNetworkConnectionBuilder>builder))buildBlock;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.m b/Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.m
deleted file mode 100644
index 979554c12..000000000
--- a/Snowplow/Internal/NetworkConnection/SPDefaultNetworkConnection.m
+++ /dev/null
@@ -1,236 +0,0 @@
-//
-//  SPDefaultNetworkConnection.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPDefaultNetworkConnection.h"
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPLogger.h"
-
-@implementation SPDefaultNetworkConnection {
-    SPHttpMethod _httpMethod;
-    SPProtocol _protocol;
-    NSString *_urlString;
-    NSUInteger _emitThreadPoolSize;
-    NSUInteger _byteLimitGet;
-    NSUInteger _byteLimitPost;
-    NSString *_customPostPath;
-    NSDictionary<NSString *, NSString *> *_requestHeaders;
-    BOOL _serverAnonymisation;
-
-    NSOperationQueue *_dataOperationQueue;
-    NSURL *_urlEndpoint;
-    BOOL _builderFinished;
-}
-
-+ (instancetype)build:(void(^)(id<SPDefaultNetworkConnectionBuilder>builder))buildBlock {
-    SPDefaultNetworkConnection* connection = [[SPDefaultNetworkConnection alloc] initWithDefaultValues];
-    if (buildBlock) {
-        buildBlock(connection);
-    }
-    [connection setup];
-    return connection;
-}
-
-- (instancetype)initWithDefaultValues {
-    if (self = [super init]) {
-        _httpMethod = SPHttpMethodPost;
-        _protocol = SPProtocolHttps;
-        _emitThreadPoolSize = 15;
-        _byteLimitGet = 40000;
-        _byteLimitPost = 40000;
-        _customPostPath = nil;
-        _requestHeaders = nil;
-        _dataOperationQueue = [[NSOperationQueue alloc] init];
-        _builderFinished = NO;
-        _serverAnonymisation = NO;
-    }
-    return self;
-}
-
-- (void) setup {
-    // Decode url to extract protocol
-    NSURL *url = [[NSURL alloc] initWithString:_urlString];
-    NSString *endpoint = _urlString;
-    if ([url.scheme isEqualToString:@"https"]) {
-        _protocol = SPProtocolHttps;
-    } else if ([url.scheme isEqualToString:@"http"]) {
-        _protocol = SPProtocolHttp;
-    } else {
-        _protocol = SPProtocolHttps;
-        endpoint = [NSString stringWithFormat:@"https://%@", _urlString];
-    }
-    
-    // Configure
-    NSString *urlPrefix = _protocol == SPProtocolHttp ? @"http://" : @"https://";
-    NSString *urlSuffix = _httpMethod == SPHttpMethodGet ? kSPEndpointGet : kSPEndpointPost;
-    if (_customPostPath && _httpMethod == SPHttpMethodPost) {
-        urlSuffix = _customPostPath;
-    }
-
-    // Remove trailing slashes from endpoint to avoid double slashes when appending path
-    endpoint = [endpoint stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]];
-
-    _urlEndpoint = [[NSURL URLWithString:endpoint] URLByAppendingPathComponent:urlSuffix];
-    
-    // Log
-    if ([_urlEndpoint scheme] && [_urlEndpoint host]) {
-        SPLogDebug(@"Emitter URL created successfully '%@'", _urlEndpoint);
-    } else {
-        SPLogDebug(@"Invalid emitter URL: '%@'", _urlEndpoint);
-    }
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    [userDefaults setObject:endpoint forKey:kSPErrorTrackerUrl];
-    [userDefaults setObject:urlSuffix forKey:kSPErrorTrackerProtocol];
-    [userDefaults setObject:urlPrefix forKey:kSPErrorTrackerMethod];
-    
-    _builderFinished = YES;
-}
-
-// Required
-
-- (void)setUrlEndpoint:(NSString *)urlEndpoint {
-    _urlString = urlEndpoint;
-    if (_builderFinished) {
-        [self setup];
-    }
-}
-
-- (void)setHttpMethod:(SPHttpMethod)method {
-    _httpMethod = method;
-    if (_builderFinished && _urlEndpoint != nil) {
-        [self setup];
-    }
-}
-
-- (void)setEmitThreadPoolSize:(NSUInteger)emitThreadPoolSize {
-    _emitThreadPoolSize = emitThreadPoolSize;
-    if (_dataOperationQueue.maxConcurrentOperationCount != emitThreadPoolSize) {
-        _dataOperationQueue.maxConcurrentOperationCount = _emitThreadPoolSize;
-    }
-}
-
-- (void)setByteLimitGet:(NSUInteger)byteLimitGet {
-    _byteLimitGet = byteLimitGet;
-}
-
-- (void)setByteLimitPost:(NSUInteger)byteLimitPost {
-    _byteLimitPost = byteLimitPost;
-}
-
-- (void)setCustomPostPath:(NSString *)customPath {
-    _customPostPath = customPath;
-}
-
-- (void)setRequestHeaders:(NSDictionary<NSString *,NSString *> *)requestHeadersKeyValue {
-    _requestHeaders = requestHeadersKeyValue;
-}
-
-- (void)setServerAnonymisation:(BOOL)serverAnonymisation {
-    _serverAnonymisation = serverAnonymisation;
-}
-
-// MARK: - Implement SPNetworkConnection protocol
-
-- (SPHttpMethod)httpMethod {
-    return _httpMethod;
-}
-
-- (NSURL *)url {
-    return _urlEndpoint.copy;
-}
-
-- (NSArray<SPRequestResult *> *)sendRequests:(NSArray<SPRequest *> *)requests {
-    NSMutableArray<SPRequestResult *> *results = [NSMutableArray new];
-    
-    for (SPRequest *request in requests) {
-        NSMutableURLRequest *urlRequest = _httpMethod == SPHttpMethodGet
-        ? [self buildGetRequest:request]
-        : [self buildPostRequest:request];
-
-        [_dataOperationQueue addOperationWithBlock:^{
-            //source: https://forums.developer.apple.com/thread/11519
-            __block NSHTTPURLResponse *httpResponse = nil;
-            __block NSError *connectionError = nil;
-            dispatch_semaphore_t sem;
-            
-            sem = dispatch_semaphore_create(0);
-            
-            [[[NSURLSession sharedSession] dataTaskWithRequest:urlRequest
-                                             completionHandler:^(NSData *data, NSURLResponse *urlResponse, NSError *error) {
-                
-                connectionError = error;
-                httpResponse = (NSHTTPURLResponse*)urlResponse;
-                dispatch_semaphore_signal(sem);
-            }] resume];
-            
-            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
-
-            SPRequestResult *result = [[SPRequestResult alloc] initWithStatusCode:[httpResponse statusCode] oversize:request.oversize storeIds:request.emitterEventIds];
-            if (![result isSuccessful]) {
-                SPLogError(@"Connection error: %@", connectionError);
-            }
-
-            @synchronized (results) {
-                [results addObject:result];
-            }
-        }];
-    }
-    [_dataOperationQueue waitUntilAllOperationsAreFinished];
-    return results;
-}
-
-// MARK: - Private methods
-
-- (NSMutableURLRequest *)buildPostRequest:(SPRequest *)request {
-    NSData *requestData = [NSJSONSerialization dataWithJSONObject:[request.payload getAsDictionary] options:0 error:nil];
-    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_urlEndpoint.absoluteString]];
-    [urlRequest setValue:[NSString stringWithFormat:@"%@", @(requestData.length).stringValue] forHTTPHeaderField:@"Content-Length"];
-    [urlRequest setValue:kSPAcceptContentHeader forHTTPHeaderField:@"Accept"];
-    [urlRequest setValue:kSPContentTypeHeader forHTTPHeaderField:@"Content-Type"];
-    if (_serverAnonymisation) {
-        [urlRequest setValue:@"*" forHTTPHeaderField:@"SP-Anonymous"];
-    }
-    [self applyValuesAndHeaderFields:_requestHeaders toRequest:urlRequest];
-    [urlRequest setHTTPMethod:@"POST"];
-    [urlRequest setHTTPBody:requestData];
-    return urlRequest;
-}
-
-- (NSMutableURLRequest *)buildGetRequest:(SPRequest *)request {
-    NSDictionary<NSString *, NSObject *> *payload = [request.payload getAsDictionary];
-    NSString *url = [NSString stringWithFormat:@"%@?%@", _urlEndpoint.absoluteString, [SPUtilities urlEncodeDictionary:payload]];
-    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
-    [urlRequest setValue:kSPAcceptContentHeader forHTTPHeaderField:@"Accept"];
-    if (_serverAnonymisation) {
-        [urlRequest setValue:@"*" forHTTPHeaderField:@"SP-Anonymous"];
-    }
-    [self applyValuesAndHeaderFields:_requestHeaders toRequest:urlRequest];
-    [urlRequest setHTTPMethod:@"GET"];
-    return urlRequest;
-}
-
-- (void)applyValuesAndHeaderFields:(NSDictionary<NSString *, NSString *> *)requestHeaders toRequest:(NSMutableURLRequest *)request {
-    [requestHeaders enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
-        [request setValue:obj forHTTPHeaderField:key];
-    }];
-}
-
-@end
diff --git a/Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.h b/Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.h
deleted file mode 100644
index c35dda9f0..000000000
--- a/Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-//  SPNetworkConfigurationUpdate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPTrackerConstants.h"
-#import "SPNetworkConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPNetworkConfigurationUpdate : NSObject
-
-@property (nonatomic, nullable) SPNetworkConfiguration *sourceConfig;
-
-@property (nonatomic, nullable) NSString *customPostPath;
-@property (nonatomic, nullable) NSDictionary<NSString *, NSString *> *requestHeaders;
-
-- (nullable NSString *)endpoint;
-- (SPHttpMethod)method;
-- (SPProtocol)protocol;
-- (nullable id<SPNetworkConnection>)networkConnection;
-
-SP_DIRTYFLAG(customPostPath)
-SP_DIRTYFLAG(requestHeaders)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.m b/Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.m
deleted file mode 100644
index 626414f05..000000000
--- a/Snowplow/Internal/NetworkConnection/SPNetworkConfigurationUpdate.m
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-//  SPNetworkConfigurationUpdate.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPNetworkConfigurationUpdate.h"
-
-@implementation SPNetworkConfigurationUpdate
-
-- (NSString *)endpoint {
-    return [self.sourceConfig endpoint];
-}
-
-- (SPHttpMethod)method {
-    return [self.sourceConfig method];
-}
-
-- (SPProtocol)protocol {
-    return [self.sourceConfig protocol];
-}
-
-- (id<SPNetworkConnection>)networkConnection {
-    return [self.sourceConfig networkConnection];
-}
-
-// SP_DIRTY_GETTER replacement as NetworkConfigurationUpdate doesn't extend NetworkConfiguration like the others updater classes.
-- (NSString *)customPostPath { return self.customPostPathUpdated ? _customPostPath : self.sourceConfig.customPostPath; }
-- (NSDictionary *)requestHeaders { return self.requestHeadersUpdated ? _requestHeaders : self.sourceConfig.requestHeaders; }
-
-@end
diff --git a/Snowplow/Internal/NetworkConnection/SPNetworkConnection.h b/Snowplow/Internal/NetworkConnection/SPNetworkConnection.h
deleted file mode 100644
index 93af1b16d..000000000
--- a/Snowplow/Internal/NetworkConnection/SPNetworkConnection.h
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-//  SPNetworkConnection.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPRequest.h"
-#import "SPRequestResult.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/*!
- @brief An enum for HTTP method types.
- */
-typedef NS_ENUM(NSInteger, SPHttpMethod) {
-    /*! GET request. */
-    SPHttpMethodGet,
-    /*! POST request. */
-    SPHttpMethodPost
-} NS_SWIFT_NAME(HttpMethodOptions);
-
-/*!
- @brief An enum for HTTP security.
- */
-typedef NS_ENUM(NSInteger, SPProtocol) {
-    /*! Use HTTP. */
-    SPProtocolHttp,
-    /*! Use HTTP over TLS. */
-    SPProtocolHttps
-} NS_SWIFT_NAME(ProtocolOptions);
-
-/**
- * Interface for the component that
- * sends events to the collector.
- */
-NS_SWIFT_NAME(NetworkConnection)
-@protocol SPNetworkConnection <NSObject>
-
-/**
- * Send requests to the collector.
- * @param requests to send,
- * @return results of the sending operation.
- */
-- (NSArray<SPRequestResult *> *)sendRequests:(NSArray<SPRequest *> *)requests;
-
-/**
- * @return http method used to send requests to the collector.
- */
-- (SPHttpMethod)httpMethod;
-
-/**
- * @return URL of the collector.
- */
-- (NSURL *)url;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/NetworkConnection/SPNetworkController.h b/Snowplow/Internal/NetworkConnection/SPNetworkController.h
deleted file mode 100644
index e7d7c82f8..000000000
--- a/Snowplow/Internal/NetworkConnection/SPNetworkController.h
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  SPNetworkController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPNetworkConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(NetworkController)
-@protocol SPNetworkController
-
-/**
- * URL used to send events to the collector.
- */
-@property (nonatomic, nullable) NSString *endpoint;
-/**
- * Method used to send events to the collector.
- */
-@property (nonatomic) SPHttpMethod method;
-
-/**
- * A custom path which will be added to the endpoint URL to specify the
- * complete URL of the collector when paired with the POST method.
- */
-@property (nonatomic, nullable) NSString *customPostPath;
-
-/**
- * Custom headers for http requests.
- */
-@property (nonatomic, nullable) NSDictionary<NSString *, NSString *> *requestHeaders;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.h b/Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.h
deleted file mode 100644
index 850d4f5f0..000000000
--- a/Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.h
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-//  SPNetworkControllerImpl.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPNetworkController.h"
-#import "SPController.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(NetworkControllerImpl)
-@interface SPNetworkControllerImpl : SPController <SPNetworkController>
-
-- (BOOL)isCustomNetworkConnection;
-
-@end
-
-NS_ASSUME_NONNULL_END
-
diff --git a/Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.m b/Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.m
deleted file mode 100644
index ac2bf73ca..000000000
--- a/Snowplow/Internal/NetworkConnection/SPNetworkControllerImpl.m
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-//  SPNetworkControllerImpl.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPNetworkControllerImpl.h"
-#import "SPDefaultNetworkConnection.h"
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPNetworkConfigurationUpdate.h"
-
-
-@implementation SPNetworkControllerImpl {
-    id<SPRequestCallback> _requestCallback;
-}
-
-- (BOOL)isCustomNetworkConnection {
-    return self.emitter.networkConnection && ![self.emitter.networkConnection isKindOfClass:SPDefaultNetworkConnection.class];
-}
-
-// MARK: - Properties
-
-- (void)setEndpoint:(NSString *)endpoint {
-    [self.emitter setUrlEndpoint:endpoint];
-}
-
-- (NSString *)endpoint {
-    return [self.emitter urlEndpoint].absoluteString;
-}
-
-- (void)setMethod:(SPHttpMethod)method {
-    [self.emitter setHttpMethod:method];
-}
-
-- (SPHttpMethod)method {
-    return [self.emitter httpMethod];
-}
-
-- (void)setCustomPostPath:(NSString *)customPostPath {
-    self.dirtyConfig.customPostPath = customPostPath;
-    self.dirtyConfig.customPostPathUpdated = YES;
-    [self.emitter setCustomPostPath:customPostPath];
-}
-
-- (NSString *)customPostPath {
-    return [self.emitter customPostPath];
-}
-
-- (void)setRequestHeaders:(NSDictionary<NSString *, NSString *> *)requestHeaders {
-    self.dirtyConfig.requestHeaders = requestHeaders;
-    self.dirtyConfig.requestHeadersUpdated = YES;
-    [self.emitter setRequestHeaders:requestHeaders];
-}
-
-- (NSDictionary<NSString *, NSString *> *)requestHeaders {
-    return [self.emitter requestHeaders];
-}
-
-// MARK: - Private methods
-
-- (SPEmitter *)emitter {
-    return self.serviceProvider.tracker.emitter;
-}
-
-- (SPNetworkConfigurationUpdate *)dirtyConfig {
-    return self.serviceProvider.networkConfigurationUpdate;
-}
-
-@end
diff --git a/Snowplow/Internal/Payload/SPPayload.h b/Snowplow/Internal/Payload/SPPayload.h
deleted file mode 100644
index 1710e8daf..000000000
--- a/Snowplow/Internal/Payload/SPPayload.h
+++ /dev/null
@@ -1,112 +0,0 @@
-//
-//  SPPayload.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_SWIFT_NAME(Payload)
-@interface SPPayload : NSObject
-
-@property (nonatomic) BOOL allowDiagnostic;
-
-/**
- *  Initializes a newly allocated SPPayload
- *  @return A SnowplowPayload.
- */
-- (id) init;
-
-/**
- *  Initializes a newly allocated SPPayload with an existing object of type NSDictionary.
- *  @param dict An object of NSDictionary.
- *  @return A SnowplowPayload.
- */
-- (id)initWithNSDictionary:(NSDictionary<NSString *, NSObject *> *)dict;
-
-/**
- *  Adds a simple name-value pair into the SPPayload intance.
- *  @param value A NSString value
- *  @param key A key of type NSString
- */
-- (void) addValueToPayload:(NSString *)value forKey:(NSString *)key;
-
-/**
- *  Adds a simple name-value pair into the SPPayload intance.
- *  @param value A NSNumber value
- *  @param key A key of type NSString
- */
-- (void) addNumericValueToPayload:(NSNumber *)value forKey:(NSString *)key;
-
-/**
- *  Adds a dictionary of attributes to be appended into the SPPayload instance. It does NOT overwrite the existing data in the object.
- *  All attribute values must be NSString types to be added; all others are discarded.
- *  @param dict An object of NSDictionary.
- */
-- (void)addDictionaryToPayload:(NSDictionary<NSString *, NSObject *> *)dict;
-
-/**
- *  Adds a dictionary of attributes to be appended into the SPPayload instance. Gives you the option to Base64 encode the data before adding it into the object.
- *  @param json NSData of JSON-compatible data to be added.
- *  @param encode Boolean option to choose whether the JSON data should be encoded.
- *  @param typeEncoded If the data is to be encoded, the result will be a value of the key in typeEncoded.
- *  @param typeNotEncoded If the data is NOT going to be encoded, the result will be a value of the key in typeWhenNotEncoded.
- */
-- (void) addJsonToPayload:(NSData *)json
-            base64Encoded:(Boolean)encode
-          typeWhenEncoded:(NSString *)typeEncoded
-       typeWhenNotEncoded:(NSString *)typeNotEncoded;
-
-/**
- *  Adds a JSON string of attributes to be appended into the SPPayload instance. Gives you the option to Base64 encode the data before adding it into the object. This method converts the string to NSData and uses the data with addJsonStringToPayload:base64Encoded:typeWhenEncoded:typeWhenNotEncoded:
- *  @param json NSData of JSON-compatible data to be added.
- *  @param encode Boolean option to choose whether the JSON data should be encoded.
- *  @param typeEncoded If the data is to be encoded, the result will be a value of the key in typeEncoded.
- *  @param typeNotEncoded If the data is NOT going to be encoded, the result will be a value of the key in typeWhenNotEncoded.
- */
-- (void) addJsonStringToPayload:(NSString *)json
-                  base64Encoded:(Boolean)encode
-                typeWhenEncoded:(NSString *)typeEncoded
-             typeWhenNotEncoded:(NSString *)typeNotEncoded;
-
-/**
- *  Adds a dictionary of attributes to be appended into the SPPayload instance. Gives you the option to Base64 encode the data before adding it into the object. This method converts the dictionary to NSData and uses the data with addJsonStringToPayload:base64Encoded:typeWhenEncoded:typeWhenNotEncoded:
- *  @param json NSDictionary of JSON-compatible data to be added.
- *  @param encode Boolean option to choose whether the JSON data should be encoded.
- *  @param typeEncoded If the data is to be encoded, the result will be a value of the key in typeEncoded.
- *  @param typeNotEncoded If the data is NOT going to be encoded, the result will be a value of the key in typeWhenNotEncoded.
- */
-- (void) addDictionaryToPayload:(NSDictionary<NSString *, NSObject *> *)json
-                  base64Encoded:(Boolean)encode
-                typeWhenEncoded:(NSString *)typeEncoded
-             typeWhenNotEncoded:(NSString *)typeNotEncoded;
-
-/**
- * Returns the payload of that particular SPPayload object.
- * @return NSDictionary of data in the object.
- */
-- (NSDictionary<NSString *, NSObject *> *) getAsDictionary;
-
-/**
- * Returns the byte size of a payload.
- * @return A long representing the byte size of the payload.
- */
-- (NSUInteger)byteSize;
-
-@end
-
diff --git a/Snowplow/Internal/Payload/SPPayload.m b/Snowplow/Internal/Payload/SPPayload.m
deleted file mode 100644
index c53349b53..000000000
--- a/Snowplow/Internal/Payload/SPPayload.m
+++ /dev/null
@@ -1,163 +0,0 @@
-//
-//  SPPayload.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPPayload.h"
-#import "SPLogger.h"
-#import "SPJSONSerialization.h"
-
-#define SPLogPayloadError(issue, format, ...) if (self.allowDiagnostic) SPLogTrack(issue, format, ##__VA_ARGS__); else SPLogError(format, ##__VA_ARGS__)
-
-@implementation SPPayload {
-    NSMutableDictionary * _payload;
-}
-
-- (id) init {
-    self = [super init];
-    if(self) {
-        _payload = [[NSMutableDictionary alloc] init];
-        self.allowDiagnostic = YES;
-    }
-    return self;
-}
-
-- (id)initWithNSDictionary:(NSDictionary<NSString *, NSObject *> *) dictionary {
-    self = [super init];
-    if (self) {
-        _payload = dictionary.mutableCopy ?: [NSMutableDictionary dictionary];
-        self.allowDiagnostic = YES;
-    }
-    return self;
-}
-
-- (void) addValueToPayload:(NSString *)value forKey:(NSString *)key {
-    if ([value length] == 0) {
-        @synchronized (self) {
-            if ([_payload valueForKey:key] != nil) {
-                [_payload removeObjectForKey:key];
-            }
-        }
-        return;
-    }
-    @synchronized (self) {
-        [_payload setObject:value forKey:key];
-    }
-}
-
-- (void) addNumericValueToPayload:(NSNumber *)value forKey:(NSString *)key {
-    @synchronized (self) {
-        if (value) {
-            [_payload setObject:value forKey:key];
-        }
-        else if ([_payload valueForKey:key]) {
-            [_payload removeObjectForKey:key];
-        }
-    }
-}
-
-- (void)addDictionaryToPayload:(NSDictionary<NSString *, NSObject *> *)dictionary {
-    if (!dictionary) return;
-    [dictionary.copy enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
-        if ([value isKindOfClass:[NSString class]]) {
-            [self addValueToPayload:(NSString *)value forKey:key];
-        }
-    }];
-}
-
-- (void) addJsonToPayload:(NSData *)json
-            base64Encoded:(Boolean)encode
-          typeWhenEncoded:(NSString *)typeEncoded
-       typeWhenNotEncoded:(NSString *)typeNotEncoded {
-    NSDictionary *object = [SPJSONSerialization deserializeData:json];
-    if (!object) {
-        return;
-    }
-    if (encode) {
-        NSString *encodedString = [json base64EncodedStringWithOptions:0];
-        
-        // We need URL safe with no padding. Since there is no built-in way to do this, we transform
-        // the encoded payload to make it URL safe by replacing chars that are different in the URL-safe
-        // alphabet. Namely, 62 is - instead of +, and 63 _ instead of /.
-        // See: https://tools.ietf.org/html/rfc4648#section-5
-        encodedString = [[encodedString stringByReplacingOccurrencesOfString:@"/" withString:@"_"]
-                         stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
-        
-        // There is also no padding since the length is implicitly known.
-        encodedString = [encodedString stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]];
-        
-        [self addValueToPayload:encodedString forKey:typeEncoded];
-    } else {
-        [self addValueToPayload:[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding] forKey:typeNotEncoded];
-    }
-}
-
-- (void) addJsonStringToPayload:(NSString *)json
-                  base64Encoded:(Boolean)encode
-                typeWhenEncoded:(NSString *)typeEncoded
-             typeWhenNotEncoded:(NSString *)typeNotEncoded {
-    NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
-
-    [self addJsonToPayload:data
-             base64Encoded:encode
-           typeWhenEncoded:typeEncoded
-        typeWhenNotEncoded:typeNotEncoded];
-    
-}
-
-- (void)addDictionaryToPayload:(NSDictionary<NSString *, NSObject *> *)dictionary
-                 base64Encoded:(Boolean)encode
-               typeWhenEncoded:(NSString *)typeEncoded
-            typeWhenNotEncoded:(NSString *)typeNotEncoded {
-    NSData *data = [SPJSONSerialization serializeDictionary:dictionary];
-    if (!data) {
-        return;
-    }
-    [self addJsonToPayload:data
-             base64Encoded:encode
-           typeWhenEncoded:typeEncoded
-        typeWhenNotEncoded:typeNotEncoded];
-}
-
-- (NSDictionary<NSString *, NSObject *> *) getAsDictionary {
-    @synchronized (self) {
-        return _payload;
-    }
-}
-
-- (NSUInteger)byteSize {
-    if (!_payload) {
-        return 0;
-    }
-    NSData *data = nil;
-    @synchronized (self) {
-        data = [SPJSONSerialization serializeDictionary:_payload];
-        if (!data) {
-            return 0;
-        }
-    }
-    return data.length;
-}
-
-- (NSString *)description {
-    return [[self getAsDictionary] description];
-}
-
-@end
diff --git a/Snowplow/Internal/Payload/SPSelfDescribingJson.h b/Snowplow/Internal/Payload/SPSelfDescribingJson.h
deleted file mode 100644
index f16aa6263..000000000
--- a/Snowplow/Internal/Payload/SPSelfDescribingJson.h
+++ /dev/null
@@ -1,116 +0,0 @@
-//
-//  SPSelfDescribingJson.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-@class SPPayload;
-
-/*!
- @class SPSelfDescribingJson
- @brief The class that represents self-describing JSONs.
-
- This class holds the information of a self-describing JSON.
-
- @see SPPayload
- */
-NS_SWIFT_NAME(SelfDescribingJson)
-@interface SPSelfDescribingJson : NSObject
-
-/// the schema URI for this self-describing JSON.
-@property (nonatomic) NSString *schema;
-/// Data of the self-describing JSON.
-@property (nonatomic, readonly) NSObject *data;
-
-- (instancetype)init NS_UNAVAILABLE;
-+ (instancetype)new NS_UNAVAILABLE;
-
-/*!
- @brief Initializes a newly allocated SPSelfDescribingJson.
-
- @param schema A valid schema string.
- @param data Data to set for data field of the self-describing JSON, should be an NSDictionary.
- @return An SPSelfDescribingJson.
- */
-- (instancetype)initWithSchema:(NSString *)schema andData:(NSObject *)data NS_DESIGNATED_INITIALIZER;
-
-/*!
- @brief Initializes a newly allocated SPSelfDescribingJson.
-
- @param schema A valid schema string.
- @param data Dictionary to set for data field of the self-describing JSON.
- @return An SPSelfDescribingJson.
- */
-- (instancetype)initWithSchema:(NSString *)schema andDictionary:(NSDictionary *)data;
-
-/*!
- @brief Initializes a newly allocated SPSelfDescribingJson.
-
- @param schema A valid schema string.
- @param data Payload to set for data field of the self-describing JSON.
- @return An SPSelfDescribingJson.
- */
-- (instancetype)initWithSchema:(NSString *)schema andPayload:(SPPayload *)data;
-
-/*!
- @brief Initializes a newly allocated SPSelfDescribingJson.
-
- @param schema A valid schema URI.
- @param data Self-describing JSON to set for data field of the self-describing JSON.
- @return An SPSelfDescribingJson.
- */
-- (instancetype)initWithSchema:(NSString *)schema andSelfDescribingJson:(SPSelfDescribingJson *)data;
-
-/*!
- @brief Sets the data field of the self-describing JSON.
-
- @param data An NSObject to be nested into the data.
- */
-- (void) setDataWithObject:(NSObject *)data;
-
-/*!
- @brief Sets the data field of the self-describing JSON.
-
- @param data An SPPayload to be nested into the data.
- */
-- (void) setDataWithPayload:(SPPayload *)data;
-
-/*!
- @brief Sets the data field of the self-describing JSON.
-
- @param data A self-describing JSON to be nested into the data.
- */
-- (void) setDataWithSelfDescribingJson:(SPSelfDescribingJson *)data;
-
-/*!
- @brief Returns the internal NSDictionary of the self-describing JSON.
-
- @return The self-describing JSON as an NSDictionary.
- */
-- (NSDictionary<NSString *, NSObject *> *) getAsDictionary;
-
-/*!
- @brief Returns a string description of the internal dictionary.
-
- @return The description of the dictionary.
- */
-- (NSString *) description;
-
-@end
diff --git a/Snowplow/Internal/Payload/SPSelfDescribingJson.m b/Snowplow/Internal/Payload/SPSelfDescribingJson.m
deleted file mode 100644
index cacde9e55..000000000
--- a/Snowplow/Internal/Payload/SPSelfDescribingJson.m
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-//  SPSelfDescribingJson.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-
-@interface SPSelfDescribingJson ()
-
-@property (nonatomic, readwrite) NSObject *data;
-
-@end
-
-@implementation SPSelfDescribingJson
-
-- (instancetype)initWithSchema:(NSString *)schema andData:(NSObject *)data {
-    if (self = [super init]) {
-        [self setSchema:schema];
-        [self setDataWithObject:data];
-    }
-    return self;
-}
-
-- (instancetype)initWithSchema:(NSString *)schema andDictionary:(NSDictionary *)data {
-    return [self initWithSchema:schema andData:data];
-}
-
-- (instancetype)initWithSchema:(NSString *)schema andPayload:(SPPayload *)data {
-    return [self initWithSchema:schema andData:[data getAsDictionary]];
-}
-
-- (instancetype)initWithSchema:(NSString *)schema andSelfDescribingJson:(SPSelfDescribingJson *)data {
-    return [self initWithSchema:schema andData:[data getAsDictionary]];
-}
-
-- (void) setSchema:(NSString *)schema {
-    [SPUtilities checkArgument:([schema length] != 0) withMessage:@"Schema cannot be nil or empty."];
-    _schema = schema;
-}
-
-- (void) setDataWithObject:(NSObject *)data {
-    self.data = data;
-}
-
-- (void) setDataWithPayload:(SPPayload *)data {
-    return [self setDataWithObject:[data getAsDictionary]];
-}
-
-- (void) setDataWithSelfDescribingJson:(SPSelfDescribingJson *)data {
-    return [self setDataWithObject:[data getAsDictionary]];
-}
-
-- (NSDictionary<NSString *, NSObject *> *) getAsDictionary {
-    return @{
-        kSPSchema: self.schema,
-        kSPData: self.data,
-    };
-}
-
-- (NSString *) description {
-    return [[self getAsDictionary] description];
-}
-
-@end
diff --git a/Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.h b/Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.h
deleted file mode 100644
index 7b07ad25e..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  SPConfigurationCache.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPFetchedConfigurationBundle.h"
-#import "SPRemoteConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPConfigurationCache : NSObject
-
-- (instancetype)initWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration;
-- (nullable SPFetchedConfigurationBundle *)readCache;
-- (void)writeCache:(SPFetchedConfigurationBundle *)configuration;
-- (void)clearCache;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.m b/Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.m
deleted file mode 100644
index cd085aac0..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPConfigurationCache.m
+++ /dev/null
@@ -1,145 +0,0 @@
-//
-//  SPConfigurationCache.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConfigurationCache.h"
-#import "SPLogger.h"
-
-@interface SPConfigurationCache ()
-
-@property (nonatomic, nonnull) NSURL *cacheFileUrl;
-@property (nonatomic, nullable) SPFetchedConfigurationBundle *configuration;
-
-@end
-
-@implementation SPConfigurationCache
-
-- (instancetype)initWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration {
-    if (self = [super init]) {
-#if !(TARGET_OS_TV) && !(TARGET_OS_WATCH)
-        [self createCachePathWithRemoteConfiguration:remoteConfiguration];
-#endif
-    }
-    return self;
-}
-
-- (nullable SPFetchedConfigurationBundle *)readCache {
-    @synchronized (self) {
-#if !(TARGET_OS_TV) && !(TARGET_OS_WATCH)
-        if (self.configuration) {
-            return self.configuration;
-        }
-        [self loadCache];
-#endif
-        return self.configuration;
-    }
-}
-
-- (void)writeCache:(SPFetchedConfigurationBundle *)configuration {
-    @synchronized (self) {
-        self.configuration = configuration;
-#if !(TARGET_OS_TV) && !(TARGET_OS_WATCH)
-        [self storeCache];
-#endif
-    }
-}
-
-- (void)clearCache {
-    NSError *error = nil;
-    @synchronized (self) {
-        self.configuration = nil;
-#if !(TARGET_OS_TV) && !(TARGET_OS_WATCH)
-        if (!self.cacheFileUrl) return;
-        [[NSFileManager defaultManager] removeItemAtURL:self.cacheFileUrl error:&error];
-#endif
-    }
-    if (error) {
-        SPLogError(@"Error on clearing configuration from cache: %@", error.localizedDescription);
-    }
-}
-
-// Private method
-
-- (void)loadCache {
-    @synchronized (self) {
-        NSData *data = [[NSData alloc] initWithContentsOfURL:self.cacheFileUrl];
-        if (!data) return;
-        @try {
-            if (@available(iOS 12, tvOS 12, watchOS 5, macOS 10.14, *)) {
-                NSError *error = nil;
-                self.configuration = (SPFetchedConfigurationBundle *)[NSKeyedUnarchiver unarchiveTopLevelObjectWithData:data error:&error];
-                if (error) {
-                    SPLogError(@"Error on getting configuration from cache: %@", error.localizedDescription);
-                    self.configuration = nil;
-                    return;
-                }
-            } else {
-                NSKeyedUnarchiver *unarchiver = nil;
-                unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
-                self.configuration = (SPFetchedConfigurationBundle *)[unarchiver decodeObject];
-                [unarchiver finishDecoding];
-            }
-        } @catch (NSException *exception) {
-            SPLogError(@"Exception on getting configuration from cache: %@", exception.reason);
-            self.configuration = nil;
-        }
-    }
-}
-
-- (void)storeCache {
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        @synchronized (self) {
-            if (!self.configuration) return;
-            @try {
-                NSMutableData *data = [NSMutableData new];
-                NSKeyedArchiver *archiver;
-                if (@available(iOS 12, tvOS 12, watchOS 5, macOS 10.14, *)) {
-                    archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
-                    [archiver encodeObject:self.configuration forKey:@"root"];
-                    [data setData:archiver.encodedData];
-                } else {
-                    archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
-                    [archiver encodeObject:self.configuration];
-                    [archiver finishEncoding];
-                }
-                NSError *error = nil;
-                [data writeToURL:self.cacheFileUrl options:NSDataWritingAtomic error:&error];
-                if (error) {
-                    SPLogError(@"Error on caching configuration: %@", error.localizedDescription);
-                }
-            } @catch (NSException *exception) {
-                SPLogError(@"Exception on caching configuration: %@", exception.reason);
-            }
-        }
-    });
-}
-
-- (void)createCachePathWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration {
-    NSFileManager *fm = [NSFileManager defaultManager];
-    NSURL *url = [fm URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask].lastObject;
-    url = [url URLByAppendingPathComponent:@"snowplow-cache"];
-    NSError *error = nil;
-    [fm createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:&error];
-    NSString *fileName = [NSString stringWithFormat:@"remoteConfig-%lu.data", (unsigned long)[remoteConfiguration.endpoint hash]];
-    url = [url URLByAppendingPathComponent:fileName isDirectory:NO];
-    self.cacheFileUrl = url;
-}
-
-@end
diff --git a/Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.h b/Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.h
deleted file mode 100644
index 0853840f6..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.h
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-//  SPConfigurationFetcher.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPRemoteConfiguration.h"
-#import "SPConfigurationProvider.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPConfigurationFetcher : NSObject
-
-- (instancetype)initWithRemoteSource:(SPRemoteConfiguration *)remoteConfiguration onFetchCallback:(OnFetchCallback)onFetchCallback;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.m b/Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.m
deleted file mode 100644
index eda217c9a..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPConfigurationFetcher.m
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-//  SPConfigurationFetcher.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConfigurationFetcher.h"
-
-@interface SPConfigurationFetcher ()
-
-@property (nonatomic, nonnull) SPRemoteConfiguration *remoteConfiguration;
-@property (nonatomic, nonnull) OnFetchCallback onFetchCallback;
-
-@end
-
-@implementation SPConfigurationFetcher
-
-- (instancetype)initWithRemoteSource:(SPRemoteConfiguration *)remoteConfiguration onFetchCallback:(OnFetchCallback)onFetchCallback {
-    if (self = [super init]) {
-        self.remoteConfiguration = remoteConfiguration;
-        self.onFetchCallback = onFetchCallback;
-        [self performRequest];
-    }
-    return self;
-}
-
-- (void)performRequest {
-    NSURL *url = [[NSURL alloc] initWithString:self.remoteConfiguration.endpoint];
-    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
-    [urlRequest setHTTPMethod:@"GET"];
-
-    __block NSHTTPURLResponse *httpResponse = nil;
-    __block NSError *connectionError = nil;
-    
-    [[[NSURLSession sharedSession] dataTaskWithRequest:urlRequest
-                                     completionHandler:^(NSData *data, NSURLResponse *urlResponse, NSError *error) {
-        connectionError = error;
-        httpResponse = (NSHTTPURLResponse *)urlResponse;
-        BOOL isSuccessful = [httpResponse statusCode] >= 200 && [httpResponse statusCode] < 300;
-        if (isSuccessful) {
-            [self resolveRequestWithData:data];
-        }
-    }] resume];
-}
-
-- (void)resolveRequestWithData:(NSData *)data {
-    NSError *jsonError = nil;
-    NSObject *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
-    if (![jsonObject isKindOfClass:NSDictionary.class]) {
-        return;
-    }
-    SPFetchedConfigurationBundle *fetchedConfigurationBundle = [[SPFetchedConfigurationBundle alloc] initWithDictionary:(NSDictionary *)jsonObject];
-    if (fetchedConfigurationBundle) {
-        self.onFetchCallback(fetchedConfigurationBundle, SPConfigurationStateFetched);
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.h b/Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.h
deleted file mode 100644
index e1aaa48b5..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-//  ConfigurationProvider.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPRemoteConfiguration.h"
-#import "SPFetchedConfigurationBundle.h"
-#import "SPConfigurationState.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-typedef void(^OnFetchCallback)(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState);
-
-/*!
- This class fetch a configuration from a remote source otherwise it provides a cached configuration.
- It can manage multiple sources and multiple caches.
- */
-@interface SPConfigurationProvider : NSObject
-
-- (instancetype)initWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration;
-
-- (instancetype)initWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration defaultConfigurationBundles:(nullable NSArray<SPConfigurationBundle *> *)defaultBundles;
-
-- (void)retrieveConfigurationOnlyRemote:(BOOL)onlyRemote onFetchCallback:(OnFetchCallback)onFetchCallback;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.m b/Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.m
deleted file mode 100644
index c86eab0b7..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPConfigurationProvider.m
+++ /dev/null
@@ -1,91 +0,0 @@
-//
-//  SPConfigurationProvider.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConfigurationProvider.h"
-#import "SPConfigurationCache.h"
-#import "SPConfigurationFetcher.h"
-
-@interface SPConfigurationProvider()
-
-@property (nonatomic, nonnull) SPRemoteConfiguration *remoteConfiguration;
-@property (nonatomic, nonnull) SPConfigurationCache *cache;
-@property (nonatomic, nullable) SPConfigurationFetcher *fetcher;
-@property (nonatomic, nullable) SPFetchedConfigurationBundle *defaultBundle;
-@property (nonatomic, nonnull) SPFetchedConfigurationBundle *cacheBundle;
-
-@end
-
-@implementation SPConfigurationProvider
-
-- (instancetype)initWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration {
-    return [self initWithRemoteConfiguration:remoteConfiguration defaultConfigurationBundles:nil];
-}
-
-- (instancetype)initWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration defaultConfigurationBundles:(nullable NSArray<SPConfigurationBundle *> *)defaultBundles {
-    if (self = [super init]) {
-        self.remoteConfiguration = remoteConfiguration;
-        self.cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfiguration];
-        if (defaultBundles) {
-            SPFetchedConfigurationBundle *bundle = [[SPFetchedConfigurationBundle alloc] init];
-            bundle.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
-            bundle.configurationVersion = NSIntegerMin;
-            bundle.configurationBundle = defaultBundles;
-            self.defaultBundle = bundle;
-        }
-    }
-    return self;
-}
-
-- (void)retrieveConfigurationOnlyRemote:(BOOL)onlyRemote onFetchCallback:(OnFetchCallback)onFetchCallback {
-    @synchronized (self) {
-        if (!onlyRemote) {
-            if (!self.cacheBundle) {
-                self.cacheBundle = [self.cache readCache];
-            }
-            if (self.cacheBundle) {
-                onFetchCallback(self.cacheBundle, SPConfigurationStateCached);
-            } else if (self.defaultBundle) {
-                onFetchCallback(self.defaultBundle, SPConfigurationStateDefault);
-            }
-        }
-        self.fetcher = [[SPConfigurationFetcher alloc] initWithRemoteSource:self.remoteConfiguration onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-            if (![self schemaCompatibility:fetchedConfigurationBundle.schema]) {
-                return;
-            }
-            @synchronized (self) {
-                if (self.cacheBundle && self.cacheBundle.configurationVersion >= fetchedConfigurationBundle.configurationVersion) {
-                    return;
-                }
-                [self.cache writeCache:fetchedConfigurationBundle];
-                self.cacheBundle = fetchedConfigurationBundle;
-                onFetchCallback(fetchedConfigurationBundle, SPConfigurationStateFetched);
-            }
-        }];
-    }
-}
-
-// Private methods
-
-- (BOOL)schemaCompatibility:(NSString *)schema {
-    return [schema hasPrefix:@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-"];
-}
-
-@end
diff --git a/Snowplow/Internal/RemoteConfiguration/SPConfigurationState.h b/Snowplow/Internal/RemoteConfiguration/SPConfigurationState.h
deleted file mode 100644
index d95659587..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPConfigurationState.h
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-//  SPConfigurationState.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#ifndef SPConfigurationState_h
-#define SPConfigurationState_h
-
-NS_ASSUME_NONNULL_BEGIN
-
-/*!
- @brief State of retrieved remote configuration that states where the configuration was retrieved from.
- */
-typedef NS_ENUM(NSUInteger, SPConfigurationState) {
-    /**
-     * The default configuration was used.
-     */
-    SPConfigurationStateDefault,
-    /**
-     * The configuration was retrieved from local cache.
-     */
-    SPConfigurationStateCached,
-    /**
-     * The configuration was retrieved from the remote configuration endpoint.
-     */
-    SPConfigurationStateFetched,
-} NS_SWIFT_NAME(ConfigurationState);
-
-NS_ASSUME_NONNULL_END
-
-#endif /* SPConfigurationState_h */
diff --git a/Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.h b/Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.h
deleted file mode 100644
index bd3d6e66a..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.h
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-//  SPFetchedConfigurationBundle.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPConfigurationBundle.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPFetchedConfigurationBundle : SPConfiguration
-
-@property (nonatomic, nonnull) NSString *schema;
-@property (nonatomic) NSInteger configurationVersion;
-@property (nonatomic, nonnull) NSArray<SPConfigurationBundle *> *configurationBundle;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.m b/Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.m
deleted file mode 100644
index 1505bc82d..000000000
--- a/Snowplow/Internal/RemoteConfiguration/SPFetchedConfigurationBundle.m
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-//  SPFetchedConfigurationBundle.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPFetchedConfigurationBundle.h"
-#import "NSDictionary+SP_TypeMethods.h"
-#import "SPLogger.h"
-
-@implementation SPFetchedConfigurationBundle
-
-- (instancetype)initWithDictionary:(NSDictionary<NSString *,NSObject *> *)dictionary {
-    if (self = [super init]) {
-        self.schema = [dictionary sp_stringForKey:@"$schema" defaultValue:nil];
-        if (!self.schema) {
-            SPLogDebug(@"Error assigning: schema");
-            return nil;
-        }
-        NSNumber *number = [dictionary sp_numberForKey:SP_STR_PROP(configurationVersion) defaultValue:nil];
-        if (!number) {
-            SPLogDebug(@"Error assigning: configurationVersion");
-            return nil;
-        }
-        self.configurationVersion = number.integerValue;
-        self.configurationBundle = [dictionary sp_arrayForKey:SP_STR_PROP(configurationBundle) itemClass:SPConfigurationBundle.class defaultValue:nil];
-        if (!self.configurationBundle) {
-            SPLogDebug(@"Error assigning: configurationBundle");
-            return nil;
-        }
-    }
-    return self;
-}
-
-// MARK: - NSCopying
-
-- (id)copyWithZone:(nullable NSZone *)zone {
-    SPFetchedConfigurationBundle *copy;
-    copy.schema = self.schema;
-    copy.configurationVersion = self.configurationVersion;
-    copy.configurationBundle = [self.configurationBundle copyWithZone:zone];
-    return copy;
-}
-
-// MARK: - NSSecureCoding
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-    [coder encodeObject:self.schema forKey:SP_STR_PROP(schema)];
-    [coder encodeInteger:self.configurationVersion forKey:SP_STR_PROP(configurationVersion)];
-    [coder encodeObject:self.configurationBundle forKey:SP_STR_PROP(configurationBundle)];
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    if (self = [super init]) {
-        self.schema = [coder decodeObjectForKey:SP_STR_PROP(schema)];
-        self.configurationVersion = [coder decodeIntegerForKey:SP_STR_PROP(configurationVersion)];
-        self.configurationBundle = [coder decodeObjectForKey:SP_STR_PROP(configurationBundle)];
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/SPController.h b/Snowplow/Internal/SPController.h
deleted file mode 100644
index 5af314d16..000000000
--- a/Snowplow/Internal/SPController.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-//  SPController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPServiceProviderProtocol.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPController : NSObject
-
-@property (nonatomic, nonnull, readonly) id<SPServiceProviderProtocol> serviceProvider;
-
-- (instancetype)initWithServiceProvider:(id<SPServiceProviderProtocol>)serviceProvider;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/SPSnowplow.h b/Snowplow/Internal/SPSnowplow.h
deleted file mode 100644
index 15731b720..000000000
--- a/Snowplow/Internal/SPSnowplow.h
+++ /dev/null
@@ -1,234 +0,0 @@
-//
-//  SPSnowplow.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPTrackerController.h"
-#import "SPNetworkConfiguration.h"
-#import "SPTrackerConfiguration.h"
-#import "SPRemoteConfiguration.h"
-#import "SPConfigurationBundle.h"
-#import "SPConfigurationState.h"
-
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_OSX
-@import WebKit;
-#endif
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * Entry point to instance a new Snowplow tracker.
- */
-NS_SWIFT_NAME(Snowplow)
-@interface SPSnowplow : NSObject
-
-- (instancetype)init NS_UNAVAILABLE;
-- (instancetype)new NS_UNAVAILABLE;
-
-/// Remote Configuration
-
-/**
- * Setup a single or a set of tracker instances which will be used inside the app to track events.
- * The app can run multiple tracker instances which will be identified by string `namespaces`.
- * The trackers configuration is automatically download from the endpoint indicated in the `RemoteConfiguration`
- * passed as argument. For more details see `RemoteConfiguration`.
- *
- * The method is asynchronous and you can receive the list of the created trackers in the callbacks once the trackers are created.
- * The callback can be called multiple times in case a cached configuration is ready and later a fetched configuration is available.
- * You can also pass as argument a default configuration in case there isn't a cached configuration and it's not able to download
- * a new one. The downloaded configuration updates the cached one only if the configuration version is greater than the cached one.
- * Otherwise the cached one is kept and the callback is not called.
- *
- * IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
- * Those events are attached to the namespace.
- * If the tracker is removed or the app relaunched with a different namespace, those events can't
- * be sent to the collector and they remain in a zombie state inside the EventStore.
- * To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
- * which will delete all the EventStores instanced with namespaces not listed in the passed list.
- *
- * @param remoteConfiguration The remote configuration used to indicate where to download the configuration from.
- * @param defaultBundles The default configuration passed by default in case there isn't a cached version and it's able to download a new one.
- * @param onSuccess The callback called when a configuration (cached or downloaded) is set.
- *                  It passes two arguments: list of the namespaces associated to the created trackers
- *                  and the state of the configuration – whether it was retrieved from cache or fetched over the network.
- */
-+ (void)setupWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration defaultConfigurationBundles:(nullable NSArray<SPConfigurationBundle *> *)defaultBundles onSuccess:(void(^)(NSArray<NSString *> * _Nullable namespaces, SPConfigurationState configurationState))onSuccess NS_SWIFT_NAME(setup(remoteConfiguration:defaultConfiguration:onSuccess:));
-
-/**
- * Reconfigure, create or delete the trackers based on the configuration downloaded remotely.
- * The trackers configuration is automatically download from the endpoint indicated in the `RemoteConfiguration`
- * previously used to setup the trackers.
- *
- * The method is asynchronous and you can receive the list of the created trackers in the callbacks once the trackers are created.
- * The downloaded configuration updates the cached one only if the configuration version is greater than the cached one.
- * Otherwise the cached one is kept and the callback is not called.
- *
- * IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
- * Those events are attached to the namespace.
- * If the tracker is removed or the app relaunched with a different namespace, those events can't
- * be sent to the collector and they remain in a zombie state inside the EventStore.
- * To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
- * which will delete all the EventStores instanced with namespaces not listed in the passed list.
- *
- * @param onSuccess The callback called when a configuration (cached or downloaded) is set It passes the list of the namespaces associated
- *                  to the created trackers.
- */
-+ (void)refreshIfRemoteUpdate:(void(^)(NSArray<NSString *> * _Nullable namespaces, SPConfigurationState configurationState))onSuccess NS_SWIFT_NAME(refresh(onSuccess:));
-
-/// Standard Configuration
-
-/**
- * Create a new tracker instance which will be used inside the app to track events.
- * The app can run multiple tracker instances which will be identified by string `namespaces`.
- * The tracker will be configured with default setting and only the collector endpoint URL need
- * to be passed for the configuration.
- * For the default configuration of the tracker see `TrackerConfiguration(String)`.
- *
- * To configure tracker with more details see `createTracker(Context, String, NetworkConfiguration, Configuration...)`
- * To use the tracker as singleton see `getDefaultTracker()`
- *
- * IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
- * Those events are attached to the namespace.
- * If the tracker is removed or the app relaunched with a different namespace, those events can't
- * be sent to the collector and they remain in a zombie state inside the EventStore.
- * To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
- * which will delete all the EventStores instanced with namespaces not listed in the passed list.
- *
- * @param namespace The namespace used to identify the current tracker among the possible
- *                  multiple tracker instances.
- * @param endpoint The URL of the collector.
- * @param method The method for the requests to the collector (GET or POST).
- * @return The tracker instance created.
- */
-+ (id<SPTrackerController>)createTrackerWithNamespace:(NSString *)namespace endpoint:(NSString *)endpoint method:(SPHttpMethod)method NS_SWIFT_NAME(createTracker(namespace:endpoint:method:));
-
-/**
- * Create a new tracker instance which will be used inside the app to track events.
- * The app can run multiple tracker instances which will be identified by string `namespaces`.
- * The tracker will be configured with default setting and only the collector endpoint URL need
- * to be passed for the configuration.
- * For the default configuration of the tracker see `TrackerConfiguration(String)`.
- *
- * To configure tracker with more details see `createTracker(Context, String, NetworkConfiguration, Configuration...)`
- * To use the tracker as singleton see `getDefaultTracker()`
- *
- * IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
- * Those events are attached to the namespace.
- * If the tracker is removed or the app relaunched with a different namespace, those events can't
- * be sent to the collector and they remain in a zombie state inside the EventStore.
- * To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
- * which will delete all the EventStores instanced with namespaces not listed in the passed list.
- *
- * @param namespace The namespace used to identify the current tracker among the possible
- *                  multiple tracker instances.
- * @param networkConfiguration The NetworkConfiguration object with settings for the communication with the
- *                collector.
- * @return The tracker instance created.
- */
-+ (id<SPTrackerController>)createTrackerWithNamespace:(NSString *)namespace network:(SPNetworkConfiguration *)networkConfiguration NS_SWIFT_NAME(createTracker(namespace:network:));
-
-/**
- * Create a new tracker instance which will be used inside the app to track events.
- * The app can run multiple tracker instances which will be identified by string `namespaces`.
- * The tracker will be configured with default setting and only the collector endpoint URL need
- * to be passed for the configuration.
- * For the default configuration of the tracker see `TrackerConfiguration(String)`.
- *
- * To configure tracker with more details see `createTracker(Context, String, NetworkConfiguration, Configuration...)`
- * To use the tracker as singleton see `getDefaultTracker()`
- *
- * IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
- * Those events are attached to the namespace.
- * If the tracker is removed or the app relaunched with a different namespace, those events can't
- * be sent to the collector and they remain in a zombie state inside the EventStore.
- * To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
- * which will delete all the EventStores instanced with namespaces not listed in the passed list.
- *
- * @param namespace The namespace used to identify the current tracker among the possible
- *                  multiple tracker instances.
- * @param networkConfiguration The NetworkConfiguration object with settings for the communication with the
- *                collector.
- * @param configurations All the configuration objects with the details about the fine tuning of
- *                       the tracker.
- * @return The tracker instance created.
- */
-+ (id<SPTrackerController>)createTrackerWithNamespace:(NSString *)namespace network:(SPNetworkConfiguration *)networkConfiguration configurations:(NSArray<SPConfiguration *> *)configurations NS_SWIFT_NAME(createTracker(namespace:network:configurations:));
-
-/**
- * The default tracker instance is the first created in the app, but that can be overridden programmatically
- * calling `setTrackerAsDefault(TrackerController)`.
- */
-+ (nullable id<SPTrackerController>)defaultTracker;
-
-/**
- * Using the namespace identifier is possible to get the trackerController if already instanced.
- *
- * @param namespace The namespace that identifies the tracker.
- * @return The tracker if it exist with that namespace.
- */
-+ (nullable id<SPTrackerController>)trackerByNamespace:(NSString *)namespace NS_SWIFT_NAME(tracker(namespace:));
-
-/**
- * Set the passed tracker as default tracker if it's registered as an active tracker in the app.
- * If the passed instance is of a tracker which is already removed (see `removeTracker`) then it can't become the new default tracker
- * and the operation fails.
- *
- * @param trackerController The new default tracker.
- * @return Whether the tracker passed is registered among the active trackers of the app.
- */
-+ (BOOL)setTrackerAsDefault:(id<SPTrackerController>)trackerController NS_SWIFT_NAME(setAsDefault(tracker:));
-
-/**
- * A tracker can be removed from the active trackers of the app.
- * Once it has been removed it can't be added again or set as default.
- * The unique way to resume a removed tracker is creating a new tracker with same namespace and
- * same configurations.
- * The removed tracker is always stopped.
- *
- * @param trackerController The tracker controller to remove.
- * @return Whether it has been able to remove it.
- */
-+ (BOOL)removeTracker:(id<SPTrackerController>)trackerController NS_SWIFT_NAME(remove(tracker:));
-
-/**
- * Remove all the trackers.
- * The removed tracker is always stopped.
- * See `removeTracker(TrackerController)`
- */
-+ (void)removeAllTrackers NS_SWIFT_NAME(removeAllTrackers());
-
-/**
- * @return Set of namespace of the active trackers in the app.
- */
-+ (NSArray<NSString *> *)instancedTrackerNamespaces;
-
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_OSX
-
-/**
- * Subscribe to events tracked in a Web view using the Snowplow WebView tracker JavaScript library.
- * @param webViewConfiguration Configuration of the Web view to subscribe to events from
- */
-+ (void)subscribeToWebViewEventsWithConfiguration:(WKWebViewConfiguration *)webViewConfiguration;
-
-#endif
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/SPSnowplow.m b/Snowplow/Internal/SPSnowplow.m
deleted file mode 100644
index d19f0e9a2..000000000
--- a/Snowplow/Internal/SPSnowplow.m
+++ /dev/null
@@ -1,196 +0,0 @@
-//
-//  SPSnowplow.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSnowplow.h"
-#import "SPServiceProvider.h"
-#import "SPConfigurationProvider.h"
-#import "SPWebViewMessageHandler.h"
-
-@interface SPSnowplow ()
-
-@property (nonatomic, nullable) SPServiceProvider *defaultServiceProvider;
-@property (nonatomic, nonnull) NSMutableDictionary<NSString *, SPServiceProvider *> *serviceProviderInstances;
-@property (nonatomic, nullable) SPConfigurationProvider *configurationProvider;
-
-@end
-
-@implementation SPSnowplow
-
-+ (void)setupWithRemoteConfiguration:(SPRemoteConfiguration *)remoteConfiguration defaultConfigurationBundles:(NSArray<SPConfigurationBundle *> *)defaultBundles onSuccess:(void (^)(NSArray<NSString *> * _Nullable, SPConfigurationState configurationState))onSuccess
-{
-    SPSnowplow *snowplow = [SPSnowplow sharedInstance];
-    snowplow.configurationProvider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfiguration defaultConfigurationBundles:defaultBundles];
-    [snowplow.configurationProvider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        NSArray<SPConfigurationBundle *> *bundles = fetchedConfigurationBundle.configurationBundle;
-        NSArray<NSString *> *namespaces = [SPSnowplow createTrackersWithConfigurationBundles:bundles];
-        onSuccess(namespaces, configurationState);
-    }];
-}
-
-+ (void)refreshIfRemoteUpdate:(void (^)(NSArray<NSString *> * _Nullable, SPConfigurationState configurationState))onSuccess {
-    SPSnowplow *snowplow = [SPSnowplow sharedInstance];
-    [snowplow.configurationProvider retrieveConfigurationOnlyRemote:YES onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle, SPConfigurationState configurationState) {
-        NSArray<SPConfigurationBundle *> *bundles = fetchedConfigurationBundle.configurationBundle;
-        NSArray<NSString *> *namespaces = [SPSnowplow createTrackersWithConfigurationBundles:bundles];
-        onSuccess(namespaces, configurationState);
-    }];
-}
-
-+ (id<SPTrackerController>)createTrackerWithNamespace:(NSString *)namespace endpoint:(NSString *)endpoint method:(SPHttpMethod)method {
-    SPNetworkConfiguration *networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:endpoint method:method];
-    return [SPSnowplow createTrackerWithNamespace:namespace network:networkConfiguration configurations:@[]];
-}
-
-+ (id<SPTrackerController>)createTrackerWithNamespace:(NSString *)namespace network:(SPNetworkConfiguration *)networkConfiguration {
-    return [SPSnowplow createTrackerWithNamespace:namespace network:networkConfiguration configurations:@[]];
-}
-
-+ (id<SPTrackerController>)createTrackerWithNamespace:(NSString *)namespace network:(SPNetworkConfiguration *)networkConfiguration configurations:(NSArray<SPConfiguration *> *)configurations {
-    SPServiceProvider *serviceProvider = [[SPSnowplow sharedInstance].serviceProviderInstances objectForKey:namespace];
-    if (serviceProvider) {
-        [serviceProvider resetWithConfigurations:[configurations arrayByAddingObject:networkConfiguration]];
-    } else {
-        serviceProvider = [[SPServiceProvider alloc] initWithNamespace:namespace network:networkConfiguration configurations:configurations];
-        [SPSnowplow.sharedInstance registerInstance:serviceProvider];
-    }
-    return serviceProvider.trackerController;
-}
-
-+ (id<SPTrackerController>)defaultTracker {
-    return [[[SPSnowplow sharedInstance] defaultServiceProvider] trackerController];
-}
-
-+ (id<SPTrackerController>)trackerByNamespace:(NSString *)namespace {
-    return [[[SPSnowplow sharedInstance].serviceProviderInstances objectForKey:namespace] trackerController];
-}
-
-+ (BOOL)setTrackerAsDefault:(id<SPTrackerController>)trackerController {
-    SPSnowplow *shared = [SPSnowplow sharedInstance];
-    @synchronized (shared) {
-        SPServiceProvider *serviceProvider = [shared.serviceProviderInstances objectForKey:trackerController.namespace];
-        if (serviceProvider) {
-            shared.defaultServiceProvider = serviceProvider;
-            return YES;
-        }
-        return NO;
-    }
-}
-
-+ (BOOL)removeTracker:(id<SPTrackerController>)trackerController {
-    SPSnowplow *shared = [SPSnowplow sharedInstance];
-    @synchronized (shared) {
-        NSString *namespace = trackerController.namespace;
-        SPServiceProvider *serviceProvider = [shared.serviceProviderInstances objectForKey:namespace];
-        if (serviceProvider) {
-            [serviceProvider shutdown];
-            [shared.serviceProviderInstances removeObjectForKey:namespace];
-            if (serviceProvider == shared.defaultServiceProvider) {
-                shared.defaultServiceProvider = nil;
-            }
-            return YES;
-        }
-        return NO;
-    }
-}
-
-+ (void)removeAllTrackers {
-    SPSnowplow *shared = [SPSnowplow sharedInstance];
-    @synchronized (shared) {
-        shared.defaultServiceProvider = nil;
-        NSArray<SPServiceProvider *> *serviceProviders = [shared.serviceProviderInstances allValues];
-        [shared.serviceProviderInstances removeAllObjects];
-        for (SPServiceProvider *sp in serviceProviders) {
-            [sp shutdown];
-        }
-    }
-}
-
-+ (NSArray<NSString *> *)instancedTrackerNamespaces {
-    return [[SPSnowplow sharedInstance].serviceProviderInstances allKeys];
-}
-
-// MARK: - Private methods
-
-- (BOOL)registerInstance:(SPServiceProvider *)serviceProvider {
-    @synchronized (self) {
-        NSString *namespace = serviceProvider.namespace;
-        BOOL isOverriding = [self.serviceProviderInstances objectForKey:namespace] != nil;
-        [self.serviceProviderInstances setObject:serviceProvider forKey:namespace];
-        if (!self.defaultServiceProvider) {
-            self.defaultServiceProvider = serviceProvider;
-        }
-        return isOverriding;
-    }
-}
-
-// Remote Configuration
-
-+ (NSArray<NSString *> *)createTrackersWithConfigurationBundles:(NSArray<SPConfigurationBundle *> *)bundles {
-    NSMutableArray<NSString *> *namespaces = [NSMutableArray new];
-    for (SPConfigurationBundle *bundle in bundles) {
-        @synchronized (SPSnowplow.class) {
-            if (!bundle.networkConfiguration) {
-                // remove tracker if it exists
-                id<SPTrackerController> tracker = [SPSnowplow trackerByNamespace:bundle.namespace];
-                if (tracker) {
-                    [SPSnowplow removeTracker:tracker];
-                }
-            } else {
-                [SPSnowplow createTrackerWithNamespace:bundle.namespace
-                                               network:bundle.networkConfiguration
-                                        configurations:bundle.configurations
-                 ];
-                [namespaces addObject:bundle.namespace];
-            }
-        }
-    }
-    return namespaces;
-}
-
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_OSX
-
-+ (void)subscribeToWebViewEventsWithConfiguration:(WKWebViewConfiguration *)webViewConfiguration {
-    SPWebViewMessageHandler *messageHandler = [[SPWebViewMessageHandler alloc] init];
-    
-    [webViewConfiguration.userContentController addScriptMessageHandler:messageHandler name:@"snowplow"];
-}
-
-#endif
-
-// Global singleton
-
-+ (instancetype)sharedInstance {
-    static SPSnowplow *sharedInstance = nil;
-    static dispatch_once_t onceToken;
-    dispatch_once(&onceToken, ^{
-        sharedInstance = [[self alloc] init];
-    });
-    return sharedInstance;
-}
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.serviceProviderInstances = [NSMutableDictionary new];
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/SPTrackerConstants.h b/Snowplow/Internal/SPTrackerConstants.h
deleted file mode 100644
index 546aae7ea..000000000
--- a/Snowplow/Internal/SPTrackerConstants.h
+++ /dev/null
@@ -1,316 +0,0 @@
-//
-//  Snowplow.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-// Macros to define what OS is running:
-// 1. iOS: iOS == 1; OSX == 1; tvOS == 0 ; watchOS == 0
-// 2. OSX: iOS == 0; OSX == 1; tvOS == 0
-// 3. TV:  iOS == 1; OSX == 1; tvOS == 1
-#define SNOWPLOW_TARGET_IOS (TARGET_OS_IPHONE && TARGET_OS_MAC && !(TARGET_OS_TV) && !(TARGET_OS_WATCH))
-#define SNOWPLOW_TARGET_OSX (!(TARGET_OS_IPHONE) && TARGET_OS_MAC && !(TARGET_OS_TV))
-#define SNOWPLOW_TARGET_TV  (TARGET_OS_IPHONE && TARGET_OS_MAC && TARGET_OS_TV)
-#define SNOWPLOW_TARGET_WATCHOS (TARGET_OS_WATCH)
-
-// Macros for iOS Versions
-#if SNOWPLOW_TARGET_IOS
-#import <UIKit/UIDevice.h>
-#define SNOWPLOW_iOS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
-#define SNOWPLOW_iOS_9_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0)
-#endif
-
-// Macros for builder pattern
-#define SP_ESCAPE(...) __VA_ARGS__
-#define SP_BUILDER_DECLARE(type, prop) - (instancetype)prop:(type)value NS_SWIFT_NAME(prop(_:));
-#define SP_BUILDER_DECLARE_NULLABLE(type, prop) - (instancetype)prop:(nullable type)value NS_SWIFT_NAME(prop(_:));
-#define SP_BUILDER_METHOD(type, prop) - (instancetype)prop:(type)value { self.prop = value; return self; }
-#define SP_DIRTYFLAG(prop) @property BOOL prop##Updated;
-#define SP_DIRTY_GETTER(type, prop) - (type)prop { return (!self.sourceConfig || self.prop##Updated) ? super.prop : self.sourceConfig.prop; }
-
-
-@interface SPTrackerConstants : NSObject
-
-// --- Version
-
-extern NSString * const kSPVersion;
-
-// --- Dictionary keys
-
-extern NSString * const kSPInstallationUserId;
-
-// --- Emitter
-
-extern NSString * const kSPContentTypeHeader;
-extern NSString * const kSPAcceptContentHeader;
-extern NSInteger  const kSPDefaultBufferTimeout;
-extern NSString * const kSPEndpointPost;
-extern NSString * const kSPEndpointGet;
-
-// --- Schema Paths
-
-extern NSString * const kSPIglu;
-extern NSString * const kSPSnowplowVendor;
-extern NSString * const kSPSchemaTag;
-extern NSString * const kSPPayloadDataSchema;
-extern NSString * const kSPUserTimingsSchema;
-extern NSString * const kSPScreenViewSchema;
-extern NSString * const kSPUnstructSchema;
-extern NSString * const kSPContextSchema;
-extern NSString * const kSPMobileContextSchema;
-extern NSString * const kSPDesktopContextSchema;
-extern NSString * const kSPSessionContextSchema;
-extern NSString * const kSPScreenContextSchema;
-extern NSString * const kSPGeoContextSchema;
-extern NSString * const kSPConsentWithdrawnSchema;
-extern NSString * const kSPConsentDocumentSchema;
-extern NSString * const kSPConsentGrantedSchema;
-extern NSString * const kSPPushNotificationSchema;
-extern NSString * const kSPApplicationContextSchema;
-extern NSString * const kSPBackgroundSchema;
-extern NSString * const kSPForegroundSchema;
-extern NSString * const kSPErrorSchema;
-extern NSString * const kSPApplicationInstallSchema;
-extern NSString * const kSPGdprContextSchema;
-extern NSString * const kSPDiagnosticErrorSchema;
-
-// --- Event Keys
-
-extern NSString * const kSPEventPageView;
-extern NSString * const kSPEventStructured;
-extern NSString * const kSPEventUnstructured;
-extern NSString * const kSPEventEcomm;
-extern NSString * const kSPEventEcommItem;
-
-// --- General Keys
-
-extern NSString * const kSPSchema;
-extern NSString * const kSPData;
-extern NSString * const kSPEvent;
-extern NSString * const kSPEid;
-extern NSString * const kSPTimestamp;
-extern NSString * const kSPTrueTimestamp;
-extern NSString * const kSPSentTimestamp;
-extern NSString * const kSPTrackerVersion;
-extern NSString * const kSPAppId;
-extern NSString * const kSPNamespace;
-extern NSString * const kSPUid;
-extern NSString * const kSPContext;
-extern NSString * const kSPContextEncoded;
-extern NSString * const kSPUnstructured;
-extern NSString * const kSPUnstructuredEncoded;
-
-// --- Subject
-
-extern NSString * const kSPPlatform;
-extern NSString * const kSPResolution;
-extern NSString * const kSPViewPort;
-extern NSString * const kSPColorDepth;
-extern NSString * const kSPTimezone;
-extern NSString * const kSPLanguage;
-extern NSString * const kSPIpAddress;
-extern NSString * const kSPUseragent;
-extern NSString * const kSPNetworkUid;
-extern NSString * const kSPDomainUid;
-
-// --- Platform Generic
-
-extern NSString * const kSPPlatformOsType;
-extern NSString * const kSPPlatformOsVersion;
-extern NSString * const kSPPlatformDeviceManu;
-extern NSString * const kSPPlatformDeviceModel;
-
-// --- Mobile Context
-
-extern NSString * const kSPMobileCarrier;
-extern NSString * const kSPMobileAppleIdfa;
-extern NSString * const kSPMobileAppleIdfv;
-extern NSString * const kSPMobileNetworkType;
-extern NSString * const kSPMobileNetworkTech;
-extern NSString * const kSPMobilePhysicalMemory;
-extern NSString * const kSPMobileAppAvailableMemory;
-extern NSString * const kSPMobileBatteryLevel;
-extern NSString * const kSPMobileBatteryState;
-extern NSString * const kSPMobileLowPowerMode;
-extern NSString * const kSPMobileAvailableStorage;
-extern NSString * const kSPMobileTotalStorage;
-
-// --- Application Context
-extern NSString * const kSPApplicationVersion;
-extern NSString * const kSPApplicationBuild;
-
-// --- Session Context
-
-extern NSString * const kSPSessionUserId;
-extern NSString * const kSPSessionId;
-extern NSString * const kSPSessionPreviousId;
-extern NSString * const kSPSessionIndex;
-extern NSString * const kSPSessionStorage;
-extern NSString * const kSPSessionFirstEventId;
-extern NSString * const kSPSessionFirstEventTimestamp;
-extern NSString * const kSPSessionEventIndex;
-extern NSString * const kSPSessionAnonymousUserId;
-
-// --- Geo-Location Context
-
-extern NSString * const kSPGeoLatitude;
-extern NSString * const kSPGeoLongitude;
-extern NSString * const kSPGeoLatLongAccuracy;
-extern NSString * const kSPGeoAltitude;
-extern NSString * const kSPGeoAltitudeAccuracy;
-extern NSString * const kSPGeoBearing;
-extern NSString * const kSPGeoSpeed;
-extern NSString * const kSPGeoTimestamp;
-
-// --- Screen Context
-extern NSString * const kSPScreenName;
-extern NSString * const kSPScreenType;
-extern NSString * const kSPScreenId;
-extern NSString * const kSPScreenViewController;
-extern NSString * const kSPScreenTopViewController;
-
-// --- Page View Event
-
-extern NSString * const kSPPageUrl;
-extern NSString * const kSPPageTitle;
-extern NSString * const kSPPageRefr;
-
-// --- Structured Event
-
-extern NSString * const kSPStuctCategory;
-extern NSString * const kSPStuctAction;
-extern NSString * const kSPStuctLabel;
-extern NSString * const kSPStuctProperty;
-extern NSString * const kSPStuctValue;
-
-// --- E-commerce Transaction Event
-
-extern NSString * const kSPEcommId;
-extern NSString * const kSPEcommTotal;
-extern NSString * const kSPEcommAffiliation;
-extern NSString * const kSPEcommTax;
-extern NSString * const kSPEcommShipping;
-extern NSString * const kSPEcommCity;
-extern NSString * const kSPEcommState;
-extern NSString * const kSPEcommCountry;
-extern NSString * const kSPEcommCurrency;
-
-// --- E-commerce Transaction Item Event
-
-extern NSString * const kSPEcommItemId;
-extern NSString * const kSPEcommItemSku;
-extern NSString * const kSPEcommItemName;
-extern NSString * const kSPEcommItemCategory;
-extern NSString * const kSPEcommItemPrice;
-extern NSString * const kSPEcommItemQuantity;
-extern NSString * const kSPEcommItemCurrency;
-
-// --- Consent Granted Event
-extern NSString * const KSPCgExpiry;
-
-// --- Consent Withdrawn Event
-extern NSString * const KSPCwAll;
-
-// --- Consent Document Event
-extern NSString * const kSPCdId;
-extern NSString * const kSPCdVersion;
-extern NSString * const kSPCdName;
-extern NSString * const KSPCdDescription;
-
-// --- Screen View Event
-
-extern NSString * const kSPSvName;
-extern NSString * const kSPSvType;
-extern NSString * const kSPSvScreenId;
-extern NSString * const kSPSvPreviousName;
-extern NSString * const kSPSvPreviousType;
-extern NSString * const kSPSvPreviousScreenId;
-extern NSString * const kSPSvTransitionType;
-extern NSString * const kSPSvViewController;
-extern NSString * const kSPSvTopViewController;
-
-// --- User Timing Event
-
-extern NSString * const kSPUtCategory;
-extern NSString * const kSPUtVariable;
-extern NSString * const kSPUtTiming;
-extern NSString * const kSPUtLabel;
-
-// --- Push Notification Event
-
-extern NSString * const kSPPushAction;
-extern NSString * const kSPPushTrigger;
-extern NSString * const kSPPushDeliveryDate;
-extern NSString * const kSPPushCategoryId;
-extern NSString * const kSPPushThreadId;
-extern NSString * const kSPPushNotification;
-extern NSString * const kSPPnTitle;
-extern NSString * const kSPPnSubtitle;
-extern NSString * const kSPPnBody;
-extern NSString * const kSPPnBadge;
-extern NSString * const kSPPnSound;
-extern NSString * const kSPPnLaunchImageName;
-extern NSString * const kSPPnUserInfo;
-extern NSString * const kSPPnAttachments;
-extern NSString * const kSPPnAttachmentId;
-extern NSString * const kSPPnAttachmentUrl;
-extern NSString * const kSPPnAttachmentType;
-
-// --- Background Event
-
-extern NSString * const kSPBackgroundIndex;
-
-// --- Foreground Event
-
-extern NSString * const kSPForegroundIndex;
-
-// --- Error Event
-
-extern NSString * const kSPErrorMessage;
-extern NSString * const kSPErrorStackTrace;
-extern NSString * const kSPErrorName;
-extern NSString * const kSPErrorLanguage;
-
-extern NSString * const kSPErrorTrackerUrl;
-extern NSString * const kSPErrorTrackerProtocol;
-extern NSString * const kSPErrorTrackerMethod;
-
-// --- Install tracking
-
-extern NSString * const kSPInstalledBefore;
-extern NSString * const kSPInstallTimestamp;
-extern NSString * const kSPPreviousInstallVersion;
-extern NSString * const kSPPreviousInstallBuild;
-
-// --- GDPR Context
-
-extern NSString * const kSPBasisForProcessing;
-extern NSString * const kSPDocumentId;
-extern NSString * const kSPDocumentVersion;
-extern NSString * const kSPDocumentDescription;
-
-// --- Tracker Diagnostic
-
-extern NSString * const kSPDiagnosticErrorMessage;
-extern NSString * const kSPDiagnosticErrorStack;
-extern NSString * const kSPDiagnosticErrorClassName;
-extern NSString * const kSPDiagnosticErrorExceptionName;
-
-@end
diff --git a/Snowplow/Internal/SPTrackerConstants.m b/Snowplow/Internal/SPTrackerConstants.m
deleted file mode 100644
index 70bb4180e..000000000
--- a/Snowplow/Internal/SPTrackerConstants.m
+++ /dev/null
@@ -1,303 +0,0 @@
-//
-//  Snowplow.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-
-@implementation SPTrackerConstants
-
-// --- Version
-
-#if SNOWPLOW_TARGET_IOS
-NSString * const kSPVersion               = @"ios-4.1.0";
-#elif SNOWPLOW_TARGET_TV
-NSString * const kSPVersion               = @"tvos-4.1.0";
-#elif SNOWPLOW_TARGET_WATCHOS
-NSString * const kSPVersion               = @"watchos-4.1.0";
-#else
-NSString * const kSPVersion               = @"osx-4.1.0";
-#endif
-
-// --- Session Dictionary keys
-
-NSString * const kSPInstallationUserId    = @"SPInstallationUserId";
-
-// --- Emitter
-
-NSString * const kSPContentTypeHeader     = @"application/json; charset=utf-8";
-NSString * const kSPAcceptContentHeader   = @"text/html, application/x-www-form-urlencoded, text/plain, image/gif";
-NSInteger  const kSPDefaultBufferTimeout  = 60;
-NSString * const kSPEndpointPost          = @"/com.snowplowanalytics.snowplow/tp2";
-NSString * const kSPEndpointGet           = @"/i";
-
-// --- Schema Paths
-
-NSString * const kSPIglu                  = @"iglu";
-NSString * const kSPSnowplowVendor        = @"com.snowplowanalytics.snowplow";
-NSString * const kSPSchemaTag             = @"jsonschema";
-NSString * const kSPPayloadDataSchema     = @"iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4";
-NSString * const kSPUserTimingsSchema     = @"iglu:com.snowplowanalytics.snowplow/timing/jsonschema/1-0-0";
-NSString * const kSPScreenViewSchema      = @"iglu:com.snowplowanalytics.mobile/screen_view/jsonschema/1-0-0";
-NSString * const kSPUnstructSchema        = @"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0";
-NSString * const kSPContextSchema         = @"iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-1";
-NSString * const kSPMobileContextSchema   = @"iglu:com.snowplowanalytics.snowplow/mobile_context/jsonschema/1-0-2";
-NSString * const kSPDesktopContextSchema  = @"iglu:com.snowplowanalytics.snowplow/desktop_context/jsonschema/1-0-0";
-NSString * const kSPSessionContextSchema  = @"iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2";
-NSString * const kSPScreenContextSchema   = @"iglu:com.snowplowanalytics.mobile/screen/jsonschema/1-0-0";
-NSString * const kSPGeoContextSchema      = @"iglu:com.snowplowanalytics.snowplow/geolocation_context/jsonschema/1-1-0";
-NSString * const kSPConsentDocumentSchema = @"iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0";
-NSString * const kSPConsentGrantedSchema = @"iglu:com.snowplowanalytics.snowplow/consent_granted/jsonschema/1-0-0";
-NSString * const kSPConsentWithdrawnSchema = @"iglu:com.snowplowanalytics.snowplow/consent_withdrawn/jsonschema/1-0-0";
-NSString * const kSPPushNotificationSchema = @"iglu:com.apple/notification_event/jsonschema/1-0-1";
-NSString * const kSPApplicationContextSchema = @"iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0";
-NSString * const kSPForegroundSchema = @"iglu:com.snowplowanalytics.snowplow/application_foreground/jsonschema/1-0-0";
-NSString * const kSPBackgroundSchema = @"iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0";
-NSString * const kSPErrorSchema = @"iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-2";
-NSString * const kSPApplicationInstallSchema = @"iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0";
-NSString * const kSPGdprContextSchema     = @"iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0";
-NSString * const kSPDiagnosticErrorSchema = @"iglu:com.snowplowanalytics.snowplow/diagnostic_error/jsonschema/1-0-0";
-
-// --- Event Keys
-
-NSString * const kSPEventPageView         = @"pv";
-NSString * const kSPEventStructured       = @"se";
-NSString * const kSPEventUnstructured     = @"ue";
-NSString * const kSPEventEcomm            = @"tr";
-NSString * const kSPEventEcommItem        = @"ti";
-
-// --- General Keys
-
-NSString * const kSPSchema                = @"schema";
-NSString * const kSPData                  = @"data";
-NSString * const kSPEvent                 = @"e";
-NSString * const kSPEid                   = @"eid";
-NSString * const kSPTimestamp             = @"dtm";
-NSString * const kSPTrueTimestamp         = @"ttm";
-NSString * const kSPSentTimestamp         = @"stm";
-NSString * const kSPTrackerVersion        = @"tv";
-NSString * const kSPAppId                 = @"aid";
-NSString * const kSPNamespace             = @"tna";
-NSString * const kSPUid                   = @"uid";
-NSString * const kSPContext               = @"co";
-NSString * const kSPContextEncoded        = @"cx";
-NSString * const kSPUnstructured          = @"ue_pr";
-NSString * const kSPUnstructuredEncoded   = @"ue_px";
-
-// --- Subject
-
-NSString * const kSPPlatform              = @"p";
-NSString * const kSPResolution            = @"res";
-NSString * const kSPViewPort              = @"vp";
-NSString * const kSPColorDepth            = @"cd";
-NSString * const kSPTimezone              = @"tz";
-NSString * const kSPLanguage              = @"lang";
-NSString * const kSPIpAddress             = @"ip";
-NSString * const kSPUseragent             = @"ua";
-NSString * const kSPNetworkUid            = @"tnuid";
-NSString * const kSPDomainUid             = @"duid";
-
-// --- Platform Generic
-
-NSString * const kSPPlatformOsType        = @"osType";
-NSString * const kSPPlatformOsVersion     = @"osVersion";
-NSString * const kSPPlatformDeviceManu    = @"deviceManufacturer";
-NSString * const kSPPlatformDeviceModel   = @"deviceModel";
-
-// --- Mobile Context
-
-NSString * const kSPMobileCarrier            = @"carrier";
-NSString * const kSPMobileAppleIdfa          = @"appleIdfa";
-NSString * const kSPMobileAppleIdfv          = @"appleIdfv";
-NSString * const kSPMobileNetworkType        = @"networkType";
-NSString * const kSPMobileNetworkTech        = @"networkTechnology";
-NSString * const kSPMobilePhysicalMemory     = @"physicalMemory";
-NSString * const kSPMobileAppAvailableMemory = @"appAvailableMemory";
-NSString * const kSPMobileBatteryLevel       = @"batteryLevel";
-NSString * const kSPMobileBatteryState       = @"batteryState";
-NSString * const kSPMobileLowPowerMode       = @"lowPowerMode";
-NSString * const kSPMobileAvailableStorage   = @"availableStorage";
-NSString * const kSPMobileTotalStorage       = @"totalStorage";
-
-// --- Application Context
-
-NSString * const kSPApplicationVersion    = @"version";
-NSString * const kSPApplicationBuild      = @"build";
-
-// --- Session Context
-
-NSString * const kSPSessionUserId              = @"userId";
-NSString * const kSPSessionId                  = @"sessionId";
-NSString * const kSPSessionPreviousId          = @"previousSessionId";
-NSString * const kSPSessionIndex               = @"sessionIndex";
-NSString * const kSPSessionStorage             = @"storageMechanism";
-NSString * const kSPSessionFirstEventId        = @"firstEventId";
-NSString * const kSPSessionFirstEventTimestamp = @"firstEventTimestamp";
-NSString * const kSPSessionEventIndex          = @"eventIndex";
-NSString * const kSPSessionAnonymousUserId     = @"00000000-0000-0000-0000-000000000000";
-
-// --- Geo-Location Context
-
-NSString * const kSPGeoLatitude           = @"latitude";
-NSString * const kSPGeoLongitude          = @"longitude";
-NSString * const kSPGeoLatLongAccuracy    = @"latitudeLongitudeAccuracy";
-NSString * const kSPGeoAltitude           = @"altitude";
-NSString * const kSPGeoAltitudeAccuracy   = @"altitudeAccuracy";
-NSString * const kSPGeoBearing            = @"bearing";
-NSString * const kSPGeoSpeed              = @"speed";
-NSString * const kSPGeoTimestamp          = @"timestamp";
-
-// --- Screen Context
-NSString * const kSPScreenName                = @"name";
-NSString * const kSPScreenType                = @"type";
-NSString * const kSPScreenId                  = @"id";
-NSString * const kSPScreenViewController      = @"viewController";
-NSString * const kSPScreenTopViewController   = @"topViewController";
-
-// --- Page View Event
-
-NSString * const kSPPageUrl               = @"url";
-NSString * const kSPPageTitle             = @"page";
-NSString * const kSPPageRefr              = @"refr";
-
-// --- Structured Event
-
-NSString * const kSPStuctCategory         = @"se_ca";
-NSString * const kSPStuctAction           = @"se_ac";
-NSString * const kSPStuctLabel            = @"se_la";
-NSString * const kSPStuctProperty         = @"se_pr";
-NSString * const kSPStuctValue            = @"se_va";
-
-// --- E-commerce Transaction Event
-
-NSString * const kSPEcommId               = @"tr_id";
-NSString * const kSPEcommTotal            = @"tr_tt";
-NSString * const kSPEcommAffiliation      = @"tr_af";
-NSString * const kSPEcommTax              = @"tr_tx";
-NSString * const kSPEcommShipping         = @"tr_sh";
-NSString * const kSPEcommCity             = @"tr_ci";
-NSString * const kSPEcommState            = @"tr_st";
-NSString * const kSPEcommCountry          = @"tr_co";
-NSString * const kSPEcommCurrency         = @"tr_cu";
-
-// --- E-commerce Transaction Item Event
-
-NSString * const kSPEcommItemId           = @"ti_id";
-NSString * const kSPEcommItemSku          = @"ti_sk";
-NSString * const kSPEcommItemName         = @"ti_nm";
-NSString * const kSPEcommItemCategory     = @"ti_ca";
-NSString * const kSPEcommItemPrice        = @"ti_pr";
-NSString * const kSPEcommItemQuantity     = @"ti_qu";
-NSString * const kSPEcommItemCurrency     = @"ti_cu";
-
-// --- Consent Granted Event
-NSString * const KSPCgExpiry              = @"expiry";
-
-// --- Consent Withdrawn Event
-NSString * const KSPCwAll                 = @"all";
-
-// --- Consent Document Event
-NSString * const kSPCdId                  = @"id";
-NSString * const kSPCdVersion             = @"version";
-NSString * const kSPCdName                = @"name";
-NSString * const KSPCdDescription         = @"description";
-
-// --- Screen View Event
-
-NSString * const kSPSvName                    = @"name";
-NSString * const kSPSvType                    = @"type";
-NSString * const kSPSvScreenId                = @"id";
-NSString * const kSPSvPreviousName            = @"previousName";
-NSString * const kSPSvPreviousType            = @"previousType";
-NSString * const kSPSvPreviousScreenId        = @"previousId";
-NSString * const kSPSvTransitionType          = @"transitionType";
-NSString * const kSPSvViewController          = @"viewController";
-NSString * const kSPSvTopViewController       = @"topViewController";
-
-
-// --- User Timing Event
-
-NSString * const kSPUtCategory            = @"category";
-NSString * const kSPUtVariable            = @"variable";
-NSString * const kSPUtTiming              = @"timing";
-NSString * const kSPUtLabel               = @"label";
-
-// --- Push Notification Event
-
-NSString * const kSPPushAction            = @"action";
-NSString * const kSPPushTrigger           = @"trigger";
-NSString * const kSPPushDeliveryDate      = @"deliveryDate";
-NSString * const kSPPushCategoryId        = @"categoryIdentifier";
-NSString * const kSPPushThreadId          = @"threadIdentifier";
-NSString * const kSPPushNotification      = @"notification";
-NSString * const kSPPnTitle               = @"title";
-NSString * const kSPPnSubtitle            = @"subtitle";
-NSString * const kSPPnBody                = @"body";
-NSString * const kSPPnBadge               = @"badge";
-NSString * const kSPPnSound               = @"sound";
-NSString * const kSPPnLaunchImageName     = @"launchImageName";
-NSString * const kSPPnUserInfo            = @"userInfo";
-NSString * const kSPPnAttachments         = @"attachments";
-NSString * const kSPPnAttachmentId        = @"identifier";
-NSString * const kSPPnAttachmentUrl       = @"url";
-NSString * const kSPPnAttachmentType      = @"type";
-
-// --- Foreground Event
-
-NSString * const kSPBackgroundIndex       = @"backgroundIndex";
-
-// --- Background Event
-
-NSString * const kSPForegroundIndex       = @"foregroundIndex";
-
-// --- Error Event
-
-NSString * const kSPErrorName             = @"exceptionName";
-NSString * const kSPErrorStackTrace       = @"stackTrace";
-NSString * const kSPErrorLanguage         = @"programmingLanguage";
-NSString * const kSPErrorMessage          = @"message";
-
-// --- Error Event - Tracker Settings Storage
-
-NSString * const kSPErrorTrackerUrl       = @"url";
-NSString * const kSPErrorTrackerProtocol  = @"protocol";
-NSString * const kSPErrorTrackerMethod    = @"method";
-
-// --- Install tracking
-
-NSString * const kSPInstalledBefore        = @"SPInstalledBefore";
-NSString * const kSPInstallTimestamp       = @"SPInstallTimestamp";
-NSString * const kSPPreviousInstallVersion = @"SPInstallVersion";
-NSString * const kSPPreviousInstallBuild   = @"SPInstallBuild";
-
-// --- GDPR Context
-
-NSString * const kSPBasisForProcessing  = @"basisForProcessing";
-NSString * const kSPDocumentId          = @"documentId";
-NSString * const kSPDocumentVersion     = @"documentVersion";
-NSString * const kSPDocumentDescription = @"documentDescription";
-
-// --- Tracker Diagnostic
-
-NSString * const kSPDiagnosticErrorMessage       = @"message";
-NSString * const kSPDiagnosticErrorStack         = @"stackTrace";
-NSString * const kSPDiagnosticErrorClassName     = @"className";
-NSString * const kSPDiagnosticErrorExceptionName = @"exceptionName";
-
-@end
diff --git a/Snowplow/Internal/ScreenViewTracking/SPScreenState.h b/Snowplow/Internal/ScreenViewTracking/SPScreenState.h
deleted file mode 100644
index 610cb8315..000000000
--- a/Snowplow/Internal/ScreenViewTracking/SPScreenState.h
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-//  SPScreenViewState.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPStateMachineProtocol.h"
-
-/** Forward declaration for SPScreenView */
-@class SPPayload;
-@protocol SPState;
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(ScreenState)
-@interface SPScreenState : NSObject <SPState, NSCopying>
-
-/** Screenview name */
-@property (nonatomic, copy, readonly) NSString * name;
-/** Screen ID */
-@property (nonatomic, copy, readonly) NSString * screenId;
-/** Screen type */
-@property (nonatomic, copy, nullable, readonly) NSString * type;
-/** Screenview transition type */
-@property (nonatomic, copy, nullable, readonly) NSString * transitionType;
-/** Top view controller class name */
-@property (nonatomic, copy, nullable, readonly) NSString * topViewControllerClassName;
-/** View controller class name */
-@property (nonatomic, copy, nullable, readonly) NSString * viewControllerClassName;
-
-/** Previous ScreenState */
-@property (nonatomic, nullable) SPScreenState *previousState;
-
-- (instancetype)init NS_UNAVAILABLE;
-- (instancetype)new NS_UNAVAILABLE;
-
-/**
- * Creates a new screen state.
- * @param theName A name to identify the screen view
- * @param theType The type of the screen view
- * @param theScreenId An ID generated for the screen
- * @param theTransitionType The transition used to arrive at the screen
- * @param theTopControllerName The top view controller class name
- * @param theControllerName The view controller class name
- */
-- (instancetype)initWithName:(NSString *)theName type:(nullable NSString *)theType screenId:(nullable NSString *)theScreenId transitionType:(nullable NSString *)theTransitionType topViewControllerClassName:(nullable NSString *)theTopControllerName viewControllerClassName:(nullable NSString *)theControllerName NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)initWithName:(NSString *)theName type:(nullable NSString *)theType topViewControllerClassName:(nullable NSString *)theTopControllerName viewControllerClassName:(nullable NSString *)theControllerName;
-
-- (instancetype)initWithName:(NSString *)theName type:(nullable NSString *)theType screenId:(nullable NSString *)theScreenId transitionType:(nullable NSString *)theTransitionType;
-
-- (instancetype)initWithName:(NSString *)theName type:(nullable NSString *)theType screenId:(nullable NSString *)theScreenId;
-
-- (instancetype)initWithName:(NSString *)theName screenId:(nullable NSString *)theScreenId;
-
-/**
- * Returns all non-nil values if the state is valid (e.g. name is not missing or empty string).
- */
-- (SPPayload *)payload;
-
-/**
- * Return if the state is valid.
- */
-- (BOOL) isValid;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/ScreenViewTracking/SPScreenState.m b/Snowplow/Internal/ScreenViewTracking/SPScreenState.m
deleted file mode 100644
index 6bd5a9df4..000000000
--- a/Snowplow/Internal/ScreenViewTracking/SPScreenState.m
+++ /dev/null
@@ -1,91 +0,0 @@
-//
-//  SPScreenState.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPScreenState.h"
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-
-@implementation SPScreenState
-
-- (instancetype)initWithName:(NSString *)theName type:(NSString *)theType screenId:(NSString *)theScreenId transitionType:(NSString *)theTransitionType topViewControllerClassName:(NSString *)theTopControllerName viewControllerClassName:(NSString *)theControllerName {
-    if (self = [super init]) {
-        _name = theName;
-        if (!theScreenId) {
-            _screenId = [[NSUUID UUID] UUIDString];
-        } else {
-            _screenId = theScreenId;
-        }
-        _type = theType;
-        _transitionType = theTransitionType;
-        _topViewControllerClassName = theTopControllerName;
-        _viewControllerClassName = theControllerName;
-    }
-    return self;
-}
-
-- (instancetype)initWithName:(NSString *)theName type:(NSString *)theType topViewControllerClassName:(NSString *)theTopControllerName viewControllerClassName:(NSString *)theControllerName {
-    return [self initWithName:theName type:theType screenId:nil transitionType:nil topViewControllerClassName:theTopControllerName viewControllerClassName:theControllerName];
-}
-
-- (instancetype)initWithName:(NSString *)theName type:(NSString *)theType screenId:(NSString *)theScreenId transitionType:(NSString *)theTransitionType {
-    return [self initWithName:theName type:theType screenId:theScreenId transitionType:nil topViewControllerClassName:nil viewControllerClassName:nil];
-}
-
-- (instancetype)initWithName:(NSString *)theName type:(NSString *)theType screenId:(NSString *)theScreenId {
-    return [self initWithName:theName type:theType screenId:theScreenId transitionType:nil];
-}
-
-- (instancetype)initWithName:(NSString *)theName screenId:(NSString *)theScreenId {
-    return [self initWithName:theName type:nil screenId:theScreenId transitionType:nil];
-}
-
-- (id)copyWithZone:(NSZone *)zone {
-    SPScreenState * state = [[[self class] allocWithZone:zone] init];
-    return [state initWithName:self.name
-                          type:self.type
-                      screenId:self.screenId
-                transitionType:self.transitionType
-    topViewControllerClassName:self.topViewControllerClassName
-       viewControllerClassName:self.viewControllerClassName];
-}
-
-- (BOOL)isValid {
-    return ([SPUtilities validateString:self.name] != nil) &&
-    ([SPUtilities validateString:self.screenId] != nil) &&
-    [SPUtilities isUUIDString:self.screenId];
-}
-
-- (SPPayload *)payload {
-    if ([self isValid]) {
-        SPPayload * validPayload = [[SPPayload alloc] init];
-        [validPayload addValueToPayload:self.name forKey:kSPScreenName];
-        [validPayload addValueToPayload:self.screenId forKey:kSPScreenId];
-        [validPayload addValueToPayload:[SPUtilities validateString:self.type] forKey:kSPScreenType];
-        [validPayload addValueToPayload:[SPUtilities validateString:self.topViewControllerClassName] forKey:kSPScreenTopViewController];
-        [validPayload addValueToPayload:[SPUtilities validateString:self.viewControllerClassName] forKey:kSPScreenViewController];
-        return validPayload;
-    }
-    return nil;
-}
-
-@end
diff --git a/Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.m b/Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.m
deleted file mode 100644
index c15699fb6..000000000
--- a/Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.m
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-//  SPScreenStateMachine.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPScreenStateMachine.h"
-#import "SPScreenView.h"
-#import "SPScreenState.h"
-#import "SPUtilities.h"
-
-@implementation SPScreenStateMachine
-
-- (NSArray<NSString *> *)subscribedEventSchemasForTransitions {
-    return @[kSPScreenViewSchema];
-}
-
-- (NSArray<NSString *> *)subscribedEventSchemasForEntitiesGeneration {
-    return @[@"*"];
-}
-
-- (NSArray<NSString *> *)subscribedEventSchemasForPayloadUpdating {
-    return @[kSPScreenViewSchema];
-}
-
-- (id<SPState>)transitionFromEvent:(SPEvent *)event state:(id<SPState>)currentState {
-    SPScreenView *screenView = (SPScreenView *)event;
-    SPScreenState *oldState = (SPScreenState *)currentState;
-    SPScreenState *newState = [self screenStateFromScreenView:screenView];
-    newState.previousState = oldState;
-    return newState;
-}
-
-- (NSArray<SPSelfDescribingJson *> *)entitiesFromEvent:(id<SPInspectableEvent>)event state:(id<SPState>)state {
-    if ([state isKindOfClass:SPScreenState.class]) {
-        SPSelfDescribingJson *entity = [self screenContextFromScreenState:(SPScreenState *)state];
-        return @[entity];
-    }
-    return nil;
-}
-
-- (NSDictionary<NSString *,NSObject *> *)payloadValuesFromEvent:(id<SPInspectableEvent>)event state:(id<SPState>)state {
-    if ([state isKindOfClass:SPScreenState.class]) {
-        SPScreenState *previousState = ((SPScreenState *)state).previousState;
-        NSMutableDictionary<NSString *,NSObject *> *addedValues = [NSMutableDictionary new];
-        [addedValues setValue:previousState.name forKey:kSPSvPreviousName];
-        [addedValues setValue:previousState.type forKey:kSPSvPreviousType];
-        [addedValues setValue:previousState.screenId forKey:kSPSvPreviousScreenId];
-        return addedValues;
-    }
-    return nil;
-}
-
-// Private methods
-
-- (SPScreenState *)screenStateFromScreenView:(SPScreenView *)screenView {
-    return [[SPScreenState alloc] initWithName:screenView.name
-                                          type:screenView.type
-                                      screenId:screenView.screenId
-                                transitionType:screenView.transitionType
-                    topViewControllerClassName:screenView.topViewControllerClassName
-                       viewControllerClassName:screenView.viewControllerClassName];
-}
-
-- (SPSelfDescribingJson *)screenContextFromScreenState:(SPScreenState *)screenState {
-    SPPayload *contextPayload = [screenState payload];
-    if (!contextPayload) {
-        return nil;
-    }
-    return [[SPSelfDescribingJson alloc] initWithSchema:kSPScreenContextSchema andPayload:contextPayload];
-}
-
-@end
diff --git a/Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.h b/Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.h
deleted file mode 100644
index 6ee22e500..000000000
--- a/Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.h
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-//  UIViewController+SPScreenView.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <TargetConditionals.h>
-
-#if !TARGET_OS_OSX
-#import <UIKit/UIKit.h>
-
-@class UIViewController;
-typedef NS_ENUM(NSInteger, SPScreenType);
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface UIViewController (SPScreenView_SWIZZLE)
-
-- (void) SP_viewDidAppear:(BOOL)animated;
-- (NSString *) _SP_getViewControllerName:(UIViewController *)viewController;
-- (SPScreenType) _SP_getViewControllerType:(UIViewController *)viewController;
-- (SPScreenType) _SP_getTopViewControllerType;
-- (UIViewController *) _SP_topViewController NS_EXTENSION_UNAVAILABLE_IOS("This is not available for App extensions.");
-- (UIViewController *) _SP_topViewController:(UIViewController *)rootViewController;
-- (NSString *) _SP_getViewControllerName;
-- (BOOL) _SP_validateString:(NSString *)string;
-- (NSString *) _SP_getSnowplowId;
-
-@end
-
-NS_ASSUME_NONNULL_END
-
-#endif
diff --git a/Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.m b/Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.m
deleted file mode 100644
index bfc42c3bc..000000000
--- a/Snowplow/Internal/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.m
+++ /dev/null
@@ -1,193 +0,0 @@
-//
-//  UIViewController+SPScreenView_SWIZZLE.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <TargetConditionals.h>
-
-#if !TARGET_OS_OSX
-
-#import "SPTracker.h"
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-#import "SPUtilities.h"
-#import "UIKit/UIKit.h"
-#import "UIViewController+SPScreenView_SWIZZLE.h"
-#import <objc/runtime.h>
-
-@implementation UIViewController (SPScreenView_SWIZZLE)
-
-+ (void) load {
-    static dispatch_once_t onceToken;
-    dispatch_once(&onceToken, ^{
-        //* Then swizzle */
-        Class class = [self class];
-
-        SEL originalSelector = @selector(viewDidAppear:);
-        SEL swizzledSelector = @selector(SP_viewDidAppear:);
-
-        Method originalMethod = class_getInstanceMethod(class, originalSelector);
-        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
-
-        BOOL didAddMethod =
-        class_addMethod(class,
-                        originalSelector,
-                        method_getImplementation(swizzledMethod),
-                        method_getTypeEncoding(swizzledMethod));
-
-        if (didAddMethod) {
-            class_replaceMethod(class,
-                                swizzledSelector,
-                                method_getImplementation(originalMethod),
-                                method_getTypeEncoding(originalMethod));
-        } else {
-            method_exchangeImplementations(originalMethod, swizzledMethod);
-        }
-    });
-}
-
-#pragma mark - Method Swizzling
-
-- (void) SP_viewDidAppear:(BOOL)animated {
-    [self SP_viewDidAppear:animated];
-    
-    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
-    if (![bundle.bundlePath hasPrefix:[NSBundle mainBundle].bundlePath]) {
-        // Ignore view controllers that don't start with the main bundle path
-        return;
-    }
-    
-    // Construct userInfo
-    NSMutableDictionary * userInfo = [[NSMutableDictionary alloc] init];
-    userInfo[@"viewControllerClassName"] = NSStringFromClass([self class]);
-    userInfo[@"topViewControllerClassName"] = NSStringFromClass([[self _SP_topViewController] class]);
-    // `name` is set to snowplowId class instance variable if it exists (hence no @"id" in userInfo)
-    userInfo[@"name"] = [self _SP_getViewControllerName];
-    userInfo[@"type"] = [[NSNumber alloc] initWithInteger:[self _SP_getTopViewControllerType]];
-
-    // Send notification to tracker
-    [[NSNotificationCenter defaultCenter]
-     postNotificationName:@"SPScreenViewDidAppear"
-     object:self
-     userInfo:userInfo];
-}
-
-- (BOOL) _SP_validateString:(NSString *)string {
-    return (string && (string.length > 0));
-}
-
-- (NSString *) _SP_getSnowplowId {
-    NSString * propertyName = @"snowplowId";
-    SEL selector = NSSelectorFromString(propertyName);
-    BOOL propertyExists = [self respondsToSelector:selector];
-    if (propertyExists) {
-        id value = [self valueForKey:propertyName];
-        if (value && ([value isKindOfClass:[NSString class]] || [NSStringFromClass([value class]) isEqualToString:@"Swift.String"])) {
-            return value;
-        }
-    }
-    return nil;
-}
-
-- (NSString *) _SP_getViewControllerName {
-    NSString * viewControllerName = [self _SP_getViewControllerName:self];
-    NSString * topViewControllerName = [self _SP_getViewControllerName:[self _SP_topViewController]];
-
-    if ([self _SP_validateString:viewControllerName]) {
-        return viewControllerName;
-    } else if ([self _SP_validateString:topViewControllerName]) {
-        return topViewControllerName;
-    }
-
-    return @"Unknown";
-}
-
-- (NSString *) _SP_getViewControllerName:(UIViewController *)viewController {
-    // check if there's an instance variable snowplowId
-    NSString * viewControllerSnowplowId = [viewController _SP_getSnowplowId];
-    if ([self _SP_validateString:viewControllerSnowplowId]) {
-        return viewControllerSnowplowId;
-    }
-
-    // otherwise return the class name
-    NSString * viewControllerClassName = NSStringFromClass([viewController class]);
-    if ([self _SP_validateString:viewControllerClassName]) {
-        return viewControllerClassName;
-    }
-
-    return nil;
-}
-
-- (SPScreenType) _SP_getViewControllerType:(UIViewController *)viewController {
-    if ([viewController isKindOfClass:[UINavigationController class]]) {
-        return SPScreenTypeNavigation;
-    }
-    if ([viewController isKindOfClass:[UITabBarController class]]) {
-        return SPScreenTypeTabBar;
-    }
-    if (viewController.presentedViewController) {
-        return SPScreenTypeModal;
-    }
-    if ([viewController isKindOfClass:[UIPageViewController class]]) {
-        return SPScreenTypePageView;
-    }
-    if ([viewController isKindOfClass:[UISplitViewController class]]) {
-        return SPScreenTypeSplitView;
-    }
-    Class uiPopoverPresentationControllerClass = NSClassFromString(@"UIPopoverPresentationController");
-    if (uiPopoverPresentationControllerClass && [viewController isKindOfClass:uiPopoverPresentationControllerClass]) {
-        return SPScreenTypePopoverPresentation;
-    }
-    return SPScreenTypeDefault;
-}
-
-- (SPScreenType) _SP_getTopViewControllerType {
-    return [self _SP_getViewControllerType:[self _SP_topViewController]];
-}
-
-- (UIViewController *) _SP_topViewController {
-    UIWindow *keyWindow = self.view.window;
-    if (!keyWindow) {
-        return nil;
-    }
-    return [self _SP_topViewController:keyWindow.rootViewController];
-}
-
-- (UIViewController *) _SP_topViewController:(UIViewController *)rootViewController {
-    if ([rootViewController isKindOfClass:[UINavigationController class]]) {
-        UINavigationController *navigationController = (UINavigationController *)rootViewController;
-        return [self _SP_topViewController:[navigationController.viewControllers lastObject]];
-    }
-
-    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
-        UITabBarController *tabController = (UITabBarController *)rootViewController;
-        return [self _SP_topViewController:tabController.selectedViewController];
-    }
-
-    UIViewController *presentedViewController = rootViewController.presentedViewController;
-    if (presentedViewController != nil) {
-        return [self _SP_topViewController:presentedViewController];
-    }
-
-    return rootViewController;
-}
-
-@end
-
-#endif
diff --git a/Snowplow/Internal/Session/SPSession.h b/Snowplow/Internal/Session/SPSession.h
deleted file mode 100644
index 92a0d5168..000000000
--- a/Snowplow/Internal/Session/SPSession.h
+++ /dev/null
@@ -1,128 +0,0 @@
-//
-//  SPSession.h
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPTracker.h"
-#import "SPSessionState.h"
-
-NS_SWIFT_NAME(Session)
-@interface SPSession : NSObject
-
-/// Callback to be called when the session is updated
-@property OnSessionStateUpdate onSessionStateUpdate;
-
-/// Returns the current session state
-@property (readonly) SPSessionState *state;
-
-/**
- * Initializes a newly allocated SnowplowSession
- * @param foregroundTimeout the session timeout while it is in the foreground
- * @param backgroundTimeout the session timeout while it is in the background
- * @return a SnowplowSession
- */
-- (instancetype)initWithForegroundTimeout:(NSInteger)foregroundTimeout
-                     andBackgroundTimeout:(NSInteger)backgroundTimeout;
-
-/**
- * Initializes a newly allocated SnowplowSession
- * @param foregroundTimeout the session timeout while it is in the foreground
- * @param backgroundTimeout the session timeout while it is in the background
- * @param tracker reference to the associated tracker of the session
- * @return a SnowplowSession
- */
-- (instancetype)initWithForegroundTimeout:(NSInteger)foregroundTimeout
-                     andBackgroundTimeout:(NSInteger)backgroundTimeout
-                               andTracker:(SPTracker *)tracker;
-
-/**
- * Starts the recurring timer check for sessions
- */
-- (void) startChecker;
-
-/**
- * Stops the recurring timer check for sessions
- */
-- (void) stopChecker;
-
-/// Expires the current session and starts a new one
-- (void)startNewSession;
-
-/**
- * Sets a new foreground timeout in milliseconds
- */
-- (void) setForegroundTimeout:(NSInteger)foregroundTimeout;
-
-/**
- * Sets a new background timeout in milliseconds
- */
-- (void) setBackgroundTimeout:(NSInteger)backgroundTimeout;
-
-/**
- * Returns the currently set Foreground Timeout in milliseconds
- */
-- (NSInteger) getForegroundTimeout;
-
-/**
- * Returns the currently set Background Timeout in milliseconds
- */
-- (NSInteger) getBackgroundTimeout;
-
-/**
- * Returns the current tracker associated with the session
- */
-- (SPTracker *) getTracker;
-
-/**
- * Returns the session dictionary
- * @param firstEventId The potential first event id of the session
- * @param firstEventTimestamp Device created timestamp of the first event of the session
- * @param userAnonymisation Whether to anonymise user identifiers
- * @return a SnowplowPayload containing the session dictionary
- */
-- (NSDictionary *) getSessionDictWithEventId:(NSString *)firstEventId
-                              eventTimestamp:(long long)firstEventTimestamp
-                           userAnonymisation:(BOOL)userAnonymisation;
-
-/**
- * Returns the foreground index count
- * @return a count of foregrounds
- */
-- (NSInteger) getForegroundIndex;
-
-/**
- * Returns the background index count
- * @return a count of backgrounds
- */
-- (NSInteger) getBackgroundIndex;
-
-/**
- * Returns whether the application is in the background or foreground
- * @return boolean truth of application location
- */
-- (BOOL) getInBackground;
-
-/**
- * Returns the session's userId
- * @return the session's userId
- */
-- (NSString*) getUserId;
-
-@end
diff --git a/Snowplow/Internal/Session/SPSession.m b/Snowplow/Internal/Session/SPSession.m
deleted file mode 100644
index 6cc1c89ee..000000000
--- a/Snowplow/Internal/Session/SPSession.m
+++ /dev/null
@@ -1,267 +0,0 @@
-//
-//  SPSession.m
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPDataPersistence.h"
-#import "SPTrackerConstants.h"
-#import "SPSession.h"
-#import "SPUtilities.h"
-#import "SPWeakTimerTarget.h"
-#import "SPTracker.h"
-#import "SPLogger.h"
-#import "NSDictionary+SP_TypeMethods.h"
-
-#import "SPBackground.h"
-#import "SPForeground.h"
-
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_TV
-#import <UIKit/UIKit.h>
-#endif
-
-@interface SPSession ()
-
-@property (atomic) NSNumber *lastSessionCheck;
-@property (weak) SPTracker *tracker;
-@property (nonatomic) SPDataPersistence *dataPersistence;
-@property (nonatomic, readwrite) SPSessionState *state;
-
-@end
-
-@implementation SPSession {
-    NSInteger   _foregroundTimeout;
-    NSInteger   _backgroundTimeout;
-    BOOL        _inBackground;
-    BOOL        _isNewSession;
-    BOOL        _isSessionCheckerEnabled;
-    NSString *  _userId;
-    NSInteger   _foregroundIndex;
-    NSInteger   _backgroundIndex;
-    NSInteger   _eventIndex;
-}
-
-- (instancetype)initWithForegroundTimeout:(NSInteger)foregroundTimeout andBackgroundTimeout:(NSInteger)backgroundTimeout {
-    return [self initWithForegroundTimeout:foregroundTimeout andBackgroundTimeout:backgroundTimeout andTracker:nil];
-}
-
-- (instancetype)initWithForegroundTimeout:(NSInteger)foregroundTimeout andBackgroundTimeout:(NSInteger)backgroundTimeout andTracker:(SPTracker *)tracker {
-    if (self = [super init]) {
-        _foregroundTimeout = foregroundTimeout * 1000;
-        _backgroundTimeout = backgroundTimeout * 1000;
-        _inBackground = NO;
-        _isNewSession = YES;
-        _eventIndex = 0;
-        self.tracker = tracker;
-        self.dataPersistence = [SPDataPersistence dataPersistenceForNamespace:tracker.trackerNamespace];
-        NSMutableDictionary *storedSessionDict = self.dataPersistence.session.mutableCopy;
-        _userId = [self retrieveUserIdWithSessionDict:storedSessionDict];
-        if (storedSessionDict && _userId) {
-            [storedSessionDict setObject:_userId forKey:kSPSessionUserId];
-            _state = [[SPSessionState alloc] initWithStoredState:storedSessionDict];
-        }
-        if (!_state) {
-            SPLogTrack(nil, @"No previous session info available");
-        }
-        
-        // Start session check
-        self.lastSessionCheck = [SPUtilities getTimestamp];
-        [self startChecker];
-
-        // Trigger notification for view changes
-        #if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_TV
-        [[NSNotificationCenter defaultCenter] addObserver:self
-                                                 selector:@selector(updateInBackground)
-                                                     name:UIApplicationWillResignActiveNotification
-                                                   object:nil];
-        [[NSNotificationCenter defaultCenter] addObserver:self
-                                                 selector:@selector(updateInForeground)
-                                                     name:UIApplicationDidBecomeActiveNotification
-                                                   object:nil];
-        #endif
-    }
-    return self;
-}
-
-// MARK: - Public
-
-- (void) startChecker {
-    _isSessionCheckerEnabled = YES;
-}
-
-- (void) stopChecker {
-    _isSessionCheckerEnabled = NO;
-}
-
-- (void)startNewSession {
-    // TODO: when the sesssion has been renewed programmatically, it has to be reported in the session context to the collector.
-    _isNewSession = YES;
-}
-
-- (void) setForegroundTimeout:(NSInteger)foregroundTimeout {
-    _foregroundTimeout = foregroundTimeout;
-}
-
-- (void) setBackgroundTimeout:(NSInteger)backgroundTimeout {
-    _backgroundTimeout = backgroundTimeout;
-}
-
-- (NSDictionary *) getSessionDictWithEventId:(NSString *)eventId eventTimestamp:(long long)eventTimestamp userAnonymisation:(BOOL)userAnonymisation {
-    NSMutableDictionary *context = nil;
-    @synchronized (self) {
-        if (_isSessionCheckerEnabled) {
-            if ([self shouldUpdateSession]) {
-                [self updateSessionWithEventId:eventId eventTimestamp:eventTimestamp];
-                if (self.onSessionStateUpdate) {
-                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-                        self.onSessionStateUpdate(self.state);
-                    });
-                }
-            }
-            self.lastSessionCheck = [SPUtilities getTimestamp];
-        }
-        
-        _eventIndex += 1;
-        
-        context = _state.sessionContext;
-        [context setObject:[NSNumber numberWithInteger:_eventIndex] forKey:kSPSessionEventIndex];
-    }
-    
-    if (userAnonymisation) { // mask the user identifier
-        NSMutableDictionary *copy = [[NSMutableDictionary alloc] initWithDictionary:context];
-        [copy setValue:kSPSessionAnonymousUserId forKey:kSPSessionUserId];
-        [copy setValue:[NSNull null] forKey:kSPSessionPreviousId];
-        return copy;
-    } else {
-        return context;
-    }
-}
-
-- (NSInteger) getForegroundTimeout {
-    return _foregroundTimeout;
-}
-
-- (NSInteger) getBackgroundTimeout {
-    return _backgroundTimeout;
-}
-
-- (BOOL) getInBackground {
-    return _inBackground;
-}
-
-- (NSString *)getUserId {
-    return _userId;
-}
-
-- (NSInteger) getBackgroundIndex {
-    return _backgroundIndex;
-}
-
-- (NSInteger) getForegroundIndex {
-    return _foregroundIndex;
-}
-
-- (SPTracker *) getTracker {
-    return self.tracker;
-}
-
-// MARK: - Private
-
-- (NSString *)retrieveUserIdWithSessionDict:(NSDictionary *)sessionDict {
-    NSString *userId = [sessionDict sp_stringForKey:kSPSessionUserId defaultValue:nil] ?: [SPUtilities getUUIDString];
-    // Session_UserID is available only if the session context is enabled.
-    // In a future version we would like to make it available even if the session context is disabled.
-    // For this reason, we store the Session_UserID in a separate storage (decoupled by session values)
-    // calling it Installation_UserID in order to remark that it isn't related to the session context.
-    // Although, for legacy, we need to copy its value in the Session_UserID of the session context
-    // as the session context schema (and related data modelling) requires it.
-    // For further details: https://discourse.snowplow.io/t/rfc-mobile-trackers-v2-0
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    NSString *storedUserId = [userDefaults stringForKey:kSPInstallationUserId];
-    if (storedUserId) {
-        userId = storedUserId;
-    } else {
-        [userDefaults setObject:userId forKey:kSPInstallationUserId];
-    }
-    return userId;
-}
-
-- (BOOL)shouldUpdateSession {
-    if (_isNewSession) {
-        return YES;
-    }
-    long long lastAccess = self.lastSessionCheck.longLongValue;
-    long long now = [SPUtilities getTimestamp].longLongValue;
-    NSInteger timeout = _inBackground ? _backgroundTimeout : _foregroundTimeout;
-    return now < lastAccess || now - lastAccess > timeout;
-}
-
-- (void)updateSessionWithEventId:(NSString *)eventId eventTimestamp: (long long)eventTimestamp {
-    _isNewSession = NO;
-    NSInteger sessionIndex = (_state.sessionIndex ?: 0) + 1;
-    NSString *eventISOTimestamp = [SPUtilities timestampToISOString:eventTimestamp];
-    _state = [[SPSessionState alloc] initWithFirstEventId:eventId firstEventTimestamp:eventISOTimestamp currentSessionId:[SPUtilities getUUIDString] previousSessionId:_state.sessionId sessionIndex:sessionIndex userId:_userId storage:@"LOCAL_STORAGE"];
-    NSDictionary<NSString *,NSObject *> *sessionToPersist = _state.sessionContext;
-    // Remove previousSessionId if nil because dictionaries with nil values aren't plist serializable
-    // and can't be stored with SPDataPersistence.
-    if (!_state.previousSessionId) {
-        NSMutableDictionary<NSString *,NSObject *> *sessionCopy = [sessionToPersist mutableCopy];
-        [sessionCopy removeObjectForKey:kSPSessionPreviousId];
-        sessionToPersist = sessionCopy;
-    }
-    self.dataPersistence.session = sessionToPersist;
-    _eventIndex = 0;
-}
-
-- (void) updateInBackground {
-    if (!_inBackground && [self.tracker getLifecycleEvents]) {
-        _backgroundIndex += 1;
-        [self sendBackgroundEvent];
-        _inBackground = YES;
-    }
-}
-
-- (void) updateInForeground {
-    if (_inBackground && [self.tracker getLifecycleEvents]) {
-        _foregroundIndex += 1;
-        [self sendForegroundEvent];
-        _inBackground = NO;
-    }
-}
-
-- (void) sendBackgroundEvent {
-    if (self.tracker) {
-        SPBackground *backgroundEvent = [[SPBackground alloc] initWithIndex:@(_backgroundIndex)];
-        [self.tracker track:backgroundEvent];
-    }
-}
-
-- (void) sendForegroundEvent {
-    if (self.tracker) {
-        SPForeground *foregroundEvent = [[SPForeground alloc] initWithIndex:@(_foregroundIndex)];
-        [self.tracker track:foregroundEvent];
-    }
-}
-
-- (void) dealloc {
-    #if SNOWPLOW_TARGET_IOS
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    #endif
-}
-
-@end
diff --git a/Snowplow/Internal/Session/SPSessionConfigurationUpdate.h b/Snowplow/Internal/Session/SPSessionConfigurationUpdate.h
deleted file mode 100644
index 9acf9411c..000000000
--- a/Snowplow/Internal/Session/SPSessionConfigurationUpdate.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPSessionConfigurationUpdate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSessionConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPSessionConfigurationUpdate : SPSessionConfiguration
-
-@property (nonatomic, nullable) SPSessionConfiguration *sourceConfig;
-
-@property (nonatomic) BOOL isPaused;
-
-SP_DIRTYFLAG(foregroundTimeoutInSeconds)
-SP_DIRTYFLAG(backgroundTimeoutInSeconds)
-SP_DIRTYFLAG(onSessionStateUpdate)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Session/SPSessionConfigurationUpdate.m b/Snowplow/Internal/Session/SPSessionConfigurationUpdate.m
deleted file mode 100644
index be326ebc5..000000000
--- a/Snowplow/Internal/Session/SPSessionConfigurationUpdate.m
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-//  SPSessionConfigurationUpdate.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSessionConfigurationUpdate.h"
-
-@implementation SPSessionConfigurationUpdate
-
-SP_DIRTY_GETTER(NSInteger, foregroundTimeoutInSeconds);
-SP_DIRTY_GETTER(NSInteger, backgroundTimeoutInSeconds);
-SP_DIRTY_GETTER(OnSessionStateUpdate, onSessionStateUpdate)
-
-@end
diff --git a/Snowplow/Internal/Session/SPSessionController.h b/Snowplow/Internal/Session/SPSessionController.h
deleted file mode 100644
index ead487c34..000000000
--- a/Snowplow/Internal/Session/SPSessionController.h
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-//  SPSessionController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPSessionConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(SessionController)
-@protocol SPSessionController <SPSessionConfigurationProtocol>
-
-/**
- * The session index.
- * An increasing number which helps to order the sequence of sessions.
- */
-@property (readonly) NSInteger sessionIndex;
-/**
- * The session identifier.
- * A unique identifier which is used to identify the session.
- */
-@property (readonly) NSString *sessionId;
-/**
- * The session user identifier.
- * It identifies this app installation and it doesn't change for the life of the app.
- * It will change only when the app is uninstalled and installed again.
- * An app update doesn't change the value.
- */
-@property (readonly) NSString *userId;
-
-/**
- * Whether the app is currently in background state or in foreground state.
- */
-@property (readonly) BOOL isInBackground;
-/**
- * Count the number of background transitions in the current session.
- */
-@property (readonly) NSInteger backgroundIndex;
-/**
- * Count the number of foreground transitions in the current session.
- */
-@property (readonly) NSInteger foregroundIndex;
-
-/**
- * Pause the session tracking.
- * Meanwhile the session is paused it can't expire and can't be updated.
- */
-- (void)pause;
-/**
- * Resume the session tracking.
- */
-- (void)resume;
-/**
- * Expire the current session also if the timeout is not triggered.
- */
-- (void)startNewSession;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Session/SPSessionControllerImpl.h b/Snowplow/Internal/Session/SPSessionControllerImpl.h
deleted file mode 100644
index 81c5a39a7..000000000
--- a/Snowplow/Internal/Session/SPSessionControllerImpl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-//  SPSessionControllerImpl.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPSessionController.h"
-#import "SPController.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(SessionControllerImpl)
-@interface SPSessionControllerImpl : SPController <SPSessionController>
-
-@property (readonly, nonatomic) BOOL isEnabled;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Session/SPSessionControllerImpl.m b/Snowplow/Internal/Session/SPSessionControllerImpl.m
deleted file mode 100644
index e1ecf8d44..000000000
--- a/Snowplow/Internal/Session/SPSessionControllerImpl.m
+++ /dev/null
@@ -1,178 +0,0 @@
-//
-//  SPSessionControllerImpl.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSessionControllerImpl.h"
-#import "SPSession.h"
-#import "SPLogger.h"
-#import "SPSessionConfigurationUpdate.h"
-
-@implementation SPSessionControllerImpl
-
-@synthesize backgroundTimeoutInSeconds;
-@synthesize foregroundTimeoutInSeconds;
-
-- (BOOL)isEnabled {
-    return self.session != nil;
-}
-
-- (void)pause {
-    self.dirtyConfig.isPaused = YES;
-    [self.session stopChecker];
-}
-
-- (void)resume {
-    self.dirtyConfig.isPaused = NO;
-    [self.session startChecker];
-}
-
-- (void)startNewSession {
-    [self.session startNewSession];
-}
-
-// MARK: - Properties
-
-- (void)setForegroundTimeout:(NSMeasurement<NSUnitDuration *> *)foregroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    NSMeasurement<NSUnitDuration *> *foreground = [foregroundTimeout measurementByConvertingToUnit:NSUnitDuration.seconds];
-    self.foregroundTimeoutInSeconds = floor(foreground.doubleValue);
-}
-
-- (void)setForegroundTimeoutInSeconds:(NSInteger)foregroundTimeoutInSeconds {
-    self.dirtyConfig.foregroundTimeoutInSeconds = foregroundTimeoutInSeconds;
-    self.dirtyConfig.foregroundTimeoutInSecondsUpdated = YES;
-    [self.session setForegroundTimeout:foregroundTimeoutInSeconds * 1000];
-}
-
-- (void)setBackgroundTimeout:(NSMeasurement<NSUnitDuration *> *)backgroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    NSMeasurement<NSUnitDuration *> *background = [backgroundTimeout measurementByConvertingToUnit:NSUnitDuration.seconds];
-    self.backgroundTimeoutInSeconds = floor(background.doubleValue);
-}
-
-- (void)setBackgroundTimeoutInSeconds:(NSInteger)backgroundTimeoutInSeconds {
-    self.dirtyConfig.backgroundTimeoutInSeconds = backgroundTimeoutInSeconds;
-    self.dirtyConfig.backgroundTimeoutInSecondsUpdated = YES;
-    [self.session setBackgroundTimeout:backgroundTimeoutInSeconds * 1000];
-}
-
-- (void)setOnSessionStateUpdate:(OnSessionStateUpdate)onSessionUpdate {
-    self.dirtyConfig.onSessionStateUpdate = onSessionUpdate;
-    self.dirtyConfig.onSessionStateUpdateUpdated = YES;
-    self.session.onSessionStateUpdate = onSessionUpdate;
-}
-
-- (NSMeasurement<NSUnitDuration *> *)foregroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    return [[NSMeasurement alloc] initWithDoubleValue:self.foregroundTimeoutInSeconds unit:NSUnitDuration.seconds];
-}
-
-- (NSInteger)foregroundTimeoutInSeconds {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return -1;
-    }
-    return floor([self.session getForegroundTimeout] / 1000);
-}
-
-- (NSMeasurement<NSUnitDuration *> *)backgroundTimeout
-API_AVAILABLE(ios(10), macosx(10.12), tvos(10.0), watchos(3.0))
-{
-    return [[NSMeasurement alloc] initWithDoubleValue:self.backgroundTimeoutInSeconds unit:NSUnitDuration.seconds];
-}
-
-- (NSInteger)backgroundTimeoutInSeconds {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return -1;
-    }
-    return floor([self.session getBackgroundTimeout] / 1000);
-}
-
-- (OnSessionStateUpdate)onSessionStateUpdate {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return nil;
-    }
-    return self.session.onSessionStateUpdate;
-}
-
-- (NSInteger)sessionIndex {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return -1;
-    }
-    return self.session.state.sessionIndex;
-}
-
-- (NSString *)sessionId {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return nil;
-    }
-    return self.session.state.sessionId;
-}
-
-- (NSString *)userId {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return nil;
-    }
-    return self.session.getUserId;
-}
-
-- (BOOL)isInBackground {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return NO;
-    }
-    return self.session.getInBackground;
-}
-
-- (NSInteger)backgroundIndex {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return -1;
-    }
-    return self.session.getBackgroundIndex;
-}
-
-- (NSInteger)foregroundIndex {
-    if (!self.isEnabled) {
-        SPLogTrack(nil, @"Attempt to access SessionController fields when disabled");
-        return -1;
-    }
-    return self.session.getForegroundIndex;
-}
-
-// MARK: - Private methods
-
-- (SPSession *)session {
-    return self.serviceProvider.tracker.session;
-}
-
-- (SPSessionConfigurationUpdate *)dirtyConfig {
-    return self.serviceProvider.sessionConfigurationUpdate;
-}
-
-@end
diff --git a/Snowplow/Internal/Session/SPSessionState.h b/Snowplow/Internal/Session/SPSessionState.h
deleted file mode 100644
index 23245c01d..000000000
--- a/Snowplow/Internal/Session/SPSessionState.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-//  SPSessionState.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPState.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPSessionState : NSObject <SPState>
-
-@property (nonatomic, nonnull, readonly) NSString *firstEventId;
-@property (nonatomic, nullable, readonly) NSString *firstEventTimestamp;
-@property (nonatomic, nullable, readonly) NSString *previousSessionId;
-@property (nonatomic, nonnull, readonly) NSString *sessionId;
-@property (nonatomic, readonly) NSInteger sessionIndex;
-@property (nonatomic, nonnull, readonly) NSString *storage;
-@property (nonatomic, nonnull) NSString *userId;
-
-@property (nonatomic, nonnull, readonly) NSMutableDictionary<NSString *, NSObject *> *sessionContext;
-
-- (instancetype)initWithFirstEventId:(NSString *)firstEventId firstEventTimestamp: (NSString *)firstEventTimestamp currentSessionId:(NSString *)currentSessionId previousSessionId:(nullable NSString *)previousSessionId sessionIndex:(NSInteger)sessionIndex userId:(NSString *)userId storage:(NSString *)storage;
-
-- (instancetype)initWithStoredState:(NSDictionary<NSString *, NSObject *> *)storedState;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Session/SPSessionState.m b/Snowplow/Internal/Session/SPSessionState.m
deleted file mode 100644
index b9772b119..000000000
--- a/Snowplow/Internal/Session/SPSessionState.m
+++ /dev/null
@@ -1,113 +0,0 @@
-//
-//  SPSessionState.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSessionState.h"
-#import "SPTrackerConstants.h"
-#import "NSDictionary+SP_TypeMethods.h"
-
-@interface SPSessionState ()
-
-@property (nonatomic, nonnull, readwrite) NSString *firstEventId;
-@property (nonatomic, nullable, readwrite) NSString *firstEventTimestamp;
-@property (nonatomic, nullable, readwrite) NSString *previousSessionId;
-@property (nonatomic, nonnull, readwrite) NSString *sessionId;
-@property (nonatomic, readwrite) NSInteger sessionIndex;
-@property (nonatomic, nonnull, readwrite) NSString *storage;
-
-@property (nonatomic, nonnull) NSMutableDictionary<NSString *, NSObject *> *sessionDictionary;
-
-@end
-
-@implementation SPSessionState
-
-+ (NSMutableDictionary<NSString *, NSObject *> *)buildSessionDictionaryWithFirstEventId:(NSString *)firstEventId firstEventTimestamp:(NSString *)firstEventTimestamp currentSessionId:(NSString *)currentSessionId previousSessionId:(NSString *)previousSessionId sessionIndex:(NSInteger)sessionIndex userId:(NSString *)userId storage:(NSString *)storage
-{
-    NSMutableDictionary *dictionary = [NSMutableDictionary new];
-    [dictionary setObject:previousSessionId ?: [NSNull null] forKey:kSPSessionPreviousId];
-    [dictionary setObject:currentSessionId forKey:kSPSessionId];
-    [dictionary setObject:firstEventId forKey:kSPSessionFirstEventId];
-    [dictionary setObject:firstEventTimestamp ?: [NSNull null] forKey:kSPSessionFirstEventTimestamp];
-    [dictionary setObject:[NSNumber numberWithInteger:sessionIndex] forKey:kSPSessionIndex];
-    [dictionary setObject:storage forKey:kSPSessionStorage];
-    [dictionary setObject:userId forKey:kSPSessionUserId];
-    return dictionary;
-}
-
-- (instancetype)initWithFirstEventId:(NSString *)firstEventId firstEventTimestamp:(NSString *)firstEventTimestamp currentSessionId:(NSString *)currentSessionId previousSessionId:(NSString *)previousSessionId sessionIndex:(NSInteger)sessionIndex userId:(NSString *)userId storage:(NSString *)storage {
-    if (self = [super init]) {
-        self.firstEventId = firstEventId;
-        self.firstEventTimestamp = firstEventTimestamp;
-        self.sessionId = currentSessionId;
-        self.previousSessionId = previousSessionId;
-        self.sessionIndex = sessionIndex;
-        self.userId = userId;
-        self.storage = storage;
-        
-        self.sessionDictionary = [SPSessionState buildSessionDictionaryWithFirstEventId:firstEventId
-                                                                    firstEventTimestamp:firstEventTimestamp
-                                                                       currentSessionId:currentSessionId
-                                                                      previousSessionId:previousSessionId
-                                                                           sessionIndex:sessionIndex
-                                                                                 userId:userId
-                                                                                storage:storage];
-    }
-    return self;
-}
-
-- (instancetype)initWithStoredState:(NSDictionary<NSString *,NSObject *> *)storedState {
-    if (self = [super init]) {
-        self.sessionId = [storedState sp_stringForKey:kSPSessionId defaultValue:nil];
-        if (!self.sessionId) return nil;
-        
-        NSNumber *sessionIndexNumber = [storedState sp_numberForKey:kSPSessionIndex defaultValue:nil];
-        if (!sessionIndexNumber) return nil;
-        self.sessionIndex = sessionIndexNumber.integerValue;
-        
-        self.userId = [storedState sp_stringForKey:kSPSessionUserId defaultValue:nil];
-        if (!self.userId) return nil;
-        
-        self.previousSessionId = [storedState sp_stringForKey:kSPSessionPreviousId defaultValue:nil];
-
-        // The FirstEventId should be stored in legacy persisted sessions even
-        // if it wasn't used. Anyway we provide a default value in order to be
-        // defensive and exclude any possible issue with a missing value.
-        self.firstEventId = [storedState sp_stringForKey:kSPSessionFirstEventId
-                                                       defaultValue:@"00000000-0000-0000-0000-000000000000"];
-        self.firstEventTimestamp = [storedState sp_stringForKey:kSPSessionFirstEventTimestamp defaultValue:nil];
-                
-        self.storage = [storedState sp_stringForKey:kSPSessionStorage defaultValue:@"LOCAL_STORAGE"];
-        
-        self.sessionDictionary = [SPSessionState buildSessionDictionaryWithFirstEventId:self.firstEventId
-                                                                    firstEventTimestamp:self.firstEventTimestamp
-                                                                       currentSessionId:self.sessionId
-                                                                      previousSessionId:self.previousSessionId
-                                                                           sessionIndex:self.sessionIndex
-                                                                                 userId:self.userId
-                                                                                storage:self.storage];
-    }
-    return self;
-}
-
-- (NSDictionary<NSString *,NSObject *> *)sessionContext {
-    return [self.sessionDictionary mutableCopy];
-}
-
-@end
diff --git a/Snowplow/Internal/Storage/SPEventStore.h b/Snowplow/Internal/Storage/SPEventStore.h
deleted file mode 100644
index 1bb370a9b..000000000
--- a/Snowplow/Internal/Storage/SPEventStore.h
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-//  SPEventStore.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-#import "SPPayload.h"
-#import "SPEmitterEvent.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(EventStore)
-@protocol SPEventStore <NSObject>
-
-/**
- * Adds an event to the store.
- * @param payload the payload to be added
- */
-- (void)addEvent:(SPPayload *)payload;
-
-/**
- * Removes an event from the store.
- * @param storeId the identifier of the event in the store.
- * @return a boolean of success to remove.
- */
-- (BOOL)removeEventWithId:(long long int)storeId;
-
-/**
- * Removes a range of events from the store.
- * @param storeIds the events' identifiers in the store.
- * @return a boolean of success to remove.
- */
-- (BOOL)removeEventsWithIds:(NSArray<NSNumber *> *)storeIds;
-
-/**
- * Empties the store of all the events.
- * @return a boolean of success to remove.
- */
-- (BOOL)removeAllEvents;
-
-/**
- * Returns amount of events currently in the store.
- * @return the count of events in the store.
- */
-- (NSUInteger)count;
-
-/**
- * Returns a list of EmitterEvent objects which contains events and related ids.
- * @param queryLimit is the maximum number of events returned.
- * @return EmitterEvent objects containing storeIds and event payloads.
- */
-- (NSArray<SPEmitterEvent *> *)emittableEventsWithQueryLimit:(NSUInteger)queryLimit;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Storage/SPMemoryEventStore.m b/Snowplow/Internal/Storage/SPMemoryEventStore.m
deleted file mode 100644
index 01a8e64af..000000000
--- a/Snowplow/Internal/Storage/SPMemoryEventStore.m
+++ /dev/null
@@ -1,104 +0,0 @@
-//
-//  SPMemoryEventStore.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPMemoryEventStore.h"
-
-@interface SPMemoryEventStore ()
-
-@property (nonatomic) NSUInteger sendLimit;
-@property (nonatomic) NSUInteger index;
-@property (nonatomic) NSMutableOrderedSet<SPEmitterEvent *> *orderedSet;
-
-@end
-
-@implementation SPMemoryEventStore
-
-- (instancetype)init {
-    return [self initWithLimit:250];
-}
-
-- (instancetype)initWithLimit:(NSUInteger)limit {
-    if (self = [super init]) {
-        self.orderedSet = [[NSMutableOrderedSet alloc] init];
-        self.sendLimit = limit;
-        self.index = 0;
-    }
-    return self;
-}
-
-// Interface methods
-
-- (void)addEvent:(nonnull SPPayload *)payload {
-    @synchronized (self) {
-        SPEmitterEvent *item = [[SPEmitterEvent alloc] initWithPayload:payload storeId:self.index++];
-        [self.orderedSet addObject:item];
-    }
-}
-
-- (NSUInteger)count {
-    @synchronized (self) {
-        return [self.orderedSet count];
-    }
-}
-
-- (nonnull NSArray<SPEmitterEvent *> *)emittableEventsWithQueryLimit:(NSUInteger)queryLimit {
-    @synchronized (self) {
-        NSUInteger setCount = [self.orderedSet count];
-        if (setCount <= 0) {
-            return @[];
-        }
-        NSUInteger len = MIN(queryLimit, setCount);
-        NSRange range = NSMakeRange(0, len);
-        SPEmitterEvent * __unsafe_unretained array[len];
-        [self.orderedSet getObjects:array range:range];
-        NSMutableArray<SPEmitterEvent *> *result = [[NSMutableArray alloc] initWithCapacity:len];
-        for (int i = 0; i < len; i++) {
-            [result addObject:array[i]];
-        }
-        return result;
-    }
-}
-
-- (BOOL)removeAllEvents {
-    @synchronized (self) {
-        [self.orderedSet removeAllObjects];
-        return YES;
-    }
-}
-
-- (BOOL)removeEventWithId:(long long)storeId {
-    return [self removeEventsWithIds:@[[NSNumber numberWithLongLong:storeId]]];
-}
-
-- (BOOL)removeEventsWithIds:(nonnull NSArray<NSNumber *> *)storeIds {
-    @synchronized (self) {
-        NSMutableArray<SPEmitterEvent *> *itemsToRemove = [NSMutableArray new];
-        for (SPEmitterEvent *item in self.orderedSet) {
-            if ([storeIds containsObject:[NSNumber numberWithLongLong:item.storeId]]) {
-                [itemsToRemove addObject:item];
-            }
-        }
-        [self.orderedSet removeObjectsInArray:itemsToRemove];
-        return YES;
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/Storage/SPSQLiteEventStore.h b/Snowplow/Internal/Storage/SPSQLiteEventStore.h
deleted file mode 100644
index dbacfbea9..000000000
--- a/Snowplow/Internal/Storage/SPSQLiteEventStore.h
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-//  SPSQLiteEventStore.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPEventStore.h"
-#import "SPEmitterEvent.h"
-
-@class SPPayload;
-@class FMDatabaseQueue;
-
-NS_SWIFT_NAME(SQLiteEventStore)
-@interface SPSQLiteEventStore :NSObject <SPEventStore>
-
-/**
- * IMPORTANT: This method is for internal use only. It's signature and behaviour might change in any
- * future tracker release.
- *
- * Clears all the EventStores not associated at any of the namespaces passed as parameter.
- *
- * @param allowedNamespaces The namespace allowed. All the EventStores not associated at any of
- *                          the allowedNamespaces will be cleared.
- * @return The list of namespaces that have been found with EventStores and have been cleared out.
- */
-+ (NSArray<NSString *> *)removeUnsentEventsExceptForNamespaces:(NSArray<NSString *> *)allowedNamespaces;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- *  Basic initializer that creates a database event table (if one does not exist) and then closes the connection.
- */
-- (instancetype)initWithNamespace:(NSString *)namespace;
-
-/**
- *  Inserts events into the sqlite table for the app identified with it's bundleId (appId).
- *  @param payload A SnowplowPayload instance to be inserted into the database.
- *  @return If the insert was successful, we return the rowId of the inserted entry, otherwise -1. We explicitly do this in the case of an error, sqlite would return the previous successful insert leading to incorrect data removals.
- */
-- (long long int)insertEvent:(SPPayload *)payload;
-
-/**
- *  Finds the row in the event table with the supplied ID.
- *  @param id_ Unique ID of the row in the events table to be returned.
- *  @return A dictionary containing data with keys: 'ID', 'eventData', and 'dateCreated'.
- */
-- (SPEmitterEvent *)getEventWithId:(long long int)id_;
-
-/**
- *  Returns all the events in an array of dictionaries.
- *  @return An array with each dictionary element containing key-value pairs of 'date', 'data', 'ID'.
- */
-- (NSArray<SPEmitterEvent *> *)getAllEvents;
-
-/**
- *  Returns limited number the events that are NOT pending in an array of dictionaries.
- *  @return An array with each dictionary element containing key-value pairs of 'date', 'data', 'ID'.
- */
-- (NSArray<SPEmitterEvent *> *)getAllEventsLimited:(NSUInteger)limit;
-
-/**
- *  The row ID of the last insert made.
- *  @return The row ID of the last insert made.
- */
-- (long long int)getLastInsertedRowId;
-
-@end
diff --git a/Snowplow/Internal/Storage/SPSQLiteEventStore.m b/Snowplow/Internal/Storage/SPSQLiteEventStore.m
deleted file mode 100644
index 236a9d3f7..000000000
--- a/Snowplow/Internal/Storage/SPSQLiteEventStore.m
+++ /dev/null
@@ -1,275 +0,0 @@
-//
-//  SPDefaultEventStore.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPSQLiteEventStore.h"
-#import "SPPayload.h"
-#import "SPUtilities.h"
-#import "SPJSONSerialization.h"
-#import "SPLogger.h"
-
-#if SWIFT_PACKAGE
-    #import <FMDB.h>
-#else
-    #import <fmdb/FMDB.h>
-#endif
-
-@interface SPSQLiteEventStore ()
-
-@property (nonatomic) NSString *namespace;
-@property (nonatomic) NSString *sqliteFilename;
-@property (nonatomic) NSString *dbPath;
-@property (nonatomic) FMDatabaseQueue *queue;
-@property NSUInteger sendLimit;
-
-@end
-
-@implementation SPSQLiteEventStore
-
-static NSString * const _queryCreateTable = @"CREATE TABLE IF NOT EXISTS 'events' (id INTEGER PRIMARY KEY, eventData BLOB, dateCreated TIMESTAMP DEFAULT CURRENT_TIMESTAMP)";
-static NSString * const _querySelectAll   = @"SELECT * FROM 'events'";
-static NSString * const _querySelectCount = @"SELECT Count(*) FROM 'events'";
-static NSString * const _queryInsertEvent = @"INSERT INTO 'events' (eventData) VALUES (?)";
-static NSString * const _querySelectId    = @"SELECT * FROM 'events' WHERE id=?";
-static NSString * const _queryDeleteId    = @"DELETE FROM 'events' WHERE id=?";
-static NSString * const _queryDeleteIds   = @"DELETE FROM 'events' WHERE id IN (%@)";
-static NSString * const _queryDeleteAll   = @"DELETE FROM 'events'";
-
-+ (NSArray<NSString *> *)removeUnsentEventsExceptForNamespaces:(NSArray<NSString *> *)allowedNamespaces {
-#if SNOWPLOW_TARGET_TV
-    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-#else
-    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-#endif
-    NSString *snowplowDirPath = [libraryPath stringByAppendingPathComponent:@"snowplow"];
-    NSArray<NSString *> *files = [NSFileManager.defaultManager contentsOfDirectoryAtPath:snowplowDirPath error:nil];
-    NSMutableArray<NSString *> *allowedFiles = [NSMutableArray new];
-    for (NSString *namespace in allowedNamespaces) {
-        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^a-zA-Z0-9_]+" options:0 error:nil];
-        NSString *sqliteSuffix = [regex stringByReplacingMatchesInString:namespace options:0 range:NSMakeRange(0, namespace.length) withTemplate:@"-"];
-        NSString *sqliteFilename = [NSString stringWithFormat:@"snowplowEvents-%@.sqlite", sqliteSuffix];
-        [allowedFiles addObject:sqliteFilename];
-    }
-    NSMutableArray<NSString *> *removedFiles = [NSMutableArray new];
-    for (NSString *file in files) {
-        if (![allowedFiles containsObject:file]) {
-            NSString *pathToRemove = [snowplowDirPath stringByAppendingPathComponent:file];
-            [NSFileManager.defaultManager removeItemAtPath:pathToRemove error:nil];
-            [removedFiles addObject:file];
-        }
-    }
-    return removedFiles.copy;
-}
-
-- (instancetype)initWithNamespace:(NSString *)namespace {
-    return [self initWithNamespace:namespace limit:250];
-}
-
-- (instancetype)initWithNamespace:(NSString *)namespace limit:(NSUInteger)limit {
-    if (self = [super init]) {
-        self.namespace = namespace;
-        self.sendLimit = limit;
-
-#if SNOWPLOW_TARGET_TV
-        NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-#else
-        NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
-#endif
-        // Create snowplow subdirectory if it doesn't exist
-        NSString *snowplowDirPath = [libraryPath stringByAppendingPathComponent:@"snowplow"];
-        [[NSFileManager defaultManager] createDirectoryAtPath:snowplowDirPath withIntermediateDirectories:YES attributes:nil error:nil];
-        
-        // Create path for the database
-        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^a-zA-Z0-9_]+" options:0 error:nil];
-        NSString *sqliteSuffix = [regex stringByReplacingMatchesInString:namespace options:0 range:NSMakeRange(0, namespace.length) withTemplate:@"-"];
-        self.sqliteFilename = [NSString stringWithFormat:@"snowplowEvents-%@.sqlite", sqliteSuffix];
-        self.dbPath = [snowplowDirPath stringByAppendingPathComponent:self.sqliteFilename];
-
-        // Migrate old database if it exists
-        NSString *oldDbPath = [libraryPath stringByAppendingPathComponent:@"snowplowEvents.sqlite"];
-        if ([[NSFileManager defaultManager] fileExistsAtPath:oldDbPath]) {
-            [[NSFileManager defaultManager] moveItemAtPath:oldDbPath toPath:self.dbPath error:nil];
-        }
-
-        // Create database
-        self.queue = [FMDatabaseQueue databaseQueueWithPath:self.dbPath];
-        [self createTable];
-    }
-    return self;
-}
-
-- (void) dealloc {
-    [self.queue close];
-}
-
-// MARK: SPEventStore implementation methods
-
-- (void)addEvent:(SPPayload *)payload {
-    [self insertDictionaryData:[payload getAsDictionary]];
-}
-
-- (BOOL)removeEventWithId:(long long)storeId {
-    __block BOOL res = NO;
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open]) {
-            SPLogDebug(@"Removing %@ from database now.", [@(storeId) stringValue]);
-            res = [db executeUpdate:_queryDeleteId, [NSNumber numberWithLongLong:storeId]];
-        }
-    }];
-    return res;
-}
-
-- (BOOL)removeEventsWithIds:(NSArray<NSNumber *> *)storeIds {
-    __block BOOL res = NO;
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open] && storeIds.count) {
-            NSString *ids = [storeIds componentsJoinedByString:@","];
-            SPLogDebug(@"Removing [%@] from database now.", ids);
-            NSString *query = [NSString stringWithFormat:_queryDeleteIds, ids];
-            res = [db executeUpdate:query];
-        }
-    }];
-    return res;
-}
-
-- (BOOL)removeAllEvents {
-    __block BOOL res = NO;
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open]) {
-            SPLogDebug(@"Removing all events from database now.");
-            res = [db executeUpdate:_queryDeleteAll];
-        }
-    }];
-    return res;
-}
-
-- (NSUInteger)count {
-    __block NSUInteger num = 0;
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open]) {
-            FMResultSet *s = [db executeQuery:_querySelectCount];
-            while ([s next]) {
-                num = [[NSNumber numberWithInt:[s intForColumnIndex:0]] integerValue];
-            }
-            [s close];
-        }
-    }];
-    return num;
-}
-
-- (NSArray<SPEmitterEvent *> *)emittableEventsWithQueryLimit:(NSUInteger)queryLimit {
-    return [self getAllEventsLimited:self.sendLimit];
-}
-
-// MARK: SPSQLiteEventStore methods
-
-- (BOOL) createTable {
-    __block BOOL res = NO;
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open]) {
-            res = [db executeStatements:_queryCreateTable];
-        }
-    }];
-    return res;
-}
-
-- (long long int) insertEvent:(SPPayload *)payload {
-    return [self insertDictionaryData:[payload getAsDictionary]];
-}
-
-- (long long int) insertDictionaryData:(NSDictionary *)dict {
-    __block long long int res = -1;
-    if (!dict) {
-      return res;
-    }
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open]) {
-            NSData *data = [SPJSONSerialization serializeDictionary:dict];
-            if (!data) {
-                return;
-            }
-            [db executeUpdate:_queryInsertEvent, data];
-            res = (long long int) [db lastInsertRowId];
-        }
-    }];
-    return res;
-}
-
-- (SPEmitterEvent *) getEventWithId:(long long int)id_ {
-    __block SPEmitterEvent *event = nil;
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open]) {
-            FMResultSet *s = [db executeQuery:_querySelectId, [NSNumber numberWithLongLong:id_]];
-            while ([s next]) {
-                NSData *data = [s dataForColumn:@"eventData"];
-                NSDictionary *dict = [SPJSONSerialization deserializeData:data];
-                if (!dict) {
-                    continue;
-                }
-                SPPayload *payload = [[SPPayload alloc] initWithNSDictionary:dict];
-                event = [[SPEmitterEvent alloc] initWithPayload:payload storeId:id_];
-            }
-            [s close];
-        }
-    }];
-    return event;
-}
-
-- (NSArray<SPEmitterEvent *> *)getAllEvents {
-    return [self getAllEventsWithQuery:_querySelectAll];
-}
-
-- (NSArray<SPEmitterEvent *> *)getAllEventsLimited:(NSUInteger)limit {
-    NSString *query = [NSString stringWithFormat:@"%@ LIMIT %@", _querySelectAll, [@(limit) stringValue]];
-    return [self getAllEventsWithQuery:query];
-}
-
-- (NSArray<SPEmitterEvent *> *) getAllEventsWithQuery:(NSString *)query {
-    __block NSMutableArray *res = [[NSMutableArray alloc] init];
-    [self.queue inDatabase:^(FMDatabase *db) {
-        if ([db open]) {
-            FMResultSet *s = [db executeQuery:query];
-            while ([s next]) {
-                long long int index = [s longLongIntForColumn:@"ID"];
-                NSData *data = [s dataForColumn:@"eventData"];
-                NSDictionary *dict = [SPJSONSerialization deserializeData:data];
-                if (!dict) {
-                    continue;
-                }
-                SPPayload *payload = [[SPPayload alloc] initWithNSDictionary:dict];
-                SPEmitterEvent *event = [[SPEmitterEvent alloc] initWithPayload:payload storeId:index];
-                [res addObject:event];
-            }
-            [s close];
-        }
-    }];
-    return res;
-}
-
-- (long long int) getLastInsertedRowId {
-    __block long long int res = -1;
-    [self.queue inDatabase:^(FMDatabase *db) {
-        res = [db lastInsertRowId];
-    }];
-    return res;
-}
-
-@end
diff --git a/Snowplow/Internal/Subject/SPDevicePlatform.h b/Snowplow/Internal/Subject/SPDevicePlatform.h
deleted file mode 100644
index 07620bf5d..000000000
--- a/Snowplow/Internal/Subject/SPDevicePlatform.h
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-//  SPDevicePlatform.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-typedef NS_CLOSED_ENUM(NSUInteger, SPDevicePlatform) {
-    SPDevicePlatformWeb = 0,
-    SPDevicePlatformMobile,
-    SPDevicePlatformDesktop,
-    SPDevicePlatformServerSideApp,
-    SPDevicePlatformGeneral,
-    SPDevicePlatformConnectedTV,
-    SPDevicePlatformGameConsole,
-    SPDevicePlatformInternetOfThings,
-} NS_SWIFT_NAME(DevicePlatform);
-
-NSString *SPDevicePlatformToString(SPDevicePlatform devicePlatform);
-SPDevicePlatform SPStringToDevicePlatform(NSString *devicePlatformString);
diff --git a/Snowplow/Internal/Subject/SPDevicePlatform.m b/Snowplow/Internal/Subject/SPDevicePlatform.m
deleted file mode 100644
index f6747be8a..000000000
--- a/Snowplow/Internal/Subject/SPDevicePlatform.m
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-//  SPDevicePlatform.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPDevicePlatform.h"
-
-NSString *SPDevicePlatformToString(SPDevicePlatform devicePlatform) {
-    switch (devicePlatform) {
-        case SPDevicePlatformWeb: return @"web";
-        case SPDevicePlatformMobile: return @"mob";
-        case SPDevicePlatformDesktop: return @"pc";
-        case SPDevicePlatformServerSideApp: return @"srv";
-        case SPDevicePlatformGeneral: return @"app";
-        case SPDevicePlatformConnectedTV: return @"tv";
-        case SPDevicePlatformGameConsole: return @"cnsl";
-        case SPDevicePlatformInternetOfThings: return @"iot";
-        default: return nil;
-    }
-}
-
-SPDevicePlatform SPStringToDevicePlatform(NSString *devicePlatformString) {
-    NSUInteger index = [@[@"web", @"mob", @"pc", @"srv", @"app", @"tv", @"cnsl", @"iot"] indexOfObject:devicePlatformString];
-    if (index == NSNotFound) {
-        index = SPDevicePlatformMobile;
-    }
-    return index;
-}
diff --git a/Snowplow/Internal/Subject/SPPlatformContext.h b/Snowplow/Internal/Subject/SPPlatformContext.h
deleted file mode 100644
index 60f23f892..000000000
--- a/Snowplow/Internal/Subject/SPPlatformContext.h
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-//  SPPlatformContext.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@class SPPayload;
-@class SPDeviceInfoMonitor;
-
-/*!
- @class SPPlatformContext
- @brief Manages a dictionary (SPPayload) with platform context. Some properties for mobile platforms are updated on fetch in set intervals.
- */
-
-@interface SPPlatformContext : NSObject
-
-/**
- * Initializes a newly allocated PlatformContext object with default update frequency
- * @return a PlatformContext object
- */
-- (instancetype) init;
-
-/**
- * Initializes a newly allocated PlatformContext object with custom update frequency for mobile and network properties
- * @param mobileDictUpdateFrequency Minimal gap between subsequent updates of mobile platform information
- * @param networkDictUpdateFrequency Minimal gap between subsequent updates of network platform information
- * @return a PlatformContext object
- */
-- (instancetype) initWithMobileDictUpdateFrequency:(NSTimeInterval)mobileDictUpdateFrequency
-                        networkDictUpdateFrequency:(NSTimeInterval)networkDictUpdateFrequency;
-
-/**
- * Initializes a newly allocated PlatformContext object with custom update frequency for mobile and network properties and a custom device info monitor
- * @param mobileDictUpdateFrequency Minimal gap between subsequent updates of mobile platform information
- * @param networkDictUpdateFrequency Minimal gap between subsequent updates of network platform information
- * @param deviceInfoMonitor Device monitor for fetching platform information
- * @return a PlatformContext object
- */
-- (instancetype) initWithMobileDictUpdateFrequency:(NSTimeInterval)mobileDictUpdateFrequency
-                        networkDictUpdateFrequency:(NSTimeInterval)networkDictUpdateFrequency
-                                 deviceInfoMonitor:(SPDeviceInfoMonitor *)deviceInfoMonitor;
-
-/**
- * Updates and returns payload dictionary with device context information.
- * @param userAnonymisation Whether to anonymise user identifiers (IDFA values)
- */
-- (nonnull SPPayload *) fetchPlatformDictWithUserAnonymisation:(BOOL)userAnonymisation;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Subject/SPPlatformContext.m b/Snowplow/Internal/Subject/SPPlatformContext.m
deleted file mode 100644
index 76f2941e4..000000000
--- a/Snowplow/Internal/Subject/SPPlatformContext.m
+++ /dev/null
@@ -1,132 +0,0 @@
-//
-//  SPPlatformContext.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import "SPPlatformContext.h"
-#import "SPPayload.h"
-#import "SPTrackerConstants.h"
-#import "SPDeviceInfoMonitor.h"
-
-@interface SPPlatformContext ()
-
-@property (strong, nonatomic) SPPayload *platformDict;
-@property (strong, nonatomic, readonly) SPDeviceInfoMonitor *deviceInfoMonitor;
-@property (nonatomic, readonly) NSTimeInterval mobileDictUpdateFrequency;
-@property (nonatomic, readonly) NSTimeInterval networkDictUpdateFrequency;
-@property (nonatomic) NSTimeInterval lastUpdatedEphemeralMobileDict;
-@property (nonatomic) NSTimeInterval lastUpdatedEphemeralNetworkDict;
-
-@end
-
-@implementation SPPlatformContext
-
-- (instancetype) init {
-    return [self initWithMobileDictUpdateFrequency:0.1 networkDictUpdateFrequency:10.0 deviceInfoMonitor:[[SPDeviceInfoMonitor alloc] init]];
-}
-
-- (instancetype) initWithMobileDictUpdateFrequency:(NSTimeInterval)mobileDictUpdateFrequency networkDictUpdateFrequency:(NSTimeInterval)networkDictUpdateFrequency {
-    return [self initWithMobileDictUpdateFrequency:mobileDictUpdateFrequency networkDictUpdateFrequency:networkDictUpdateFrequency deviceInfoMonitor:[[SPDeviceInfoMonitor alloc] init]];
-}
-
-- (instancetype) initWithMobileDictUpdateFrequency:(NSTimeInterval)mobileDictUpdateFrequency networkDictUpdateFrequency:(NSTimeInterval)networkDictUpdateFrequency deviceInfoMonitor:(SPDeviceInfoMonitor *)deviceInfoMonitor {
-    if (self = [super init]) {
-        _mobileDictUpdateFrequency = mobileDictUpdateFrequency;
-        _networkDictUpdateFrequency = networkDictUpdateFrequency;
-        _deviceInfoMonitor = deviceInfoMonitor;
-#if SNOWPLOW_TARGET_IOS
-        [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
-#endif
-        [self setPlatformDict];
-    }
-    return self;
-}
-
-- (SPPayload *) fetchPlatformDictWithUserAnonymisation:(BOOL)userAnonymisation {
-#if SNOWPLOW_TARGET_IOS
-    @synchronized (self) {
-        NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
-        if (now - self.lastUpdatedEphemeralMobileDict >= self.mobileDictUpdateFrequency) {
-            [self setEphemeralMobileDict];
-        }
-        if (now - self.lastUpdatedEphemeralNetworkDict >= self.networkDictUpdateFrequency) {
-            [self setEphemeralNetworkDict];
-        }
-    }
-#endif
-    if (userAnonymisation) { // mask user identifiers
-        SPPayload *copy = [[SPPayload alloc] initWithNSDictionary:[self.platformDict getAsDictionary]];
-        [copy addValueToPayload:nil forKey:kSPMobileAppleIdfa];
-        [copy addValueToPayload:nil forKey:kSPMobileAppleIdfv];
-        return copy;
-    } else {
-        return self.platformDict;
-    }
-}
-
-// MARK: - Private methods
-
-- (void) setPlatformDict {
-    self.platformDict = [[SPPayload alloc] init];
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor osType]       forKey:kSPPlatformOsType];
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor osVersion]    forKey:kSPPlatformOsVersion];
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor deviceVendor] forKey:kSPPlatformDeviceManu];
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor deviceModel]  forKey:kSPPlatformDeviceModel];
-    
-#if SNOWPLOW_TARGET_IOS
-    [self setMobileDict];
-#endif
-}
-
-- (void) setMobileDict {
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor carrierName]           forKey:kSPMobileCarrier];
-    [self.platformDict addNumericValueToPayload:[self.deviceInfoMonitor totalStorage]   forKey:kSPMobileTotalStorage];
-    [self.platformDict addNumericValueToPayload:[self.deviceInfoMonitor physicalMemory] forKey:kSPMobilePhysicalMemory];
-    
-    [self setEphemeralMobileDict];
-    [self setEphemeralNetworkDict];
-}
-
-- (void) setEphemeralMobileDict {
-    self.lastUpdatedEphemeralMobileDict = [[NSDate date] timeIntervalSince1970];
-    
-    NSDictionary *currentDict = [self.platformDict getAsDictionary];
-    if ([currentDict valueForKey:kSPMobileAppleIdfa] == nil) {
-        [self.platformDict addValueToPayload:[self.deviceInfoMonitor appleIdfa] forKey:kSPMobileAppleIdfa];
-    }
-    if ([currentDict valueForKey:kSPMobileAppleIdfv] == nil) {
-        [self.platformDict addValueToPayload:[self.deviceInfoMonitor appleIdfv] forKey:kSPMobileAppleIdfv];
-    }
-    
-    [self.platformDict addNumericValueToPayload:[self.deviceInfoMonitor batteryLevel]          forKey:kSPMobileBatteryLevel];
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor batteryState]                 forKey:kSPMobileBatteryState];
-    [self.platformDict addNumericValueToPayload:[self.deviceInfoMonitor isLowPowerModeEnabled] forKey:kSPMobileLowPowerMode];
-    [self.platformDict addNumericValueToPayload:[self.deviceInfoMonitor availableStorage]      forKey:kSPMobileAvailableStorage];
-    [self.platformDict addNumericValueToPayload:[self.deviceInfoMonitor appAvailableMemory]    forKey:kSPMobileAppAvailableMemory];
-}
-
-- (void) setEphemeralNetworkDict {
-    self.lastUpdatedEphemeralNetworkDict = [[NSDate date] timeIntervalSince1970];
-    
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor networkTechnology] forKey:kSPMobileNetworkTech];
-    [self.platformDict addValueToPayload:[self.deviceInfoMonitor networkType]       forKey:kSPMobileNetworkType];
-}
-
-
-@end
diff --git a/Snowplow/Internal/Subject/SPSubject.h b/Snowplow/Internal/Subject/SPSubject.h
deleted file mode 100644
index 930320239..000000000
--- a/Snowplow/Internal/Subject/SPSubject.h
+++ /dev/null
@@ -1,217 +0,0 @@
-//
-//  SPSubject.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPSubjectConfiguration.h"
-
-@class SPPayload;
-
-/*!
- @class SPSubject
- This class is used to access and persist user information, it represents the current user being tracked.
- */
-NS_SWIFT_NAME(Subject)
-@interface SPSubject : NSObject
-
-@property (nonatomic) BOOL platformContext;
-@property (nonatomic) BOOL geoLocationContext;
-
-@property (nonatomic) NSString *userId;
-@property (nonatomic) NSString *networkUserId;
-@property (nonatomic) NSString *domainUserId;
-@property (nonatomic) NSString *useragent;
-@property (nonatomic) NSString *ipAddress;
-@property (nonatomic) NSString *timezone;
-@property (nonatomic) NSString *language;
-@property (nonatomic) SPSize *screenResolution;
-@property (nonatomic) SPSize *screenViewPort;
-@property (nonatomic) NSInteger colorDepth;
-
-
-/*!
- @brief Initializes a newly allocated SPSubject object.
- @return A new SPSubject.
- */
-- (id) init;
-
-/*!
- @brief Creates a subject which optionally adds platform and geolocation pairs.
- @param platformContext Whether to enable the platform context.
- @param geoContext Whether to enabled the geolocation context.
- @return A new SPSubject.
- */
-- (id) initWithPlatformContext:(BOOL)platformContext andGeoContext:(BOOL)geoContext;
-
-/*!
- @warning Internal method - do not use in production
- */
-- (instancetype)initWithPlatformContext:(BOOL)platformContext geoLocationContext:(BOOL)geoContext subjectConfiguration:(SPSubjectConfiguration *)configuration;
-
-/*!
- @brief Gets all standard dictionary pairs to decorate the event with.
- @param userAnonymisation Whether to anonymise user identifiers
- @return A SPPayload with all standard pairs.
- */
-- (SPPayload *) getStandardDictWithUserAnonymisation:(BOOL)userAnonymisation;
-
-/*!
- @brief Gets all platform dictionary pairs to decorate event with. Returns nil if not enabled.
- @param userAnonymisation Whether to anonymise user identifiers
- @return A SPPayload with all platform specific pairs.
- */
-- (SPPayload *) getPlatformDictWithUserAnonymisation:(BOOL)userAnonymisation;
-
-/*!
- @brief Gets the geolocation dictionary if the required keys are available. Returns nil if not enabled.
- @return A dictionary with key-value pairs of the geolocation context.
- */
-- (NSDictionary *) getGeoLocationDict;
-
-/*!
- @brief Sets the user ID.
- @param uid The user's ID.
- */
-- (void) setUserId:(NSString *)uid;
-
-/*!
- @brief Sets the screen resolution.
- @param width The screen resolution width in pixels.
- @param height The screen resolution height in pixels.
- */
-- (void) setResolutionWithWidth:(NSInteger)width andHeight:(NSInteger)height;
-
-/*!
- @brief Sets the viewport dimensions.
- @param width The viewport width in pixels.
- @param height The viewport height in pixels.
- */
-- (void) setViewPortWithWidth:(NSInteger)width andHeight:(NSInteger)height;
-
-/*!
- @brief Sets the color depth.
- @param depth The user's color depth.
- */
-- (void) setColorDepth:(NSInteger)depth;
-
-/*!
- @brief Sets the timezone.
- @param timezone The user's timezone.
- */
-- (void) setTimezone:(NSString *)timezone;
-
-/*!
- @brief Sets the language.
- @param lang The user's language.
- */
-- (void) setLanguage:(NSString *)lang;
-
-/*!
- @brief Sets the IP Address.
- @param ip The user's IP address.
- */
-- (void) setIpAddress:(NSString *)ip;
-
-/*!
- @brief Sets the user agent (also known as browser string).
- @param useragent The user agent (also known as browser string).
- */
-- (void) setUseragent:(NSString *)useragent;
-
-/*!
- @brief Sets the Network User ID.
- @param nuid The network UID.
- */
-- (void) setNetworkUserId:(NSString *)nuid;
-
-/*!
- @brief Sets the Domain User ID.
- @param duid The domain UID.
- */
-- (void) setDomainUserId:(NSString *)duid;
-
-/*!
- @brief Sets the standard pairs for the Subject, called automatically on object creation.
- */
-- (void) setStandardDict;
-
-/*!
- @brief Optional geolocation context, if run will allocate memory for the geolocation context
- */
-- (void) setGeoDict;
-
-/*!
- @brief Sets the latitude value for the geolocation context.
- @param latitude A non-nil number.
- */
-- (void) setGeoLatitude:(float)latitude;
-- (NSNumber *)geoLatitude;
-
-/*!
- @brief Sets the longitude value for the geo context.
- @param longitude A non-nil number.
- */
-- (void) setGeoLongitude:(float)longitude;
-- (NSNumber *)geoLongitude;
-
-/*!
- @brief Sets the latitudeLongitudeAccuracy value for the geolocation context.
- @param latitudeLongitudeAccuracy A non-nil number
- */
-- (void) setGeoLatitudeLongitudeAccuracy:(float)latitudeLongitudeAccuracy;
-- (NSNumber *)geoLatitudeLongitudeAccuracy;
-
-/*!
- @brief Sets the altitude value for the geolocation context.
- @param altitude A non-nil number.
- */
-- (void) setGeoAltitude:(float)altitude;
-- (NSNumber *)geoAltitude;
-
-/*!
- @brief Sets the altitudeAccuracy value for the geolocation context.
- @param altitudeAccuracy A non-nil number.
- */
-- (void) setGeoAltitudeAccuracy:(float)altitudeAccuracy;
-- (NSNumber *)geoAltitudeAccuracy;
-
-/*!
- @brief Sets the bearing value for the geolocation context.
- @param bearing A non-nil number.
- */
-- (void) setGeoBearing:(float)bearing;
-- (NSNumber *)geoBearing;
-
-/*!
- @brief Sets the speed value for the geolocation context.
- @param speed A non-nil number.
- */
-- (void) setGeoSpeed:(float)speed;
-- (NSNumber *)geoSpeed;
-
-/*!
- @brief Sets the timestamp value for the geolocation context.
- @param timestamp The timestamp.
- */
-- (void) setGeoTimestamp:(NSNumber *)timestamp;
-- (NSNumber *)geoTimestamp;
-
-@end
-
diff --git a/Snowplow/Internal/Subject/SPSubject.m b/Snowplow/Internal/Subject/SPSubject.m
deleted file mode 100644
index 1e057f434..000000000
--- a/Snowplow/Internal/Subject/SPSubject.m
+++ /dev/null
@@ -1,276 +0,0 @@
-//
-//  SPSubject.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPSubject.h"
-#import "SPPayload.h"
-#import "SPUtilities.h"
-#import "SPLogger.h"
-#import "SPPlatformContext.h"
-
-
-@implementation SPSubject {
-    SPPayload *           _standardDict;
-    SPPlatformContext *   _platformContextManager;
-    NSMutableDictionary * _geoLocationDict;
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-- (id) init {
-    return [self initWithPlatformContext:false andGeoContext:false];
-}
-
-- (id) initWithPlatformContext:(BOOL)platformContext andGeoContext:(BOOL)geoContext {
-    return [self initWithPlatformContext:platformContext geoLocationContext:geoContext subjectConfiguration:nil];
-}
-
-- (instancetype)initWithPlatformContext:(BOOL)platformContext geoLocationContext:(BOOL)geoContext subjectConfiguration:(SPSubjectConfiguration *)config {
-    if (self = [super init]) {
-        self.platformContext = platformContext;
-        self.geoLocationContext = geoContext;
-        _standardDict = [[SPPayload alloc] init];
-        _platformContextManager = [[SPPlatformContext alloc] init];
-        [self setStandardDict];
-        [self setGeoDict];
-        if (config) {
-            if (config.userId) [self setUserId:config.userId];
-            if (config.networkUserId) [self setNetworkUserId:config.networkUserId];
-            if (config.domainUserId) [self setDomainUserId:config.domainUserId];
-            if (config.useragent) [self setUseragent:config.useragent];
-            if (config.ipAddress) [self setIpAddress:config.ipAddress];
-            NSString *timezone = config.timezone ?: [NSTimeZone localTimeZone].name;
-            if (timezone) [self setTimezone:timezone];
-            NSString *language = config.language ?: [NSLocale preferredLanguages].firstObject;
-            if (config.language) [self setLanguage:language];
-            if (config.screenResolution) {
-                SPSize *size = config.screenResolution;
-                [self setResolutionWithWidth:size.width andHeight:size.height];
-            }
-            if (config.screenViewPort) {
-                SPSize *size = config.screenViewPort;
-                [self setViewPortWithWidth:size.width andHeight:size.height];
-            }
-            if (config.colorDepth) {
-                [self setColorDepth:config.colorDepth.integerValue];
-            }
-            // geolocation
-            if (config.geoLatitude) {
-                [self setGeoLatitude:config.geoLatitude.floatValue];
-            }
-            if (config.geoLongitude) {
-                [self setGeoLongitude:config.geoLongitude.floatValue];
-            }
-            if (config.geoLatitudeLongitudeAccuracy) {
-                [self setGeoLatitudeLongitudeAccuracy:config.geoLatitudeLongitudeAccuracy.floatValue];
-            }
-            if (config.geoAltitude) {
-                [self setGeoAltitude:config.geoAltitude.floatValue];
-            }
-            if (config.geoAltitudeAccuracy) {
-                [self setGeoAltitudeAccuracy:config.geoAltitudeAccuracy.floatValue];
-            }
-            if (config.geoSpeed) {
-                [self setGeoSpeed:config.geoSpeed.floatValue];
-            }
-            if (config.geoBearing) {
-                [self setGeoBearing:config.geoBearing.floatValue];
-            }
-            if (config.geoTimestamp) {
-                [self setGeoTimestamp:config.geoTimestamp];
-            }
-        }
-    }
-    return self;
-}
-
-#pragma clang diagnostic pop
-
-- (SPPayload *) getStandardDictWithUserAnonymisation:(BOOL)userAnonymisation {
-    if (userAnonymisation) {
-        NSMutableDictionary *copy = [[NSMutableDictionary alloc] initWithDictionary:[_standardDict getAsDictionary]];
-        [copy removeObjectForKey:kSPUid];
-        [copy removeObjectForKey:kSPDomainUid];
-        [copy removeObjectForKey:kSPNetworkUid];
-        [copy removeObjectForKey:kSPIpAddress];
-        return [[SPPayload alloc] initWithNSDictionary:copy];
-    }
-    return _standardDict;
-}
-
-- (SPPayload *) getPlatformDictWithUserAnonymisation:(BOOL)userAnonymisation {
-    if (self.platformContext) {
-        return [_platformContextManager fetchPlatformDictWithUserAnonymisation:userAnonymisation];
-    } else {
-        return nil;
-    }
-}
-
-- (NSDictionary *) getGeoLocationDict {
-    if (self.geoLocationContext) {
-        if (_geoLocationDict[kSPGeoLatitude] && _geoLocationDict[kSPGeoLongitude]) {
-            return _geoLocationDict;
-        } else {
-            SPLogDebug(@"GeoLocation missing required fields; cannot get.");
-            return nil;
-        }
-    } else {
-        return nil;
-    }
-}
-
-// MARK: - Standard Dictionary
-
-- (void) setStandardDict {
-    [_standardDict addValueToPayload:[SPUtilities getResolution] forKey:kSPResolution];
-    [_standardDict addValueToPayload:[SPUtilities getViewPort]   forKey:kSPViewPort];
-    [_standardDict addValueToPayload:[SPUtilities getLanguage]   forKey:kSPLanguage];
-}
-
-- (void) setUserId:(NSString *)uid {
-    _userId = uid;
-    [_standardDict addValueToPayload:uid forKey:kSPUid];
-}
-
-- (void) identifyUser:(NSString *)uid {
-    [self setUserId:uid];
-}
-
-- (void) setResolutionWithWidth:(NSInteger)width andHeight:(NSInteger)height {
-    _screenResolution = [[SPSize alloc] initWithWidth:width height:height];
-    NSString * res = [NSString stringWithFormat:@"%@x%@", [@(width) stringValue], [@(height) stringValue]];
-    [_standardDict addValueToPayload:res forKey:kSPResolution];
-}
-
-- (void) setViewPortWithWidth:(NSInteger)width andHeight:(NSInteger)height {
-    _screenViewPort = [[SPSize alloc] initWithWidth:width height:height];
-    NSString * res = [NSString stringWithFormat:@"%@x%@", [@(width) stringValue], [@(height) stringValue]];
-    [_standardDict addValueToPayload:res forKey:kSPViewPort];
-}
-
-- (void) setColorDepth:(NSInteger)depth {
-    _colorDepth = depth;
-    NSString * res = [NSString stringWithFormat:@"%@", [@(depth) stringValue]];
-    [_standardDict addValueToPayload:res forKey:kSPColorDepth];
-}
-
-- (void) setTimezone:(NSString *)timezone {
-    _timezone = timezone;
-    [_standardDict addValueToPayload:timezone forKey:kSPTimezone];
-}
-
-- (void) setLanguage:(NSString *)lang {
-    _language = lang;
-    [_standardDict addValueToPayload:lang forKey:kSPLanguage];
-}
-
-- (void) setIpAddress:(NSString *)ip {
-    _ipAddress = ip;
-    [_standardDict addValueToPayload:ip forKey:kSPIpAddress];
-}
-
-- (void) setUseragent:(NSString *)useragent {
-    _useragent = useragent;
-    [_standardDict addValueToPayload:useragent forKey:kSPUseragent];
-}
-
-- (void) setNetworkUserId:(NSString *)nuid {
-    _networkUserId = nuid;
-    [_standardDict addValueToPayload:nuid forKey:kSPNetworkUid];
-}
-
-- (void) setDomainUserId:(NSString *)duid {
-    _domainUserId = duid;
-    [_standardDict addValueToPayload:duid forKey:kSPDomainUid];
-}
-
-// MARK: - GeoLocation Dictionary
-
-- (void) setGeoDict {
-    _geoLocationDict = [[NSMutableDictionary alloc] init];
-}
-
-- (void) setGeoLatitude:(float)latitude {
-    [_geoLocationDict setObject:[NSNumber numberWithFloat:latitude] forKey:kSPGeoLatitude];
-}
-
-- (NSNumber *)geoLatitude {
-    return (NSNumber *)_geoLocationDict[kSPGeoLatitude];
-}
-
-- (void) setGeoLongitude:(float)longitude {
-    [_geoLocationDict setObject:[NSNumber numberWithFloat:longitude] forKey:kSPGeoLongitude];
-}
-
-- (NSNumber *)geoLongitude {
-    return (NSNumber *)_geoLocationDict[kSPGeoLongitude];
-}
-
-- (void) setGeoLatitudeLongitudeAccuracy:(float)latitudeLongitudeAccuracy {
-    [_geoLocationDict setObject:[NSNumber numberWithFloat:latitudeLongitudeAccuracy] forKey:kSPGeoLatLongAccuracy];
-}
-
-- (NSNumber *)geoLatitudeLongitudeAccuracy {
-    return (NSNumber *)_geoLocationDict[kSPGeoLatLongAccuracy];
-}
-
-- (void) setGeoAltitude:(float)altitude {
-    [_geoLocationDict setObject:[NSNumber numberWithFloat:altitude] forKey:kSPGeoAltitude];
-}
-
-- (NSNumber *)geoAltitude {
-    return (NSNumber *)_geoLocationDict[kSPGeoAltitude];
-}
-
-- (void) setGeoAltitudeAccuracy:(float)altitudeAccuracy {
-    [_geoLocationDict setObject:[NSNumber numberWithFloat:altitudeAccuracy] forKey:kSPGeoAltitudeAccuracy];
-}
-
-- (NSNumber *)geoAltitudeAccuracy {
-    return (NSNumber *)_geoLocationDict[kSPGeoAltitudeAccuracy];
-}
-
-- (void) setGeoBearing:(float)bearing {
-    [_geoLocationDict setObject:[NSNumber numberWithFloat:bearing] forKey:kSPGeoBearing];
-}
-
-- (NSNumber *)geoBearing {
-    return (NSNumber *)_geoLocationDict[kSPGeoBearing];
-}
-
-- (void) setGeoSpeed:(float)speed {
-    [_geoLocationDict setObject:[NSNumber numberWithFloat:speed] forKey:kSPGeoSpeed];
-}
-
-- (NSNumber *)geoSpeed {
-    return (NSNumber *)_geoLocationDict[kSPGeoSpeed];
-}
-
-- (void) setGeoTimestamp:(NSNumber *)timestamp {
-    [_geoLocationDict setObject:timestamp forKey:kSPGeoTimestamp];
-}
-
-- (NSNumber *)geoTimestamp {
-    return (NSNumber *)_geoLocationDict[kSPGeoTimestamp];
-}
-
-@end
diff --git a/Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.h b/Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.h
deleted file mode 100644
index 009cb51e4..000000000
--- a/Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  SPSubjectConfigurationUpdate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSubjectConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPSubjectConfigurationUpdate : SPSubjectConfiguration
-
-@property (nonatomic, nullable) SPSubjectConfiguration *sourceConfig;
-
-SP_DIRTYFLAG(userId)
-SP_DIRTYFLAG(networkUserId)
-SP_DIRTYFLAG(domainUserId)
-SP_DIRTYFLAG(useragent)
-SP_DIRTYFLAG(ipAddress)
-SP_DIRTYFLAG(timezone)
-SP_DIRTYFLAG(language)
-SP_DIRTYFLAG(screenResolution)
-SP_DIRTYFLAG(screenViewPort)
-SP_DIRTYFLAG(colorDepth)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.m b/Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.m
deleted file mode 100644
index 8a76874a1..000000000
--- a/Snowplow/Internal/Subject/SPSubjectConfigurationUpdate.m
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  SPSubjectConfigurationUpdate.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSubjectConfigurationUpdate.h"
-
-@implementation SPSubjectConfigurationUpdate
-
-SP_DIRTY_GETTER(NSString *, userId)
-SP_DIRTY_GETTER(NSString *, networkUserId)
-SP_DIRTY_GETTER(NSString *, domainUserId)
-SP_DIRTY_GETTER(NSString *, useragent)
-SP_DIRTY_GETTER(NSString *, ipAddress)
-SP_DIRTY_GETTER(NSString *, timezone)
-SP_DIRTY_GETTER(NSString *, language)
-SP_DIRTY_GETTER(SPSize *, screenResolution)
-SP_DIRTY_GETTER(SPSize *, screenViewPort)
-SP_DIRTY_GETTER(NSNumber *, colorDepth)
-
-@end
diff --git a/Snowplow/Internal/Subject/SPSubjectControllerImpl.h b/Snowplow/Internal/Subject/SPSubjectControllerImpl.h
deleted file mode 100644
index 340bee72f..000000000
--- a/Snowplow/Internal/Subject/SPSubjectControllerImpl.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-//  SPSubjectControllerImpl.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPSubjectController.h"
-#import "SPController.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPSubjectControllerImpl : SPController <SPSubjectController>
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Subject/SPSubjectControllerImpl.m b/Snowplow/Internal/Subject/SPSubjectControllerImpl.m
deleted file mode 100644
index 6e4494a2e..000000000
--- a/Snowplow/Internal/Subject/SPSubjectControllerImpl.m
+++ /dev/null
@@ -1,229 +0,0 @@
-//
-//  SPSubjectControllerImpl.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPSubjectControllerImpl.h"
-#import "SPPayload.h"
-#import "SPTracker.h"
-#import "SPSubject.h"
-#import "SPSubjectConfigurationUpdate.h"
-
-
-@implementation SPSubjectControllerImpl
-@synthesize userId;
-@synthesize networkUserId;
-@synthesize domainUserId;
-@synthesize useragent;
-@synthesize ipAddress;
-@synthesize timezone;
-@synthesize language;
-@synthesize screenResolution;
-@synthesize screenViewPort;
-@synthesize colorDepth;
-
-@synthesize geoLatitude;
-@synthesize geoLongitude;
-@synthesize geoLatitudeLongitudeAccuracy;
-@synthesize geoAltitude;
-@synthesize geoAltitudeAccuracy;
-@synthesize geoSpeed;
-@synthesize geoBearing;
-@synthesize geoTimestamp;
-
-// MARK: - Properties
-
-- (void)setUserId:(NSString *)userId {
-    self.dirtyConfig.userId = userId;
-    self.dirtyConfig.userIdUpdated = YES;
-    [self.subject setUserId:userId];
-}
-
-- (NSString *)userId {
-    return self.subject.userId;
-}
-
-- (void)setNetworkUserId:(NSString *)networkUserId {
-    self.dirtyConfig.networkUserId = networkUserId;
-    self.dirtyConfig.networkUserIdUpdated = YES;
-    [self.subject setNetworkUserId:networkUserId];
-}
-
-- (NSString *)networkUserId {
-    return [self.subject networkUserId];
-}
-
-- (void)setDomainUserId:(NSString *)domainUserId {
-    self.dirtyConfig.domainUserId = domainUserId;
-    self.dirtyConfig.domainUserIdUpdated = YES;
-    [self.subject setDomainUserId:domainUserId];
-}
-
-- (NSString *)domainUserId {
-    return [self.subject domainUserId];
-}
-
-- (void)setUseragent:(NSString *)useragent {
-    self.dirtyConfig.useragent = useragent;
-    self.dirtyConfig.useragentUpdated = YES;
-    [self.subject setUseragent:useragent];
-}
-
-- (NSString *)useragent {
-    return [self.subject useragent];
-}
-
-- (void)setIpAddress:(NSString *)ipAddress {
-    self.dirtyConfig.ipAddress = ipAddress;
-    self.dirtyConfig.ipAddressUpdated = YES;
-    [self.subject setIpAddress:ipAddress];
-}
-
-- (NSString *)ipAddress {
-    return [self.subject ipAddress];
-}
-
-- (void)setTimezone:(NSString *)timezone {
-    self.dirtyConfig.timezone = timezone;
-    self.dirtyConfig.timezoneUpdated = YES;
-    [self.subject setTimezone:timezone];
-}
-
-- (NSString *)timezone {
-    return [self.subject timezone];
-}
-
-- (void)setLanguage:(NSString *)language {
-    self.dirtyConfig.language = language;
-    self.dirtyConfig.languageUpdated = YES;
-    [self.subject setLanguage:language];
-}
-
-- (NSString *)language {
-    return [self.subject language];
-}
-
-- (void)setScreenResolution:(SPSize *)screenResolution {
-    self.dirtyConfig.screenResolution = screenResolution;
-    self.dirtyConfig.screenResolutionUpdated = YES;
-    [self.subject setResolutionWithWidth:screenResolution.width andHeight:screenResolution.height];
-}
-
-- (SPSize *)screenResolution {
-    return [self.subject screenResolution];
-}
-
-- (void)setScreenViewPort:(SPSize *)screenViewPort {
-    self.dirtyConfig.screenViewPort = screenViewPort;
-    self.dirtyConfig.screenViewPortUpdated = YES;
-    [self.subject setViewPortWithWidth:screenResolution.width andHeight:screenResolution.height];
-}
-
-- (SPSize *)screenViewPort {
-    return [self.subject screenViewPort];
-}
-
-- (void)setColorDepth:(NSNumber *)colorDepth {
-    self.dirtyConfig.colorDepth = colorDepth;
-    self.dirtyConfig.colorDepthUpdated = YES;
-    [self.subject setColorDepth:colorDepth.intValue];
-}
-
-- (NSNumber *)colorDepth {
-    return @([self.subject colorDepth]);
-}
-
-// MARK: - GeoLocalization
-
-- (void)setGeoLatitude:(NSNumber *)geoLatitude {
-    [self.subject setGeoLatitude:geoLatitude.floatValue];
-}
-
-- (NSNumber *)geoLatitude {
-    return [self.subject geoLatitude];
-}
-
-- (void)setGeoLongitude:(NSNumber *)geoLongitude {
-    [self.subject setGeoLongitude:geoLongitude.floatValue];
-}
-
-- (NSNumber *)geoLongitude {
-    return [self.subject geoLongitude];
-}
-
-- (void)setGeoLatitudeLongitudeAccuracy:(NSNumber *)geoLatitudeLongitudeAccuracy {
-    [self.subject setGeoLatitudeLongitudeAccuracy:geoLatitudeLongitudeAccuracy.floatValue];
-}
-
-- (NSNumber *)geoLatitudeLongitudeAccuracy {
-    return [self.subject geoLatitudeLongitudeAccuracy];
-}
-
-- (void)setGeoAltitude:(NSNumber *)geoAltitude {
-    [self.subject setGeoAltitude:geoAltitude.floatValue];
-}
-
-- (NSNumber *)geoAltitude {
-    return [self.subject geoAltitude];
-}
-
-- (void)setGeoAltitudeAccuracy:(NSNumber *)geoAltitudeAccuracy {
-    [self.subject setGeoAltitudeAccuracy:geoAltitudeAccuracy.floatValue];
-}
-
-- (NSNumber *)geoAltitudeAccuracy {
-    return [self.subject geoAltitudeAccuracy];
-}
-
-- (void)setGeoSpeed:(NSNumber *)geoSpeed {
-    [self.subject setGeoSpeed:geoSpeed.floatValue];
-}
-
-- (NSNumber *)geoSpeed {
-    return [self.subject geoSpeed];
-}
-
-- (void)setGeoBearing:(NSNumber *)geoBearing {
-    [self.subject setGeoBearing:geoBearing.floatValue];
-}
-
-- (NSNumber *)geoBearing {
-    return [self.subject geoBearing];
-}
-
-- (void)setGeoTimestamp:(NSNumber *)geoTimestamp {
-    [self.subject setGeoTimestamp:geoTimestamp];
-}
-
-- (NSNumber *)geoTimestamp {
-    return [self.subject geoTimestamp];
-}
-
-// MARK: - Private methods
-
-- (SPSubject *)subject {
-    return self.serviceProvider.tracker.subject;
-}
-
-- (SPSubjectConfigurationUpdate *)dirtyConfig {
-    return self.serviceProvider.subjectConfigurationUpdate;
-}
-
-@end
-
diff --git a/Snowplow/Internal/Tracker/SPDeepLinkState.h b/Snowplow/Internal/Tracker/SPDeepLinkState.h
deleted file mode 100644
index 24856fd9d..000000000
--- a/Snowplow/Internal/Tracker/SPDeepLinkState.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  SPDeepLinkState.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPStateMachineProtocol.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPDeepLinkState : NSObject <SPState>
-
-@property (readonly) NSString *url;
-@property (readonly) NSString *referrer;
-@property BOOL readyForOutput;
-
-- (instancetype)initWithUrl:(NSString *)url referrer:(nullable NSString *)referrer;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPDeepLinkStateMachine.m b/Snowplow/Internal/Tracker/SPDeepLinkStateMachine.m
deleted file mode 100644
index be19b2370..000000000
--- a/Snowplow/Internal/Tracker/SPDeepLinkStateMachine.m
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-//  SPDeepLinkStateMachine.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPDeepLinkStateMachine.h"
-#import "SPDeepLinkReceived.h"
-#import "SPDeepLinkState.h"
-#import "SPDeepLinkEntity.h"
-
-@implementation SPDeepLinkStateMachine
-
-/*
- States: Init, DeepLink, ReadyForOutput
- Events: DL (DeepLinkReceived), SV (ScreenView)
- Transitions:
-  - Init (DL) DeepLink
-  - DeepLink (SV) ReadyForOutput
-  - ReadyForOutput (DL) DeepLink
-  - ReadyForOutput (SV) Init
- Entity Generation:
-  - ReadyForOutput
- */
-
-- (nonnull NSArray<NSString *> *)subscribedEventSchemasForTransitions {
-    return @[kSPDeepLinkReceivedSchema, kSPScreenViewSchema];
-}
-
-- (nonnull NSArray<NSString *> *)subscribedEventSchemasForEntitiesGeneration {
-    return @[kSPScreenViewSchema];
-}
-
-- (nonnull NSArray<NSString *> *)subscribedEventSchemasForPayloadUpdating {
-    return @[];
-}
-
-- (id<SPState>)transitionFromEvent:(SPEvent *)event state:(id<SPState>)state {
-    if ([event isKindOfClass:SPDeepLinkReceived.class]) {
-        SPDeepLinkReceived *dlEvent = (SPDeepLinkReceived *)event;
-        return [[SPDeepLinkState alloc] initWithUrl:dlEvent.url referrer:dlEvent.referrer];
-    } else {
-        if (!state) {
-            return nil;
-        }
-        SPDeepLinkState *dlState = (SPDeepLinkState *)state;
-        if (dlState.readyForOutput) {
-            return nil;
-        }
-        SPDeepLinkState *currentState = [[SPDeepLinkState alloc] initWithUrl:dlState.url referrer:dlState.referrer];
-        currentState.readyForOutput = YES;
-        return currentState;
-    }
-}
-
-- (NSArray<SPSelfDescribingJson *> *)entitiesFromEvent:(id<SPInspectableEvent>)event state:(id<SPState>)state {
-    if (!state) return nil;
-    SPDeepLinkState *deepLinkState = (SPDeepLinkState *)state;
-    if (!deepLinkState.readyForOutput) return nil;
-    SPDeepLinkEntity *entity = [[[SPDeepLinkEntity alloc] initWithUrl:deepLinkState.url]
-                          referrer:deepLinkState.referrer];
-    return @[entity];
-}
-
-- (NSDictionary<NSString *,NSObject *> *)payloadValuesFromEvent:(id<SPInspectableEvent>)event state:(id<SPState>)state {
-    return nil;
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPInstallTracker.h b/Snowplow/Internal/Tracker/SPInstallTracker.h
deleted file mode 100644
index 0c6ee6b8c..000000000
--- a/Snowplow/Internal/Tracker/SPInstallTracker.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPInstallTracker.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_SWIFT_NAME(InstallTracker)
-@interface SPInstallTracker : NSObject
-
-/** Installation status */
-@property (nonatomic) BOOL isNewInstall;
-
-@property (nonatomic, readonly) NSDate *previousInstallTimestamp;
-
-- (id) init;
-
-- (void)clearPreviousInstallTimestamp;
-
-- (void)saveBuildAndVersion;
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPInstallTracker.m b/Snowplow/Internal/Tracker/SPInstallTracker.m
deleted file mode 100644
index a64d12566..000000000
--- a/Snowplow/Internal/Tracker/SPInstallTracker.m
+++ /dev/null
@@ -1,75 +0,0 @@
-//
-//  SPInstallTracker.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
-
-#import "SPInstallTracker.h"
-#import "SPUtilities.h"
-#import "SPTrackerConstants.h"
-
-@implementation SPInstallTracker
-
-- (id)init {
-    if (self = [super init]) {
-        NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
-        if ([userDefaults objectForKey:kSPInstalledBefore] == nil) {
-            // mark the install if there's no value in userDefaults
-            [userDefaults setObject:@YES forKey:kSPInstalledBefore];
-            [userDefaults setObject:[NSDate new] forKey:kSPInstallTimestamp];
-            // since the value was missing in userDefaults, we're assuming this is a new install
-            self.isNewInstall = YES;
-        } else {
-            // if there's an object in standardUserDefaults - someone has been there!
-            self.isNewInstall = NO;
-        }
-        return self;
-    }
-    return nil;
-}
-
-- (NSDate *)previousInstallTimestamp {
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    id value = [userDefaults objectForKey:kSPInstallTimestamp];
-    if (!value) {
-        return nil;
-    } else if ([value isKindOfClass:NSDate.class]) {  // v2.0 format
-        return (NSDate *)value;
-    } else if ([value isKindOfClass:NSNumber.class]) { // v1.7 format
-        NSTimeInterval timeInterval = ((NSNumber *)value).doubleValue / 1000;
-        return [[NSDate alloc] initWithTimeIntervalSince1970:timeInterval];
-    }
-    return nil;
-}
-
-- (void)clearPreviousInstallTimestamp {
-    NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
-    [userDefaults removeObjectForKey:kSPInstallTimestamp];
-}
-
-- (void)saveBuildAndVersion {
-    NSString * build = [SPUtilities getAppBuild];
-    NSString * version = [SPUtilities getAppVersion];
-    if (build && version) {
-        NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
-        [userDefaults setObject:build forKey:kSPPreviousInstallBuild];
-        [userDefaults setObject:version forKey:kSPPreviousInstallVersion];
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPLifecycleState.h b/Snowplow/Internal/Tracker/SPLifecycleState.h
deleted file mode 100644
index 045e5f9a6..000000000
--- a/Snowplow/Internal/Tracker/SPLifecycleState.h
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-//  SPLifecycleState.h
-//  Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPStateMachineProtocol.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPLifecycleState : NSObject <SPState>
-
-@property (readonly) BOOL isForeground;
-@property (readonly, nullable) NSNumber *index;
-
-- (instancetype)initAsForegroundWithIndex:(nullable NSNumber *)index;
-- (instancetype)initAsBackgroundWithIndex:(nullable NSNumber *)index;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPLifecycleState.m b/Snowplow/Internal/Tracker/SPLifecycleState.m
deleted file mode 100644
index 72bb12f21..000000000
--- a/Snowplow/Internal/Tracker/SPLifecycleState.m
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-//  SPLifecycleState.m
-//  Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPLifecycleState.h"
-
-@interface SPLifecycleState ()
-
-@property (readwrite) BOOL isForeground;
-@property (readwrite) NSNumber *index;
-
-@end
-
-@implementation SPLifecycleState
-
-- (instancetype)initAsForegroundWithIndex:(NSNumber *)index {
-    if (self = [super init]) {
-        self.isForeground = YES;
-        self.index = index;
-    }
-    return self;
-}
-
-- (instancetype)initAsBackgroundWithIndex:(NSNumber *)index {
-    if (self = [super init]) {
-        self.isForeground = NO;
-        self.index = index;
-    }
-    return self;
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPLifecycleStateMachine.m b/Snowplow/Internal/Tracker/SPLifecycleStateMachine.m
deleted file mode 100644
index 791c16e84..000000000
--- a/Snowplow/Internal/Tracker/SPLifecycleStateMachine.m
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-//  SPLifecycleStateMachine.m
-//  Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-// This program is licensed to you under the Apache License Version 2.0,
-// and you may not use this file except in compliance with the Apache License
-// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-// http://www.apache.org/licenses/LICENSE-2.0.
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the Apache License Version 2.0 is distributed on
-// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the Apache License Version 2.0 for the specific
-// language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
-
-#import "SPLifecycleStateMachine.h"
-#import "SPLifecycleState.h"
-#import "SPBackground.h"
-#import "SPForeground.h"
-#import "SPLifecycleEntity.h"
-
-@implementation SPLifecycleStateMachine
-
-- (NSArray<NSString *> *)subscribedEventSchemasForTransitions {
-    return @[kSPBackgroundSchema, kSPForegroundSchema];
-}
-
-- (id<SPState>)transitionFromEvent:(SPEvent *)event state:(id<SPState>)currentState {
-    if ([event isKindOfClass:SPForeground.class]) {
-        SPForeground *e = (SPForeground *)event;
-        return [[SPLifecycleState alloc] initAsForegroundWithIndex:e.index];
-    }
-    if ([event isKindOfClass:SPBackground.class]) {
-        SPBackground *e = (SPBackground *)event;
-        return [[SPLifecycleState alloc] initAsBackgroundWithIndex:e.index];
-    }
-    return nil;
-}
-
-- (NSArray<NSString *> *)subscribedEventSchemasForEntitiesGeneration {
-    return @[@"*"];
-}
-
-- (NSArray<SPSelfDescribingJson *> *)entitiesFromEvent:(id<SPInspectableEvent>)event state:(id<SPState>)state {
-    if (!state) return @[[[[SPLifecycleEntity alloc] initWithIsVisible:YES] index:0]];
-    SPLifecycleState *s = (SPLifecycleState *)state;
-    return @[[[[SPLifecycleEntity alloc] initWithIsVisible:s.isForeground] index:s.index]];
-}
-
-- (nonnull NSArray<NSString *> *)subscribedEventSchemasForPayloadUpdating {
-    return @[];
-}
-
-- (NSDictionary<NSString *,NSObject *> *)payloadValuesFromEvent:(id<SPInspectableEvent>)event state:(id<SPState>)state {
-    return nil;
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPServiceProvider.h b/Snowplow/Internal/Tracker/SPServiceProvider.h
deleted file mode 100644
index 75354b832..000000000
--- a/Snowplow/Internal/Tracker/SPServiceProvider.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPServiceProvider.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPServiceProviderProtocol.h"
-#import "SPNetworkConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPServiceProvider : NSObject <SPServiceProviderProtocol>
-
-- (instancetype)initWithNamespace:(NSString *)namespace network:(SPNetworkConfiguration *)networkConfiguration configurations:(NSArray<SPConfiguration *> *)configurations;
-
-- (void)resetWithConfigurations:(NSArray<SPConfiguration *> *)configurations;
-
-- (void)shutdown;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPServiceProvider.m b/Snowplow/Internal/Tracker/SPServiceProvider.m
deleted file mode 100644
index 4868ad0b7..000000000
--- a/Snowplow/Internal/Tracker/SPServiceProvider.m
+++ /dev/null
@@ -1,396 +0,0 @@
-//
-//  SPServiceProvider.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPServiceProvider.h"
-#import "SPDefaultNetworkConnection.h"
-#import "SPGdprContext.h"
-
-#import "SPEmitter.h"
-#import "SPSubject.h"
-#import "SPTracker.h"
-#import "SPSession.h"
-
-#import "SPTrackerControllerImpl.h"
-#import "SPEmitterControllerImpl.h"
-#import "SPNetworkControllerImpl.h"
-#import "SPSubjectControllerImpl.h"
-#import "SPSessionControllerImpl.h"
-#import "SPGlobalContextsControllerImpl.h"
-#import "SPGDPRControllerImpl.h"
-
-#import "SPNetworkConfigurationUpdate.h"
-#import "SPTrackerConfigurationUpdate.h"
-#import "SPEmitterConfigurationUpdate.h"
-#import "SPSubjectConfigurationUpdate.h"
-#import "SPSessionConfigurationUpdate.h"
-#import "SPGDPRConfigurationUpdate.h"
-
-@interface SPServiceProvider ()
-
-@property (nonatomic, nonnull, readwrite) NSString *namespace;
-
-// Internal services
-@property (nonatomic, nullable) SPTracker *tracker;
-@property (nonatomic, nullable) SPEmitter *emitter;
-@property (nonatomic, nullable) SPSubject *subject;
-
-// Controllers
-@property (nonatomic, nullable) SPTrackerControllerImpl *trackerController;
-@property (nonatomic, nullable) SPEmitterControllerImpl *emitterController;
-@property (nonatomic, nullable) SPNetworkControllerImpl *networkController;
-@property (nonatomic, nullable) SPGDPRControllerImpl *gdprController;
-@property (nonatomic, nullable) SPGlobalContextsControllerImpl *globalContextsController;
-@property (nonatomic, nullable) SPSubjectControllerImpl *subjectController;
-@property (nonatomic, nullable) SPSessionControllerImpl *sessionController;
-
-// Original configurations
-@property (nonatomic) SPGlobalContextsConfiguration *globalContextConfiguration;
-
-// Configuration updates
-@property (nonatomic) SPNetworkConfigurationUpdate *networkConfigurationUpdate;
-@property (nonatomic) SPTrackerConfigurationUpdate *trackerConfigurationUpdate;
-@property (nonatomic) SPEmitterConfigurationUpdate *emitterConfigurationUpdate;
-@property (nonatomic) SPSubjectConfigurationUpdate *subjectConfigurationUpdate;
-@property (nonatomic) SPSessionConfigurationUpdate *sessionConfigurationUpdate;
-@property (nonatomic) SPGDPRConfigurationUpdate *gdprConfigurationUpdate;
-
-@end
-
-@implementation SPServiceProvider
-@synthesize emitter = _emitter;
-@synthesize subject = _subject;
-@synthesize tracker = _tracker;
-@synthesize trackerController = _trackerController;
-@synthesize emitterController = _emitterController;
-@synthesize networkController = _networkController;
-@synthesize sessionController = _sessionController;
-@synthesize subjectController = _subjectController;
-@synthesize gdprController = _gdprController;
-@synthesize globalContextsController = _globalContextsController;
-
-// MARK: - Init
-
-- (instancetype)initWithNamespace:(NSString *)namespace network:(SPNetworkConfiguration *)networkConfiguration configurations:(NSArray<SPConfiguration *> *)configurations {
-    if (self = [super init]) {
-        [self initializeConfigurationUpdates];
-        self.namespace = namespace;
-        self.networkConfigurationUpdate.sourceConfig = networkConfiguration;
-        [self processConfigurations:configurations];
-        if (!self.trackerConfigurationUpdate.sourceConfig) {
-            self.trackerConfigurationUpdate.sourceConfig = [SPTrackerConfiguration new];
-        }
-        [self tracker]; // Build tracker to initialize NotificationCenter receivers
-    }
-    return self;
-}
-
-- (void)resetWithConfigurations:(NSArray<SPConfiguration *> *)configurations {
-    [self stopServices];
-    [self resetConfigurationUpdates];
-    [self processConfigurations:configurations];
-    [self resetServices];
-    [self tracker];
-}
-
-- (void)shutdown {
-    [_tracker pauseEventTracking];
-    [self stopServices];
-    [self resetServices];
-    [self resetControllers];
-    [self initializeConfigurationUpdates];
-}
-
-// MARK: - Private methods
-
-- (void)processConfigurations:(NSArray<SPConfiguration *> *)configurations {
-    for (SPConfiguration *configuration in configurations) {
-        if ([configuration isKindOfClass:SPNetworkConfiguration.class]) {
-            self.networkConfigurationUpdate.sourceConfig = (SPNetworkConfiguration *)configuration;
-            continue;
-        }
-        if ([configuration isKindOfClass:SPTrackerConfiguration.class]) {
-            self.trackerConfigurationUpdate.sourceConfig = (SPTrackerConfiguration *)configuration;
-            continue;
-        }
-        if ([configuration isKindOfClass:SPSubjectConfiguration.class]) {
-            self.subjectConfigurationUpdate.sourceConfig = (SPSubjectConfiguration *)configuration;
-            continue;
-        }
-        if ([configuration isKindOfClass:SPSessionConfiguration.class]) {
-            self.sessionConfigurationUpdate.sourceConfig = (SPSessionConfiguration *)configuration;
-            continue;
-        }
-        if ([configuration isKindOfClass:SPEmitterConfiguration.class]) {
-            self.emitterConfigurationUpdate.sourceConfig = (SPEmitterConfiguration *)configuration;
-            continue;
-        }
-        if ([configuration isKindOfClass:SPGDPRConfiguration.class]) {
-            self.gdprConfigurationUpdate.sourceConfig = (SPGDPRConfiguration *)configuration;
-            continue;
-        }
-        if ([configuration isKindOfClass:SPGlobalContextsConfiguration.class]) {
-            self.globalContextConfiguration = (SPGlobalContextsConfiguration *)configuration;
-            continue;
-        }
-    }
-}
-
-- (void)stopServices {
-    [_emitter pauseTimer];
-}
-
-- (void)resetServices {
-    _emitter = nil;
-    _subject = nil;
-    _tracker = nil;
-}
-
-- (void)resetControllers {
-    _trackerController = nil;
-    _sessionController = nil;
-    _emitterController = nil;
-    _gdprController = nil;
-    _globalContextsController = nil;
-    _subjectController = nil;
-    _networkController = nil;
-}
-
-- (void)resetConfigurationUpdates {
-    // Don't reset networkConfiguration as it's needed in case it's not passed in the new configurations.
-    // Set a default trackerConfiguration to reset to default if not passed.
-    self.trackerConfigurationUpdate.sourceConfig = [SPTrackerConfiguration new];
-    self.emitterConfigurationUpdate.sourceConfig = nil;
-    self.subjectConfigurationUpdate.sourceConfig = nil;
-    self.sessionConfigurationUpdate.sourceConfig = nil;
-    self.gdprConfigurationUpdate.sourceConfig = nil;
-}
-
-- (void)initializeConfigurationUpdates {
-    self.networkConfigurationUpdate = [SPNetworkConfigurationUpdate new];
-    self.trackerConfigurationUpdate = [SPTrackerConfigurationUpdate new];
-    self.emitterConfigurationUpdate = [SPEmitterConfigurationUpdate new];
-    self.subjectConfigurationUpdate = [SPSubjectConfigurationUpdate new];
-    self.sessionConfigurationUpdate = [SPSessionConfigurationUpdate new];
-    self.gdprConfigurationUpdate = [SPGDPRConfigurationUpdate new];
-}
-
-// MARK: - Getters
-
-- (BOOL)isTrackerInitialized {
-    return _tracker != nil;
-}
-
-- (SPSubject *)subject {
-    if (_subject) return _subject;
-    _subject = [self makeSubject];
-    return _subject;
-}
-
-- (SPEmitter *)emitter {
-    if (_emitter) return _emitter;
-    _emitter = [self makeEmitter];
-    return _emitter;
-}
-
-- (SPTracker *)tracker {
-    if (_tracker) return _tracker;
-    _tracker = [self makeTracker];
-    return _tracker;
-}
-
-- (SPTrackerControllerImpl *)trackerController {
-    if (_trackerController) return _trackerController;
-    _trackerController = [self makeTrackerController];
-    return _trackerController;
-}
-
-- (SPSessionControllerImpl *)sessionController {
-    if (_sessionController) return _sessionController;
-    _sessionController = [self makeSessionController];
-    return _sessionController;
-}
-
-- (SPEmitterControllerImpl *)emitterController {
-    if (_emitterController) return _emitterController;
-    _emitterController = [self makeEmitterController];
-    return _emitterController;
-}
-
-- (SPGDPRControllerImpl *)gdprController {
-    if (_gdprController) return _gdprController;
-    _gdprController = [self makeGDPRController];
-    return _gdprController;
-}
-
-- (SPGlobalContextsControllerImpl *)globalContextsController {
-    if (_globalContextsController) return _globalContextsController;
-    _globalContextsController = [self makeGlobalContextsController];
-    return _globalContextsController;
-}
-
-- (SPSubjectControllerImpl *)subjectController {
-    if (_subjectController) return _subjectController;
-    _subjectController = [self makeSubjectController];
-    return _subjectController;
-}
-
-- (SPNetworkControllerImpl *)networkController {
-    if (_networkController) return _networkController;
-    _networkController = [self makeNetworkController];
-    return _networkController;
-}
-
-// MARK: - Factories
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-- (SPSubject *)makeSubject {
-    return [[SPSubject alloc] initWithPlatformContext:self.trackerConfigurationUpdate.platformContext
-                                   geoLocationContext:self.trackerConfigurationUpdate.geoLocationContext
-                                 subjectConfiguration:self.subjectConfigurationUpdate];
-}
-
-- (SPEmitter *)makeEmitter {
-    SPNetworkConfigurationUpdate *networkConfig = self.networkConfigurationUpdate;
-    SPEmitterConfigurationUpdate *emitterConfig = self.emitterConfigurationUpdate;
-    SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
-        if (networkConfig.networkConnection) {
-            [builder setNetworkConnection:networkConfig.networkConnection];
-        } else {
-            [builder setHttpMethod:networkConfig.method];
-            [builder setProtocol:networkConfig.protocol];
-            [builder setUrlEndpoint:networkConfig.endpoint];
-        }
-        [builder setCustomPostPath:networkConfig.customPostPath];
-        [builder setRequestHeaders:networkConfig.requestHeaders];
-        if (emitterConfig) {
-            [builder setEmitRange:emitterConfig.emitRange];
-            [builder setBufferOption:emitterConfig.bufferOption];
-            [builder setEventStore:emitterConfig.eventStore];
-            [builder setByteLimitPost:emitterConfig.byteLimitPost];
-            [builder setByteLimitGet:emitterConfig.byteLimitGet];
-            [builder setEmitThreadPoolSize:emitterConfig.threadPoolSize];
-            [builder setCallback:emitterConfig.requestCallback];
-            [builder setCustomRetryForStatusCodes:emitterConfig.customRetryForStatusCodes];
-            [builder setServerAnonymisation:emitterConfig.serverAnonymisation];
-        }
-    }];
-    if (emitterConfig && emitterConfig.isPaused) {
-        [emitter pauseEmit];
-    }
-    return emitter;
-}
-
-- (SPTracker *)makeTracker {
-    SPEmitter *emitter = self.emitter;
-    SPSubject *subject = self.subject;
-    SPTrackerConfiguration *trackerConfig = self.trackerConfigurationUpdate;
-    SPSessionConfiguration *sessionConfig = self.sessionConfigurationUpdate;
-    SPGlobalContextsConfiguration *gcConfig = self.globalContextConfiguration;
-    SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder> builder) {
-        [builder setTrackerNamespace:self.namespace];
-        [builder setEmitter:emitter];
-        [builder setSubject:subject];
-        [builder setAppId:trackerConfig.appId];
-        [builder setTrackerVersionSuffix:trackerConfig.trackerVersionSuffix];
-        [builder setBase64Encoded:trackerConfig.base64Encoding];
-        [builder setLogLevel:trackerConfig.logLevel];
-        [builder setLoggerDelegate:trackerConfig.loggerDelegate];
-        [builder setDevicePlatform:trackerConfig.devicePlatform];
-        [builder setSessionContext:trackerConfig.sessionContext];
-        [builder setApplicationContext:trackerConfig.applicationContext];
-        [builder setDeepLinkContext:trackerConfig.deepLinkContext];
-        [builder setScreenContext:trackerConfig.screenContext];
-        [builder setAutotrackScreenViews:trackerConfig.screenViewAutotracking];
-        [builder setLifecycleEvents:trackerConfig.lifecycleAutotracking];
-        [builder setInstallEvent:trackerConfig.installAutotracking];
-        [builder setExceptionEvents:trackerConfig.exceptionAutotracking];
-        [builder setTrackerDiagnostic:trackerConfig.diagnosticAutotracking];
-        [builder setUserAnonymisation:trackerConfig.userAnonymisation];
-        if (sessionConfig) {
-            [builder setBackgroundTimeout:sessionConfig.backgroundTimeoutInSeconds];
-            [builder setForegroundTimeout:sessionConfig.foregroundTimeoutInSeconds];
-        }
-        if (gcConfig) {
-            [builder setGlobalContextGenerators:gcConfig.contextGenerators];
-        }
-        SPGDPRConfigurationUpdate *gdprConfig = self.gdprConfigurationUpdate;
-        if (gdprConfig.sourceConfig) {
-            [builder setGdprContextWithBasis:gdprConfig.basisForProcessing documentId:gdprConfig.documentId documentVersion:gdprConfig.documentVersion documentDescription:gdprConfig.documentDescription];
-        }
-    }];
-    if (self.trackerConfigurationUpdate.isPaused) {
-        [tracker pauseEventTracking];
-    }
-    if (tracker.session) {
-        if (self.sessionConfigurationUpdate.isPaused) {
-            [tracker.session stopChecker];
-        }
-        if (self.sessionConfigurationUpdate.onSessionStateUpdate) {
-            tracker.session.onSessionStateUpdate = self.sessionConfigurationUpdate.onSessionStateUpdate;
-        }
-    }
-    return tracker;
-}
-
-- (SPTrackerControllerImpl *)makeTrackerController {
-    SPTrackerControllerImpl *controller = [[SPTrackerControllerImpl alloc] initWithServiceProvider:self];
-    return controller;
-}
-
-- (SPSessionControllerImpl *)makeSessionController {
-    SPSessionControllerImpl *controller = [[SPSessionControllerImpl alloc] initWithServiceProvider:self];
-    return controller;
-}
-
-- (SPEmitterControllerImpl *)makeEmitterController {
-    SPEmitterControllerImpl *controller = [[SPEmitterControllerImpl alloc] initWithServiceProvider:self];
-    return controller;
-}
-
-- (SPGDPRControllerImpl *)makeGDPRController {
-    SPGDPRControllerImpl *controller = [[SPGDPRControllerImpl alloc] initWithServiceProvider:self];
-    SPGdprContext *gdpr = self.tracker.gdprContext;
-    if (gdpr) {
-        [controller resetWithBasis:gdpr.basis documentId:gdpr.documentId documentVersion:gdpr.documentVersion documentDescription:gdpr.documentDescription];
-    }
-    return controller;
-}
-
-- (SPGlobalContextsControllerImpl *)makeGlobalContextsController {
-    SPGlobalContextsControllerImpl *controller = [[SPGlobalContextsControllerImpl alloc] initWithServiceProvider:self];
-    return controller;
-}
-
-- (SPSubjectControllerImpl *)makeSubjectController {
-    SPSubjectControllerImpl *controller = [[SPSubjectControllerImpl alloc] initWithServiceProvider:self];
-    return controller;
-}
-
-- (SPNetworkControllerImpl *)makeNetworkController {
-    SPNetworkControllerImpl *controller = [[SPNetworkControllerImpl alloc] initWithServiceProvider:self];
-    return controller;
-}
-
-#pragma clang diagnostic pop
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPServiceProviderProtocol.h b/Snowplow/Internal/Tracker/SPServiceProviderProtocol.h
deleted file mode 100644
index c03e01f10..000000000
--- a/Snowplow/Internal/Tracker/SPServiceProviderProtocol.h
+++ /dev/null
@@ -1,71 +0,0 @@
-//
-//  SPServiceProviderProtocol.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-@class SPSubject;
-@class SPEmitter;
-@class SPTracker;
-@protocol SPTrackerController;
-@class SPEmitterControllerImpl;
-@class SPNetworkControllerImpl;
-@class SPGDPRControllerImpl;
-@class SPGlobalContextsControllerImpl;
-@class SPSubjectControllerImpl;
-@class SPSessionControllerImpl;
-
-@class SPNetworkConfigurationUpdate;
-@class SPTrackerConfigurationUpdate;
-@class SPEmitterConfigurationUpdate;
-@class SPSubjectConfigurationUpdate;
-@class SPSessionConfigurationUpdate;
-@class SPGDPRConfigurationUpdate;
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol SPServiceProviderProtocol
-
-@property (nonatomic, nonnull, readonly) NSString *namespace;
-
-- (BOOL)isTrackerInitialized;
-
-- (SPTracker *)tracker;
-- (SPEmitter *)emitter;
-- (SPSubject *)subject;
-
-- (id<SPTrackerController>)trackerController;
-- (SPEmitterControllerImpl *)emitterController;
-- (SPNetworkControllerImpl *)networkController;
-- (SPGDPRControllerImpl *)gdprController;
-- (SPGlobalContextsControllerImpl *)globalContextsController;
-- (SPSubjectControllerImpl *)subjectController;
-- (SPSessionControllerImpl *)sessionController;
-
-- (SPNetworkConfigurationUpdate *)networkConfigurationUpdate;
-- (SPTrackerConfigurationUpdate *)trackerConfigurationUpdate;
-- (SPEmitterConfigurationUpdate *)emitterConfigurationUpdate;
-- (SPSubjectConfigurationUpdate *)subjectConfigurationUpdate;
-- (SPSessionConfigurationUpdate *)sessionConfigurationUpdate;
-- (SPGDPRConfigurationUpdate *)gdprConfigurationUpdate;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPStateFuture.h b/Snowplow/Internal/Tracker/SPStateFuture.h
deleted file mode 100644
index 9c3a3e7fd..000000000
--- a/Snowplow/Internal/Tracker/SPStateFuture.h
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-//  SPStateFuture.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPStateMachineProtocol.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- StateFuture represents the placeholder of a future computation.
- The proper state value is computed when it's observed. Until that moment the StateFuture keeps the elements
- (event, previous StateFuture, StateMachine) needed to calculate the real state value.
- For this reason, the StateFuture can be the head of StateFuture chain which will collapse once the StateFuture
- head is asked to get the real state value.
- */
-@interface SPStateFuture : NSObject
-
-@property (readonly, atomic, nullable) id<SPState> state;
-
-- (instancetype)initWithEvent:(SPEvent *)event previousState:(nullable SPStateFuture *)previousState stateMachine:(id<SPStateMachineProtocol>)stateMachine;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPStateFuture.m b/Snowplow/Internal/Tracker/SPStateFuture.m
deleted file mode 100644
index d55756697..000000000
--- a/Snowplow/Internal/Tracker/SPStateFuture.m
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-//  SPStateFuture.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPStateFuture.h"
-
-
-@interface SPStateFuture ()
-
-@property (nonatomic) SPEvent *event;
-@property (nonatomic) SPStateFuture *previousState;
-@property (nonatomic) id<SPStateMachineProtocol> stateMachine;
-
-@property (nonatomic) id<SPState> computedState;
-
-@end
-
-@implementation SPStateFuture
-
-- (instancetype)initWithEvent:(SPEvent *)event previousState:(SPStateFuture *)previousState stateMachine:(id<SPStateMachineProtocol>)stateMachine {
-    if (self = [super init]) {
-        self.event = event;
-        self.previousState = previousState;
-        self.stateMachine = stateMachine;
-    }
-    return self;
-}
-
-- (id<SPState>)state {
-    @synchronized (self) {
-        if (!self.computedState && self.stateMachine) {
-            self.computedState = [self.stateMachine transitionFromEvent:self.event state:self.previousState.state];
-            self.event = nil;
-            self.previousState = nil;
-            self.stateMachine = nil;
-        }
-        return self.computedState;
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPStateMachineProtocol.h b/Snowplow/Internal/Tracker/SPStateMachineProtocol.h
deleted file mode 100644
index 8f6915e67..000000000
--- a/Snowplow/Internal/Tracker/SPStateMachineProtocol.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  SPStateMachineProtocol.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol SPStateMachineProtocol <NSObject>
-
-- (NSArray<NSString *> *)subscribedEventSchemasForTransitions;
-- (NSArray<NSString *> *)subscribedEventSchemasForEntitiesGeneration;
-- (NSArray<NSString *> *)subscribedEventSchemasForPayloadUpdating;
-
-- (nullable id<SPState>)transitionFromEvent:(SPEvent *)event state:(nullable id<SPState>)state;
-
-- (nullable NSArray<SPSelfDescribingJson *> *)entitiesFromEvent:(id<SPInspectableEvent>)event state:(nullable id<SPState>)state;
-
-- (nullable NSDictionary<NSString *, NSObject *> *)payloadValuesFromEvent:(id<SPInspectableEvent>)event state:(nullable id<SPState>)state;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPStateManager.h b/Snowplow/Internal/Tracker/SPStateManager.h
deleted file mode 100644
index 309543618..000000000
--- a/Snowplow/Internal/Tracker/SPStateManager.h
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-//  SPStateManager.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPTrackerState.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPStateManager : NSObject
-
-- (void)addOrReplaceStateMachine:(id<SPStateMachineProtocol>)stateMachine identifier:(NSString *)stateMachineIdentifier;
-- (BOOL)removeStateMachine:(NSString *)stateMachineIdentifier;
-
-- (id<SPTrackerStateSnapshot>)trackerStateForProcessedEvent:(SPEvent *)event;
-
-- (NSArray<SPSelfDescribingJson *> *)entitiesForProcessedEvent:(id<SPInspectableEvent>)event;
-
-- (BOOL)addPayloadValuesToEvent:(id<SPInspectableEvent>)event;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPStateManager.m b/Snowplow/Internal/Tracker/SPStateManager.m
deleted file mode 100644
index ea511f5a1..000000000
--- a/Snowplow/Internal/Tracker/SPStateManager.m
+++ /dev/null
@@ -1,175 +0,0 @@
-//
-//  SPStateManager.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPStateManager.h"
-
-@interface SPStateManager ()
-
-@property (nonatomic) NSMutableDictionary<NSString *, id<SPStateMachineProtocol>> *identifierToStateMachine;
-@property (nonatomic) NSMapTable<id<SPStateMachineProtocol>, NSString *> *stateMachineToIdentifier;
-@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<id<SPStateMachineProtocol>> *> *eventSchemaToStateMachine;
-@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<id<SPStateMachineProtocol>> *> *eventSchemaToEntitiesGenerator;
-@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<id<SPStateMachineProtocol>> *> *eventSchemaToPayloadUpdater;
-@property (nonatomic) SPTrackerState *trackerState;
-
-@end
-
-@implementation SPStateManager
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.identifierToStateMachine = [NSMutableDictionary new];
-        self.stateMachineToIdentifier = [NSMapTable weakToStrongObjectsMapTable];
-        self.eventSchemaToStateMachine = [NSMutableDictionary new];
-        self.eventSchemaToEntitiesGenerator = [NSMutableDictionary new];
-        self.eventSchemaToPayloadUpdater = [NSMutableDictionary new];
-        self.trackerState = [SPTrackerState new];
-    }
-    return self;
-}
-
-- (void)addOrReplaceStateMachine:(id<SPStateMachineProtocol>)stateMachine identifier:(NSString *)stateMachineIdentifier {
-    @synchronized (self) {
-        id<SPStateMachineProtocol> previousStateMachine = [self.identifierToStateMachine objectForKey:stateMachineIdentifier];
-        if (previousStateMachine) {
-            if ([stateMachine isMemberOfClass:[previousStateMachine class]]) {
-                return;
-            }
-            [self removeStateMachine:stateMachineIdentifier];
-        }
-        self.identifierToStateMachine[stateMachineIdentifier] = stateMachine;
-        [self.stateMachineToIdentifier setObject:stateMachineIdentifier forKey:stateMachine];
-        [self addToSchemaRegistry:self.eventSchemaToStateMachine
-                          schemas:[stateMachine subscribedEventSchemasForTransitions]
-                     stateMachine:stateMachine];
-        [self addToSchemaRegistry:self.eventSchemaToEntitiesGenerator
-                          schemas:[stateMachine subscribedEventSchemasForEntitiesGeneration]
-                     stateMachine:stateMachine];
-        [self addToSchemaRegistry:self.eventSchemaToPayloadUpdater
-                          schemas:[stateMachine subscribedEventSchemasForPayloadUpdating]
-                     stateMachine:stateMachine];
-    }
-}
-
-- (BOOL)removeStateMachine:(NSString *)stateMachineIdentifier {
-    id<SPStateMachineProtocol> stateMachine = self.identifierToStateMachine[stateMachineIdentifier];
-    if (!stateMachine) {
-        return NO;
-    }
-    [self.identifierToStateMachine removeObjectForKey:stateMachineIdentifier];
-    [self.stateMachineToIdentifier removeObjectForKey:stateMachine];
-    [self.trackerState removeStateWithIdentifier:stateMachineIdentifier];
-    [self removeFromSchemaRegistry:self.eventSchemaToStateMachine
-                           schemas:[stateMachine subscribedEventSchemasForTransitions]
-                      stateMachine:stateMachine];
-    [self removeFromSchemaRegistry:self.eventSchemaToEntitiesGenerator
-                           schemas:[stateMachine subscribedEventSchemasForEntitiesGeneration]
-                      stateMachine:stateMachine];
-    [self removeFromSchemaRegistry:self.eventSchemaToPayloadUpdater
-                           schemas:[stateMachine subscribedEventSchemasForPayloadUpdating]
-                      stateMachine:stateMachine];
-    return YES;
-}
-
-- (id<SPTrackerStateSnapshot>)trackerStateForProcessedEvent:(SPEvent *)event {
-    @synchronized (self) {
-        if ([event isKindOfClass:SPSelfDescribingAbstract.class]) {
-            SPSelfDescribingAbstract *sdEvent = (SPSelfDescribingAbstract *)event;
-            NSMutableArray<id<SPStateMachineProtocol>> *stateMachines = self.eventSchemaToStateMachine[sdEvent.schema].mutableCopy ?: [NSMutableArray new];
-            [stateMachines addObjectsFromArray:self.eventSchemaToStateMachine[@"*"]];
-            for (id<SPStateMachineProtocol> stateMachine in stateMachines) {
-                NSString *stateIdentifier = [self.stateMachineToIdentifier objectForKey:stateMachine];
-                SPStateFuture *previousStateFuture = [self.trackerState stateFutureWithIdentifier:stateIdentifier];
-                SPStateFuture *currentStateFuture = [[SPStateFuture alloc] initWithEvent:sdEvent previousState:previousStateFuture stateMachine:stateMachine];
-                [self.trackerState setStateFuture:currentStateFuture identifier:stateIdentifier];
-                // TODO: Remove early state computation.
-                /*
-                The early state-computation causes low performance as it's executed synchronously on
-                the track method thread. Ideally, the state computation should be executed only on
-                entities generation or payload updating (outputs). In that case there are two problems
-                to address:
-                 - long chains of StateFuture filling the memory (in case the outputs are not generated)
-                 - event object reuse by the user (the event object in the StateFuture could be modified
-                   externally)
-                 Remove the early state-computation only when these two problems are fixed.
-                 */
-                [currentStateFuture state]; // Early state-computation
-            }
-        }
-        return self.trackerState.snapshot;
-    }
-}
-
-- (NSArray<SPSelfDescribingJson *> *)entitiesForProcessedEvent:(id<SPInspectableEvent>)event {
-    @synchronized (self) {
-        NSMutableArray<SPSelfDescribingJson *> *result = [NSMutableArray new];
-        NSMutableArray<id<SPStateMachineProtocol>> *stateMachines = self.eventSchemaToEntitiesGenerator[event.schema].mutableCopy ?: [NSMutableArray new];
-        [stateMachines addObjectsFromArray:self.eventSchemaToEntitiesGenerator[@"*"]];
-        for (id<SPStateMachineProtocol> stateMachine in stateMachines) {
-            NSString *stateIdentifier = [self.stateMachineToIdentifier objectForKey:stateMachine];
-            id<SPState> state = [event.state stateWithIdentifier:stateIdentifier];
-            NSArray<SPSelfDescribingJson *> *entities = [stateMachine entitiesFromEvent:event state:state];
-            if (entities) {
-                [result addObjectsFromArray:entities];
-            }
-        }
-        return result;
-    }
-}
-
-- (BOOL)addPayloadValuesToEvent:(id<SPInspectableEvent>)event {
-    @synchronized (self) {
-        int failures = 0;
-        NSMutableArray<id<SPStateMachineProtocol>> *stateMachines = self.eventSchemaToPayloadUpdater[event.schema] ?: [NSMutableArray new];
-        [stateMachines addObjectsFromArray:self.eventSchemaToPayloadUpdater[@"*"]];
-        for (id<SPStateMachineProtocol> stateMachine in stateMachines) {
-            NSString *stateIdentifier = [self.stateMachineToIdentifier objectForKey:stateMachine];
-            id<SPState> state = [event.state stateWithIdentifier:stateIdentifier];
-            NSDictionary<NSString *, NSObject *> *payloadValues = [stateMachine payloadValuesFromEvent:event state:state];
-            if (payloadValues && ![event addPayloadValues:payloadValues]) {
-                failures++;
-            }
-        }
-        return failures == 0;
-    }
-}
-
-// MARK: - Private methods
-
-- (void)addToSchemaRegistry:(NSMutableDictionary<NSString *, NSMutableArray<id<SPStateMachineProtocol>> *> *)schemaRegistry schemas:(NSArray<NSString *> *)schemas stateMachine:(id<SPStateMachineProtocol>)stateMachine {
-    for (NSString *eventSchema in schemas) {
-        NSMutableArray *array = schemaRegistry[eventSchema];
-        if (!array) {
-            array = [NSMutableArray new];
-            schemaRegistry[eventSchema] = array;
-        }
-        [array addObject:stateMachine];
-    }
-}
-
-- (void)removeFromSchemaRegistry:(NSMutableDictionary<NSString *, NSMutableArray<id<SPStateMachineProtocol>> *> *)schemaRegistry schemas:(NSArray<NSString *> *)schemas stateMachine:(id<SPStateMachineProtocol>)stateMachine {
-    for (NSString *eventSchema in schemas) {
-        NSMutableArray *array = schemaRegistry[eventSchema];
-        [array removeObject:stateMachine];
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPTracker.h b/Snowplow/Internal/Tracker/SPTracker.h
deleted file mode 100644
index 168ff6f23..000000000
--- a/Snowplow/Internal/Tracker/SPTracker.h
+++ /dev/null
@@ -1,352 +0,0 @@
-//
-//  SPTracker.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-/*!
- @file SPTracker.h
-
- @brief Header file for SPTracker.
- */
-
-#import <Foundation/Foundation.h>
-#import "SPNetworkConfiguration.h"
-#import "SPGDPRConfiguration.h"
-
-#import "SPTrackerController.h"
-#import "SPSessionController.h"
-
-#import "SPEmitterEventProcessing.h"
-
-#import "SPDevicePlatform.h"
-#import "SPEventBase.h"
-#import "SPLoggerDelegate.h"
-#import "SPGdprContext.h"
-
-
-void uncaughtExceptionHandler(NSException * _Nullable exception);
-
-@class SPEmitter;
-@class SPPayload;
-@class SPSubject;
-@class SPSession;
-@class SPPageView;
-@class SPStructured;
-@class SPSelfDescribing;
-@class SPScreenView;
-@class SPTiming;
-@class SPEcommerce;
-@class SPSelfDescribingJson;
-@class SPConsentWithdrawn;
-@class SPConsentGranted;
-@class SPPushNotification;
-@class SPForeground;
-@class SPBackground;
-@class SPScreenState;
-@class SNOWError;
-
-@class SPGlobalContext;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/*!
- @brief The builder for SPTracker.
- */
-NS_SWIFT_NAME(TrackerBuilder)
-@protocol SPTrackerBuilder <NSObject>
-
-/*!
- @brief Tracker builder method to set an emitter.
-
- @param emitter The emitter used by the tracker.
- */
-- (void) setEmitter:(SPEmitter *)emitter;
-
-/*!
- @brief Tracker builder method to set a subject.
- The subject represents a current user, and holds associated information
- @param subject An associated subject
- */
-- (void) setSubject:(nullable SPSubject *)subject;
-
-/*!
- @brief Tracker builder method to set the app ID.
-
- @param appId The tracker's app ID.
- */
-- (void) setAppId:(NSString *)appId;
-
-/*!
- @brief Tracker builder method to set whether events will be sent Base64 encoded.
- @param base64Encoded Whether events are Base64 encoded
- */
-- (void) setBase64Encoded:(BOOL)base64Encoded;
-
-/*!
- @brief Tracker builder method to set the tracker namespace.
- @param trackerNamespace The tracker's namespace.
- */
-- (void) setTrackerNamespace:(NSString *)trackerNamespace;
-
-/*!
- Internal use only.
- Decorate the `tv` (tracker version) field in the tracker protocol.
- */
-- (void) setTrackerVersionSuffix:(NSString *)trackerVersionSuffix;
-
-/*!
- @brief Tracker builder method to set the device platform the tracker is running on
- @param devicePlatform The SPDevicePlatform enum indicating the current platform.
- */
-- (void) setDevicePlatform:(SPDevicePlatform)devicePlatform;
-
-/*!
- @brief Tracker builder method to set the log level desired for logging.
- @param logLevel The SPLogLevel enum indicating the current log level.
- */
-- (void) setLogLevel:(SPLogLevel)logLevel;
-
-/*!
- @brief Tracker builder method to set the delegate for log messages tracker's generated.
- @param delegate The logger delegate that received logs from the tracker.
-*/
-- (void)setLoggerDelegate:(nullable id<SPLoggerDelegate>)delegate;
-
-/*!
- @brief Tracker builder method to set whether events will include session context
- @param sessionContext Whether session context is enabled.
- */
-- (void) setSessionContext:(BOOL)sessionContext;
-
-/*!
- @brief Tracker builder method to set the foreground timeout.
- @param foregroundTimeout Length of timeout in the foreground in seconds.
- */
-- (void) setForegroundTimeout:(NSInteger)foregroundTimeout;
-
-/*!
- @brief Tracker builder method to set the background timeout.
- @param backgroundTimeout Length of timeout in the foreground in seconds.
- */
-- (void) setBackgroundTimeout:(NSInteger)backgroundTimeout;
-
-/*!
- @brief Tracker builder method to set whether events will include application context.
- @param applicationContext Whether application context is enabled.
- */
-- (void) setApplicationContext:(BOOL)applicationContext;
-
-/*!
- @brief Tracker builder method to set whether deepLink contexts will be added to ScreenView events.
- @param deepLinkContext Whether screen contexts are enabled.
- */
-- (void) setDeepLinkContext:(BOOL)deepLinkContext;
-
-/*!
- @brief Tracker builder method to set whether screen contexts will be added to all events.
- @param screenContext Whether screen contexts are enabled.
- */
-- (void) setScreenContext:(BOOL)screenContext;
-
-/*!
- @brief Tracker builder method to set whether foreground and background events are sent on app suspend and resume.
- @param lifecycleEvents Whether foreground and background events are enabled.
- */
-- (void) setLifecycleEvents:(BOOL)lifecycleEvents;
-
-/*!
- @brief Tracker builder method to set whether exceptions should be autotracked.
- @param exceptionEvents Whether to autotrack exceptions.
- */
-- (void) setExceptionEvents:(BOOL)exceptionEvents;
-
-/*!
- @brief Tracker builder method to set whether screenviews will be autotracked.
- @param autotrackScreenViews Whether to autotrack screenviews.
- */
-- (void) setAutotrackScreenViews:(BOOL)autotrackScreenViews;
-
-/*!
- @brief Tracker builder method to set whether application install should be autotracked.
- @param installEvent Whether to autotrack application installs.
- */
-- (void) setInstallEvent:(BOOL)installEvent;
-
-/*!
- @brief Tracker builder method to set whether tracker should send tracker diagnostic events.
- @param trackerDiagnostic Whether to enable tracker diagnostic.
- */
-- (void) setTrackerDiagnostic:(BOOL)trackerDiagnostic;
-
-/*!
- @brief Add global context generators to be used by tracker.
- @param globalContexts The global context generators to be used and related string tag for identification.
- */
-- (void)setGlobalContextGenerators:(NSDictionary<NSString *, SPGlobalContext *> *)globalContexts;
-
-/*!
- @brief Tracker builder method to set a GDPR context for the tracker
- @param basisForProcessing Enum one of valid legal bases for processing.
- @param documentId Document ID.
- @param documentVersion Version of the document.
- @param documentDescription Description of the document.
- */
-- (void)setGdprContextWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                     documentId:(nullable NSString *)documentId
-                documentVersion:(nullable NSString *)documentVersion
-            documentDescription:(nullable NSString *)documentDescription;
-
-/*!
- @brief Tracker builder method to set whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
- @param userAnonymisation Whether to anonymise client-side user identifiers
- */
-- (void) setUserAnonymisation:(BOOL)userAnonymisation;
-
-@end
-
-
-/*!
- This class is used for tracking events, and delegates them to other classes responsible for sending, storage, etc.
- */
-@interface SPTracker : NSObject <SPTrackerBuilder>
-
-/*! @brief The emitter used to send events. */
-@property (readonly, nonatomic, strong) SPEmitter * emitter;
-/*! @brief The subject used to represent the current user and persist user information. */
-@property (readonly, nonatomic, strong) SPSubject * subject;
-/*! @brief The object used for sessionization, i.e. it characterizes user activity. */
-@property (readonly, nonatomic, strong) SPSession *session;
-/*! @brief A unique identifier for an application. */
-@property (readonly, nonatomic, strong) NSString * appId;
-/*! @brief The identifier for the current tracker. */
-@property (readonly, nonatomic, strong) NSString * trackerNamespace;
-/*! @brief Whether to use Base64 encoding for events. */
-@property (readonly, nonatomic) BOOL base64Encoded;
-/*! @brief Whether to use Base64 encoding for events. */
-@property (readonly, nonatomic) SPDevicePlatform devicePlatform;
-/*! @brief Version suffix for tracker wrappers. */
-@property (readonly, nonatomic) NSString *trackerVersionSuffix;
-/*! @brief Previous screen view state. */
-@property (readonly, nonatomic, strong) SPScreenState * previousScreenState;
-/*! @brief Current screen view state. */
-@property (readonly, nonatomic, strong) SPScreenState * currentScreenState;
-/*! @brief List of tags associated to global contexts. */
-@property (readonly, nonatomic) NSArray<NSString *> *globalContextTags;
-/*! @brief Dictionary of global contexts generators. */
-@property (nonatomic) NSMutableDictionary<NSString *, SPGlobalContext *> *globalContextGenerators;
-
-// MARK: - Added property methods
-
-- (BOOL)applicationContext;
-- (BOOL)exceptionEvents;
-- (BOOL)installEvent;
-- (BOOL)deepLinkContext;
-- (BOOL)screenContext;
-- (BOOL)autoTrackScreenView;
-- (BOOL)sessionContext;
-- (BOOL)trackerDiagnostic;
-- (BOOL)userAnonymisation;
-
-// MARK: - methods
-
-/*!
- @brief Method that allows for builder pattern in creating the tracker.
- */
-+ (instancetype) build:(void(^)(id<SPTrackerBuilder>builder))buildBlock;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-- (void) receiveScreenViewNotification:(NSNotification *)notification;
-
-/*!
- @brief Pauses all event tracking, storage and session checking.
- */
-- (void) pauseEventTracking;
-
-/*!
- @brief Resumes all event tracking and restarts the session checking.
- */
-- (void) resumeEventTracking;
-
-/*!
- @brief Returns whether the application is in the background or foreground.
- @return Whether application is suspended.
- */
-- (BOOL) getInBackground;
-
-/*!
- @brief Returns whether the tracker is currently collecting data.
- @return Whether the tracker is tracking.
- */
-- (BOOL) getIsTracking;
-
-/*!
- @brief Returns whether lifecyle events is enabled.
- @return Whether background and foreground events are sent.
- */
-- (BOOL) getLifecycleEvents;
-
-/*!
- Add new generator for global contexts associated with a string tag.
- If the string tag has been already set the new global context is not assigned.
- @param generator The global context generator.
- @param tag The tag associated to the global context.
- @return Weather the global context has been added.
- */
-- (BOOL)addGlobalContext:(SPGlobalContext *)generator tag:(NSString *)tag;
-
-/*!
- Remove the global context associated with the string tag.
- If the string tag exist it returns the global context generator associated with.
- @param tag The tag associated to the global context.
- @return The global context associated with the tag or `nil` in case of any entry with that string tag.
- */
-- (nullable SPGlobalContext *)removeGlobalContext:(NSString *)tag;
-
-/*!
- Enables GDPR context to be sent with every event.
- @param basisForProcessing GDPR Basis for processing.
- @param documentId ID of a GDPR basis document.
- @param documentVersion Version of the document.
- @param documentDescription Description of the document.
- */
-- (void)enableGdprContextWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                        documentId:(nullable NSString *)documentId
-                   documentVersion:(nullable NSString *)documentVersion
-               documentDescription:(nullable NSString *)documentDescription;
-
-/// Disable GDPR context.
-- (void)disableGdprContext;
-
-/// Return GDPR context
-- (nullable SPGdprContext *)gdprContext;
-
-#pragma mark - Events tracking methods
-
-/*!
- @brief Tracks an event despite its specific type.
- @param event The event to track
- @return The event ID or nil in case tracking is paused
- */
-- (nullable NSUUID *)track:(SPEvent *)event;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPTracker.m b/Snowplow/Internal/Tracker/SPTracker.m
deleted file mode 100644
index 8bc775d75..000000000
--- a/Snowplow/Internal/Tracker/SPTracker.m
+++ /dev/null
@@ -1,743 +0,0 @@
-//
-//  SPTracker.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPSubject.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "SPUtilities.h"
-#import "SPSession.h"
-#import "SPInstallTracker.h"
-#import "SPGlobalContext.h"
-
-#import "SNOWError.h"
-#import "SPStructured.h"
-#import "SPSelfDescribing.h"
-#import "SPScreenView.h"
-#import "SPPageView.h"
-#import "SPTiming.h"
-#import "SPEcommerce.h"
-#import "SPEcommerceItem.h"
-#import "SPConsentWithdrawn.h"
-#import "SPConsentGranted.h"
-#import "SPForeground.h"
-#import "SPBackground.h"
-#import "SPPushNotification.h"
-#import "SPDeepLinkReceived.h"
-#import "SPDeepLinkEntity.h"
-#import "SPTrackerEvent.h"
-#import "SPTrackerError.h"
-#import "SPLogger.h"
-
-#import "SPSubjectConfiguration.h"
-#import "SPSessionConfiguration.h"
-
-#import "SPServiceProvider.h"
-#import "SPTrackerControllerImpl.h"
-#import "SPEmitterEventProcessing.h"
-
-#import "SPStateManager.h"
-#import "SPScreenStateMachine.h"
-#import "SPDeepLinkStateMachine.h"
-#import "SPLifecycleStateMachine.h"
-
-/** A class extension that makes the screen view states mutable internally. */
-@interface SPTracker ()
-
-@property (nonatomic) SPGdprContext *gdpr;
-
-@property (nonatomic) SPStateManager *stateManager;
-
-/*!
- @brief This method is called to send an auto-tracked screen view event.
-
- @param notification The notification raised by a UIViewController
- */
-- (void) receiveScreenViewNotification:(NSNotification *)notification;
-
-@end
-
-void uncaughtExceptionHandler(NSException *exception) {
-    NSArray* symbols = [exception callStackSymbols];
-    NSString * stacktrace = [NSString stringWithFormat:@"Stacktrace:\n%@", symbols];
-    NSString * message = [exception reason];
-    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        if (message == nil || [message length] == 0) {
-            return;
-        }
-        
-        // Construct userInfo
-        NSMutableDictionary<NSString *, NSObject *> *userInfo = [NSMutableDictionary new];
-        userInfo[@"message"] = message;
-        userInfo[@"stacktrace"] = stacktrace;
-        
-        // Send notification to tracker
-        [[NSNotificationCenter defaultCenter]
-         postNotificationName:@"SPCrashReporting"
-         object:nil
-         userInfo:userInfo];
-        
-        [NSThread sleepForTimeInterval:2.0f];
-    });
-}
-
-#pragma mark - SPTracker implementation
-
-@implementation SPTracker {
-    NSMutableDictionary *  _trackerData;
-    NSString *             _platformContextSchema;
-    BOOL                   _dataCollection;
-    SPSession *            _session;
-    BOOL                   _sessionContext;
-    BOOL                   _deepLinkContext;
-    BOOL                   _screenContext;
-    BOOL                   _applicationContext;
-    BOOL                   _autotrackScreenViews;
-    BOOL                   _lifecycleEvents;
-    NSInteger              _foregroundTimeout;
-    NSInteger              _backgroundTimeout;
-    BOOL                   _builderFinished;
-    BOOL                   _exceptionEvents;
-    BOOL                   _installEvent;
-    BOOL                   _trackerDiagnostic;
-    BOOL                   _userAnonymisation;
-    NSString *             _trackerVersionSuffix;
-}
-
-// MARK: - Added property methods
-
-- (BOOL)applicationContext {
-    return _applicationContext;
-}
-
-- (BOOL)exceptionEvents {
-    return _exceptionEvents;
-}
-
-- (BOOL)installEvent {
-    return _installEvent;
-}
-
-- (BOOL)deepLinkContext {
-    return _deepLinkContext;
-}
-
-- (BOOL)screenContext {
-    return _screenContext;
-}
-
-- (BOOL)autoTrackScreenView {
-    return _autotrackScreenViews;
-}
-
-- (BOOL)sessionContext {
-    return _sessionContext;
-}
-
-- (BOOL)trackerDiagnostic {
-    return _trackerDiagnostic;
-}
-
-- (BOOL)userAnonymisation {
-    return _userAnonymisation;
-}
-
-// MARK: - Methods
-
-+ (instancetype) build:(void(^)(id<SPTrackerBuilder>builder))buildBlock {
-    SPTracker *tracker = [[SPTracker alloc] initWithDefaultValues];
-    if (buildBlock) {
-        buildBlock(tracker);
-    }
-    [tracker setup];
-    [tracker checkInstall];
-    return tracker;
-}
-
-- (instancetype) initWithDefaultValues {
-    self = [super init];
-    if (self) {
-        _trackerNamespace = nil;
-        _appId = nil;
-        _trackerVersionSuffix = nil;
-        _devicePlatform = [SPUtilities getPlatform];
-        _base64Encoded = YES;
-        _dataCollection = YES;
-        _sessionContext = NO;
-        _applicationContext = NO;
-        _screenContext = NO;
-        _lifecycleEvents = NO;
-        _autotrackScreenViews = NO;
-        _foregroundTimeout = 1800;
-        _backgroundTimeout = 1800;
-        _builderFinished = NO;
-        self.globalContextGenerators = [NSMutableDictionary dictionary];
-        self.stateManager = [SPStateManager new];
-        _exceptionEvents = NO;
-        _installEvent = NO;
-        _trackerDiagnostic = NO;
-        _userAnonymisation = NO;
-#if SNOWPLOW_TARGET_IOS
-        _platformContextSchema = kSPMobileContextSchema;
-#else
-        _platformContextSchema = kSPDesktopContextSchema;
-#endif
-    }
-    return self;
-}
-
-- (void) setup {
-    [SPUtilities checkArgument:(_emitter != nil) withMessage:@"Emitter cannot be nil."];
-    _trackerNamespace = _trackerNamespace ?: @"default";
-    [_emitter setNamespace:_trackerNamespace]; // Needed to correctly send events to the right EventStore
-
-    [self setTrackerData];
-    if (_sessionContext) {
-        _session = [[SPSession alloc] initWithForegroundTimeout:_foregroundTimeout
-                                           andBackgroundTimeout:_backgroundTimeout
-                                                     andTracker:self];
-    }
-
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(receiveScreenViewNotification:)
-                                                 name:@"SPScreenViewDidAppear"
-                                               object:nil];
-    
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(receiveDiagnosticNotification:)
-                                                 name:@"SPTrackerDiagnostic"
-                                               object:nil];
-    
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(receiveCrashReportingNotification:)
-                                                 name:@"SPCrashReporting"
-                                               object:nil];
-    
-    if (_exceptionEvents) {
-        NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
-    }
-    
-    _builderFinished = YES;
-}
-
-- (void)checkInstall {
-    if (_installEvent) {
-        SPInstallTracker * installTracker = [[SPInstallTracker alloc] init];
-        NSDate *previousTimestamp = installTracker.previousInstallTimestamp;
-        [installTracker clearPreviousInstallTimestamp];
-        if (!installTracker.isNewInstall && previousTimestamp == nil) {
-            return;
-        }
-        SPSelfDescribingJson *installEvent = [[SPSelfDescribingJson alloc] initWithSchema:kSPApplicationInstallSchema andData:@{}];
-        SPSelfDescribing *event = [[SPSelfDescribing alloc] initWithEventData:installEvent];
-        event.trueTimestamp = previousTimestamp; // it can be nil
-        [self track:event];
-    }
-}
-
-- (void) setTrackerData {
-    NSString *trackerVersion = kSPVersion;
-    if ([_trackerVersionSuffix length]) {
-        NSMutableCharacterSet *allowedCharSet = [NSMutableCharacterSet alphanumericCharacterSet];
-        [allowedCharSet formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@".-"]];
-        NSString *suffix = [[_trackerVersionSuffix componentsSeparatedByCharactersInSet:[allowedCharSet invertedSet]] componentsJoinedByString:@""];
-        if ([suffix length]) {
-            trackerVersion = [NSString stringWithFormat:@"%@ %@", trackerVersion, suffix];
-        }
-    }
-    _trackerData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-                    trackerVersion, kSPTrackerVersion,
-                    _trackerNamespace, kSPNamespace,
-                    _appId != nil ? _appId : [NSNull null], kSPAppId, nil];
-}
-
-#pragma mark - Setter
-
-- (void) setEmitter:(SPEmitter *)emitter {
-    if (emitter != nil) {
-        _emitter = emitter;
-    }
-}
-
-- (void) setSubject:(SPSubject *)subject {
-    _subject = subject;
-}
-
-- (void) setBase64Encoded:(BOOL)encoded {
-    _base64Encoded = encoded;
-}
-
-- (void) setAppId:(NSString *)appId {
-    _appId = appId;
-    if (_builderFinished && _trackerData != nil) {
-        [self setTrackerData];
-    }
-}
-
-- (void) setTrackerNamespace:(NSString *)trackerNamespace {
-    _trackerNamespace = trackerNamespace;
-    if (_builderFinished && _trackerData != nil) {
-        [self setTrackerData];
-    }
-}
-
-- (void) setTrackerVersionSuffix:(NSString *)trackerVersionSuffix {
-    _trackerVersionSuffix = trackerVersionSuffix;
-    if (_builderFinished && _trackerData != nil) {
-        [self setTrackerData];
-    }
-}
-
-- (void) setDevicePlatform:(SPDevicePlatform)devicePlatform {
-    _devicePlatform = devicePlatform;
-}
-
-- (void)setLogLevel:(SPLogLevel)logLevel {
-    [SPLogger setLogLevel:logLevel];
-}
-
-- (void)setLoggerDelegate:(id<SPLoggerDelegate>)delegate {
-    [SPLogger setDelegate:delegate];
-}
-
-- (void) setSessionContext:(BOOL)sessionContext {
-    _sessionContext = sessionContext;
-    if (_session != nil && !sessionContext) {
-        [_session stopChecker];
-        _session = nil;
-    } else if (_builderFinished && _session == nil && sessionContext) {
-        _session = [[SPSession alloc] initWithForegroundTimeout:_foregroundTimeout
-                                           andBackgroundTimeout:_backgroundTimeout
-                                                     andTracker:self];
-    }
-}
-
-- (void) setDeepLinkContext:(BOOL)deepLinkContext {
-    @synchronized (self) {
-        _deepLinkContext = deepLinkContext;
-        if (deepLinkContext) {
-            [self.stateManager addOrReplaceStateMachine:[SPDeepLinkStateMachine new] identifier:@"SPDeepLinkContext"];
-        } else {
-            [self.stateManager removeStateMachine:@"SPDeepLinkContext"];
-        }
-    }
-}
-
-- (void) setScreenContext:(BOOL)screenContext {
-    @synchronized (self) {
-        _screenContext = screenContext;
-        if (screenContext) {
-            [self.stateManager addOrReplaceStateMachine:[SPScreenStateMachine new] identifier:@"SPScreenContext"];
-        } else {
-            [self.stateManager removeStateMachine:@"SPScreenContext"];
-        }
-    }
-}
-
-- (void) setApplicationContext:(BOOL)applicationContext {
-    _applicationContext = applicationContext;
-}
-
-- (void) setAutotrackScreenViews:(BOOL)autotrackScreenViews {
-    _autotrackScreenViews = autotrackScreenViews;
-}
-
-- (void) setForegroundTimeout:(NSInteger)foregroundTimeout {
-    _foregroundTimeout = foregroundTimeout;
-    if (_builderFinished && _session != nil) {
-        [_session setForegroundTimeout:foregroundTimeout];
-    }
-}
-
-- (void) setBackgroundTimeout:(NSInteger)backgroundTimeout {
-    _backgroundTimeout = backgroundTimeout;
-    if (_builderFinished && _session != nil) {
-        [_session setBackgroundTimeout:backgroundTimeout];
-    }
-}
-
-- (void) setLifecycleEvents:(BOOL)lifecycleEvents {
-    @synchronized (self) {
-        _lifecycleEvents = lifecycleEvents;
-        if (lifecycleEvents) {
-            [self.stateManager addOrReplaceStateMachine:[SPLifecycleStateMachine new] identifier:@"SPLifecycle"];
-        } else {
-            [self.stateManager removeStateMachine:@"SPLifecycle"];
-        }
-    }
-}
-
-- (void) setExceptionEvents:(BOOL)exceptionEvents {
-    _exceptionEvents = exceptionEvents;
-}
-
-- (void) setInstallEvent:(BOOL)installEvent {
-    _installEvent = installEvent;
-}
-
-- (void)setTrackerDiagnostic:(BOOL)trackerDiagnostic {
-    _trackerDiagnostic = trackerDiagnostic;
-}
-
-- (void)setUserAnonymisation:(BOOL)userAnonymisation {
-    if (_userAnonymisation != userAnonymisation) {
-        _userAnonymisation = userAnonymisation;
-        if (_session != nil) {
-            [_session startNewSession];
-        }
-    }
-}
-
-#pragma mark - Global Contexts methods
-
-- (void)setGlobalContextGenerators:(NSDictionary<NSString *, SPGlobalContext *> *)globalContexts {
-    _globalContextGenerators = globalContexts.mutableCopy ?: [NSMutableDictionary dictionary];
-}
-
-- (BOOL)addGlobalContext:(SPGlobalContext *)generator tag:(NSString *)tag {
-    if ([self.globalContextGenerators objectForKey:tag]) {
-        return NO;
-    }
-    [self.globalContextGenerators setObject:generator forKey:tag];
-    return YES;
-}
-
-- (SPGlobalContext *)removeGlobalContext:(NSString *)tag {
-    SPGlobalContext *toDelete = [self.globalContextGenerators objectForKey:tag];
-    if (toDelete) {
-        [self.globalContextGenerators removeObjectForKey:tag];
-    }
-    return toDelete;
-}
-
-#pragma mark - GDPR methods
-
-- (void)setGdprContextWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                     documentId:(NSString *)documentId
-                documentVersion:(NSString *)documentVersion
-            documentDescription:(NSString *)documentDescription
-{
-    self.gdpr = [[SPGdprContext alloc] initWithBasis:basisForProcessing
-                                          documentId:documentId
-                                     documentVersion:documentVersion
-                                 documentDescription:documentDescription];
-}
-
-- (void)enableGdprContextWithBasis:(SPGdprProcessingBasis)basisForProcessing
-                        documentId:(NSString *)documentId
-                   documentVersion:(NSString *)documentVersion
-               documentDescription:(NSString *)documentDescription
-{
-    self.gdpr = [[SPGdprContext alloc] initWithBasis:basisForProcessing
-                                          documentId:documentId
-                                     documentVersion:documentVersion
-                                 documentDescription:documentDescription];
-}
-
-- (void)disableGdprContext {
-    self.gdpr = nil;
-}
-
-- (SPGdprContext *)gdprContext {
-    return self.gdpr;
-}
-
-#pragma mark - Extra Functions
-
-- (void) pauseEventTracking {
-    _dataCollection = NO;
-    [_emitter pauseTimer];
-    [_session stopChecker];
-}
-
-- (void) resumeEventTracking {
-    _dataCollection = YES;
-    [_emitter resumeTimer];
-    [_session startChecker];
-}
-
-#pragma mark - Getters
-
-- (BOOL) getInBackground {
-    return [_session getInBackground];
-}
-
-- (BOOL) getIsTracking {
-    return _dataCollection;
-}
-
-- (BOOL) getLifecycleEvents {
-    return _lifecycleEvents;
-}
-
-- (NSArray<NSString *> *)globalContextTags {
-    return self.globalContextGenerators.allKeys;
-}
-
-#pragma mark - Notifications management
-
-- (void) receiveScreenViewNotification:(NSNotification *)notification {
-    NSString *name = [[notification userInfo] objectForKey:@"name"];
-    NSString *type = stringWithSPScreenType([[[notification userInfo] objectForKey:@"type"] integerValue]);
-    NSString *topViewControllerClassName = [[notification userInfo] objectForKey:@"topViewControllerClassName"];
-    NSString *viewControllerClassName = [[notification userInfo] objectForKey:@"viewControllerClassName"];
-
-    if (_autotrackScreenViews) {
-        SPScreenView *event = [[SPScreenView alloc] initWithName:name screenId:nil];
-        event.type = type;
-        event.viewControllerClassName = viewControllerClassName;
-        event.topViewControllerClassName = topViewControllerClassName;
-        [self track:event];
-    }
-}
-
-- (void)receiveDiagnosticNotification:(NSNotification *)notification {
-    NSDictionary *userInfo = [notification userInfo];
-    NSString *tag = [userInfo objectForKey:@"tag"];
-    NSString *message = [userInfo objectForKey:@"message"];
-    NSError *error = [userInfo objectForKey:@"error"];
-    NSException *exception = [userInfo objectForKey:@"exception"];
-
-    if (_trackerDiagnostic) {
-        SPTrackerError *event = [[SPTrackerError alloc] initWithSource:tag message:message error:error exception:exception];
-        [self track:event];
-    }
-}
-
-- (void)receiveCrashReportingNotification:(NSNotification *)notification {
-    NSDictionary *userInfo = [notification userInfo];
-    NSString *message = [userInfo objectForKey:@"message"];
-    NSString *stacktrace = [userInfo objectForKey:@"stacktrace"];
-    
-    
-    if (_exceptionEvents) {
-        SNOWError *event = [[[SNOWError alloc] initWithMessage:message] stackTrace:stacktrace];
-        [self track:event];
-    }
-}
-
-#pragma mark - Event Tracking Functions
-
-- (NSUUID *)track:(SPEvent *)event {
-    if (!event || !_dataCollection) return nil;
-    [event beginProcessingWithTracker:self];
-    NSUUID *eventId = [self processEvent:event];
-    [event endProcessingWithTracker:self];
-    return eventId;
-}
-
-#pragma mark - Event Decoration
-
-- (NSUUID *)processEvent:(SPEvent *)event {
-    SPTrackerState *stateSnapshot;
-    @synchronized (self) {
-        stateSnapshot = [self.stateManager trackerStateForProcessedEvent:event];
-    }
-    SPTrackerEvent *trackerEvent = [[SPTrackerEvent alloc] initWithEvent:event state:stateSnapshot];
-    [self transformEvent:trackerEvent];
-    SPPayload *payload = [self payloadWithEvent:trackerEvent];
-    [_emitter addPayloadToBuffer:payload];
-    return [trackerEvent eventId];
-}
-
-- (void)transformEvent:(SPTrackerEvent *)event {
-    // Application_install event needs the timestamp to the real installation event.
-    if ([event.schema isEqualToString:kSPApplicationInstallSchema] && event.trueTimestamp) {
-        event.timestamp = event.trueTimestamp.timeIntervalSince1970 * 1000;
-        event.trueTimestamp = nil;
-    }
-    // Payload can be optionally updated with values based on internal state
-    [self.stateManager addPayloadValuesToEvent:event];
-}
-
-- (SPPayload *)payloadWithEvent:(SPTrackerEvent *)event {
-    SPPayload *payload = [SPPayload new];
-    payload.allowDiagnostic = !event.isService;
-
-    [self addBasicPropertiesToPayload:payload event:event];
-    if (event.isPrimitive) {
-        [self addPrimitivePropertiesToPayload:payload event:event];
-    } else {
-        [self addSelfDescribingPropertiesToPayload:payload event:event];
-    }
-    NSMutableArray<SPSelfDescribingJson *> *contexts = event.contexts;
-    [self addBasicContextsToContexts:contexts event:event];
-    [self addGlobalContextsToContexts:contexts event:event];
-    [self addStateMachineEntitiesToContexts:contexts event:event];
-    [self wrapContexts:contexts toPayload:payload];
-    if (!event.isPrimitive) {
-        // TODO: To remove when Atomic table refactoring is finished
-        [self workaroundForCampaignAttributionEnrichment:payload event:event contexts:contexts];
-    }
-    return payload;
-}
-
-- (void)addBasicPropertiesToPayload:(SPPayload *)payload event:(SPTrackerEvent *)event {
-    [payload addValueToPayload:event.eventId.UUIDString forKey:kSPEid];
-    [payload addValueToPayload:[NSString stringWithFormat:@"%lld", event.timestamp] forKey:kSPTimestamp];
-    if (event.trueTimestamp) {
-        long long ttInMilliSeconds = event.trueTimestamp.timeIntervalSince1970 * 1000;
-        [payload addValueToPayload:[NSString stringWithFormat:@"%lld", ttInMilliSeconds] forKey:kSPTrueTimestamp];
-    }
-    [payload addDictionaryToPayload:_trackerData];
-    if (_subject != nil) {
-        [payload addDictionaryToPayload:[[_subject getStandardDictWithUserAnonymisation:self.userAnonymisation] getAsDictionary]];
-    }
-    [payload addValueToPayload:SPDevicePlatformToString(_devicePlatform) forKey:kSPPlatform];
-}
-
-- (void)addPrimitivePropertiesToPayload:(SPPayload *)payload event:(SPTrackerEvent *)event {
-    [payload addValueToPayload:event.eventName forKey:kSPEvent];
-    [payload addDictionaryToPayload:event.payload];
-}
-
-- (void)addSelfDescribingPropertiesToPayload:(SPPayload *)payload event:(SPTrackerEvent *)event {
-    [payload addValueToPayload:kSPEventUnstructured forKey:kSPEvent];
-
-    SPSelfDescribingJson *data = [[SPSelfDescribingJson alloc] initWithSchema:event.schema andData:event.payload];
-    NSDictionary *unstructuredEventPayload = @{
-        kSPSchema: kSPUnstructSchema,
-        kSPData: [data getAsDictionary],
-    };
-    [payload addDictionaryToPayload:unstructuredEventPayload
-                      base64Encoded:_base64Encoded
-                    typeWhenEncoded:kSPUnstructuredEncoded
-                 typeWhenNotEncoded:kSPUnstructured];
-}
-
-/*
- This is needed because the campaign-attribution-enrichment (in the pipeline) is able to parse
- the `url` and `referrer` only if they are part of a PageView event.
- The PageView event is an atomic event but the DeepLinkReceived and ScreenView are SelfDescribing events.
- For this reason we copy these two fields in the atomic fields in order to let the enrichment
- to process correctly the fields even if the event is not a PageView and it's a SelfDescribing event.
- This is a hack that should be removed once the atomic event table is dismissed and all the events
- will be SelfDescribing.
- */
-- (void)workaroundForCampaignAttributionEnrichment:(SPPayload *)payload event:(SPTrackerEvent *)event contexts:(NSMutableArray<SPSelfDescribingJson *> *)contexts {
-    NSString *url = nil;
-    NSString *referrer = nil;
-    
-    if ([event.schema isEqualToString:kSPDeepLinkReceivedSchema]) {
-        url = (NSString *)[event.payload objectForKey:kSPDeepLinkReceivedParamUrl];
-        referrer = (NSString *)[event.payload objectForKey:kSPDeepLinkReceivedParamReferrer];
-    } else if ([event.schema isEqualToString:kSPScreenViewSchema]) {
-        for (SPSelfDescribingJson *entity in contexts) {
-            if ([[entity schema] isEqualToString:kSPDeepLinkSchema]) {
-                NSDictionary *data = (NSDictionary *)[entity data];
-                url = (NSString *)[data valueForKey:kSPDeepLinkReceivedParamUrl];
-                referrer = (NSString *)[data valueForKey:kSPDeepLinkParamReferrer];
-                break;
-            }
-        }
-    }
-    
-    if (url != nil) {
-        [payload addValueToPayload:url forKey:kSPPageUrl];
-    }
-    if (referrer != nil) {
-        [payload addValueToPayload:referrer forKey:kSPPageRefr];
-    }
-}
-
-- (void)addBasicContextsToContexts:(NSMutableArray<SPSelfDescribingJson *> *)contexts event:(SPTrackerEvent *)event {
-    [self addBasicContextsToContexts:contexts eventId:event.eventId.UUIDString eventTimestamp:event.timestamp isService:event.isService];
-}
-
-- (void)addBasicContextsToContexts:(NSMutableArray<SPSelfDescribingJson *> *)contexts eventId:(NSString *)eventId eventTimestamp:(long long)eventTimestamp isService:(BOOL)isService {
-    if (_subject) {
-        NSDictionary * platformDict = [[_subject getPlatformDictWithUserAnonymisation:self.userAnonymisation] getAsDictionary];
-        if (platformDict != nil) {
-            [contexts addObject:[[SPSelfDescribingJson alloc] initWithSchema:_platformContextSchema andData:platformDict]];
-        }
-        NSDictionary * geoLocationDict = [_subject getGeoLocationDict];
-        if (geoLocationDict != nil) {
-            [contexts addObject:[[SPSelfDescribingJson alloc] initWithSchema:kSPGeoContextSchema andData:geoLocationDict]];
-        }
-    }
-
-    if (_applicationContext) {
-        SPSelfDescribingJson * contextJson = [SPUtilities getApplicationContext];
-        if (contextJson != nil) {
-            [contexts addObject:contextJson];
-        }
-    }
-    
-    if (isService) {
-        return;
-    }
-
-    // Add session
-    if (_session) {
-        NSDictionary *sessionDict = [_session getSessionDictWithEventId:eventId eventTimestamp:eventTimestamp userAnonymisation:self.userAnonymisation];
-        if (sessionDict) {
-            [contexts addObject:[[SPSelfDescribingJson alloc] initWithSchema:kSPSessionContextSchema andData:sessionDict]];
-        } else {
-            SPLogTrack(nil, @"Unable to get session context for eventId: %@", eventId);
-        }
-    }
-    
-    // Add GDPR context
-    if (self.gdpr) {
-        SPSelfDescribingJson *gdprContext = self.gdpr.context;
-        if (gdprContext) {
-            [contexts addObject:gdprContext];
-        }
-    }
-}
-
-- (void)addGlobalContextsToContexts:(NSMutableArray<SPSelfDescribingJson *> *)contexts event:(id<SPInspectableEvent>)event {
-    [self.globalContextGenerators enumerateKeysAndObjectsUsingBlock:^(NSString *key, SPGlobalContext *generator, BOOL *stop) {
-        [contexts addObjectsFromArray:[generator contextsFromEvent:event]];
-    }];
-}
-
-- (void)addStateMachineEntitiesToContexts:(NSMutableArray<SPSelfDescribingJson *> *)contexts event:(id<SPInspectableEvent>)event {
-    NSArray<SPSelfDescribingJson *> *stateManagerEntities = [self.stateManager entitiesForProcessedEvent:event];
-    [contexts addObjectsFromArray:stateManagerEntities];
-}
-
-- (void)wrapContexts:(NSArray<SPSelfDescribingJson *> *)contexts toPayload:(SPPayload *)payload {
-    if (contexts.count == 0) {
-        return;
-    }
-    NSMutableArray<NSDictionary *> *data = [NSMutableArray new];
-    for (SPSelfDescribingJson *context in contexts) {
-        [data addObject:[context getAsDictionary]];
-    }
-
-    SPSelfDescribingJson *finalContext = [[SPSelfDescribingJson alloc] initWithSchema:kSPContextSchema andData:data];
-    if (finalContext == nil) {
-        return;
-    }
-    [payload addDictionaryToPayload:[finalContext getAsDictionary]
-                      base64Encoded:_base64Encoded
-                    typeWhenEncoded:kSPContextEncoded
-                 typeWhenNotEncoded:kSPContext];
-}
-
-- (void)dealloc {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.h b/Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.h
deleted file mode 100644
index 6fea33903..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-//  SPTrackerConfigurationUpdate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPTrackerConfigurationUpdate : SPTrackerConfiguration
-
-@property (nonatomic, nullable) SPTrackerConfiguration *sourceConfig;
-
-@property (nonatomic) BOOL isPaused;
-
-SP_DIRTYFLAG(appId)
-SP_DIRTYFLAG(devicePlatform)
-SP_DIRTYFLAG(base64Encoding)
-SP_DIRTYFLAG(logLevel)
-SP_DIRTYFLAG(loggerDelegate)
-SP_DIRTYFLAG(applicationContext)
-SP_DIRTYFLAG(platformContext)
-SP_DIRTYFLAG(geoLocationContext)
-SP_DIRTYFLAG(deepLinkContext)
-SP_DIRTYFLAG(sessionContext)
-SP_DIRTYFLAG(screenContext)
-SP_DIRTYFLAG(screenViewAutotracking)
-SP_DIRTYFLAG(lifecycleAutotracking)
-SP_DIRTYFLAG(installAutotracking)
-SP_DIRTYFLAG(exceptionAutotracking)
-SP_DIRTYFLAG(diagnosticAutotracking)
-SP_DIRTYFLAG(userAnonymisation)
-SP_DIRTYFLAG(trackerVersionSuffix)
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.m b/Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.m
deleted file mode 100644
index c61a19c53..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerConfigurationUpdate.m
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-//  SPTrackerConfigurationUpdate.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConfigurationUpdate.h"
-
-@implementation SPTrackerConfigurationUpdate
-
-SP_DIRTY_GETTER(NSString *, appId)
-SP_DIRTY_GETTER(SPDevicePlatform, devicePlatform)
-SP_DIRTY_GETTER(BOOL, base64Encoding)
-SP_DIRTY_GETTER(SPLogLevel, logLevel)
-SP_DIRTY_GETTER(id<SPLoggerDelegate>, loggerDelegate)
-SP_DIRTY_GETTER(BOOL, applicationContext)
-SP_DIRTY_GETTER(BOOL, platformContext)
-SP_DIRTY_GETTER(BOOL, geoLocationContext)
-SP_DIRTY_GETTER(BOOL, deepLinkContext)
-SP_DIRTY_GETTER(BOOL, sessionContext)
-SP_DIRTY_GETTER(BOOL, screenContext)
-SP_DIRTY_GETTER(BOOL, screenViewAutotracking)
-SP_DIRTY_GETTER(BOOL, lifecycleAutotracking)
-SP_DIRTY_GETTER(BOOL, installAutotracking)
-SP_DIRTY_GETTER(BOOL, exceptionAutotracking)
-SP_DIRTY_GETTER(BOOL, diagnosticAutotracking)
-SP_DIRTY_GETTER(BOOL, userAnonymisation)
-SP_DIRTY_GETTER(NSString *, trackerVersionSuffix)
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPTrackerController.h b/Snowplow/Internal/Tracker/SPTrackerController.h
deleted file mode 100644
index fdb90fa52..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerController.h
+++ /dev/null
@@ -1,106 +0,0 @@
-//
-//  SPTrackerController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPTrackerConfiguration.h"
-#import "SPNetworkConfiguration.h"
-
-#import "SPSubjectController.h"
-#import "SPSessionController.h"
-#import "SPEmitterController.h"
-#import "SPNetworkController.h"
-#import "SPGDPRController.h"
-#import "SPGlobalContextsController.h"
-
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(TrackerController)
-@protocol SPTrackerController <SPTrackerConfigurationProtocol>
-
-/** Version of the tracker. */
-@property (readonly, nonatomic) NSString *version;
-/**
- * Whether the tracker is running and able to collect/send events.
- * See `pause()` and `resume()`.
- */
-@property (readonly, nonatomic) BOOL isTracking;
-/**
- * Namespace of the tracker.
- * It is used to identify the tracker among multiple trackers running in the same app.
- */
-@property (readonly, nonatomic) NSString *namespace;
-
-/**
- * SubjectController.
- * @apiNote Don't retain the reference. It may change on tracker reconfiguration.
- */
-@property (readonly, nonatomic, nullable) id<SPSubjectController> subject;
-/**
- * SessionController.
- * @apiNote Don't retain the reference. It may change on tracker reconfiguration.
- */
-@property (readonly, nonatomic, nullable) id<SPSessionController> session;
-/**
- * NetworkController.
- * @apiNote Don't retain the reference. It may change on tracker reconfiguration.
- */
-@property (readonly, nonatomic, nullable) id<SPNetworkController> network;
-/**
- * EmitterController.
- * @apiNote Don't retain the reference. It may change on tracker reconfiguration.
- */
-@property (readonly, nonatomic) id<SPEmitterController> emitter;
-/**
- * GdprController.
- * @apiNote Don't retain the reference. It may change on tracker reconfiguration.
- */
-@property (readonly, nonatomic) id<SPGDPRController> gdpr;
-/**
- * GlobalContextsController.
- * @apiNote Don't retain the reference. It may change on tracker reconfiguration.
- */
-@property (readonly, nonatomic) id<SPGlobalContextsController> globalContexts;
-
-/**
- * Track the event.
- * The tracker will take care to process and send the event assigning `event_id` and `device_timestamp`.
- * @param event The event to track.
- * @return The event ID or nil in case tracking is paused
- */
-- (nullable NSUUID *)track:(SPEvent *)event;
-/**
- * Pause the tracker.
- * The tracker will stop any new activity tracking but it will continue to send remaining events
- * already tracked but not sent yet.
- * Calling a track method will not have any effect and event tracked will be lost.
- */
-- (void)pause;
-/**
- * Resume the tracker.
- * The tracker will start tracking again.
- */
-- (void)resume;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPTrackerControllerImpl.h b/Snowplow/Internal/Tracker/SPTrackerControllerImpl.h
deleted file mode 100644
index c7d0c1226..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerControllerImpl.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-//  SPTrackerController.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPTrackerController.h"
-#import "SPController.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(TrackerControllerImpl)
-@interface SPTrackerControllerImpl : SPController <SPTrackerController>
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPTrackerControllerImpl.m b/Snowplow/Internal/Tracker/SPTrackerControllerImpl.m
deleted file mode 100644
index a49255bcd..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerControllerImpl.m
+++ /dev/null
@@ -1,298 +0,0 @@
-//
-//  SPTrackerController.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPServiceProviderProtocol.h"
-#import "SPTrackerControllerImpl.h"
-#import "SPEmitterControllerImpl.h"
-#import "SPNetworkControllerImpl.h"
-#import "SPGDPRControllerImpl.h"
-#import "SPGlobalContextsControllerImpl.h"
-#import "SPSubjectControllerImpl.h"
-#import "SPSessionControllerImpl.h"
-
-#import "SPTrackerConstants.h"
-#import "SPTracker.h"
-#import "SPEmitter.h"
-#import "SPDefaultNetworkConnection.h"
-#import "SPSubject.h"
-#import "SPLogger.h"
-
-#import "SPTrackerConfigurationUpdate.h"
-
-@implementation SPTrackerControllerImpl
-
-// MARK: - Controllers
-
-- (id<SPNetworkController>)network {
-    return self.serviceProvider.networkController;
-}
-
-- (id<SPEmitterController>)emitter {
-    return self.serviceProvider.emitterController;
-}
-
-- (id<SPGDPRController>)gdpr {
-    return self.serviceProvider.gdprController;
-}
-
-- (id<SPGlobalContextsController>)globalContexts {
-    return self.serviceProvider.globalContextsController;
-}
-
-- (id<SPSubjectController>)subject {
-    return self.serviceProvider.subjectController;
-}
-
-- (SPSessionControllerImpl *)sessionController {
-    return self.serviceProvider.sessionController;
-}
-
-- (nullable id<SPSessionController>)session {
-    SPSessionControllerImpl *sessionController = self.serviceProvider.sessionController;
-    return sessionController.isEnabled ? sessionController : nil;
-}
-
-// MARK: - Control methods
-
-- (void)pause {
-    self.dirtyConfig.isPaused = YES;
-    [self.tracker pauseEventTracking];
-}
-
-- (void)resume {
-    self.dirtyConfig.isPaused = NO;
-    [self.tracker resumeEventTracking];
-}
-
-- (NSUUID *)track:(nonnull SPEvent *)event {
-    return [self.tracker track:event];
-}
-
-// MARK: - Properties' setters and getters
-
-- (void)setAppId:(NSString *)appId {
-    self.dirtyConfig.appId = appId;
-    self.dirtyConfig.appIdUpdated = YES;
-    [self.tracker setAppId:appId];
-}
-
-- (NSString *)appId {
-    return [self.tracker appId];
-}
-
-- (NSString *)namespace {
-    return [self.tracker trackerNamespace];
-}
-
-- (void)setDevicePlatform:(SPDevicePlatform)devicePlatform {
-    self.dirtyConfig.devicePlatform = devicePlatform;
-    self.dirtyConfig.devicePlatformUpdated = YES;
-    [self.tracker setDevicePlatform:devicePlatform];
-}
-
-- (SPDevicePlatform)devicePlatform {
-    return [self.tracker devicePlatform];
-}
-
-- (void)setBase64Encoding:(BOOL)base64Encoding {
-    self.dirtyConfig.base64Encoding = base64Encoding;
-    self.dirtyConfig.base64EncodingUpdated = YES;
-    [self.tracker setBase64Encoded:base64Encoding];
-}
-
-- (BOOL)base64Encoding {
-    return [self.tracker base64Encoded];
-}
-
-- (void)setLogLevel:(SPLogLevel)logLevel {
-    self.dirtyConfig.logLevel = logLevel;
-    self.dirtyConfig.logLevelUpdated = YES;
-    [SPLogger setLogLevel:logLevel];
-}
-
-- (SPLogLevel)logLevel {
-    return [SPLogger logLevel];
-}
-
-- (void)setLoggerDelegate:(id<SPLoggerDelegate>)loggerDelegate {
-    [SPLogger setDelegate:loggerDelegate];
-}
-
-- (id<SPLoggerDelegate>)loggerDelegate {
-    return [SPLogger delegate];
-}
-
-- (void)setApplicationContext:(BOOL)applicationContext {
-    self.dirtyConfig.applicationContext = applicationContext;
-    self.dirtyConfig.applicationContextUpdated = YES;
-    [self.tracker setApplicationContext:applicationContext];
-}
-
-- (BOOL)applicationContext {
-    return [self.tracker applicationContext];
-}
-
-- (void)setPlatformContext:(BOOL)platformContext {
-    self.dirtyConfig.platformContext = platformContext;
-    self.dirtyConfig.platformContextUpdated = YES;
-    if (self.tracker.subject) {
-        self.tracker.subject.platformContext = platformContext;
-    }
-}
-
-- (BOOL)platformContext {
-    return self.tracker.subject.platformContext;
-}
-
-- (void)setGeoLocationContext:(BOOL)geoLocationContext {
-    self.dirtyConfig.geoLocationContext = geoLocationContext;
-    self.dirtyConfig.geoLocationContextUpdated = YES;
-    if (self.tracker.subject) {
-        self.tracker.subject.geoLocationContext = geoLocationContext;
-    }
-}
-
-- (BOOL)geoLocationContext {
-    return self.tracker.subject.geoLocationContext;
-}
-
-- (void)setDiagnosticAutotracking:(BOOL)diagnosticAutotracking {
-    self.dirtyConfig.diagnosticAutotracking = diagnosticAutotracking;
-    self.dirtyConfig.diagnosticAutotrackingUpdated = YES;
-    [self.tracker setTrackerDiagnostic:diagnosticAutotracking];
-}
-
-- (BOOL)diagnosticAutotracking {
-    return self.tracker.trackerDiagnostic;
-}
-
-- (void)setExceptionAutotracking:(BOOL)exceptionAutotracking {
-    self.dirtyConfig.exceptionAutotracking = exceptionAutotracking;
-    self.dirtyConfig.exceptionAutotrackingUpdated = YES;
-    [self.tracker setExceptionEvents:exceptionAutotracking];
-}
-
-- (BOOL)exceptionAutotracking {
-    return [self.tracker exceptionEvents];
-}
-
-- (void)setInstallAutotracking:(BOOL)installAutotracking {
-    self.dirtyConfig.installAutotracking = installAutotracking;
-    self.dirtyConfig.installAutotrackingUpdated = YES;
-    [self.tracker setInstallEvent:installAutotracking];
-}
-
-- (BOOL)installAutotracking {
-    return [self.tracker installEvent];
-}
-
-- (void)setLifecycleAutotracking:(BOOL)lifecycleAutotracking {
-    self.dirtyConfig.lifecycleAutotracking = lifecycleAutotracking;
-    self.dirtyConfig.lifecycleAutotrackingUpdated = YES;
-    [self.tracker setLifecycleEvents:lifecycleAutotracking];
-}
-
-- (BOOL)lifecycleAutotracking {
-    return [self.tracker getLifecycleEvents];
-}
-
-- (void)setDeepLinkContext:(BOOL)deepLinkContext {
-    self.dirtyConfig.deepLinkContext = deepLinkContext;
-    self.dirtyConfig.deepLinkContextUpdated = YES;
-    [self.tracker setDeepLinkContext:deepLinkContext];
-}
-
-- (BOOL)deepLinkContext {
-    return [self.tracker deepLinkContext];
-}
-
-- (void)setScreenContext:(BOOL)screenContext {
-    self.dirtyConfig.screenContext = screenContext;
-    self.dirtyConfig.screenContextUpdated = YES;
-    [self.tracker setScreenContext:screenContext];
-}
-
-- (BOOL)screenContext {
-    return [self.tracker screenContext];
-}
-
-- (void)setScreenViewAutotracking:(BOOL)screenViewAutotracking {
-    self.dirtyConfig.screenViewAutotracking = screenViewAutotracking;
-    self.dirtyConfig.screenViewAutotrackingUpdated = YES;
-    [self.tracker setAutotrackScreenViews:screenViewAutotracking];
-}
-
-- (BOOL)screenViewAutotracking {
-    return [self.tracker autoTrackScreenView];
-}
-
-- (void)setTrackerVersionSuffix:(NSString *)trackerVersionSuffix {
-    self.dirtyConfig.trackerVersionSuffix = trackerVersionSuffix;
-    self.dirtyConfig.trackerVersionSuffixUpdated = YES;
-    [self.tracker setTrackerVersionSuffix:trackerVersionSuffix];
-}
-
-- (NSString *)trackerVersionSuffix {
-    return [self.tracker trackerVersionSuffix];
-}
-
-- (void)setSessionContext:(BOOL)sessionContext {
-    self.dirtyConfig.sessionContext = sessionContext;
-    self.dirtyConfig.sessionContextUpdated = YES;
-    [self.tracker setSessionContext:sessionContext];
-}
-
-- (BOOL)sessionContext {
-    return [self.tracker sessionContext];
-}
-
-- (void)setUserAnonymisation:(BOOL)userAnonymisation {
-    self.dirtyConfig.userAnonymisation = userAnonymisation;
-    self.dirtyConfig.userAnonymisationUpdated = YES;
-    [self.tracker setUserAnonymisation:userAnonymisation];
-}
-
-- (BOOL)userAnonymisation {
-    return self.tracker.userAnonymisation;
-}
-
-- (BOOL)isTracking {
-    return [self.tracker getIsTracking];
-}
-
-- (NSString *)version {
-    return kSPVersion;
-}
-
-// MARK: - Private methods
-
-- (SPTracker *)tracker {
-    if (![self.serviceProvider isTrackerInitialized]) {
-        SPLogError(@"Recreating tracker instance after it was removed. This will not be supported in future versions.");
-    }
-    return self.serviceProvider.tracker;
-}
-
-- (SPTrackerConfigurationUpdate *)dirtyConfig {
-    return self.serviceProvider.trackerConfigurationUpdate;
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPTrackerEvent.h b/Snowplow/Internal/Tracker/SPTrackerEvent.h
deleted file mode 100644
index 41113c705..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerEvent.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-//  SPTrackerEvent.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-#import "SPTrackerState.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPTrackerEvent : NSObject <SPInspectableEvent>
-
-@property (nonatomic) NSMutableDictionary<NSString *, NSObject *> *payload;
-@property (nonatomic) NSString *schema;
-@property (nonatomic) NSString *eventName;
-@property (nonatomic) NSUUID *eventId;
-@property (nonatomic) long long timestamp;
-@property (nonatomic, nullable) NSDate *trueTimestamp;
-@property (nonatomic) NSMutableArray<SPSelfDescribingJson *> *contexts;
-@property (nonatomic) id<SPTrackerStateSnapshot> state;
-
-@property (nonatomic) BOOL isPrimitive;
-@property (nonatomic) BOOL isService;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-- (instancetype)initWithEvent:(SPEvent *)event;
-- (instancetype)initWithEvent:(SPEvent *)event state:(nullable id<SPTrackerStateSnapshot>)state;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPTrackerEvent.m b/Snowplow/Internal/Tracker/SPTrackerEvent.m
deleted file mode 100644
index ba223f70e..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerEvent.m
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-//  SPTrackerEvent.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerEvent.h"
-#import "SPSelfDescribingJson.h"
-#import "SPTrackerError.h"
-
-@implementation SPTrackerEvent
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // to ignore warnings for deprecated methods that we are forced to use until the next major version release
-
-- (instancetype)initWithEvent:(SPEvent *)event {
-    return [self initWithEvent:event state:nil];
-}
-
-- (instancetype)initWithEvent:(SPEvent *)event state:(id<SPTrackerStateSnapshot>)state {
-    if (self = [super init]) {
-        self.eventId = [NSUUID UUID];
-        self.timestamp = (long long)([[[NSDate alloc] init] timeIntervalSince1970] * 1000);
-        self.trueTimestamp = event.trueTimestamp;
-        self.contexts = [event.contexts mutableCopy];
-        self.payload = [event.payload mutableCopy];
-        self.state = state ?: [SPTrackerState new];
-
-        self.isService = [event isKindOfClass:SPTrackerError.class];
-        if ([event isKindOfClass:SPPrimitiveAbstract.class]) {
-            self.eventName = [(SPPrimitiveAbstract *)event eventName];
-            self.isPrimitive = true;
-        } else {
-            self.schema = [(SPSelfDescribingAbstract *)event schema];
-            self.isPrimitive = false;
-        }
-    }
-    return self;
-}
-
-#pragma GCC diagnostic pop
-
-- (BOOL)addPayloadValues:(nonnull NSDictionary<NSString *,NSObject *> *)payload {
-    __block BOOL result = YES;
-    [payload enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSObject * _Nonnull obj, BOOL * _Nonnull stop) {
-        if (![self.payload objectForKey:key]) {
-            [self.payload setObject:obj forKey:key];
-        } else {
-            result = NO;
-        }
-    }];
-    return result;
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPTrackerState.h b/Snowplow/Internal/Tracker/SPTrackerState.h
deleted file mode 100644
index 2195eea79..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerState.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-//  SPTrackerState.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPStateFuture.h"
-#import "SPTrackerStateSnapshot.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// The global tracker state which collects all the state generated by the tracker state machines.
-@interface SPTrackerState : NSObject <SPTrackerStateSnapshot>
-
-/// Set a future computable state with a specific state identifier
-- (void)setStateFuture:(SPStateFuture *)state identifier:(NSString *)stateIdentifier;
-
-/// Get a future computable state associated with a state identifier
-- (nullable SPStateFuture *)stateFutureWithIdentifier:(NSString *)stateIdentifier;
-
-/// Remove the state associated with a state identifier
-- (void)removeStateWithIdentifier:(NSString *)stateIdentifer;
-
-/// Get an immutable copy of the whole tracker state
-- (id<SPTrackerStateSnapshot>)snapshot;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPTrackerState.m b/Snowplow/Internal/Tracker/SPTrackerState.m
deleted file mode 100644
index be50520fb..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerState.m
+++ /dev/null
@@ -1,71 +0,0 @@
-//
-//  SPTrackerState.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerState.h"
-
-@interface SPTrackerState ()
-
-@property (nonatomic) NSMutableDictionary<NSString *, SPStateFuture *> *trackerState;
-
-@end
-
-@implementation SPTrackerState
-
-- (instancetype)init {
-    if (self = [super init]) {
-        self.trackerState = [NSMutableDictionary new];
-    }
-    return self;
-}
-
-- (void)setStateFuture:(SPStateFuture *)state identifier:(NSString *)stateIdentifier {
-    @synchronized (self) {
-        self.trackerState[stateIdentifier] = state;
-    }
-}
-
-- (SPStateFuture *)stateFutureWithIdentifier:(NSString *)stateIdentifier {
-    @synchronized (self) {
-        return self.trackerState[stateIdentifier];
-    }
-}
-
-- (void)removeStateWithIdentifier:(NSString *)stateIdentifer {
-    @synchronized (self) {
-        [self.trackerState removeObjectForKey:stateIdentifer];
-    }
-}
-
-- (id<SPTrackerStateSnapshot>)snapshot {
-    @synchronized (self) {
-        SPTrackerState *newTrackerState = [SPTrackerState new];
-        newTrackerState.trackerState = [self.trackerState mutableCopy];
-        return newTrackerState;
-    }
-}
-
-// Protocol SPTrackerStateSnapshot
-
-- (id<SPState>)stateWithIdentifier:(NSString *)stateIdentifier {
-    return [self stateFutureWithIdentifier:stateIdentifier].state;
-}
-
-@end
diff --git a/Snowplow/Internal/Tracker/SPTrackerStateSnapshot.h b/Snowplow/Internal/Tracker/SPTrackerStateSnapshot.h
deleted file mode 100644
index c2abfd861..000000000
--- a/Snowplow/Internal/Tracker/SPTrackerStateSnapshot.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-//  SPTrackerStateSnapshot.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPState.h"
-
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol SPTrackerStateSnapshot <NSObject>
-
-/// Get a computed state with a specific state identifier
-- (nullable id<SPState>)stateWithIdentifier:(NSString *)stateIdentifier;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Tracker/SPWebViewMessageHandler.m b/Snowplow/Internal/Tracker/SPWebViewMessageHandler.m
deleted file mode 100644
index fc61fd17b..000000000
--- a/Snowplow/Internal/Tracker/SPWebViewMessageHandler.m
+++ /dev/null
@@ -1,153 +0,0 @@
-//
-//  SPWebViewMessageHandler.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import "SPWebViewMessageHandler.h"
-#import "SPSnowplow.h"
-#import "SPSelfDescribing.h"
-#import "SPStructured.h"
-#import "SPPageView.h"
-#import "SPScreenView.h"
-
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_OSX
-
-@implementation SPWebViewMessageHandler
-
-/**
- * Callback called when the message handler receives a new message.
- *
- * The message dictionary should contain three properties:
- * 1. "event" with a dictionary containing the event information (structure depends on the tracked event)
- * 2. "context" (optional) with a list of self-describing JSONs
- * 3. "trackers" (optional) with a list of tracker namespaces to track the event with
- */
-- (void)userContentController:(WKUserContentController *)userContentController
-      didReceiveScriptMessage:(WKScriptMessage *)message {
-    NSDictionary *event = message.body[@"event"];
-    NSArray<NSDictionary *> *context = message.body[@"context"];
-    NSArray<NSString *> *trackers = message.body[@"trackers"];
-    NSString *command = message.body[@"command"];
-
-    if ([command isEqual:@"trackSelfDescribingEvent"]) {
-        [self trackSelfDescribing:event withContext:context andTrackers:trackers];
-    } else if ([command isEqual: @"trackStructEvent"]) {
-        [self trackStructEvent:event withContext:context andTrackers:trackers];
-    } else if ([command isEqual: @"trackPageView"]) {
-        [self trackPageView:event withContext:context andTrackers:trackers];
-    } else if ([command isEqual: @"trackScreenView"]) {
-        [self trackScreenView:event withContext:context andTrackers:trackers];
-    }
-}
-
-- (void)trackSelfDescribing:(NSDictionary *)event withContext:(NSArray<NSDictionary *> *)context andTrackers:(NSArray<NSString *> *)trackers {
-    NSString *schema = [event objectForKey:@"schema"];
-    NSDictionary *payload = [event objectForKey:@"data"];
-    
-    if (schema && payload) {
-        SPSelfDescribing *selfDescribing = [[SPSelfDescribing alloc] initWithSchema:schema payload:payload];
-        [self track:selfDescribing withContext:context andTrackers:trackers];
-    }
-}
-
-- (void)trackStructEvent:(NSDictionary *)event withContext:(NSArray<NSDictionary *> *)context andTrackers:(NSArray<NSString *> *)trackers {
-    NSString *category = [event objectForKey:@"category"];
-    NSString *action = [event objectForKey:@"action"];
-    NSString *label = [event objectForKey:@"label"];
-    NSString *property = [event objectForKey:@"property"];
-    NSNumber *value = [event objectForKey:@"value"];
-    
-    if (category && action) {
-        SPStructured *structured = [[SPStructured alloc] initWithCategory:category action:action];
-        if (label) { structured.label = label; }
-        if (property) { structured.property = property; }
-        if (value) { structured.value = value; }
-        [self track:structured withContext:context andTrackers:trackers];
-    }
-}
-
-- (void)trackPageView:(NSDictionary *)event withContext:(NSArray<NSDictionary *> *)context andTrackers:(NSArray<NSString *> *)trackers {
-    NSString *url = [event objectForKey:@"url"];
-    NSString *title = [event objectForKey:@"title"];
-    NSString *referrer = [event objectForKey:@"referrer"];
-    
-    if (url) {
-        SPPageView *pageView = [[SPPageView alloc] initWithPageUrl:url];
-        if (title) { pageView.pageTitle = title; }
-        if (referrer) { pageView.referrer = referrer; }
-        [self track:pageView withContext:context andTrackers:trackers];
-    }
-}
-
-- (void)trackScreenView:(NSDictionary *)event withContext:(NSArray<NSDictionary *> *)context andTrackers:(NSArray<NSString *> *)trackers {
-    NSString *name = [event objectForKey:@"name"];
-    NSString *screenId = [event objectForKey:@"id"];
-    NSString *type = [event objectForKey:@"type"];
-    NSString *previousName = [event objectForKey:@"previousName"];
-    NSString *previousId = [event objectForKey:@"previousId"];
-    NSString *previousType = [event objectForKey:@"previousType"];
-    NSString *transitionType = [event objectForKey:@"transitionType"];
-    
-    if (name && screenId) {
-        NSUUID *screenUuid = [[NSUUID alloc] initWithUUIDString:screenId];
-        SPScreenView *screenView = [[SPScreenView alloc] initWithName:name screenId:screenUuid];
-        if (type) { screenView.type = type; }
-        if (previousName) { screenView.previousName = previousName; }
-        if (previousId) { screenView.previousId = previousId; }
-        if (previousType) { screenView.previousType = previousType; }
-        if (transitionType) { screenView.transitionType = transitionType; }
-        [self track:screenView withContext:context andTrackers:trackers];
-    }
-}
-
-- (void)track:(SPEvent *)event withContext:(NSArray<NSDictionary *> *)context andTrackers:(NSArray<NSString *> *)trackers {
-    if (context) {
-        [event setContexts:[self parseContext:context]];
-    }
-    if (trackers.count > 0) {
-        for (NSString *namespace in trackers) {
-            id<SPTrackerController> tracker = [SPSnowplow trackerByNamespace:namespace];
-            if (tracker) {
-                [tracker track:event];
-            }
-        }
-    } else {
-        [[SPSnowplow defaultTracker] track:event];
-    }
-}
-
-- (NSMutableArray<SPSelfDescribingJson *> *) parseContext:(NSArray<NSDictionary *> *)context {
-    NSMutableArray<SPSelfDescribingJson *> *contextEntities = [[NSMutableArray alloc] init];
-
-    for (NSDictionary *entityJson in context) {
-        NSString *schema = [entityJson objectForKey:@"schema"];
-        NSDictionary *payload = [entityJson objectForKey:@"data"];
-        
-        if (schema && payload) {
-            SPSelfDescribingJson *entity = [[SPSelfDescribingJson alloc] initWithSchema:schema andDictionary:payload];
-            [contextEntities addObject:entity];
-        }
-    }
-    
-    return contextEntities;
-}
-
-@end
-
-#endif
diff --git a/Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.h b/Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.h
deleted file mode 100644
index 78e366019..000000000
--- a/Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-//  NSDictionary+SP_TypeMethods.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import "SPConfiguration.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface NSDictionary (SP_TypeMethods)
-
-- (nullable NSString *)sp_stringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue;
-- (nullable NSNumber *)sp_numberForKey:(NSString *)key defaultValue:(nullable NSNumber *)defaultValue;
-- (BOOL)sp_boolForKey:(NSString *)key defaultValue:(BOOL)defaultValue;
-- (nullable NSDictionary *)sp_dictionaryForKey:(NSString *)key defaultValue:(nullable NSDictionary *)defaultValue;
-- (nullable NSArray *)sp_arrayForKey:(NSString *)key itemClass:(nullable Class)itemClass defaultValue:(nullable NSArray *)defaultValue;
-- (nullable NSObject *)sp_objectForKey:(NSString *)key objectClass:(nullable Class)objectClass defaultValue:(nullable NSArray *)defaultValue;
-- (nullable SPConfiguration *)sp_configurationForKey:(NSString *)key configurationClass:(Class)configurationClass defaultValue:(nullable SPConfiguration *)defaultValue;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.m b/Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.m
deleted file mode 100644
index 4182702bb..000000000
--- a/Snowplow/Internal/Utils/NSDictionary+SP_TypeMethods.m
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-//  NSDictionary+SP_TypeMethods.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "NSDictionary+SP_TypeMethods.h"
-
-@implementation NSDictionary (SP_TypeMethods)
-
-- (nullable NSString *)sp_stringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue {
-    NSObject *obj = [self objectForKey:key];
-    return [obj isKindOfClass:NSString.class] ? (NSString *)obj : defaultValue;
-}
-
-- (nullable NSNumber *)sp_numberForKey:(NSString *)key defaultValue:(nullable NSNumber *)defaultValue {
-    NSObject *obj = [self objectForKey:key];
-    return [obj isKindOfClass:NSNumber.class] ? (NSNumber *)obj : defaultValue;
-}
-
-- (BOOL)sp_boolForKey:(NSString *)key defaultValue:(BOOL)defaultValue {
-    NSNumber *num = [self sp_numberForKey:key defaultValue:nil];
-    return num ? num.boolValue : defaultValue;
-}
-
-- (nullable NSDictionary *)sp_dictionaryForKey:(NSString *)key defaultValue:(nullable NSDictionary *)defaultValue {
-    NSObject *obj = [self objectForKey:key];
-    return [obj isKindOfClass:NSDictionary.class] ? (NSDictionary *)obj : defaultValue;
-}
-
-- (nullable NSArray *)sp_arrayForKey:(NSString *)key itemClass:(nullable Class)itemClass defaultValue:(nullable NSArray *)defaultValue {
-    NSObject *obj = [self objectForKey:key];
-    if (![obj isKindOfClass:NSArray.class]) {
-        return defaultValue;
-    }
-    NSArray *array = (NSArray *)obj;
-    if (!itemClass || [array.firstObject isKindOfClass:itemClass]) {
-        return array;
-    }
-    NSMutableArray *resultArray = [NSMutableArray new];
-    if ([itemClass isSubclassOfClass:SPConfiguration.class] && [array.firstObject isKindOfClass:NSDictionary.class]) {
-        for (int i = 0; i < array.count; i++) {
-            NSDictionary *dictionary = (NSDictionary *)[array objectAtIndex:i];
-            SPConfiguration *configuration = [[itemClass alloc] initWithDictionary:dictionary];
-            if (!configuration) {
-                return defaultValue;
-            }
-            [resultArray addObject:configuration];
-        }
-    }
-    return resultArray;
-}
-
-- (nullable NSObject *)sp_objectForKey:(NSString *)key objectClass:(Class)objectClass defaultValue:(NSObject *)defaultValue {
-    NSObject *obj = [self objectForKey:key];
-    return (obj && (!objectClass || [obj isKindOfClass:objectClass])) ? obj : defaultValue;
-}
-
-- (nullable SPConfiguration *)sp_configurationForKey:(NSString *)key configurationClass:(Class)configurationClass defaultValue:(SPConfiguration *)defaultValue {
-    NSObject *obj = [self objectForKey:key];
-    if (!obj) {
-        return defaultValue;
-    }
-    if ([obj isKindOfClass:configurationClass]) {
-        return (SPConfiguration *)obj;
-    }
-    SPConfiguration *configuration = nil;
-    if ([obj isKindOfClass:NSDictionary.class] && [configurationClass isSubclassOfClass:SPConfiguration.class]) {
-        configuration = (SPConfiguration *)[[configurationClass alloc] initWithDictionary:(NSDictionary *)obj];
-    }
-    return (SPConfiguration *)configuration ?: defaultValue;
-}
-
-@end
diff --git a/Snowplow/Internal/Utils/SNOWReachability.h b/Snowplow/Internal/Utils/SNOWReachability.h
deleted file mode 100644
index a1b88b44e..000000000
--- a/Snowplow/Internal/Utils/SNOWReachability.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-//  SNOWReachability.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#import <SystemConfiguration/SystemConfiguration.h>
-
-typedef NS_ENUM(NSUInteger, SNOWNetworkStatus) {
-    SNOWNetworkStatusOffline,
-    SNOWNetworkStatusWifi,
-    SNOWNetworkStatusWWAN,
-};
-
-@interface SNOWReachability: NSObject
-
-@property (nonatomic,assign) SNOWNetworkStatus networkStatus;
-
-+ (instancetype)reachabilityForInternetConnection;
-
-@end
diff --git a/Snowplow/Internal/Utils/SNOWReachability.m b/Snowplow/Internal/Utils/SNOWReachability.m
deleted file mode 100644
index cb23ff0ba..000000000
--- a/Snowplow/Internal/Utils/SNOWReachability.m
+++ /dev/null
@@ -1,130 +0,0 @@
-//
-//  SNOWReachabilty.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <sys/socket.h>
-#import <netinet/in.h>
-
-#import "SNOWReachability.h"
-
-#pragma mark - Supporting functions
-
-#define kShouldPrintReachabilityFlags 1
-
-static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) {
-#if kShouldPrintReachabilityFlags
-    NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
-#if SNOWPLOW_TARGET_IOS
-          (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
-#else
-          '-',
-#endif
-          (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
-          
-          (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
-          (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
-          (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
-          (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
-          (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
-          (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
-          (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
-          comment
-          );
-#endif
-}
-
-#pragma mark - SNOWReachability implementation
-
-@implementation SNOWReachability {
-    SCNetworkReachabilityRef _reachabilityRef;
-}
-
-@synthesize networkStatus;
-
-+ (instancetype) reachabilityForInternetConnection {
-    struct sockaddr_in zeroAddress;
-    bzero(&zeroAddress, sizeof(zeroAddress));
-    zeroAddress.sin_len = sizeof(zeroAddress);
-    zeroAddress.sin_family = AF_INET;
-    
-    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *) &zeroAddress);
-    if (reachability == NULL) return NULL;
-    
-    SNOWReachability* returnValue = [[self alloc] init];
-    if (returnValue == NULL) {
-        CFRelease(reachability);
-        return NULL;
-    }
-    
-    returnValue->_reachabilityRef = reachability;
-    return returnValue;
-}
-
-- (SNOWNetworkStatus) networkStatus {
-    NSAssert(_reachabilityRef != NULL, @"currentReachabilityStatus called with NULL SCNetworkReachabilityRef");
-    SCNetworkReachabilityFlags flags;
-    if (!SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
-        return SNOWNetworkStatusOffline;
-    }
-    return [self reachabilityStatusForFlags:flags];
-}
-
-# pragma mark - Private methods
-
-- (SNOWNetworkStatus) reachabilityStatusForFlags:(SCNetworkReachabilityFlags)flags {
-    PrintReachabilityFlags(flags, "reachabilityStatusForFlags");
-    BOOL isReachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
-    BOOL isConnectionRequired = (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0;
-    BOOL isOnDemand = (flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0;
-    BOOL isOnTraffic = (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0;
-    BOOL isInterventionRequired = (flags & kSCNetworkReachabilityFlagsInterventionRequired) != 0;
-    
-    if (!isReachable) {
-        return SNOWNetworkStatusOffline;
-    }
-
-#if SNOWPLOW_TARGET_IOS
-    BOOL isWWAN = (flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN;
-    if (isWWAN) {
-        return SNOWNetworkStatusWWAN;
-    }
-#endif
-
-    SNOWNetworkStatus returnValue = SNOWNetworkStatusOffline;
-    if (!isConnectionRequired) {
-        // If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi
-        returnValue = SNOWNetworkStatusWifi;
-    }
-    if (isOnDemand || isOnTraffic) {
-        //... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
-        if (!isInterventionRequired) {
-            //... and no [user] intervention is needed...
-            returnValue = SNOWNetworkStatusWifi;
-        }
-    }
-    return returnValue;
-}
-
-
-- (void)dealloc {
-    CFRelease(_reachabilityRef);
-}
-
-@end
diff --git a/Snowplow/Internal/Utils/SPDataPersistence.h b/Snowplow/Internal/Utils/SPDataPersistence.h
deleted file mode 100644
index 6b7bb840b..000000000
--- a/Snowplow/Internal/Utils/SPDataPersistence.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  SPDataPersistence.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPDataPersistence : NSObject
-
-@property (nonatomic) NSDictionary<NSString *, NSDictionary<NSString *, NSObject *> *> *data;
-@property (nonatomic) NSDictionary<NSString *, NSObject *> *session;
-@property (readonly) BOOL isStoredOnFile;
-
-+ (instancetype) new NS_UNAVAILABLE;
-- (instancetype) init NS_UNAVAILABLE;
-
-+ (SPDataPersistence *)dataPersistenceForNamespace:(NSString *)namespace;
-+ (SPDataPersistence *)dataPersistenceForNamespace:(NSString *)namespace storedOnFile:(BOOL)isStoredOnFile;
-+ (BOOL)removeDataPersistenceWithNamespace:(NSString *)namespace;
-
-+ (NSString *)stringFromNamespace:(NSString *)namespace;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Utils/SPDataPersistence.m b/Snowplow/Internal/Utils/SPDataPersistence.m
deleted file mode 100644
index 4b0a898ea..000000000
--- a/Snowplow/Internal/Utils/SPDataPersistence.m
+++ /dev/null
@@ -1,240 +0,0 @@
-//
-//  SPDataPersistence.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPDataPersistence.h"
-#import "SPTrackerConstants.h"
-#import "SPLogger.h"
-
-@interface SPDataPersistence ()
-
-@property (nonatomic) NSString *escapedNamespace;
-
-@property (nonatomic) NSString *userDefaultsKey;
-@property (nonatomic) NSURL *directoryUrl;
-@property (nonatomic) NSURL *fileUrl;
-
-@end
-
-@implementation SPDataPersistence
-
-static NSMutableDictionary<NSString *, SPDataPersistence *> *instances = nil;
-
-NSString *const kSPSessionDictionaryPrefix = @"SPSessionDictionary";
-NSString *const kFilename = @"namespace";
-NSString *const kFilenameExt = @"dict";
-NSString *const kSessionFilenameV1 = @"session.dict";
-NSString *const kSessionFilenamePrefixV2_2 = @"session";
-
-NSString *sessionKey = @"session";
-
-+ (SPDataPersistence *)dataPersistenceForNamespace:(NSString *)namespace {
-    return [SPDataPersistence dataPersistenceForNamespace:namespace storedOnFile:YES];
-}
-
-+ (SPDataPersistence *)dataPersistenceForNamespace:(NSString *)namespace storedOnFile:(BOOL)isStoredOnFile {
-    NSString *escapedNamespace = [SPDataPersistence stringFromNamespace:namespace];
-    if ([escapedNamespace length] <= 0) return nil;
-    @synchronized (SPDataPersistence.class) {
-        SPDataPersistence *instance = nil;
-        if (instances) {
-            instance = [instances objectForKey:escapedNamespace];
-            if (instance) {
-                return instance;
-            }
-        } else {
-            instances = [NSMutableDictionary new];
-        }
-        instance = [[SPDataPersistence alloc] initWithNamespace:escapedNamespace storedOnFile:isStoredOnFile];
-        [instances setValue:instance forKey:escapedNamespace];
-        return instance;
-    }
-}
-
-+ (BOOL)removeDataPersistenceWithNamespace:(NSString *)namespace {
-    SPDataPersistence *instance = [SPDataPersistence dataPersistenceForNamespace:namespace];
-    if (!instance) return NO;
-    @synchronized (SPDataPersistence.class) {
-        [instances removeObjectForKey:instance.escapedNamespace];
-    }
-    [instance removeAll];
-    return YES;
-}
-
-+ (NSString *)stringFromNamespace:(NSString *)namespace {
-    if (!namespace) return nil;
-    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^a-zA-Z0-9_]+" options:0 error:nil];
-    return [regex stringByReplacingMatchesInString:namespace options:0 range:NSMakeRange(0, namespace.length) withTemplate:@"-"];
-}
-
-// MARK: - Property accessor methods
-
-- (NSDictionary<NSString *, NSDictionary<NSString *, NSObject *> *> *)data {
-    @synchronized (self) {
-        if (!self.isStoredOnFile) {
-            return [[NSUserDefaults standardUserDefaults] dictionaryForKey:self.userDefaultsKey] ?: @{};
-        }
-        NSMutableDictionary<NSString *, NSDictionary<NSString *, NSObject *> *> *result =
-        [NSMutableDictionary dictionaryWithContentsOfURL:self.fileUrl];
-        
-        if (!result) {
-            // Initialise
-            result = [NSMutableDictionary new];
-            // Migrate legacy session data
-            NSMutableDictionary *sessionDict = [self sessionDictionaryFromLegacyTrackerV2_2].mutableCopy
-                ?: [self sessionDictionaryFromLegacyTrackerV1].mutableCopy
-                ?: [NSMutableDictionary new];
-            // Add missing fields
-            [sessionDict setObject:@"" forKey:kSPSessionFirstEventId];
-            [sessionDict setObject:@"LOCAL_STORAGE" forKey:kSPSessionStorage];
-            // Wrap up
-            [result setObject:sessionDict forKey:sessionKey];
-            [self storeDictionary:result fileURL:self.fileUrl];
-        }
-        
-        return result;
-    }
-}
-
-- (void)setData:(NSDictionary<NSString *,NSDictionary<NSString *, NSObject *> *> *)data {
-    @synchronized (self) {
-        if (self.fileUrl) {
-            [self storeDictionary:data fileURL:self.fileUrl];
-        } else {
-            [[NSUserDefaults standardUserDefaults] setObject:data forKey:self.userDefaultsKey];
-        }
-    }
-}
-
-- (NSDictionary<NSString *, NSObject *> *)session {
-    return [self.data objectForKey:sessionKey];
-}
-
-- (void)setSession:(NSDictionary<NSString *, NSObject *> *)session {
-    @synchronized (self) {
-        NSMutableDictionary<NSString *, NSDictionary *> *data = [self.data mutableCopy];
-        [data setValue:session forKey:sessionKey];
-        self.data = data;
-    }
-}
-
-- (BOOL)isStoredOnFile {
-    return self.fileUrl != nil;
-}
-
-// MARK: - Private instance methods
-
-- (instancetype)initWithNamespace:(NSString *)escapedNamespace storedOnFile:(BOOL)isStoredOnFile {
-    if (self = [super init]) {
-        self.escapedNamespace = escapedNamespace;
-        self.userDefaultsKey = [NSString stringWithFormat:@"%@_%@", kSPSessionDictionaryPrefix, escapedNamespace];
-#if !(TARGET_OS_TV || TARGET_OS_WATCH)
-        if (isStoredOnFile) {
-            self.directoryUrl = [self createDirectoryUrl];
-            if (self.directoryUrl) {
-                NSString *filename = [NSString stringWithFormat:@"%@_%@.%@", kFilename, escapedNamespace, kFilenameExt];
-                self.fileUrl = [self.directoryUrl URLByAppendingPathComponent:filename];
-            }
-        }
-#endif
-    }
-    return self;
-}
-
-- (BOOL)removeAll {
-    @synchronized (self) {
-        [[NSUserDefaults standardUserDefaults] removeObjectForKey:self.userDefaultsKey];
-        NSError *error = nil;
-        if (self.fileUrl && ![[NSFileManager defaultManager] removeItemAtURL:self.fileUrl error:&error]) {
-            SPLogError(@"%@", error.localizedDescription);
-            return NO;
-        }
-        return YES;
-    }
-}
-
-- (NSURL *)createDirectoryUrl {
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    NSURL *url = [fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask].lastObject;
-    url = [url URLByAppendingPathComponent:@"snowplow" isDirectory:YES];
-    NSError *error = nil;
-    if ([url checkResourceIsReachableAndReturnError:&error]) {
-        return url;
-    }
-    if ([fileManager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:&error]) {
-        return url;
-    }
-    SPLogError(@"Unable to create directory for tracker data persistence: %@", error.localizedDescription);
-    return nil;
-}
-
-- (BOOL)storeDictionary:(NSDictionary *)dictionary fileURL:(NSURL *)fileUrl {
-    BOOL result = NO;
-    NSError *error = nil;
-    if (@available(iOS 11.0, macOS 10.13, watchOS 4.0, *)) {
-        result = [dictionary writeToURL:fileUrl error:&error];
-    } else {
-        result = [dictionary writeToURL:fileUrl atomically:YES];
-    }
-    if (result) {
-        return YES;
-    }
-    SPLogError(@"Unable to write file for sessions: %@", error.localizedDescription ?: @"-");
-    return NO;
-}
-
-// Migration methods
-
-- (NSDictionary *)sessionDictionaryFromLegacyTrackerV2_2 {
-    @synchronized (self) {
-        NSString *filename = [NSString stringWithFormat:@"%@_%@.%@", kSessionFilenamePrefixV2_2, self.escapedNamespace, kFilenameExt];
-        NSURL *fileUrl = [self.directoryUrl URLByAppendingPathComponent:filename];
-        NSDictionary *sessionDict = nil;
-        sessionDict = [NSDictionary dictionaryWithContentsOfURL:fileUrl];
-        if (!sessionDict) {
-            return nil;
-        }
-        NSError *error = nil;
-        [[NSFileManager defaultManager] removeItemAtURL:fileUrl error:&error];
-        if (error) {
-            SPLogError(@"%@", error.localizedDescription);
-        }
-        return sessionDict;
-    }
-}
-
-- (NSDictionary *)sessionDictionaryFromLegacyTrackerV1 {
-    @synchronized (SPDataPersistence.class) {
-        NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
-        path = [path stringByAppendingPathComponent:kSessionFilenameV1];
-        NSDictionary *sessionDict = [NSDictionary dictionaryWithContentsOfFile:path];
-        if (!sessionDict) {
-            return nil;
-        }
-        NSError *error = nil;
-        [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
-        if (error) {
-            SPLogError(@"%@", error.localizedDescription);
-        }
-        return sessionDict;
-    }
-}
-
-@end
diff --git a/Snowplow/Internal/Utils/SPDeviceInfoMonitor.h b/Snowplow/Internal/Utils/SPDeviceInfoMonitor.h
deleted file mode 100644
index 42385036b..000000000
--- a/Snowplow/Internal/Utils/SPDeviceInfoMonitor.h
+++ /dev/null
@@ -1,126 +0,0 @@
-//
-//  SPDeviceInfoMonitor.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPDeviceInfoMonitor : NSObject
-
-/*!
- @brief Returns a generated string unique to each device, used only for serving advertisements. This works only if you have the AdSupport library in your project and you enable the compiler flag <code>SNOWPLOW_IDFA_ENABLED</code> to your build settings.
- @return A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
- */
-- (nullable NSString *) appleIdfa;
-
-/*!
- @brief Returns the generated identifier for vendors. More info can be found in UIDevice's identifierForVendor documentation. If you do not want to use IDFV, add the comiler flag <code>SNOWPLOW_NO_IDFV</code> to your build settings.
- @return A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
- */
-- (nullable NSString *) appleIdfv;
-
-/*!
- @brief Returns the current device's vendor in the form of a string.
- @return A string with vendor, i.e. "Apple Inc."
- */
-- (nullable NSString *) deviceVendor;
-
-/*!
- @brief Returns the current device's model in the form of a string.
- @return A string with device model.
- */
-- (nullable NSString *) deviceModel;
-
-/*!
- @brief This is to detect what the version of mobile OS of the current device.
- @return The current device's OS version type as a string.
- */
-- (nullable NSString *) osVersion;
-
-/*!
- @brief This is to detect what the type of mobile OS of the current device.
- @return The current device's OS type as a string.
- */
-- (nullable NSString *) osType;
-
-/*!
- @brief Returns the carrier of the SIM inserted in the device.
- @return A string containing the carrier name of the service provider.
- */
-- (nullable NSString *) carrierName;
-
-/*!
- @brief Returns the Network Type the device is connected to.
- @return A string containing the Network Type.
- */
-- (nullable NSString *) networkType;
-
-/*!
- @brief Returns the Network Technology the device is using.
- @return A string containing the Network Technology.
- */
-- (nullable NSString *) networkTechnology;
-
-/*!
- @brief Returns remaining battery level as an integer percentage of total battery capacity.
- @return Battery level.
- */
-- (NSNumber *) batteryLevel;
-
-/*!
- @brief Returns battery state for the device.
- @return One of "charging", "full", "unplugged" or NULL
- */
-- (NSString *) batteryState;
-
-/*!
- @brief Returns whether low power mode is activated.
- @return Boolean indicating the state of low power mode.
- */
-- (NSNumber *) isLowPowerModeEnabled;
-
-/*!
- @brief Returns total physical system memory in bytes.
- @return Total physical system memory in bytes.
- */
-- (NSNumber *) physicalMemory;
-
-/*!
- @brief Returns the amount of memory in bytes available to the current app (iOS 13+).
- @return Amount of memory in bytes available to the current app (or 0 if not supported).
- */
-- (NSNumber *) appAvailableMemory;
-
-/*!
- @brief Returns number of bytes of storage remaining. The information is requested from the home directory.
- @return Bytes of storage remaining.
- */
-- (NSNumber *) availableStorage;
-
-/*!
- @brief Returns the total number of bytes of storage. The information is requested from the home directory.
- @return Total size of storage in bytes.
- */
-- (NSNumber *) totalStorage;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Utils/SPDeviceInfoMonitor.m b/Snowplow/Internal/Utils/SPDeviceInfoMonitor.m
deleted file mode 100644
index e8e8a8db7..000000000
--- a/Snowplow/Internal/Utils/SPDeviceInfoMonitor.m
+++ /dev/null
@@ -1,333 +0,0 @@
-//
-//  SPDeviceInfoMonitor.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import "SPDeviceInfoMonitor.h"
-#import "SPLogger.h"
-#import "SPTrackerConstants.h"
-
-#if SNOWPLOW_TARGET_IOS
-
-#import <UIKit/UIScreen.h>
-#import <CoreTelephony/CTCarrier.h>
-#import <CoreTelephony/CTTelephonyNetworkInfo.h>
-#import "SNOWReachability.h"
-#include <sys/mount.h>
-#if __has_include(<os/proc.h>)
-#import <os/proc.h>
-#define __HAS_OS_PROC_H__
-#endif
-
-#elif SNOWPLOW_TARGET_TV
-
-#import <UIKit/UIScreen.h>
-
-#elif SNOWPLOW_TARGET_WATCHOS
-
-#import <WatchKit/WatchKit.h>
-
-#endif
-
-#include <sys/sysctl.h>
-
-@implementation SPDeviceInfoMonitor
-
-/*
- The IDFA can be retrieved using selectors rather than proper instance methods because
- the compiler would complain about the missing AdSupport framework.
- As stated in the header file, this only works if you have the AdSupport library in your project.
- If you have it and you want to use IDFA, add the compiler flag <code>SNOWPLOW_IDFA_ENABLED</code> to your build settings.
- If you haven't AdSupport framework in your project or SNOWPLOW_IDFA_ENABLED it's not set, it just compiles returning a nil advertisingIdentifier.
- 
- Note that `advertisingIdentifier` returns a sequence of 0s when used in the simulator.
- Use a real device if you want a proper IDFA.
- */
-- (NSString *) appleIdfa {
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_TV
-#ifdef SNOWPLOW_IDFA_ENABLED
-    NSString *errorMsg = @"ASIdentifierManager not found. Please, add the AdSupport.framework if you want to use it.";
-    Class identifierManagerClass = NSClassFromString(@"ASIdentifierManager");
-    if (!identifierManagerClass) {
-        SPLogError(errorMsg);
-        return nil;
-    }
-
-    SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
-    if (![identifierManagerClass respondsToSelector:sharedManagerSelector]) {
-        SPLogError(errorMsg);
-        return nil;
-    }
-
-    id identifierManager = ((id (*)(id, SEL))[identifierManagerClass methodForSelector:sharedManagerSelector])(identifierManagerClass, sharedManagerSelector);
-    
-    if (@available(iOS 14.0, *)) {
-        errorMsg = @"ATTrackingManager not found. Please, add the AppTrackingTransparency.framework if you want to use it.";
-        Class trackingManagerClass = NSClassFromString(@"ATTrackingManager");
-        if (!trackingManagerClass) {
-            SPLogError(errorMsg);
-            return nil;
-        }
-        
-        SEL trackingStatusSelector = NSSelectorFromString(@"trackingAuthorizationStatus");
-        if (![trackingManagerClass respondsToSelector:trackingStatusSelector]) {
-            SPLogError(errorMsg);
-            return nil;
-        }
-        
-        //notDetermined = 0, restricted = 1, denied = 2, authorized = 3
-        NSInteger authorizationStatus = ((NSInteger (*)(id, SEL))[trackingManagerClass methodForSelector:trackingStatusSelector])(trackingManagerClass, trackingStatusSelector);
-        
-        if (authorizationStatus != 3)  {
-            SPLogDebug(@"The user didn't let tracking of IDFA. Authorization status is: %d", authorizationStatus);
-            return nil;
-        }
-    } else {
-        SEL isAdvertisingTrackingEnabledSelector = NSSelectorFromString(@"isAdvertisingTrackingEnabled");
-        if (![identifierManager respondsToSelector:isAdvertisingTrackingEnabledSelector]) {
-            SPLogError(errorMsg);
-            return nil;
-        }
-        
-        BOOL isAdvertisingTrackingEnabled = ((BOOL (*)(id, SEL))[identifierManager methodForSelector:isAdvertisingTrackingEnabledSelector])(identifierManager, isAdvertisingTrackingEnabledSelector);
-        if (!isAdvertisingTrackingEnabled) {
-            SPLogError(@"The user didn't let tracking of IDFA.");
-            return nil;
-        }
-    }
-    
-    SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier");
-    if (![identifierManager respondsToSelector:advertisingIdentifierSelector]) {
-        SPLogError(@"ASIdentifierManager doesn't respond to selector `advertisingIdentifier`.");
-        return nil;
-    }
-
-    NSUUID *uuid = ((NSUUID* (*)(id, SEL))[identifierManager methodForSelector:advertisingIdentifierSelector])(identifierManager, advertisingIdentifierSelector);
-    return [uuid UUIDString];
-#endif
-#endif
-    return nil;
-}
-
-- (NSString *) appleIdfv {
-    NSString * idfv = nil;
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_TV
-#ifndef SNOWPLOW_NO_IDFV
-    idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
-#endif
-#endif
-    return idfv;
-}
-
-- (NSString *) deviceVendor {
-    return @"Apple Inc.";
-}
-
-- (NSString *) deviceModel {
-    NSString *simulatorModel = [NSProcessInfo.processInfo.environment objectForKey: @"SIMULATOR_MODEL_IDENTIFIER"];
-    if (simulatorModel) return simulatorModel;
-
-    size_t size;
-    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
-    char *machine = malloc(size);
-    sysctlbyname("hw.machine", machine, &size, NULL, 0);
-    NSString *platform = [NSString stringWithUTF8String:machine];
-    free(machine);
-    return platform;
-}
-
-- (NSString *) osVersion {
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_TV
-    return [[UIDevice currentDevice] systemVersion];
-#elif SNOWPLOW_TARGET_WATCHOS
-    return [[WKInterfaceDevice currentDevice] systemVersion];
-#else
-    SInt32 osxMajorVersion;
-    SInt32 osxMinorVersion;
-    SInt32 osxPatchFixVersion;
-    NSProcessInfo *info = [NSProcessInfo processInfo];
-    NSOperatingSystemVersion systemVersion = [info operatingSystemVersion];
-    osxMajorVersion = (int)systemVersion.majorVersion;
-    osxMinorVersion = (int)systemVersion.minorVersion;
-    osxPatchFixVersion = (int)systemVersion.patchVersion;
-    NSString *versionString = [NSString stringWithFormat:@"%d.%d.%d", osxMajorVersion,
-                               osxMinorVersion, osxPatchFixVersion];
-    return versionString;
-#endif
-}
-
-- (NSString *) osType {
-#if SNOWPLOW_TARGET_IOS
-    return @"ios";
-#elif SNOWPLOW_TARGET_TV
-    return @"tvos";
-#elif SNOWPLOW_TARGET_WATCHOS
-    return @"watchos";
-#else
-    return @"osx";
-#endif
-}
-
-- (NSString *) carrierName {
-#if SNOWPLOW_TARGET_IOS
-    CTTelephonyNetworkInfo *networkInfo = [CTTelephonyNetworkInfo new];
-    CTCarrier *carrier;
-    if (@available(iOS 12.1, *)) {
-        // `serviceSubscribersCellularProviders` has a bug in the iOS 12.0 so we use it from iOS 12.1
-        NSString *carrierKey = [self carrierKey];
-        if (!carrierKey) {
-            return nil;
-        }
-        NSDictionary<NSString *,CTCarrier *> *services = [networkInfo serviceSubscriberCellularProviders];
-        carrier = services[carrierKey];
-    } else {
-        carrier = [networkInfo subscriberCellularProvider];
-    }
-    return [carrier carrierName];
-#endif
-    return nil;
-}
-
-- (NSString *) networkTechnology {
-#if SNOWPLOW_TARGET_IOS
-    CTTelephonyNetworkInfo *networkInfo = [CTTelephonyNetworkInfo new];
-    if (@available(iOS 12.1, *)) {
-        // `serviceCurrentRadioAccessTechnology` has a bug in the iOS 12.0 so we use it from iOS 12.1
-        NSString *carrierKey = [self carrierKey];
-        if (!carrierKey) {
-            return nil;
-        }
-        NSDictionary<NSString *, NSString *> *services = [networkInfo serviceCurrentRadioAccessTechnology];
-        return services[carrierKey];
-    } else {
-        return [networkInfo currentRadioAccessTechnology];
-    }
-#endif
-    return nil;
-}
-
-- (NSString *) carrierKey {
-#if SNOWPLOW_TARGET_IOS
-    if (@available(iOS 12.1, *)) {
-        CTTelephonyNetworkInfo *networkInfo = [CTTelephonyNetworkInfo new];
-        // `serviceSubscribersCellularProviders` has a bug in the iOS 12.0 so we use it from iOS 12.1
-        NSDictionary<NSString *,CTCarrier *> *services = [networkInfo serviceSubscriberCellularProviders];
-        NSArray<NSString *> *carrierKeys = services.allKeys;
-        // From iOS 12, iPhones with eSIMs can return multiple carrier providers.
-        // We can't prefer anyone of them so we track the first reported.
-        return carrierKeys.firstObject;
-    }
-#endif
-    return nil;
-}
-
-- (NSString *) networkType {
-#if SNOWPLOW_TARGET_IOS
-    SNOWNetworkStatus networkStatus = [SNOWReachability reachabilityForInternetConnection].networkStatus;
-    switch (networkStatus) {
-        case SNOWNetworkStatusOffline:
-            return @"offline";
-        case SNOWNetworkStatusWifi:
-            return @"wifi";
-        case SNOWNetworkStatusWWAN:
-            return @"mobile";
-    }
-#endif
-    return @"offline";
-}
-
-- (NSNumber *) batteryLevel {
-#if SNOWPLOW_TARGET_IOS
-    float batteryLevel = [[UIDevice currentDevice] batteryLevel];
-    if (batteryLevel != UIDeviceBatteryStateUnknown && batteryLevel >= 0) {
-        return [[NSNumber alloc] initWithInt: (int) (batteryLevel * 100)];
-    }
-#endif
-    return nil;
-}
-
-- (NSString *) batteryState {
-#if SNOWPLOW_TARGET_IOS
-    switch ([[UIDevice currentDevice] batteryState]) {
-        case UIDeviceBatteryStateCharging:
-            return @"charging";
-        case UIDeviceBatteryStateFull:
-            return @"full";
-        case UIDeviceBatteryStateUnplugged:
-            return @"unplugged";
-        default:
-            return nil;
-    }
-#else
-    return nil;
-#endif
-}
-
-- (NSNumber *) isLowPowerModeEnabled {
-#if SNOWPLOW_TARGET_IOS
-    bool isEnabled = [[NSProcessInfo processInfo] isLowPowerModeEnabled];
-    return [[NSNumber alloc] initWithBool:isEnabled];
-#else
-    return nil;
-#endif
-}
-
-- (NSNumber *) physicalMemory {
-    unsigned long long physicalMemory = [[NSProcessInfo processInfo] physicalMemory];
-    return [[NSNumber alloc] initWithUnsignedLongLong:physicalMemory];
-}
-
-- (NSNumber *) appAvailableMemory {
-#if SNOWPLOW_TARGET_IOS
-#ifdef __HAS_OS_PROC_H__
-    if (@available(iOS 13.0, *)) {
-        unsigned long availableMemory = os_proc_available_memory();
-        return [[NSNumber alloc] initWithUnsignedLong:availableMemory];
-    }
-#endif
-#endif
-    return nil;
-}
-
-- (NSNumber *) availableStorage {
-#if SNOWPLOW_TARGET_IOS
-    struct statfs tStats;
-    if (statfs([NSHomeDirectory() UTF8String], &tStats) == 0) {
-        return [[NSNumber alloc] initWithUnsignedLongLong: tStats.f_bavail * tStats.f_bsize];
-    } else {
-        SPLogError(@"Failed to read available storage size");
-    }
-#endif
-    return nil;
-}
-
-- (NSNumber *) totalStorage {
-#if SNOWPLOW_TARGET_IOS
-    struct statfs tStats;
-    if (statfs([NSHomeDirectory() UTF8String], &tStats) == 0) {
-        return [[NSNumber alloc] initWithUnsignedLongLong: tStats.f_blocks * tStats.f_bsize];
-    } else {
-        SPLogError(@"Failed to read total storage size");
-    }
-#endif
-    return nil;
-}
-
-
-@end
diff --git a/Snowplow/Internal/Utils/SPJSONSerialization.h b/Snowplow/Internal/Utils/SPJSONSerialization.h
deleted file mode 100644
index 5c69a8315..000000000
--- a/Snowplow/Internal/Utils/SPJSONSerialization.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-//  SPJSONSerialization.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPJSONSerialization : NSObject
-
-+ (nullable NSData *)serializeDictionary:(nonnull NSDictionary *)dictionary;
-+ (nullable NSDictionary *)deserializeData:(nonnull NSData *)data;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Snowplow/Internal/Utils/SPJSONSerialization.m b/Snowplow/Internal/Utils/SPJSONSerialization.m
deleted file mode 100644
index 3c193b6b9..000000000
--- a/Snowplow/Internal/Utils/SPJSONSerialization.m
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-//  SPJSONSerialization.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
-
-#import "SPJSONSerialization.h"
-#import "SPLogger.h"
-
-@implementation SPJSONSerialization
-
-+ (NSData *)serializeDictionary:(NSDictionary *)dictionary {
-    NSError *error = nil;
-    NSData *data;
-    @try {
-        data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error];
-    }
-    @catch (NSException *exception) {
-        SPLogError(@"Dictionary to Data Exception, %@", exception.name);
-        return nil;
-    }
-    if (error) {
-        SPLogError(@"Dictionary to Data Error, %@", error);
-        return nil;
-    }
-    return data;
-}
-
-+ (NSDictionary *)deserializeData:(NSData *)data {
-    NSError *error = nil;
-    NSDictionary *dictionary;
-    @try {
-        dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
-    }
-    @catch (NSException *exception) {
-        SPLogError(@"Data to Dictionary Exception, %@", exception.name);
-        return nil;
-    }
-    if (error) {
-        SPLogError(@"Data to Dictionary Error, %@", error);
-        return nil;
-    }
-    if (![dictionary isKindOfClass:NSDictionary.class]) {
-        SPLogError(@"Serialized json isn't a Dictionary type");
-        return nil;
-    }
-    return dictionary;
-}
-
-@end
diff --git a/Snowplow/Internal/Utils/SPUtilities.h b/Snowplow/Internal/Utils/SPUtilities.h
deleted file mode 100644
index edacca03f..000000000
--- a/Snowplow/Internal/Utils/SPUtilities.h
+++ /dev/null
@@ -1,172 +0,0 @@
-//
-//  SPUtils.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-#include "SPDevicePlatform.h"
-#import "SPTrackerConstants.h"
-
-@class SPPayload;
-@class SPSelfDescribingJson;
-@class SPScreenState;
-
-/*!
- This is a class that contains utility functions used throughout the tracker.
- */
-@interface SPUtilities : NSObject
-
-/*!
- @brief Returns the system timezone region.
- @return A string of the timezone region (e.g. 'Toronto/Canada').
- */
-+ (NSString *) getTimezone;
-
-/*!
- @brief Returns the system language currently used on the device.
- @return A string of the current language.
- */
-+ (NSString *) getLanguage;
-
-/*!
- @brief Returns the platform type of the device..
- @return A string of the platform type.
- */
-+ (SPDevicePlatform) getPlatform;
-
-/*!
- @brief Returns a randomly generated UUID (type 4).
- @return A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
- */
-+ (NSString *) getUUIDString;
-
-/*!
- @brief Check if the value is a valid UUID (type 4).
- @param uuidString UUID string to validate.
- @return Weither is a valid UUID string.
- */
-+ (bool ) isUUIDString:(NSString *)uuidString;
-
-/*!
- @brief Returns the timestamp (in milliseconds) generated at the point it was called.
- @return A double of the timestamp from when the method was called.
- */
-+ (NSNumber *) getTimestamp;
-
-/*!
- @brief Converts a timestamp (in milliseconds) to ISO8601 formatted string
- @return ISO8601 formatted string
- */
-+ (NSString *) timestampToISOString:(long long)timestamp;
-
-/*!
- @brief Calculates the resolution of the screen in-terms of actual pixels of the device. This doesn't count Retine-pixels which are technically subpixels.
- @return A formatted string with resolution 'width' and 'height'.
- */
-+ (NSString *) getResolution;
-
-/*!
- @brief Calculates the viewport of the app as it is on the screen. Currently, returns the same value as getResolution.
- @return A formatted string with viewport width and height.
- */
-+ (NSString *) getViewPort;
-
-/*!
- @brief Returns the Application ID
- @return The device bundle application id
- */
-+ (NSString *) getAppId;
-
-/*!
- @brief URL encodes a string so that it is suitable to use in a query-string. A nil s returns @"".
- @return The url encoded string
- */
-+ (NSString *)urlEncodeString:(NSString *)s;
-
-/*!
- @brief URL encodes a dictionary as key=value pairs separated by &, so that it can be used in a query-string.
- This method can encode string, numbers, and bool values, and not embedded arrays or dictionaries.
- It encodes bool as 1 and 0.
- @return The url encoded string of the dictionary.
- */
-+ (NSString *)urlEncodeDictionary:(NSDictionary *)d;
-
-/*!
- @brief Checks an expression and will log if it is false.
- This allows for rudimentary Preconditions for object setup.
- @param argument The argument to check.
- @param message The message to log.
- */
-+ (void) checkArgument:(BOOL)argument withMessage:(NSString *)message;
-
-/*!
- @brief Removes all entries which have a value of NSNull from the dictionary.
- @param dict An NSDictionary to be cleaned.
- @return The same NSDictionary without any Null values.
- */
-+ (NSDictionary *) removeNullValuesFromDictWithDict:(NSDictionary *)dict;
-
-/*!
- @brief Converts a kebab-case string keys into a camel-case string keys.
- @param dict The dictionary to convert.
- @return A dictionary.
- */
-+ (NSDictionary *) replaceHyphenatedKeysWithCamelcase:(NSDictionary *)dict;
-
-/*!
- Converts a kebab-case string into a camel-case string.
- @param key A kebab-case key.
- @return A camel-case string.
- */
-+ (NSString *) camelcaseParsedKey:(NSString *)key;
-
-/*!
- Return nil if value is nil or empty string, otherwise return string.
- @param aString Some string
- @return A string or nil
- */
-+ (NSString *) validateString:(NSString *)aString;
-
-/*!
- Returns the app version.
- @return App version string.
- */
-+ (NSString *) getAppVersion;
- 
- /*!
- Returns the app build.
- @return App build string.
- */
-+ (NSString *) getAppBuild;
-
-/*!
- Returns the application build and version as a payload to be used in the application context.
- @return A context SDJ.
- */
-+ (SPSelfDescribingJson *) getApplicationContext;
-
-/*!
- Returns the application build and version as a payload to be used in the application context.
- @param version The application version
- @param build The application build
- @return A context SDJ.
- */
-+ (SPSelfDescribingJson *) getApplicationContextWithVersion:(NSString *)version andBuild:(NSString *)build;
-
-@end
diff --git a/Snowplow/Internal/Utils/SPUtilities.m b/Snowplow/Internal/Utils/SPUtilities.m
deleted file mode 100644
index b2c3d79cf..000000000
--- a/Snowplow/Internal/Utils/SPUtilities.m
+++ /dev/null
@@ -1,245 +0,0 @@
-//
-//  SPUtils.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPTrackerConstants.h"
-#import "SPDevicePlatform.h"
-#import "SPUtilities.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "SPScreenState.h"
-#import "SPLogger.h"
-#import "SPDeviceInfoMonitor.h"
-
-#if SNOWPLOW_TARGET_IOS
-
-#import <UIKit/UIScreen.h>
-
-#elif SNOWPLOW_TARGET_OSX
-
-#import <AppKit/AppKit.h>
-#import <Carbon/Carbon.h>
-
-#elif SNOWPLOW_TARGET_TV
-
-#import <UIKit/UIScreen.h>
-
-#elif SNOWPLOW_TARGET_WATCHOS
-
-#import <WatchKit/WatchKit.h>
-
-#endif
-
-@implementation SPUtilities
-
-+ (NSString *) getTimezone {
-    NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
-    return [timeZone name];
-}
-
-+ (NSString *) getLanguage {
-    return [[NSLocale preferredLanguages] objectAtIndex:0];
-}
-
-+ (SPDevicePlatform) getPlatform {
-#if SNOWPLOW_TARGET_IOS
-    return SPDevicePlatformMobile;
-#else
-    return SPDevicePlatformDesktop;
-#endif
-}
-
-+ (NSString *) getUUIDString {
-    // Generates type 4 UUID
-    return [[NSUUID UUID] UUIDString].lowercaseString;
-}
-
-+ (bool ) isUUIDString:(nonnull NSString *)uuidString {
-    return [[NSUUID alloc] initWithUUIDString:uuidString] != nil;
-}
-
-+ (NSNumber *) getTimestamp {
-    NSDate *time = [[NSDate alloc] init];
-    return @([time timeIntervalSince1970] * 1000);
-}
-
-+ (NSString *) timestampToISOString:(long long)timestamp {
-    NSDate *eventDate = [NSDate dateWithTimeIntervalSince1970:timestamp / 1000.0];
-    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
-    [formatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
-    [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
-    [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
-    return [formatter stringFromDate:eventDate];
-}
-
-+ (NSString *) getResolution {
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_TV
-    CGRect mainScreen = [[UIScreen mainScreen] bounds];
-    CGFloat screenScale = [[UIScreen mainScreen] scale];
-#elif SNOWPLOW_TARGET_WATCHOS
-    CGRect mainScreen = [[WKInterfaceDevice currentDevice] screenBounds];
-    CGFloat screenScale = [[WKInterfaceDevice currentDevice] screenScale];
-#else
-    CGRect mainScreen = [[NSScreen mainScreen] frame];
-    CGFloat screenScale = [[NSScreen mainScreen] backingScaleFactor];
-#endif
-    CGFloat screenWidth = mainScreen.size.width * screenScale;
-    CGFloat screenHeight = mainScreen.size.height * screenScale;
-    NSString *res = [NSString stringWithFormat:@"%.0fx%.0f", screenWidth, screenHeight];
-    return res;
-}
-
-+ (NSString *) getViewPort {
-    // This probably doesn't change as well
-    return [self getResolution];
-}
-
-+ (NSString *) getAppId {
-    return [[NSBundle mainBundle] bundleIdentifier];
-}
-
-+ (NSString *)urlEncodeString:(NSString *)string {
-    if (!string) {
-        return @"";   
-    }
-    NSMutableCharacterSet *allowedCharSet = [NSCharacterSet URLQueryAllowedCharacterSet].mutableCopy;
-    [allowedCharSet removeCharactersInString:@"!*'\"();:@&=+$,/?%#[]% "];
-    return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharSet];
-}
-
-+ (NSString *)urlEncodeDictionary:(NSDictionary *)d {
-    NSMutableArray *keyValuePairs = [NSMutableArray arrayWithCapacity:d.count];
-    [d enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
-        [keyValuePairs addObject:[NSString stringWithFormat:@"%@=%@", [self urlEncodeString:key], [self urlEncodeString:[value description]]]];
-    }];
-    return [keyValuePairs componentsJoinedByString:@"&"];
-}
-
-+ (void) checkArgument:(BOOL)argument withMessage:(NSString *)message {
-    if (!argument) {
-        SPLogDebug(@"Error occurred while checking argument: %@", message);
-         #if DEBUG
-            @throw [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-         #endif
-    }
-}
-
-+ (NSDictionary *) removeNullValuesFromDictWithDict:(NSDictionary *)dict {
-    NSMutableDictionary *cleanDictionary = [NSMutableDictionary dictionary];
-    for (NSString * key in [dict allKeys]) {
-        if (![[dict objectForKey:key] isKindOfClass:[NSNull class]]) {
-            [cleanDictionary setObject:[dict objectForKey:key] forKey:key];
-        }
-    }
-    return cleanDictionary;
-}
-
-+ (NSDictionary *) replaceHyphenatedKeysWithCamelcase:(NSDictionary *)dict{
-    NSMutableDictionary * newDictionary = [[NSMutableDictionary alloc] init];
-    for (NSString * key in dict) {
-        if ([self string:key contains:@"-"]) {
-            if ([dict[key] isKindOfClass:[NSDictionary class]]) {
-                newDictionary[[self camelcaseParsedKey:key]] = [self replaceHyphenatedKeysWithCamelcase:dict[key]];
-            } else {
-                newDictionary[[self camelcaseParsedKey:key]] = dict[key];
-            }
-        } else {
-            if ([dict[key] isKindOfClass:[NSDictionary class]]) {
-                newDictionary[key] = [self replaceHyphenatedKeysWithCamelcase:dict[key]];
-            } else {
-                newDictionary[key] = dict[key];
-            }
-        }
-    }
-    
-    return [[NSDictionary alloc] initWithDictionary:newDictionary copyItems:YES];
-}
-
-+ (BOOL) string:(NSString *)string contains:(NSString *)subString {
-    if (!subString) return false;
-    if (@available(macOS 10.10, *)) {
-        return [string containsString:subString];
-    } else {
-        return ([string rangeOfString:subString].location != NSNotFound);
-    }
-}
-
-+ (NSString *) camelcaseParsedKey:(NSString *)key {
-    NSScanner * scanner = [[NSScanner alloc] initWithString:key];
-    NSMutableArray * words = [[NSMutableArray alloc] init];
-    NSString * scannedWord = [[NSString alloc] init];
-
-    while (![scanner isAtEnd]) {
-        [scanner scanUpToString:@"-" intoString:&scannedWord];
-        [words addObject:scannedWord];
-        SPLogVerbose(@"scanned word: %@", scannedWord);
-        [scanner scanString:@"-" intoString:nil];
-    }
-
-    SPLogVerbose(@"%@", words);
-    if ([words count] == 0) {
-        return @"";
-    } else if ([words count] == 1) {
-        return [[NSString alloc] initWithString:[words[0] lowercaseString]];
-    } else {
-        NSMutableString * camelcaseKey = [[NSMutableString alloc] initWithString:[words[0] lowercaseString]];
-        NSRange range;
-        range.length = words.count-1;
-        range.location = 1;
-        for (NSString * word in [words subarrayWithRange:range]) {
-            [camelcaseKey appendString:[word capitalizedString]];
-        }
-        return camelcaseKey;
-    }
-}
-
-+ (NSString *) validateString:(NSString *)aString {
-    if (!aString | ([aString length] == 0)) {
-        return nil;
-    }
-    return aString;
-}
-
-+ (SPSelfDescribingJson *) getApplicationContext {
-    NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"];
-    NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
-    return [self getApplicationContextWithVersion:version andBuild:build];
-}
-
-+ (SPSelfDescribingJson *) getApplicationContextWithVersion:(NSString *)version andBuild:(NSString *)build {
-    SPPayload * payload = [[SPPayload alloc] init];
-    [payload addValueToPayload:build forKey:kSPApplicationBuild];
-    [payload addValueToPayload:version forKey:kSPApplicationVersion];
-    if (payload != nil && [[payload getAsDictionary] count] > 0) {
-        return [[SPSelfDescribingJson alloc] initWithSchema:kSPApplicationContextSchema andPayload:payload];
-    } else {
-        return nil;
-    }
-}
-
-+ (NSString *) getAppVersion {
-    return [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"];
-}
-
-+ (NSString *) getAppBuild {
-    return [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
-}
-
-@end
diff --git a/Snowplow/Internal/Utils/SPWeakTimerTarget.h b/Snowplow/Internal/Utils/SPWeakTimerTarget.h
deleted file mode 100644
index 2fd069472..000000000
--- a/Snowplow/Internal/Utils/SPWeakTimerTarget.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  SPWeakTimer.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import <Foundation/Foundation.h>
-
-@interface SPWeakTimerTarget : NSObject
-
-/**
- * Creates a new WeakTarget object which simply prevents circular retention when using things like NSTimer.
- * @param target The parent object to store
- */
-- (id)initWithTarget:(id)target andSelector:(SEL)selector;
-
-/**
- * Fires the timer method in the target object.
- * @param timer The timer object passed from the parent target
- */
-- (void)timerFired:(NSTimer *)timer;
-
-@end
diff --git a/Snowplow/Internal/Utils/SPWeakTimerTarget.m b/Snowplow/Internal/Utils/SPWeakTimerTarget.m
deleted file mode 100644
index bcbdc890f..000000000
--- a/Snowplow/Internal/Utils/SPWeakTimerTarget.m
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-//  SPWeakTimer.m
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
-
-#import "SPWeakTimerTarget.h"
-#import "SPEmitter.h"
-
-@implementation SPWeakTimerTarget {
-    __weak id _target;
-    SEL       _selector;
-}
-
-- (id)initWithTarget:(id)target andSelector:(SEL)selector {
-    self = [super init];
-    if (self) {
-        _target = target;
-        _selector = selector;
-    }
-    return self;
-}
-
-- (void)timerFired:(NSTimer *)timer {
-    if (_target) {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
-        [_target performSelector:_selector withObject:timer];
-#pragma clang diagnostic pop
-    } else {
-        [timer invalidate];
-    }
-}
-
-@end
diff --git a/Snowplow/Snowplow-umbrella-header.h b/Snowplow/Snowplow-umbrella-header.h
deleted file mode 100644
index 8c759a109..000000000
--- a/Snowplow/Snowplow-umbrella-header.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#import "SPSnowplow.h"
-#import "SPTrackerConstants.h"
-#import "SPLoggerDelegate.h"
-#import "SPPayload.h"
-#import "SPSelfDescribingJson.h"
-#import "SPDevicePlatform.h"
-
-// Configurations
-#import "SPConfiguration.h"
-#import "SPRemoteConfiguration.h"
-#import "SPTrackerConfiguration.h"
-#import "SPNetworkConfiguration.h"
-#import "SPSubjectConfiguration.h"
-#import "SPSessionConfiguration.h"
-#import "SPEmitterConfiguration.h"
-#import "SPGDPRConfiguration.h"
-#import "SPGlobalContextsConfiguration.h"
-#import "SPConfigurationBundle.h"
-
-// Controllers
-#import "SPTrackerController.h"
-#import "SPSessionController.h"
-#import "SPSubjectController.h"
-#import "SPNetworkController.h"
-#import "SPEmitterController.h"
-#import "SPGDPRController.h"
-#import "SPGlobalContextsController.h"
-
-// NetworkConnection
-#import "SPNetworkConnection.h"
-#import "SPDefaultNetworkConnection.h"
-
-// EventStore
-#import "SPEventStore.h"
-#import "SPSQLiteEventStore.h"
-#import "SPMemoryEventStore.h"
-
-// Emitter
-#import "SPRequest.h"
-#import "SPRequestResult.h"
-#import "SPEmitterEvent.h"
-#import "SPRequestCallback.h"
-
-// Events
-#import "SPEventBase.h"
-#import "SPPageView.h"
-#import "SPStructured.h"
-#import "SPSelfDescribing.h"
-#import "SPScreenView.h"
-#import "SPConsentWithdrawn.h"
-#import "SPConsentDocument.h"
-#import "SPConsentGranted.h"
-#import "SPDeepLinkReceived.h"
-#import "SPTiming.h"
-#import "SPEcommerce.h"
-#import "SPEcommerceItem.h"
-#import "SPPushNotification.h"
-#import "SPForeground.h"
-#import "SPBackground.h"
-#import "SNOWError.h"
-#import "SPMessageNotification.h"
-#import "SPMessageNotificationAttachment.h"
-
-// Entities
-#import "SPDeepLinkEntity.h"
-#import "SPLifecycleEntity.h"
-
-// Global Contexts and State Management
-#import "SPGlobalContext.h"
-#import "SPSchemaRuleset.h"
-#import "SPSchemaRule.h"
-#import "SPTrackerStateSnapshot.h"
-#import "SPState.h"
-#import "SPSessionState.h"
diff --git a/Snowplow/buildPhaseScript/copy_public_headers.sh b/Snowplow/buildPhaseScript/copy_public_headers.sh
deleted file mode 100644
index a595e3668..000000000
--- a/Snowplow/buildPhaseScript/copy_public_headers.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-
-function create_link {
-    # 1. Store the first parameter to the script in a variable.
-    file_name="$1"
-
-    # 2. Now that we expect a parameter, lets check if it was provided.
-    if [[ -z "$file_name" ]]; then
-        echo "Script expects a parameter"
-        exit 1
-    fi
-
-    # 3. Create the symlink
-    pushd $SRCROOT/Snowplow/include
-    path=$(find ../Internal -name $file_name)
-    ln -vs $path .
-    popd
-}
-
-function check_link {
-    # 1. Store the first parameter to the script in a variable.
-    path="$1"
-
-    # 2. Now that we expect a parameter, lets check if it was provided.
-    if [[ -z "$path" ]]; then
-        echo "Script expects a parameter"
-        exit 1
-    fi
-
-    # 3. Check if the parameter is a symlink.
-    if [[ -L "$path" ]]; then
-        # 4. Check if it links to a valid path.
-        if [[ -e "$path" ]]; then
-            echo "$path is a valid link!"
-        else
-            unlink "$path"
-            echo "Cleaned up broken link: $path"
-        fi
-    else
-        echo "$path is not a symlink."
-    fi
-}
-
-for i in `ls $TARGET_BUILD_DIR/$PUBLIC_HEADERS_FOLDER_PATH`; do create_link $i ; done
-
-for i in $SRCROOT/Snowplow/include/*.h; do check_link $i ; done 
diff --git a/Snowplow/include/SNOWError.h b/Snowplow/include/SNOWError.h
deleted file mode 120000
index b5ba2eab5..000000000
--- a/Snowplow/include/SNOWError.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SNOWError.h
\ No newline at end of file
diff --git a/Snowplow/include/SPBackground.h b/Snowplow/include/SPBackground.h
deleted file mode 120000
index 667929f4a..000000000
--- a/Snowplow/include/SPBackground.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPBackground.h
\ No newline at end of file
diff --git a/Snowplow/include/SPConfiguration.h b/Snowplow/include/SPConfiguration.h
deleted file mode 120000
index 4b3e49037..000000000
--- a/Snowplow/include/SPConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPConfigurationBundle.h b/Snowplow/include/SPConfigurationBundle.h
deleted file mode 120000
index 4439adb32..000000000
--- a/Snowplow/include/SPConfigurationBundle.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPConfigurationBundle.h
\ No newline at end of file
diff --git a/Snowplow/include/SPConfigurationState.h b/Snowplow/include/SPConfigurationState.h
deleted file mode 120000
index 972cadf3e..000000000
--- a/Snowplow/include/SPConfigurationState.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/RemoteConfiguration/SPConfigurationState.h
\ No newline at end of file
diff --git a/Snowplow/include/SPConsentDocument.h b/Snowplow/include/SPConsentDocument.h
deleted file mode 120000
index a727c46ca..000000000
--- a/Snowplow/include/SPConsentDocument.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPConsentDocument.h
\ No newline at end of file
diff --git a/Snowplow/include/SPConsentGranted.h b/Snowplow/include/SPConsentGranted.h
deleted file mode 120000
index fc4daad77..000000000
--- a/Snowplow/include/SPConsentGranted.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPConsentGranted.h
\ No newline at end of file
diff --git a/Snowplow/include/SPConsentWithdrawn.h b/Snowplow/include/SPConsentWithdrawn.h
deleted file mode 120000
index 14a0fa8b2..000000000
--- a/Snowplow/include/SPConsentWithdrawn.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPConsentWithdrawn.h
\ No newline at end of file
diff --git a/Snowplow/include/SPDeepLinkEntity.h b/Snowplow/include/SPDeepLinkEntity.h
deleted file mode 120000
index c2a2acea1..000000000
--- a/Snowplow/include/SPDeepLinkEntity.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Entities/SPDeepLinkEntity.h
\ No newline at end of file
diff --git a/Snowplow/include/SPDeepLinkReceived.h b/Snowplow/include/SPDeepLinkReceived.h
deleted file mode 120000
index 3683cb766..000000000
--- a/Snowplow/include/SPDeepLinkReceived.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPDeepLinkReceived.h
\ No newline at end of file
diff --git a/Snowplow/include/SPDefaultNetworkConnection.h b/Snowplow/include/SPDefaultNetworkConnection.h
deleted file mode 120000
index c6b2809a5..000000000
--- a/Snowplow/include/SPDefaultNetworkConnection.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/NetworkConnection/SPDefaultNetworkConnection.h
\ No newline at end of file
diff --git a/Snowplow/include/SPDevicePlatform.h b/Snowplow/include/SPDevicePlatform.h
deleted file mode 120000
index 2a1b81c86..000000000
--- a/Snowplow/include/SPDevicePlatform.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Subject/SPDevicePlatform.h
\ No newline at end of file
diff --git a/Snowplow/include/SPEcommerce.h b/Snowplow/include/SPEcommerce.h
deleted file mode 120000
index bf4c48e3f..000000000
--- a/Snowplow/include/SPEcommerce.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPEcommerce.h
\ No newline at end of file
diff --git a/Snowplow/include/SPEcommerceItem.h b/Snowplow/include/SPEcommerceItem.h
deleted file mode 120000
index 6f1771f9d..000000000
--- a/Snowplow/include/SPEcommerceItem.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPEcommerceItem.h
\ No newline at end of file
diff --git a/Snowplow/include/SPEmitterConfiguration.h b/Snowplow/include/SPEmitterConfiguration.h
deleted file mode 120000
index 2d38832d5..000000000
--- a/Snowplow/include/SPEmitterConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPEmitterConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPEmitterController.h b/Snowplow/include/SPEmitterController.h
deleted file mode 120000
index 0d77c5cda..000000000
--- a/Snowplow/include/SPEmitterController.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Emitter/SPEmitterController.h
\ No newline at end of file
diff --git a/Snowplow/include/SPEmitterEvent.h b/Snowplow/include/SPEmitterEvent.h
deleted file mode 120000
index 78c23c576..000000000
--- a/Snowplow/include/SPEmitterEvent.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Emitter/SPEmitterEvent.h
\ No newline at end of file
diff --git a/Snowplow/include/SPEventBase.h b/Snowplow/include/SPEventBase.h
deleted file mode 120000
index 2cd1aafd9..000000000
--- a/Snowplow/include/SPEventBase.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPEventBase.h
\ No newline at end of file
diff --git a/Snowplow/include/SPEventStore.h b/Snowplow/include/SPEventStore.h
deleted file mode 120000
index 527ba51a4..000000000
--- a/Snowplow/include/SPEventStore.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Storage/SPEventStore.h
\ No newline at end of file
diff --git a/Snowplow/include/SPForeground.h b/Snowplow/include/SPForeground.h
deleted file mode 120000
index 2b9494a06..000000000
--- a/Snowplow/include/SPForeground.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPForeground.h
\ No newline at end of file
diff --git a/Snowplow/include/SPGDPRConfiguration.h b/Snowplow/include/SPGDPRConfiguration.h
deleted file mode 120000
index 11b178ccc..000000000
--- a/Snowplow/include/SPGDPRConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPGDPRConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPGDPRController.h b/Snowplow/include/SPGDPRController.h
deleted file mode 120000
index 3356bf874..000000000
--- a/Snowplow/include/SPGDPRController.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/GDPR/SPGDPRController.h
\ No newline at end of file
diff --git a/Snowplow/include/SPGlobalContext.h b/Snowplow/include/SPGlobalContext.h
deleted file mode 120000
index fafbda403..000000000
--- a/Snowplow/include/SPGlobalContext.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/GlobalContexts/SPGlobalContext.h
\ No newline at end of file
diff --git a/Snowplow/include/SPGlobalContextsConfiguration.h b/Snowplow/include/SPGlobalContextsConfiguration.h
deleted file mode 120000
index f69d5ef20..000000000
--- a/Snowplow/include/SPGlobalContextsConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPGlobalContextsConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPGlobalContextsController.h b/Snowplow/include/SPGlobalContextsController.h
deleted file mode 120000
index 507fcb270..000000000
--- a/Snowplow/include/SPGlobalContextsController.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/GlobalContexts/SPGlobalContextsController.h
\ No newline at end of file
diff --git a/Snowplow/include/SPLifecycleEntity.h b/Snowplow/include/SPLifecycleEntity.h
deleted file mode 120000
index 53c25d46f..000000000
--- a/Snowplow/include/SPLifecycleEntity.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Entities/SPLifecycleEntity.h
\ No newline at end of file
diff --git a/Snowplow/include/SPLoggerDelegate.h b/Snowplow/include/SPLoggerDelegate.h
deleted file mode 120000
index e63c7c39c..000000000
--- a/Snowplow/include/SPLoggerDelegate.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Logger/SPLoggerDelegate.h
\ No newline at end of file
diff --git a/Snowplow/include/SPMemoryEventStore.h b/Snowplow/include/SPMemoryEventStore.h
deleted file mode 120000
index cbb8c0d7b..000000000
--- a/Snowplow/include/SPMemoryEventStore.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Storage/SPMemoryEventStore.h
\ No newline at end of file
diff --git a/Snowplow/include/SPMessageNotification.h b/Snowplow/include/SPMessageNotification.h
deleted file mode 120000
index 17bbe5501..000000000
--- a/Snowplow/include/SPMessageNotification.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPMessageNotification.h
\ No newline at end of file
diff --git a/Snowplow/include/SPMessageNotificationAttachment.h b/Snowplow/include/SPMessageNotificationAttachment.h
deleted file mode 120000
index a7dc1d81c..000000000
--- a/Snowplow/include/SPMessageNotificationAttachment.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPMessageNotificationAttachment.h
\ No newline at end of file
diff --git a/Snowplow/include/SPNetworkConfiguration.h b/Snowplow/include/SPNetworkConfiguration.h
deleted file mode 120000
index 82b9bcede..000000000
--- a/Snowplow/include/SPNetworkConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPNetworkConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPNetworkConnection.h b/Snowplow/include/SPNetworkConnection.h
deleted file mode 120000
index 2ec679238..000000000
--- a/Snowplow/include/SPNetworkConnection.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/NetworkConnection/SPNetworkConnection.h
\ No newline at end of file
diff --git a/Snowplow/include/SPNetworkController.h b/Snowplow/include/SPNetworkController.h
deleted file mode 120000
index 7309326ec..000000000
--- a/Snowplow/include/SPNetworkController.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/NetworkConnection/SPNetworkController.h
\ No newline at end of file
diff --git a/Snowplow/include/SPPageView.h b/Snowplow/include/SPPageView.h
deleted file mode 120000
index c38b49a00..000000000
--- a/Snowplow/include/SPPageView.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPPageView.h
\ No newline at end of file
diff --git a/Snowplow/include/SPPayload.h b/Snowplow/include/SPPayload.h
deleted file mode 120000
index 5c5b53ece..000000000
--- a/Snowplow/include/SPPayload.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Payload/SPPayload.h
\ No newline at end of file
diff --git a/Snowplow/include/SPPushNotification.h b/Snowplow/include/SPPushNotification.h
deleted file mode 120000
index 170be71b3..000000000
--- a/Snowplow/include/SPPushNotification.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPPushNotification.h
\ No newline at end of file
diff --git a/Snowplow/include/SPRemoteConfiguration.h b/Snowplow/include/SPRemoteConfiguration.h
deleted file mode 120000
index 868d311de..000000000
--- a/Snowplow/include/SPRemoteConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPRemoteConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPRequest.h b/Snowplow/include/SPRequest.h
deleted file mode 120000
index 15ba5dc9d..000000000
--- a/Snowplow/include/SPRequest.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Emitter/SPRequest.h
\ No newline at end of file
diff --git a/Snowplow/include/SPRequestCallback.h b/Snowplow/include/SPRequestCallback.h
deleted file mode 120000
index ca55290a6..000000000
--- a/Snowplow/include/SPRequestCallback.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Emitter/SPRequestCallback.h
\ No newline at end of file
diff --git a/Snowplow/include/SPRequestResult.h b/Snowplow/include/SPRequestResult.h
deleted file mode 120000
index 747b4acb3..000000000
--- a/Snowplow/include/SPRequestResult.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Emitter/SPRequestResult.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSQLiteEventStore.h b/Snowplow/include/SPSQLiteEventStore.h
deleted file mode 120000
index b2c288b14..000000000
--- a/Snowplow/include/SPSQLiteEventStore.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Storage/SPSQLiteEventStore.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSchemaRule.h b/Snowplow/include/SPSchemaRule.h
deleted file mode 120000
index 29ea69313..000000000
--- a/Snowplow/include/SPSchemaRule.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/GlobalContexts/SPSchemaRule.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSchemaRuleset.h b/Snowplow/include/SPSchemaRuleset.h
deleted file mode 120000
index 65ec5fea6..000000000
--- a/Snowplow/include/SPSchemaRuleset.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/GlobalContexts/SPSchemaRuleset.h
\ No newline at end of file
diff --git a/Snowplow/include/SPScreenView.h b/Snowplow/include/SPScreenView.h
deleted file mode 120000
index e04dd1bf1..000000000
--- a/Snowplow/include/SPScreenView.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPScreenView.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSelfDescribing.h b/Snowplow/include/SPSelfDescribing.h
deleted file mode 120000
index e1103fcfa..000000000
--- a/Snowplow/include/SPSelfDescribing.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPSelfDescribing.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSelfDescribingJson.h b/Snowplow/include/SPSelfDescribingJson.h
deleted file mode 120000
index 065575b79..000000000
--- a/Snowplow/include/SPSelfDescribingJson.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Payload/SPSelfDescribingJson.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSessionConfiguration.h b/Snowplow/include/SPSessionConfiguration.h
deleted file mode 120000
index 695aeb840..000000000
--- a/Snowplow/include/SPSessionConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPSessionConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSessionController.h b/Snowplow/include/SPSessionController.h
deleted file mode 120000
index e3423beca..000000000
--- a/Snowplow/include/SPSessionController.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Session/SPSessionController.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSessionState.h b/Snowplow/include/SPSessionState.h
deleted file mode 120000
index 536eec708..000000000
--- a/Snowplow/include/SPSessionState.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Session/SPSessionState.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSnowplow.h b/Snowplow/include/SPSnowplow.h
deleted file mode 120000
index 289dfbf02..000000000
--- a/Snowplow/include/SPSnowplow.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/SPSnowplow.h
\ No newline at end of file
diff --git a/Snowplow/include/SPState.h b/Snowplow/include/SPState.h
deleted file mode 120000
index a35405293..000000000
--- a/Snowplow/include/SPState.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Tracker/SPState.h
\ No newline at end of file
diff --git a/Snowplow/include/SPStructured.h b/Snowplow/include/SPStructured.h
deleted file mode 120000
index a1a6f9fb9..000000000
--- a/Snowplow/include/SPStructured.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPStructured.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSubjectConfiguration.h b/Snowplow/include/SPSubjectConfiguration.h
deleted file mode 120000
index 23c631439..000000000
--- a/Snowplow/include/SPSubjectConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPSubjectConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPSubjectController.h b/Snowplow/include/SPSubjectController.h
deleted file mode 120000
index b37f597de..000000000
--- a/Snowplow/include/SPSubjectController.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Subject/SPSubjectController.h
\ No newline at end of file
diff --git a/Snowplow/include/SPTiming.h b/Snowplow/include/SPTiming.h
deleted file mode 120000
index 323466557..000000000
--- a/Snowplow/include/SPTiming.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Events/SPTiming.h
\ No newline at end of file
diff --git a/Snowplow/include/SPTrackerConfiguration.h b/Snowplow/include/SPTrackerConfiguration.h
deleted file mode 120000
index 6fbc4d72b..000000000
--- a/Snowplow/include/SPTrackerConfiguration.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Configurations/SPTrackerConfiguration.h
\ No newline at end of file
diff --git a/Snowplow/include/SPTrackerConstants.h b/Snowplow/include/SPTrackerConstants.h
deleted file mode 120000
index 6df2c421d..000000000
--- a/Snowplow/include/SPTrackerConstants.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/SPTrackerConstants.h
\ No newline at end of file
diff --git a/Snowplow/include/SPTrackerController.h b/Snowplow/include/SPTrackerController.h
deleted file mode 120000
index 382500e9e..000000000
--- a/Snowplow/include/SPTrackerController.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Tracker/SPTrackerController.h
\ No newline at end of file
diff --git a/Snowplow/include/SPTrackerStateSnapshot.h b/Snowplow/include/SPTrackerStateSnapshot.h
deleted file mode 120000
index 124287c72..000000000
--- a/Snowplow/include/SPTrackerStateSnapshot.h
+++ /dev/null
@@ -1 +0,0 @@
-../Internal/Tracker/SPTrackerStateSnapshot.h
\ No newline at end of file
diff --git a/Snowplow/ios.modulemap b/Snowplow/ios.modulemap
deleted file mode 100644
index ce7c45a1b..000000000
--- a/Snowplow/ios.modulemap
+++ /dev/null
@@ -1,5 +0,0 @@
-framework module SnowplowTracker {
-    umbrella header "Snowplow-umbrella-header.h"
-    
-    export *
-}
diff --git a/Snowplow/watchos.modulemap b/Snowplow/watchos.modulemap
deleted file mode 100644
index ce7c45a1b..000000000
--- a/Snowplow/watchos.modulemap
+++ /dev/null
@@ -1,5 +0,0 @@
-framework module SnowplowTracker {
-    umbrella header "Snowplow-umbrella-header.h"
-    
-    export *
-}
diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec
index c77bdabd5..87c0c40a3 100644
--- a/SnowplowTracker.podspec
+++ b/SnowplowTracker.podspec
@@ -1,103 +1,31 @@
 Pod::Spec.new do |s|
-  s.name             = "SnowplowTracker"
-  s.version          = "4.1.0"
-  s.summary          = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
-  s.description      = <<-DESC
-  Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
-                       DESC
-  s.homepage         = "http://snowplow.io"
-  s.screenshots      = "https://d3i6fms1cm1j0i.cloudfront.net/github-wiki/images/snowplow-logo-large.png"
-  s.license          = 'Apache License, Version 2.0'
-  s.author           = { "Snowplow Analytics Ltd" => "support@snowplow.io" }
-  s.source           = { :git => "https://github.com/snowplow/snowplow-objc-tracker.git", :tag => s.version.to_s }
-  s.social_media_url = 'https://twitter.com/SnowPlowData'
-  s.documentation_url	= 'https://github.com/snowplow/snowplow/wiki/iOS-Tracker'
-
-  s.swift_version = '5.0'
-  s.ios.deployment_target = '9.0'
-  s.osx.deployment_target = '10.10'
-  s.tvos.deployment_target = '9.0'
-  s.watchos.deployment_target = '2.0'
-
-  s.requires_arc = true
-
-  s.source_files = 'Snowplow/**/*.{m,h}'
-  s.exclude_files = 'Snowplow/include/*.{m,h}'
-  s.public_header_files = [
-    'Snowplow/Internal/**/SPSnowplow.h',
-    'Snowplow/Internal/**/SPTrackerConstants.h',
-    'Snowplow/Internal/**/SPLoggerDelegate.h',
-    'Snowplow/Internal/**/SPPayload.h',
-    'Snowplow/Internal/**/SPSelfDescribingJson.h',
-    'Snowplow/Internal/**/SPDevicePlatform.h',
-    'Snowplow/Internal/**/SPConfiguration.h',
-    'Snowplow/Internal/**/SPRemoteConfiguration.h',
-    'Snowplow/Internal/**/SPTrackerConfiguration.h',
-    'Snowplow/Internal/**/SPNetworkConfiguration.h',
-    'Snowplow/Internal/**/SPSubjectConfiguration.h',
-    'Snowplow/Internal/**/SPSessionConfiguration.h',
-    'Snowplow/Internal/**/SPEmitterConfiguration.h',
-    'Snowplow/Internal/**/SPGDPRConfiguration.h',
-    'Snowplow/Internal/**/SPGlobalContextsConfiguration.h',
-    'Snowplow/Internal/**/SPConfigurationBundle.h',
-    'Snowplow/Internal/**/SPTrackerController.h',
-    'Snowplow/Internal/**/SPSessionController.h',
-    'Snowplow/Internal/**/SPSubjectController.h',
-    'Snowplow/Internal/**/SPNetworkController.h',
-    'Snowplow/Internal/**/SPEmitterController.h',
-    'Snowplow/Internal/**/SPGDPRController.h',
-    'Snowplow/Internal/**/SPGlobalContextsController.h',
-    'Snowplow/Internal/**/SPNetworkConnection.h',
-    'Snowplow/Internal/**/SPDefaultNetworkConnection.h',
-    'Snowplow/Internal/**/SPEventStore.h',
-    'Snowplow/Internal/**/SPSQLiteEventStore.h',
-    'Snowplow/Internal/**/SPMemoryEventStore.h',
-    'Snowplow/Internal/**/SPRequest.h',
-    'Snowplow/Internal/**/SPRequestResult.h',
-    'Snowplow/Internal/**/SPEmitterEvent.h',
-    'Snowplow/Internal/**/SPRequestCallback.h',
-    'Snowplow/Internal/**/SPEventBase.h',
-    'Snowplow/Internal/**/SPPageView.h',
-    'Snowplow/Internal/**/SPStructured.h',
-    'Snowplow/Internal/**/SPSelfDescribing.h',
-    'Snowplow/Internal/**/SPScreenView.h',
-    'Snowplow/Internal/**/SPConsentWithdrawn.h',
-    'Snowplow/Internal/**/SPConsentDocument.h',
-    'Snowplow/Internal/**/SPConsentGranted.h',
-    'Snowplow/Internal/**/SPDeepLinkReceived.h',
-    'Snowplow/Internal/**/SPTiming.h',
-    'Snowplow/Internal/**/SPEcommerce.h',
-    'Snowplow/Internal/**/SPEcommerceItem.h',
-    'Snowplow/Internal/**/SPPushNotification.h',
-    'Snowplow/Internal/**/SPForeground.h',
-    'Snowplow/Internal/**/SPBackground.h',
-    'Snowplow/Internal/**/SNOWError.h',
-    'Snowplow/Internal/**/SPMessageNotification.h',
-    'Snowplow/Internal/**/SPMessageNotificationAttachment.h',
-    'Snowplow/Internal/**/SPDeepLinkEntity.h',
-    'Snowplow/Internal/**/SPLifecycleEntity.h',
-    'Snowplow/Internal/**/SPGlobalContext.h',
-    'Snowplow/Internal/**/SPSchemaRuleset.h',
-    'Snowplow/Internal/**/SPSchemaRule.h',
-    'Snowplow/Internal/**/SPTrackerStateSnapshot.h',
-    'Snowplow/Internal/**/SPState.h',
-    'Snowplow/Internal/**/SPSessionState.h',
-    'Snowplow/Internal/**/SPConfigurationState.h'
-  ]
-
-  s.osx.exclude_files = 'Snowplow/**/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.*'
-  s.tvos.exclude_files = 'Snowplow/**/ScreenViewTracking/UIViewController+SPScreenView_SWIZZLE.*'
-  s.watchos.exclude_files = [
-    'Snowplow/**/SNOWReachability.*',
-    'Snowplow/**/UIViewController+SPScreenView_SWIZZLE.*'
-  ]
-
-  s.ios.frameworks = 'CoreTelephony', 'UIKit', 'Foundation'
-  s.osx.frameworks = 'AppKit', 'Foundation'
-  s.tvos.frameworks = 'UIKit', 'Foundation'
-
-  s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }
-
-  s.dependency 'FMDB', '~> 2.7'
-end
-
+    s.name             = "SnowplowTracker"
+    s.version          = "4.1.0"
+    s.summary          = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
+    s.description      = <<-DESC
+    Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
+                         DESC
+    s.homepage         = "http://snowplow.io"
+    s.screenshots      = "https://d3i6fms1cm1j0i.cloudfront.net/github-wiki/images/snowplow-logo-large.png"
+    s.license          = 'Apache License, Version 2.0'
+    s.author           = { "Snowplow Analytics Ltd" => "support@snowplow.io" }
+    s.source           = { :git => "https://github.com/snowplow/snowplow-objc-tracker.git", :tag => s.version.to_s }
+    s.social_media_url = 'https://twitter.com/SnowPlowData'
+    s.documentation_url	= 'https://github.com/snowplow/snowplow/wiki/iOS-Tracker'
+  
+    s.swift_version = '5.0'
+    s.ios.deployment_target = '11.0'
+    s.osx.deployment_target = '10.13'
+    s.tvos.deployment_target = '11.0'
+    s.watchos.deployment_target = '4.0'
+  
+    s.source_files = 'Sources/**/*.swift'
+  
+    s.ios.frameworks = 'CoreTelephony', 'UIKit', 'Foundation'
+    s.osx.frameworks = 'AppKit', 'Foundation'
+    s.tvos.frameworks = 'UIKit', 'Foundation'
+  
+    s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }
+  
+    s.dependency 'FMDB', '~> 2.7'
+  end
diff --git a/Sources/Core/Emitter/Emitter.swift b/Sources/Core/Emitter/Emitter.swift
new file mode 100644
index 000000000..915de3b9b
--- /dev/null
+++ b/Sources/Core/Emitter/Emitter.swift
@@ -0,0 +1,539 @@
+//
+//  Emitter.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// This class sends events to the collector.
+let POST_WRAPPER_BYTES = 88
+
+class Emitter: NSObject, EmitterEventProcessing {
+    
+    private var timer: Timer?
+    /// Whether the emitter is currently sending.
+    private(set) var isSending = false
+    private var dataOperationQueue: OperationQueue = OperationQueue()
+    private var builderFinished = false
+
+    private var pausedEmit = false
+
+    private var _urlEndpoint: String?
+    /// Collector endpoint.
+    var urlEndpoint: String? {
+        get {
+            if builderFinished {
+                return networkConnection?.urlEndpoint?.absoluteString
+            }
+            return _urlEndpoint
+        }
+        set {
+            _urlEndpoint = newValue
+            if builderFinished {
+                setupNetworkConnection()
+            }
+        }
+    }
+
+    private var _namespace: String?
+    var namespace: String? {
+        get {
+            return _namespace
+        }
+        set(namespace) {
+            _namespace = namespace
+            if builderFinished && eventStore == nil {
+                #if os(tvOS) || os(watchOS)
+                eventStore = MemoryEventStore()
+                #else
+                eventStore = SQLiteEventStore(namespace: _namespace)
+                #endif
+            }
+        }
+    }
+
+    private var _method: HttpMethodOptions = EmitterDefaults.httpMethod
+    /// Chosen HTTP method - .get or .post.
+    var method: HttpMethodOptions {
+        get {
+            return _method
+        }
+        set(method) {
+            _method = method
+            if builderFinished && networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+    
+    private var _protocol: ProtocolOptions = EmitterDefaults.httpProtocol
+    /// Security of requests - ProtocolHttp or ProtocolHttps.
+    var `protocol`: ProtocolOptions {
+        get {
+            return _protocol
+        }
+        set(`protocol`) {
+            _protocol = `protocol`
+            if builderFinished && networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+    private var _bufferOption: BufferOption = EmitterDefaults.bufferOption
+    /// Buffer option
+    var bufferOption: BufferOption {
+        get {
+            return _bufferOption
+        }
+        set(bufferOption) {
+            if !isSending {
+                _bufferOption = bufferOption
+            }
+        }
+    }
+    
+    private weak var _callback: RequestCallback?
+    /// Callbacks supplied with number of failures and successes of sent events.
+    var callback: RequestCallback? {
+        get {
+            return _callback
+        }
+        set(callback) {
+            _callback = callback
+        }
+    }
+    
+    private var _emitRange = EmitterDefaults.emitRange
+    /// Number of events retrieved from the database when needed.
+    var emitRange: Int {
+        get {
+            return _emitRange
+        }
+        set(emitRange) {
+            if emitRange > 0 {
+                _emitRange = emitRange
+            }
+        }
+    }
+    
+    private var _emitThreadPoolSize = EmitterDefaults.emitThreadPoolSize
+    /// Number of threads used for emitting events.
+    var emitThreadPoolSize: Int {
+        get {
+            return _emitThreadPoolSize
+        }
+        set(emitThreadPoolSize) {
+            if emitThreadPoolSize > 0 {
+                _emitThreadPoolSize = emitThreadPoolSize
+                if dataOperationQueue.maxConcurrentOperationCount != emitThreadPoolSize {
+                    dataOperationQueue.maxConcurrentOperationCount = _emitThreadPoolSize
+                }
+                if builderFinished && networkConnection != nil {
+                    setupNetworkConnection()
+                }
+            }
+        }
+    }
+    
+    private var _byteLimitGet = EmitterDefaults.byteLimitGet
+    /// Byte limit for GET requests.
+    var byteLimitGet: Int {
+        get {
+            return _byteLimitGet
+        }
+        set(byteLimitGet) {
+            _byteLimitGet = byteLimitGet
+            if builderFinished && networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+    
+    private var _byteLimitPost = EmitterDefaults.byteLimitPost
+    /// Byte limit for POST requests.
+    var byteLimitPost: Int {
+        get {
+            return _byteLimitPost
+        }
+        set(byteLimitPost) {
+            _byteLimitPost = byteLimitPost
+            if builderFinished && networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+
+    private var _serverAnonymisation = EmitterDefaults.serverAnonymisation
+    /// Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`.
+    var serverAnonymisation: Bool {
+        get {
+            return _serverAnonymisation
+        }
+        set(serverAnonymisation) {
+            _serverAnonymisation = serverAnonymisation
+            if builderFinished && networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+
+    private var _customPostPath: String?
+    /// Custom endpoint path for POST requests.
+    var customPostPath: String? {
+        get {
+            return _customPostPath
+        }
+        set(customPath) {
+            _customPostPath = customPath
+            if builderFinished && networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+
+    /// Custom header requests.
+    private var _requestHeaders: [String : String]?
+    var requestHeaders: [String : String]? {
+        get {
+            return _requestHeaders
+        }
+        set(requestHeaders) {
+            _requestHeaders = requestHeaders
+            if builderFinished && networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+
+    private var _networkConnection: NetworkConnection?
+    /// Custom NetworkConnection istance to handle connection outside the emitter.
+    var networkConnection: NetworkConnection? {
+        get {
+            return _networkConnection
+        }
+        set(networkConnection) {
+            _networkConnection = networkConnection
+            if builderFinished && _networkConnection != nil {
+                setupNetworkConnection()
+            }
+        }
+    }
+    
+    private var _eventStore: EventStore?
+    var eventStore: EventStore? {
+        get {
+            return _eventStore
+        }
+        set(eventStore) {
+            if !builderFinished || self.eventStore == nil || self.eventStore?.count() == 0 {
+                _eventStore = eventStore
+            }
+        }
+    }
+    
+    /// Custom retry rules for HTTP status codes.
+    private var _customRetryForStatusCodes: [Int : Bool] = [:]
+    var customRetryForStatusCodes: [Int : Bool]? {
+        get {
+            return _customRetryForStatusCodes
+        }
+        set(customRetryForStatusCodes) {
+            _customRetryForStatusCodes = customRetryForStatusCodes ?? [:]
+        }
+    }
+
+    /// Returns the number of events in the DB.
+    var dbCount: Int {
+        return Int(eventStore?.count() ?? 0)
+    }
+    
+    // MARK: - Initialization
+    
+    init(urlEndpoint: String,
+         builder: ((Emitter) -> (Void))) {
+        super.init()
+        self._urlEndpoint = urlEndpoint
+        
+        builder(self)
+        setup()
+   }
+    
+    init(networkConnection: NetworkConnection,
+         builder: ((Emitter) -> (Void))) {
+        super.init()
+        self._networkConnection = networkConnection
+        
+        builder(self)
+        setup()
+    }
+
+    private func setup() {
+        dataOperationQueue.maxConcurrentOperationCount = emitThreadPoolSize
+        setupNetworkConnection()
+        resumeTimer()
+        builderFinished = true
+    }
+
+    private func setupNetworkConnection() {
+        if !builderFinished && networkConnection != nil {
+            return
+        }
+        if let url = _urlEndpoint {
+            var endpoint = "\(url)"
+            if !endpoint.hasPrefix("http") {
+                let `protocol` = self.protocol == .https ? "https://" : "http://"
+                endpoint = `protocol` + endpoint
+            }
+            let defaultNetworkConnection = DefaultNetworkConnection(
+                urlString: endpoint,
+                httpMethod: method,
+                customPostPath: customPostPath
+            )
+            defaultNetworkConnection.requestHeaders = requestHeaders
+            defaultNetworkConnection.emitThreadPoolSize = emitThreadPoolSize
+            defaultNetworkConnection.byteLimitGet = byteLimitGet
+            defaultNetworkConnection.byteLimitPost = byteLimitPost
+            defaultNetworkConnection.serverAnonymisation = serverAnonymisation
+            _networkConnection = defaultNetworkConnection
+        }
+    }
+
+    // MARK: - Pause/Resume methods
+
+    func resumeTimer() {
+        weak var weakSelf = self
+
+        if timer != nil {
+            pauseTimer()
+        }
+
+        DispatchQueue.main.async {
+            weakSelf?.timer = Timer.scheduledTimer(withTimeInterval: TimeInterval(kSPDefaultBufferTimeout), repeats: true) { [weak self] timer in
+                self?.flush()
+            }
+        }
+    }
+
+    /// Suspends timer for periodically sending events to collector.
+    func pauseTimer() {
+        timer?.invalidate()
+        timer = nil
+    }
+
+    /// Allows sending events to collector.
+    func resumeEmit() {
+        pausedEmit = false
+        flush()
+    }
+
+    /// Suspends sending events to collector.
+    func pauseEmit() {
+        pausedEmit = true
+    }
+
+    /// Insert a Payload object into the buffer to be sent to collector.
+    /// This method will add the payload to the database and flush (send all events).
+    /// - Parameter eventPayload: A Payload containing a completed event to be added into the buffer.
+    func addPayload(toBuffer eventPayload: Payload) {
+        weak var weakSelf = self
+
+        DispatchQueue.global(qos: .default).async {
+            let strongSelf = weakSelf
+            if strongSelf == nil {
+                return
+            }
+
+            strongSelf?.eventStore?.addEvent(eventPayload)
+            strongSelf?.flush()
+        }
+    }
+
+    /// Empties the buffer of events using the respective HTTP request method.
+    func flush() {
+        if Thread.isMainThread {
+            DispatchQueue.global(qos: .default).async { [self] in
+                sendGuard()
+            }
+        } else {
+            sendGuard()
+        }
+    }
+
+    // MARK: - Control methods
+
+    func sendGuard() {
+        if isSending || pausedEmit {
+            return
+        }
+        objc_sync_enter(self)
+        if !isSending && !pausedEmit {
+            isSending = true
+            attemptEmit()
+        }
+        objc_sync_exit(self)
+    }
+
+    func attemptEmit() {
+        guard let eventStore = eventStore else { return }
+        if eventStore.count() == 0 {
+            logDebug(message: "Database empty. Returning.")
+            isSending = false
+            return
+        }
+
+        let events = eventStore.emittableEvents(withQueryLimit: UInt(emitRange))
+        let requests = buildRequests(fromEvents: events)
+        let sendResults = networkConnection?.sendRequests(requests)
+
+        logVerbose(message: "Processing emitter results.")
+
+        var successCount = 0
+        var failedWillRetryCount = 0
+        var failedWontRetryCount = 0
+        var removableEvents: [NSNumber] = []
+
+        for result in sendResults ?? [] {
+            let resultIndexArray = result.storeIds
+            if result.isSuccessful {
+                successCount += resultIndexArray?.count ?? 0
+                if let array = resultIndexArray {
+                    removableEvents.append(contentsOf: array)
+                }
+            } else if result.shouldRetry(customRetryForStatusCodes) {
+                failedWillRetryCount += resultIndexArray?.count ?? 0
+            } else {
+                failedWontRetryCount += resultIndexArray?.count ?? 0
+                if let array = resultIndexArray {
+                    removableEvents.append(contentsOf: array)
+                }
+                logError(message: String(format: "Sending events to Collector failed with status %ld. Events will be dropped.", result.statusCode ?? -1))
+            }
+        }
+        let allFailureCount = failedWillRetryCount + failedWontRetryCount
+
+        let _ = eventStore.removeEvents(withIds: removableEvents)
+
+        logDebug(message: String(format: "Success Count: %d", successCount))
+        logDebug(message: String(format: "Failure Count: %d", allFailureCount))
+
+        if callback != nil {
+            if allFailureCount == 0 {
+                callback?.onSuccess(withCount: successCount)
+            } else {
+                callback?.onFailure(withCount: allFailureCount, successCount: successCount)
+            }
+        }
+
+        if failedWillRetryCount > 0 && successCount == 0 {
+            logDebug(message: "Ending emitter run as all requests failed.")
+            Thread.sleep(forTimeInterval: 5)
+            isSending = false
+            return
+        } else {
+            self.attemptEmit()
+        }
+    }
+
+    func buildRequests(fromEvents events: [EmitterEvent]) -> [Request] {
+        var requests: [Request] = []
+        guard let networkConnection = networkConnection else { return requests }
+        
+        let sendingTime = Utilities.getTimestamp()
+        let httpMethod = networkConnection.httpMethod
+
+        if httpMethod == .get {
+            for event in events {
+                let payload = event.payload
+                addSendingTime(to: payload, timestamp: sendingTime)
+                let oversize = isOversize(payload)
+                let request = Request(payload: payload, emitterEventId: event.storeId, oversize: oversize)
+                requests.append(request)
+            }
+        } else {
+            var i = 0
+            while i < events.count {
+                var eventArray: [Payload] = []
+                var indexArray: [NSNumber] = []
+
+                let iUntil = min(i + bufferOption.rawValue, events.count)
+                for j in i..<iUntil {
+                    let event = events[j]
+
+                    let payload = event.payload
+                    let emitterEventId = NSNumber(value: event.storeId)
+                    addSendingTime(to: payload, timestamp: sendingTime)
+
+                    if isOversize(payload) {
+                        let request = Request(payload: payload, emitterEventId: emitterEventId.int64Value, oversize: true)
+                        requests.append(request)
+                    } else if isOversize(payload, previousPayloads: eventArray) {
+                        let request = Request(payloads: eventArray, emitterEventIds: indexArray)
+                        requests.append(request)
+
+                        // Clear collection and build a new POST
+                        eventArray = []
+                        indexArray = []
+
+                        // Build and store the request
+                        eventArray.append(payload)
+                        indexArray.append(emitterEventId)
+                    } else {
+                        // Add event to collections
+                        eventArray.append(payload)
+                        indexArray.append(emitterEventId)
+                    }
+                }
+
+                // Check if all payloads have been processed
+                if eventArray.count != 0 {
+                    let request = Request(payloads: eventArray, emitterEventIds: indexArray)
+                    requests.append(request)
+                }
+                i += bufferOption.rawValue
+            }
+        }
+        return requests
+    }
+
+    func isOversize(_ payload: Payload) -> Bool {
+        return isOversize(payload, previousPayloads: [])
+    }
+
+    func isOversize(_ payload: Payload, previousPayloads: [Payload]) -> Bool {
+        let byteLimit = networkConnection?.httpMethod == .get ? byteLimitGet : byteLimitPost
+        return isOversize(payload, byteLimit: byteLimit, previousPayloads: previousPayloads)
+    }
+
+    func isOversize(_ payload: Payload, byteLimit: Int, previousPayloads: [Payload]) -> Bool {
+        var totalByteSize = payload.byteSize
+        for previousPayload in previousPayloads {
+            totalByteSize += previousPayload.byteSize
+        }
+        let wrapperBytes = previousPayloads.count > 0 ? (previousPayloads.count + POST_WRAPPER_BYTES) : 0
+        return totalByteSize + wrapperBytes > byteLimit
+    }
+
+    func addSendingTime(to payload: Payload, timestamp: NSNumber) {
+        payload.addValueToPayload(String(format: "%lld", timestamp.int64Value), forKey: kSPSentTimestamp)
+    }
+
+    deinit {
+        pauseTimer()
+    }
+}
diff --git a/Sources/Core/Emitter/EmitterConfigurationUpdate.swift b/Sources/Core/Emitter/EmitterConfigurationUpdate.swift
new file mode 100644
index 000000000..26464f75c
--- /dev/null
+++ b/Sources/Core/Emitter/EmitterConfigurationUpdate.swift
@@ -0,0 +1,126 @@
+//
+//  SPEmitterConfigurationUpdate.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class EmitterConfigurationUpdate: EmitterConfiguration {
+    var sourceConfig: EmitterConfiguration?
+    var isPaused = false
+    var bufferOptionUpdated = false
+    var byteLimitGetUpdated = false
+    var byteLimitPostUpdated = false
+    var emitRangeUpdated = false
+    var threadPoolSizeUpdated = false
+    var customRetryForStatusCodesUpdated = false
+    var serverAnonymisationUpdated = false
+    var eventStoreUpdated = false
+    var requestCallbackUpdated = false
+
+    override var eventStore: EventStore? {
+        get {
+            return ((sourceConfig == nil || eventStoreUpdated) ? super.eventStore : sourceConfig?.eventStore)
+        }
+        set {
+            super.eventStore = newValue
+            eventStoreUpdated = true
+        }
+    }
+
+    override var requestCallback: RequestCallback? {
+        get {
+            return ((sourceConfig == nil || requestCallbackUpdated) ? super.requestCallback : sourceConfig?.requestCallback)
+        }
+        set {
+            super.requestCallback = newValue
+            requestCallbackUpdated = true
+        }
+    }
+
+    override var bufferOption: BufferOption {
+        get {
+            return ((sourceConfig == nil || bufferOptionUpdated) ? super.bufferOption : sourceConfig?.bufferOption) ?? EmitterDefaults.bufferOption
+        }
+        set {
+            super.bufferOption = newValue
+            bufferOptionUpdated = true
+        }
+    }
+
+    override var emitRange: Int {
+        get {
+            return ((sourceConfig == nil || emitRangeUpdated) ? super.emitRange : sourceConfig?.emitRange) ?? 0
+        }
+        set {
+            super.emitRange = newValue
+            emitRangeUpdated = true
+        }
+    }
+
+    override var threadPoolSize: Int {
+        get {
+            return ((sourceConfig == nil || threadPoolSizeUpdated) ? super.threadPoolSize : sourceConfig?.threadPoolSize) ?? EmitterDefaults.emitThreadPoolSize
+        }
+        set {
+            super.threadPoolSize = newValue
+            threadPoolSizeUpdated = true
+        }
+    }
+
+    override var byteLimitGet: Int {
+        get {
+            return ((sourceConfig == nil || byteLimitGetUpdated) ? super.byteLimitGet : sourceConfig?.byteLimitGet) ?? EmitterDefaults.byteLimitGet
+        }
+        set {
+            super.byteLimitGet = newValue
+            byteLimitGetUpdated = true
+        }
+    }
+
+    override var byteLimitPost: Int {
+        get {
+            return ((sourceConfig == nil || byteLimitPostUpdated) ? super.byteLimitPost : sourceConfig?.byteLimitPost) ?? EmitterDefaults.byteLimitPost
+        }
+        set {
+            super.byteLimitPost = newValue
+            byteLimitPostUpdated = true
+        }
+    }
+
+    override var customRetryForStatusCodes: [Int : Bool]? {
+        get {
+            return ((sourceConfig == nil || customRetryForStatusCodesUpdated) ? super.customRetryForStatusCodes : sourceConfig?.customRetryForStatusCodes)
+        }
+        set {
+            super.customRetryForStatusCodes = newValue
+            customRetryForStatusCodesUpdated = true
+        }
+    }
+
+    override var serverAnonymisation: Bool {
+        get {
+            return ((sourceConfig == nil || serverAnonymisationUpdated) ? super.serverAnonymisation : sourceConfig?.serverAnonymisation) ?? EmitterDefaults.serverAnonymisation
+        }
+        set {
+            super.serverAnonymisation = newValue
+            serverAnonymisationUpdated = true
+        }
+    }
+}
diff --git a/Sources/Core/Emitter/EmitterControllerImpl.swift b/Sources/Core/Emitter/EmitterControllerImpl.swift
new file mode 100644
index 000000000..090a71112
--- /dev/null
+++ b/Sources/Core/Emitter/EmitterControllerImpl.swift
@@ -0,0 +1,149 @@
+//
+//  SPEmitterControllerImpl.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class EmitterControllerImpl: Controller, EmitterController {
+
+    // MARK: - Properties
+
+    var bufferOption: BufferOption {
+        get {
+            return emitter.bufferOption
+        }
+        set {
+            dirtyConfig.bufferOption = newValue
+            dirtyConfig.bufferOptionUpdated = true
+            emitter.bufferOption = newValue
+        }
+    }
+
+    var byteLimitGet: Int {
+        get {
+            return emitter.byteLimitGet
+        }
+        set {
+            dirtyConfig.byteLimitGet = newValue
+            dirtyConfig.byteLimitGetUpdated = true
+            emitter.byteLimitGet = newValue
+        }
+    }
+
+    var byteLimitPost: Int {
+        get {
+            return emitter.byteLimitPost
+        }
+        set {
+            dirtyConfig.byteLimitPost = newValue
+            dirtyConfig.byteLimitPostUpdated = true
+            emitter.byteLimitPost = newValue
+        }
+    }
+
+    var serverAnonymisation: Bool {
+        get {
+            return emitter.serverAnonymisation
+        }
+        set {
+            dirtyConfig.serverAnonymisation = newValue
+            dirtyConfig.serverAnonymisationUpdated = true
+            emitter.serverAnonymisation = newValue
+        }
+    }
+
+    var emitRange: Int {
+        get {
+            return emitter.emitRange
+        }
+        set {
+            dirtyConfig.emitRange = newValue
+            dirtyConfig.emitRangeUpdated = true
+            emitter.emitRange = newValue
+        }
+    }
+
+    var threadPoolSize: Int {
+        get {
+            return emitter.emitThreadPoolSize
+        }
+        set {
+            dirtyConfig.threadPoolSize = newValue
+            dirtyConfig.threadPoolSizeUpdated = true
+            emitter.emitThreadPoolSize = newValue
+        }
+    }
+
+    private var _requestCallback: RequestCallback?
+    var requestCallback: RequestCallback? {
+        get {
+            return _requestCallback
+        }
+        set {
+            _requestCallback = newValue
+            emitter.callback = newValue
+        }
+    }
+
+    var dbCount: Int {
+        return Int(emitter.dbCount)
+    }
+
+    var isSending: Bool {
+        return emitter.isSending
+    }
+    
+    var customRetryForStatusCodes: [Int : Bool]? {
+        get {
+            return emitter.customRetryForStatusCodes
+        }
+        set {
+            dirtyConfig.customRetryForStatusCodes = newValue
+            dirtyConfig.customRetryForStatusCodesUpdated = true
+            emitter.customRetryForStatusCodes = newValue
+        }
+    }
+
+    // MARK: - Methods
+
+    func flush() {
+        emitter.flush()
+    }
+
+    func pause() {
+        dirtyConfig.isPaused = true
+        emitter.pauseEmit()
+    }
+
+    func resume() {
+        dirtyConfig.isPaused = false
+        emitter.resumeEmit()
+    }
+
+    // MARK: - Private methods
+
+    private var emitter: Emitter {
+        return serviceProvider.tracker.emitter
+    }
+
+    private var dirtyConfig: EmitterConfigurationUpdate {
+        return serviceProvider.emitterConfigurationUpdate
+    }
+}
diff --git a/Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.h b/Sources/Core/Emitter/EmitterEventProcessing.swift
similarity index 78%
rename from Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.h
rename to Sources/Core/Emitter/EmitterEventProcessing.swift
index 79339f665..d87419151 100644
--- a/Snowplow/Internal/ScreenViewTracking/SPScreenStateMachine.h
+++ b/Sources/Core/Emitter/EmitterEventProcessing.swift
@@ -1,5 +1,5 @@
 //
-//  SPScreenStateMachine.h
+//  SPEmitterEventProcessing.h
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,13 +19,11 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPStateMachineProtocol.h"
+import Foundation
 
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPScreenStateMachine : NSObject <SPStateMachineProtocol>
-
-@end
-
-NS_ASSUME_NONNULL_END
+protocol EmitterEventProcessing: AnyObject {
+    func addPayload(toBuffer eventPayload: Payload)
+    func pauseTimer()
+    func resumeTimer()
+    func flush()
+}
diff --git a/Sources/Core/GDPR/GDPRConfigurationUpdate.swift b/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
new file mode 100644
index 000000000..7b264e988
--- /dev/null
+++ b/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
@@ -0,0 +1,97 @@
+//
+//  SPGDPRConfigurationUpdate.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class GDPRConfigurationUpdate: GDPRConfiguration {
+    var sourceConfig: GDPRConfiguration?
+    var gdpr: GDPRContext?
+    var isEnabled = false
+    var gdprUpdated = false
+
+    override var basisForProcessing: GDPRProcessingBasis {
+        get {
+            return ((sourceConfig == nil || basisForProcessingUpdated) ? super.basisForProcessing : sourceConfig?.basisForProcessing)!
+        }
+        set {
+            super.basisForProcessing = newValue
+            basisForProcessingUpdated = true
+        }
+    }
+
+    override var documentId: String? {
+        get {
+            return ((sourceConfig == nil || documentIdUpdated) ? super.documentId : sourceConfig?.documentId)
+        }
+        set {
+            super.documentId = newValue
+            documentIdUpdated = true
+        }
+    }
+
+    override var documentVersion: String? {
+        get {
+            return ((sourceConfig == nil || documentVersionUpdated) ? super.documentVersion : sourceConfig?.documentVersion)
+        }
+        set {
+            super.documentVersion = newValue
+            documentVersionUpdated = true
+        }
+    }
+
+    override var documentDescription: String? {
+        get {
+            return ((sourceConfig == nil || documentDescriptionUpdated) ? super.documentDescription : sourceConfig?.documentDescription)
+        }
+        set {
+            super.documentDescription = newValue
+            documentDescriptionUpdated = true
+        }
+    }
+
+    var basisForProcessingUpdated: Bool {
+        get { return gdprUpdated }
+        set { gdprUpdated = newValue }
+    }
+
+    var documentIdUpdated: Bool {
+        get { return gdprUpdated }
+        set { gdprUpdated = newValue }
+    }
+
+    var documentVersionUpdated: Bool {
+        get { return gdprUpdated }
+        set { gdprUpdated = newValue }
+    }
+
+    var documentDescriptionUpdated: Bool {
+        get { return gdprUpdated }
+        set { gdprUpdated = newValue }
+    }
+    
+    public init() {
+        super.init(basis: .consent, documentId: nil, documentVersion: nil, documentDescription: nil)
+    }
+    
+    required init?(coder: NSCoder) {
+        super.init(coder: coder)
+    }
+}
diff --git a/Sources/Core/GDPR/GDPRContext.swift b/Sources/Core/GDPR/GDPRContext.swift
new file mode 100644
index 000000000..29dcfaf85
--- /dev/null
+++ b/Sources/Core/GDPR/GDPRContext.swift
@@ -0,0 +1,81 @@
+//
+//  SPGdprContext.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class GDPRContext: NSObject {
+    private(set) var basis: GDPRProcessingBasis
+    private(set) var basisString: String
+    private(set) var documentId: String?
+    private(set) var documentVersion: String?
+    private(set) var documentDescription: String?
+
+    /// Set a GDPR context for the tracker
+    /// - Parameters:
+    ///   - basisForProcessing: Enum one of valid legal bases for processing.
+    ///   - documentId: Document ID.
+    ///   - documentVersion: Version of the document.
+    ///   - documentDescription: Description of the document.
+    init(
+        basis basisForProcessing: GDPRProcessingBasis,
+        documentId: String?,
+        documentVersion: String?,
+        documentDescription: String?
+    ) {
+        basisString = GDPRContext.string(from: basisForProcessing)
+        basis = basisForProcessing
+        self.documentId = documentId
+        self.documentVersion = documentVersion
+        self.documentDescription = documentDescription
+        super.init()
+    }
+
+    /// Return context with value stored about GDPR processing.
+    var context: SelfDescribingJson {
+        get {
+            var data: [String : String] = [:]
+            data[kSPBasisForProcessing] = basisString
+            data[kSPDocumentId] = documentId
+            data[kSPDocumentVersion] = documentVersion
+            data[kSPDocumentDescription] = documentDescription
+            return SelfDescribingJson(schema: kSPGdprContextSchema, andDictionary: data)
+        }
+    }
+
+    // MARK: Private methods
+
+    static func string(from basis: GDPRProcessingBasis) -> String {
+        switch basis {
+        case .consent:
+            return "consent"
+        case .contract:
+            return "contract"
+        case .legalObligation:
+            return "legal_obligation"
+        case .vitalInterest:
+            return "vital_interests"
+        case .publicTask:
+            return "public_task"
+        case .legitimateInterests:
+            return "legitimate_interests"
+        }
+    }
+}
diff --git a/Sources/Core/GDPR/GDPRControllerImpl.swift b/Sources/Core/GDPR/GDPRControllerImpl.swift
new file mode 100644
index 000000000..fa30531c8
--- /dev/null
+++ b/Sources/Core/GDPR/GDPRControllerImpl.swift
@@ -0,0 +1,99 @@
+//  SPGDPRControllerImpl.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class GDPRControllerImpl: Controller, GDPRController {
+    var gdpr: GDPRContext?
+    
+    // MARK: - Methods
+
+    func reset(
+        basis basisForProcessing: GDPRProcessingBasis,
+        documentId: String?,
+        documentVersion: String?,
+        documentDescription: String?
+    ) {
+        gdpr = GDPRContext(
+            basis: basisForProcessing,
+            documentId: documentId,
+            documentVersion: documentVersion,
+            documentDescription: documentDescription)
+        tracker.gdprContext = gdpr
+        dirtyConfig.gdpr = gdpr
+        dirtyConfig.gdprUpdated = true
+    }
+
+    func disable() {
+        dirtyConfig.isEnabled = false
+        tracker.gdprContext = nil
+    }
+
+    var isEnabled: Bool {
+        get {
+            return tracker.gdprContext != nil
+        }
+    }
+
+    func enable() -> Bool {
+        if let gdpr = gdpr { tracker.gdprContext = gdpr }
+        else { return false }
+        dirtyConfig.isEnabled = true
+        return true
+    }
+
+    var basisForProcessing: GDPRProcessingBasis {
+        get {
+            return ((gdpr)?.basis)!
+        }
+    }
+
+    var documentId: String? {
+        get {
+            return (gdpr)?.documentId
+        }
+    }
+
+    var documentVersion: String? {
+        get {
+            return (gdpr)?.documentVersion
+        }
+    }
+
+    var documentDescription: String? {
+        get {
+            return (gdpr)?.documentDescription
+        }
+    }
+
+    // MARK: - Private methods
+
+    private var tracker: Tracker {
+        get {
+            return serviceProvider.tracker
+        }
+    }
+
+    private var dirtyConfig: GDPRConfigurationUpdate {
+        get {
+            return serviceProvider.gdprConfigurationUpdate
+        }
+    }
+}
diff --git a/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift b/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
new file mode 100644
index 000000000..dccc68f4f
--- /dev/null
+++ b/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
@@ -0,0 +1,52 @@
+//
+//  SPGlobalContextsControllerImpl.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class GlobalContextsControllerImpl: Controller, GlobalContextsController {
+
+    var contextGenerators: [String : GlobalContext] {
+        get {
+            return tracker.globalContextGenerators
+        }
+        set {
+            tracker.globalContextGenerators = newValue
+        }
+    }
+
+    func add(tag: String, contextGenerator generator: GlobalContext) -> Bool {
+        return tracker.add(generator, tag: tag)
+    }
+
+    func remove(tag: String) -> GlobalContext? {
+        return tracker.removeGlobalContext(tag)
+    }
+
+    var tags: [String] {
+        return tracker.globalContextTags
+    }
+
+    // MARK: - Private methods
+
+    private var tracker: Tracker {
+        return serviceProvider.tracker
+    }
+}
diff --git a/Sources/Core/GlobalContexts/SchemaRule.swift b/Sources/Core/GlobalContexts/SchemaRule.swift
new file mode 100644
index 000000000..ee907482d
--- /dev/null
+++ b/Sources/Core/GlobalContexts/SchemaRule.swift
@@ -0,0 +1,142 @@
+//
+//  SchemaRule.swift
+//  Snowplow-iOS
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+let kRulePattern = "^iglu:((?:(?:[a-zA-Z0-9-_]+|\\*)\\.)+(?:[a-zA-Z0-9-_]+|\\*))\\/([a-zA-Z0-9-_\\.]+|\\*)\\/([a-zA-Z0-9-_\\.]+|\\*)\\/([1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)$"
+let kUriPattern = "^iglu:((?:(?:[a-zA-Z0-9-_]+)\\.)+(?:[a-zA-Z0-9-_]+))\\/([a-zA-Z0-9-_]+)\\/([a-zA-Z0-9-_]+)\\/([1-9][0-9]*)\\-(0|[1-9][0-9]*)\\-(0|[1-9][0-9]*)$"
+
+class SchemaRule: Equatable {
+    private(set) var rule: String
+    private(set) var ruleParts: [String]
+
+    func copy(with zone: NSZone? = nil) -> Any {
+        return SchemaRule(rule: rule) as Any
+    }
+
+    required init?(rule: String) {
+        self.rule = rule
+        guard let parts = SchemaRule.parts(fromUri: rule, regexPattern: kRulePattern) else { return nil }
+        // reject rule if vendor format isn't valid
+        if (parts.count == 0 || !SchemaRule.validateVendor(parts[0])) {
+            return nil
+        }
+        ruleParts = parts
+    }
+
+    func match(withUri uri: String) -> Bool {
+        guard let uriParts = SchemaRule.parts(fromUri: uri, regexPattern: kUriPattern) else {
+            return false
+        }
+        if uriParts.count < ruleParts.count {
+            return false
+        }
+        // Check vendor part
+        let ruleVendor = ruleParts[0].components(separatedBy: ".")
+        let uriVendor = uriParts[0].components(separatedBy: ".")
+        if uriVendor.count != ruleVendor.count {
+            return false
+        }
+        var index = 0
+        for ruleVendorPart in ruleVendor {
+            if ("*" != ruleVendorPart) && (uriVendor[index] != ruleVendorPart) {
+                return false
+            }
+            index += 1
+        }
+        // Check the rest of the rule
+        index = 1
+        for rulePart in (ruleParts as NSArray).subarray(with: NSRange(location: 1, length: ruleParts.count - 1)) {
+            guard let rulePart = rulePart as? String else {
+                continue
+            }
+            if ("*" != rulePart) && (uriParts[index] != rulePart) {
+                return false
+            }
+            index += 1
+        }
+        return true
+    }
+
+    // MARK: - Private methods
+
+    class func parts(fromUri uri: String, regexPattern pattern: String) -> [String]? {
+        var regex: NSRegularExpression? = nil
+        do {
+            regex = try NSRegularExpression(pattern: pattern, options: [])
+        } catch {
+        }
+        let match = regex?.firstMatch(in: uri, options: [], range: NSRange(location: 0, length: uri.count))
+        if match == nil {
+            return nil
+        }
+        var parts: [String] = []
+        for i in 1..<(match?.numberOfRanges ?? 0) {
+            if i > 6 {
+                return nil
+            }
+            if let range = match?.range(at: i) {
+                let part = (uri as NSString).substring(with: range)
+                parts.append(part)
+            }
+        }
+        return parts
+    }
+
+    class func validateVendor(_ vendor: String) -> Bool {
+        // the components array will be generated like this from vendor:
+        // "com.acme.marketing" => ["com", "acme", "marketing"]
+        let components = vendor.components(separatedBy: ".")
+        // check that vendor doesn't begin or end with period
+        // e.g. ".snowplowanalytics.snowplow." => ["", "snowplowanalytics", "snowplow", ""]
+        if components.count > 1 && (components[0].count == 0 || components[components.count - 1].count == 0) {
+            return false
+        }
+        // reject vendors with criteria that are too broad & don't make sense, i.e. "*.*.marketing"
+        if ("*" == components[0]) || ("*" == components[1]) {
+            return false
+        }
+        // now validate the remaining parts, vendors should follow matching that never breaks trailing specificity
+        // in other words, once we use an asterisk, we must continue using asterisks for parts or stop
+        // e.g. "com.acme.marketing.*.*" is allowed, but "com.acme.*.marketing.*" or "com.acme.*.marketing" is forbidden
+        if components.count <= 2 {
+            return true
+        }
+        // trailingComponents are the remaining parts after the first two
+        let trailingComponents = (components as NSArray).subarray(with: NSRange(location: 2, length: components.count - 2)) as? [String]
+        var asterisk = false
+        for part in trailingComponents ?? [] {
+            if "*" == part {
+                // mark when we've found a wildcard
+                asterisk = true
+            } else if asterisk {
+                // invalid when alpha parts come after wildcard
+                return false
+            }
+        }
+        return true
+    }
+    
+    static func == (lhs: SchemaRule, rhs: SchemaRule) -> Bool {
+        return lhs.rule == rhs.rule
+    }
+
+}
diff --git a/Sources/Core/Logger/Logger.swift b/Sources/Core/Logger/Logger.swift
new file mode 100644
index 000000000..b37ab98dc
--- /dev/null
+++ b/Sources/Core/Logger/Logger.swift
@@ -0,0 +1,144 @@
+//
+//  Logger.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+func logDiagnostic(message: String,
+                   errorOrException: Any? = nil,
+                   file: String = #file,
+                   line: Int = #line,
+                   function: String = #function) {
+    Logger.diagnostic("\(file):\(line) : \(function)", message: message, errorOrException: errorOrException)
+}
+
+func logError(message: String,
+              file: String = #file,
+              line: Int = #line,
+              function: String = #function) {
+    Logger.error("\(file):\(line) : \(function)", message: message)
+}
+
+func logDebug(message: String,
+              file: String = #file,
+              line: Int = #line,
+              function: String = #function) {
+    Logger.debug("\(file):\(line) : \(function)", message: message)
+}
+
+func logVerbose(message: String,
+                file: String = #file,
+                line: Int = #line,
+                function: String = #function) {
+    Logger.verbose("\(file):\(line) : \(function)", message: message)
+}
+
+class Logger: NSObject {
+    private static var _logLevel: LogLevel = .off
+    class var logLevel: LogLevel {
+        get {
+            return _logLevel
+        }
+        set(logLevel) {
+            _logLevel = logLevel
+            if logLevel == .off {
+                #if SNOWPLOW_DEBUG
+                _logLevel = .debug
+                #elseif DEBUG
+                _logLevel = .error
+                #else
+                _logLevel = .off
+                #endif
+            }
+        }
+    }
+
+    static var delegate: LoggerDelegate?
+
+    class func diagnostic(_ tag: String, message: String, errorOrException: Any?) {
+        log(.error, tag: tag, message: message)
+        trackError(withTag: tag, message: message, errorOrException: errorOrException)
+    }
+
+    class func error(_ tag: String, message: String) {
+        log(.error, tag: tag, message: message)
+    }
+
+    class func debug(_ tag: String, message: String) {
+        log(.debug, tag: tag, message: message)
+    }
+
+    class func verbose(_ tag: String, message: String) {
+        log(.verbose, tag: tag, message: message)
+    }
+
+    // MARK: - Private methods
+
+    private class func log(_ level: LogLevel, tag: String, message: String) {
+        if level.rawValue > logLevel.rawValue {
+            return
+        }
+        if let delegate = delegate {
+            switch level {
+            case .off:
+                // do nothing.
+                break
+            case .error:
+                delegate.error(tag, message: message)
+            case .debug:
+                delegate.debug(tag, message: message)
+            case .verbose:
+                delegate.verbose(tag, message: message)
+            }
+            return
+        }
+        #if SNOWPLOW_TEST
+        // NSLog doesn't work on test target
+        let output = "[\(["Off", "Error", "Error", "Debug", "Verbose"][level.rawValue])] \(tag): \(message)"
+        print("\(output.utf8CString)")
+        #elseif DEBUG
+        // Log should be printed only during debugging
+        print("[\(["Off", "Error", "Debug", "Verbose"][level.rawValue])] \(tag): \(message)")
+        #endif
+    }
+
+    private class func trackError(withTag tag: String, message: String, errorOrException: Any?) {
+        var error: Error?
+        var exception: NSException?
+        if errorOrException is Error {
+            error = errorOrException as? Error
+        } else if errorOrException is NSException {
+            exception = errorOrException as? NSException
+        }
+
+        // Construct userInfo
+        var userInfo: [String : NSObject] = [:]
+        userInfo["tag"] = tag as NSObject
+        userInfo["message"] = message as NSObject
+        userInfo["error"] = error as NSObject?
+        userInfo["exception"] = exception as NSObject?
+
+        // Send notification to tracker
+        NotificationCenter.default.post(
+            name: NSNotification.Name("SPTrackerDiagnostic"),
+            object: self,
+            userInfo: userInfo)
+    }
+}
diff --git a/Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift b/Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift
new file mode 100644
index 000000000..5c31aebc8
--- /dev/null
+++ b/Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift
@@ -0,0 +1,66 @@
+//
+//  SPNetworkConfigurationUpdate.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class NetworkConfigurationUpdate: NSObject {
+    public var sourceConfig: NetworkConfiguration?
+
+    var customPostPathUpdated = false
+    private var _customPostPath: String?
+    var customPostPath: String? {
+        get {
+            return customPostPathUpdated ? _customPostPath : sourceConfig?.customPostPath
+        }
+        set {
+            _customPostPath = newValue
+            customPostPathUpdated = true
+        }
+    }
+
+    var requestHeadersUpdated = false
+    private var _requestHeaders: [String : String]?
+    var requestHeaders: [String : String]? {
+        get {
+            return requestHeadersUpdated ? _requestHeaders : sourceConfig?.requestHeaders
+        }
+        set {
+            _requestHeaders = newValue
+            requestHeadersUpdated = true
+        }
+    }
+
+    var endpoint: String? {
+        return (sourceConfig)?.endpoint
+    }
+
+    var method: HttpMethodOptions? {
+        return ((sourceConfig)?.method)
+    }
+
+    var `protocol`: ProtocolOptions? {
+        return ((sourceConfig)?.protocol)
+    }
+
+    var networkConnection: NetworkConnection? {
+        return (sourceConfig)?.networkConnection
+    }
+}
diff --git a/Sources/Core/NetworkConnection/NetworkControllerImpl.swift b/Sources/Core/NetworkConnection/NetworkControllerImpl.swift
new file mode 100644
index 000000000..d8107922d
--- /dev/null
+++ b/Sources/Core/NetworkConnection/NetworkControllerImpl.swift
@@ -0,0 +1,82 @@
+//
+//  SPNetworkControllerImpl.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class NetworkControllerImpl: Controller, NetworkController {
+    private var requestCallback: RequestCallback?
+
+    var isCustomNetworkConnection: Bool {
+        return emitter.networkConnection != nil && !(emitter.networkConnection is DefaultNetworkConnection)
+    }
+
+    // MARK: - Properties
+
+    var endpoint: String? {
+        get {
+            return emitter.urlEndpoint
+        }
+        set {
+            emitter.urlEndpoint = newValue
+        }
+    }
+
+    var method: HttpMethodOptions {
+        get {
+            return emitter.method
+        }
+        set {
+            emitter.method = newValue
+        }
+    }
+
+    var customPostPath: String? {
+        get {
+            return emitter.customPostPath
+        }
+        set {
+            dirtyConfig.customPostPath = newValue
+            dirtyConfig.customPostPathUpdated = true
+            emitter.customPostPath = newValue
+        }
+    }
+
+    var requestHeaders: [String : String]? {
+        get {
+            return emitter.requestHeaders
+        }
+        set {
+            dirtyConfig.requestHeaders = requestHeaders
+            dirtyConfig.requestHeadersUpdated = true
+            emitter.requestHeaders = newValue
+        }
+    }
+
+    // MARK: - Private methods
+
+    private var emitter: Emitter {
+        return serviceProvider.tracker.emitter
+    }
+
+    private var dirtyConfig: NetworkConfigurationUpdate {
+        return serviceProvider.networkConfigurationUpdate
+    }
+}
diff --git a/Sources/Core/RemoteConfiguration/ConfigurationCache.swift b/Sources/Core/RemoteConfiguration/ConfigurationCache.swift
new file mode 100644
index 000000000..4150b5b97
--- /dev/null
+++ b/Sources/Core/RemoteConfiguration/ConfigurationCache.swift
@@ -0,0 +1,137 @@
+//
+//  SPConfigurationCache.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class ConfigurationCache: NSObject {
+    private var cacheFileUrl: URL?
+    private var configuration: FetchedConfigurationBundle?
+
+    init(remoteConfiguration: RemoteConfiguration) {
+        super.init()
+        #if !(os(tvOS)) && !(os(watchOS))
+        createCachePath(with: remoteConfiguration)
+        #endif
+    }
+
+    func read() -> FetchedConfigurationBundle? {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        #if !(os(tvOS)) && !(os(watchOS))
+        if let configuration = configuration {
+            return configuration
+        }
+        load()
+        #endif
+        return configuration
+    }
+
+    func write(_ configuration: FetchedConfigurationBundle) {
+        objc_sync_enter(self)
+        self.configuration = configuration
+        #if !(os(tvOS)) && !(os(watchOS))
+        store()
+        #endif
+        objc_sync_exit(self)
+    }
+
+    func clear() {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        configuration = nil
+        #if !(os(tvOS)) && !(os(watchOS))
+        if let cacheFileUrl = cacheFileUrl {
+            do {
+                try FileManager.default.removeItem(at: cacheFileUrl)
+            } catch let error {
+                logError(message: String(format: "Error on clearing configuration from cache: %@", error.localizedDescription))
+            }
+        }
+        #endif
+    }
+
+    // Private method
+
+    func load() {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        guard let cacheFileUrl = cacheFileUrl,
+              let data = try? Data(contentsOf: cacheFileUrl) else { return }
+        if #available(iOS 12, tvOS 12, watchOS 5, macOS 10.14, *) {
+            do {
+                configuration = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? FetchedConfigurationBundle
+            } catch let error {
+                logError(message: String(format: "Exception on getting configuration from cache: %@", error.localizedDescription))
+                configuration = nil
+            }
+        } else {
+            let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
+            configuration = unarchiver.decodeObject() as? FetchedConfigurationBundle
+            unarchiver.finishDecoding()
+        }
+    }
+    
+    func store() {
+        _ = DispatchQueue.global(qos: .default)
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        guard let configuration = configuration,
+              let cacheFileUrl = cacheFileUrl else { return }
+        
+        do {
+            var data = Data()
+            var archiver: NSKeyedArchiver?
+            
+            if #available(iOS 12, tvOS 12, watchOS 5, macOS 10.14, *) {
+                archiver = NSKeyedArchiver(requiringSecureCoding: true)
+                archiver?.encode(configuration, forKey: "root")
+                if let encodedData = archiver?.encodedData {
+                    data = encodedData
+                }
+            } else {
+                archiver = NSKeyedArchiver(forWritingWith: data as! NSMutableData)
+                archiver?.encode(configuration)
+                archiver?.finishEncoding()
+            }
+            try data.write(to: cacheFileUrl, options: .atomic)
+        } catch let error {
+            logError(message: String(format: "Error on caching configuration: %@", error.localizedDescription))
+        }
+    }
+
+    func createCachePath(with remoteConfiguration: RemoteConfiguration) {
+        let fm = FileManager.default
+        var url = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask).last
+        url = url?.appendingPathComponent("snowplow-cache")
+        do {
+            if let url = url {
+                try fm.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
+            }
+        } catch {
+        }
+        let fileName = String(format: "remoteConfig-%lu.data", UInt((remoteConfiguration.endpoint).hash))
+        url = url?.appendingPathComponent(fileName, isDirectory: false)
+        if let url = url {
+            cacheFileUrl = url
+        }
+    }
+}
diff --git a/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift b/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
new file mode 100644
index 000000000..9ff84b2b9
--- /dev/null
+++ b/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
@@ -0,0 +1,57 @@
+//
+//  SPConfigurationFetcher.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class ConfigurationFetcher: NSObject {
+    private var remoteConfiguration: RemoteConfiguration
+    private var onFetchCallback: OnFetchCallback
+
+    init(remoteSource remoteConfiguration: RemoteConfiguration, onFetchCallback: @escaping OnFetchCallback) {
+        self.remoteConfiguration = remoteConfiguration
+        self.onFetchCallback = onFetchCallback
+        super.init()
+        performRequest()
+    }
+
+    func performRequest() {
+        guard let url = URL(string: remoteConfiguration.endpoint) else { return }
+        var urlRequest = URLRequest(url: url)
+        urlRequest.httpMethod = "GET"
+
+        var httpResponse: HTTPURLResponse? = nil
+
+        URLSession.shared.dataTask(with: urlRequest) { data, urlResponse, error in
+            httpResponse = urlResponse as? HTTPURLResponse
+            let isSuccessful = (httpResponse?.statusCode ?? 0) >= 200 && (httpResponse?.statusCode ?? 0) < 300
+            if isSuccessful {
+                if let data = data { self.resolveRequest(with: data) }
+            }
+        }.resume()
+    }
+
+    func resolveRequest(with data: Data) {
+        if let jsonObject = try? JSONSerialization.jsonObject(with: data) as? [String : NSObject],
+           let fetchedConfigurationBundle = FetchedConfigurationBundle(dictionary: jsonObject) {
+            onFetchCallback(fetchedConfigurationBundle, ConfigurationState.fetched)
+        }
+    }
+}
diff --git a/Sources/Core/RemoteConfiguration/ConfigurationProvider.swift b/Sources/Core/RemoteConfiguration/ConfigurationProvider.swift
new file mode 100644
index 000000000..6d47b91a2
--- /dev/null
+++ b/Sources/Core/RemoteConfiguration/ConfigurationProvider.swift
@@ -0,0 +1,86 @@
+//
+//  ConfigurationProvider.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+typealias OnFetchCallback = (FetchedConfigurationBundle, ConfigurationState) -> Void
+
+/// This class fetches a configuration from a remote source otherwise it provides a cached configuration.
+/// It can manage multiple sources and multiple caches.
+class ConfigurationProvider {
+    private var remoteConfiguration: RemoteConfiguration
+    private var cache: ConfigurationCache
+    private var fetcher: ConfigurationFetcher?
+    private var defaultBundle: FetchedConfigurationBundle?
+    private var cacheBundle: FetchedConfigurationBundle?
+
+    convenience init(remoteConfiguration: RemoteConfiguration) {
+        self.init(remoteConfiguration: remoteConfiguration, defaultConfigurationBundles: nil)
+    }
+
+    init(remoteConfiguration: RemoteConfiguration, defaultConfigurationBundles defaultBundles: [ConfigurationBundle]?) {
+        self.remoteConfiguration = remoteConfiguration
+        cache = ConfigurationCache(remoteConfiguration: remoteConfiguration)
+        if let defaultBundles = defaultBundles {
+            let bundle = FetchedConfigurationBundle(
+                schema: "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0",
+                configurationVersion: NSInteger.min)
+            bundle.configurationBundle = defaultBundles
+            defaultBundle = bundle
+        }
+    }
+
+    func retrieveConfigurationOnlyRemote(_ onlyRemote: Bool, onFetchCallback: @escaping OnFetchCallback) {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        if !onlyRemote {
+            if cacheBundle == nil {
+                cacheBundle = cache.read()
+            }
+            if let cacheBundle = cacheBundle {
+                onFetchCallback(cacheBundle, .cached)
+            } else if let defaultBundle = defaultBundle {
+                onFetchCallback(defaultBundle, .default)
+            }
+        }
+        fetcher = ConfigurationFetcher(remoteSource: remoteConfiguration) { bundle, state in
+            if !self.schemaCompatibility(bundle.schema) {
+                return
+            }
+            objc_sync_enter(self)
+            defer { objc_sync_exit(self) }
+            if let cacheBundle = self.cacheBundle {
+                if cacheBundle.configurationVersion >= bundle.configurationVersion {
+                    return
+                }
+            }
+            self.cache.write(bundle)
+            self.cacheBundle = bundle
+            onFetchCallback(bundle, ConfigurationState.fetched)
+        }
+    }
+
+    // Private methods
+
+    private func schemaCompatibility(_ schema: String) -> Bool {
+        return schema.hasPrefix("http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-")
+    }
+}
diff --git a/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
new file mode 100644
index 000000000..a19156325
--- /dev/null
+++ b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
@@ -0,0 +1,79 @@
+//
+//  SPFetchedConfigurationBundle.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class FetchedConfigurationBundle: Configuration {
+    var schema: String
+    var configurationVersion: Int
+    var configurationBundle: [ConfigurationBundle] = []
+    
+    init(schema: String, configurationVersion: Int) {
+        self.schema = schema
+        self.configurationVersion = configurationVersion
+    }
+    
+    init?(dictionary: [String : NSObject]) {
+        guard let schema = dictionary["$schema"] as? String else {
+            logDebug(message: "Error assigning: schema")
+            return nil
+        }
+        self.schema = schema
+        guard let configurationVersion = dictionary["configurationVersion"] as? Int else {
+            logDebug(message: "Error assigning: configurationVersion")
+            return nil
+        }
+        self.configurationVersion = configurationVersion
+        guard let bundles = dictionary["configurationBundle"] as? [[String : NSObject]] else {
+            logDebug(message: "Error assigning: configurationBundle")
+            return nil
+        }
+        self.configurationBundle = bundles.map { ConfigurationBundle(dictionary: $0) }.filter { $0 != nil }.map { $0! }
+    }
+
+    // MARK: - NSCopying
+
+    override func copy(with zone: NSZone? = nil) -> Any {
+        let copy: FetchedConfigurationBundle? = nil
+        copy?.schema = schema
+        copy?.configurationVersion = configurationVersion
+        copy?.configurationBundle = configurationBundle.map { $0.copy(with: zone) as! ConfigurationBundle }
+        return copy!
+    }
+
+    // MARK: - NSSecureCoding
+    
+    override class var supportsSecureCoding: Bool { return true }
+
+    override func encode(with coder: NSCoder) {
+        coder.encode(schema, forKey: "schema")
+        coder.encode(configurationVersion, forKey: "configurationVersion")
+        coder.encode(configurationBundle, forKey: "configurationBundle")
+    }
+
+    required init?(coder: NSCoder) {
+        schema = coder.decodeObject(forKey: "schema") as? String ?? ""
+        configurationVersion = coder.decodeInteger(forKey: "configurationVersion")
+        if let decodeObject = coder.decodeObject(forKey: "configurationBundle") as? [ConfigurationBundle] {
+            configurationBundle = decodeObject
+        }
+    }
+}
diff --git a/Sources/Core/ScreenViewTracking/ScreenState.swift b/Sources/Core/ScreenViewTracking/ScreenState.swift
new file mode 100644
index 000000000..117caa53d
--- /dev/null
+++ b/Sources/Core/ScreenViewTracking/ScreenState.swift
@@ -0,0 +1,105 @@
+//
+//  ScreenState.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Forward declaration for SPScreenView
+class ScreenState: NSObject, State, NSCopying {
+    /// Screenview name
+    private(set) var name: String
+    /// Screen ID
+    private(set) var screenId: String
+    /// Screen type
+    private(set) var type: String?
+    /// Screenview transition type
+    private(set) var transitionType: String?
+    /// Top view controller class name
+    private(set) var topViewControllerClassName: String?
+    /// View controller class name
+    private(set) var viewControllerClassName: String?
+    /// Previous ScreenState
+    var previousState: ScreenState?
+
+    /// Creates a new screen state.
+    /// - Parameters:
+    ///   - theName: A name to identify the screen view
+    ///   - theType: The type of the screen view
+    ///   - theScreenId: An ID generated for the screen
+    ///   - theTransitionType: The transition used to arrive at the screen
+    ///   - theTopControllerName: The top view controller class name
+    ///   - theControllerName: The view controller class name
+    required init(name theName: String, type theType: String?, screenId theScreenId: String?, transitionType theTransitionType: String?, topViewControllerClassName theTopControllerName: String?, viewControllerClassName theControllerName: String?) {
+            name = theName
+        if theScreenId == nil {
+            screenId = UUID().uuidString
+        } else {
+            screenId = theScreenId ?? ""
+        }
+        type = theType
+        transitionType = theTransitionType
+        topViewControllerClassName = theTopControllerName
+        viewControllerClassName = theControllerName
+    }
+
+    convenience init(name theName: String, type theType: String?, topViewControllerClassName theTopControllerName: String?, viewControllerClassName theControllerName: String?) {
+        self.init(name: theName, type: theType, screenId: nil, transitionType: nil, topViewControllerClassName: theTopControllerName, viewControllerClassName: theControllerName)
+    }
+
+    convenience init(name theName: String, type theType: String?, screenId theScreenId: String?, transitionType theTransitionType: String?) {
+        self.init(name: theName, type: theType, screenId: theScreenId, transitionType: nil, topViewControllerClassName: nil, viewControllerClassName: nil)
+    }
+
+    convenience init(name theName: String, type theType: String?, screenId theScreenId: String?) {
+        self.init(name: theName, type: theType, screenId: theScreenId, transitionType: nil)
+    }
+
+    convenience init(name theName: String, screenId theScreenId: String?) {
+        self.init(name: theName, type: nil, screenId: theScreenId, transitionType: nil)
+    }
+
+    func copy(with zone: NSZone? = nil) -> Any {
+        return ScreenState(name: name,
+                           type: type,
+                           screenId: screenId,
+                           transitionType: transitionType,
+                           topViewControllerClassName: topViewControllerClassName,
+                           viewControllerClassName: viewControllerClassName)
+    }
+
+    /// Return if the state is valid.
+    var isValid: Bool {
+        return (Utilities.validate(name) != nil) && (Utilities.validate(screenId) != nil) && Utilities.isUUIDString(screenId)
+    }
+
+    /// Returns all non-nil values if the state is valid (e.g. name is not missing or empty string).
+    var payload: Payload? {
+        if isValid {
+            let validPayload = Payload()
+            validPayload.addValueToPayload(name, forKey: kSPScreenName)
+            validPayload.addValueToPayload(screenId, forKey: kSPScreenId)
+            validPayload.addValueToPayload(type, forKey: kSPScreenType)
+            validPayload.addValueToPayload(topViewControllerClassName, forKey: kSPScreenTopViewController)
+            validPayload.addValueToPayload(viewControllerClassName, forKey: kSPScreenViewController)
+            return validPayload
+        }
+        return nil
+    }
+}
diff --git a/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
new file mode 100644
index 000000000..06a62433c
--- /dev/null
+++ b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
@@ -0,0 +1,87 @@
+//
+//  SPScreenStateMachine.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class ScreenStateMachine: StateMachineProtocol {
+    static var identifier: String { return "ScreenContext" }
+    var identifier: String { return ScreenStateMachine.identifier }
+
+    var subscribedEventSchemasForTransitions: [String] {
+        return [kSPScreenViewSchema]
+    }
+
+    var subscribedEventSchemasForEntitiesGeneration: [String] {
+        return ["*"]
+    }
+
+    var subscribedEventSchemasForPayloadUpdating: [String] {
+        return [kSPScreenViewSchema]
+    }
+
+    func transition(from event: Event, state currentState: State?) -> State? {
+        if let screenView = event as? ScreenView {
+            let newState: ScreenState = screenState(from: screenView)
+            newState.previousState = currentState as? ScreenState
+            return newState
+        }
+        return nil
+    }
+
+    func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]? {
+        if let state = state as? ScreenState,
+           let entity = screenContext(from: state) {
+            return [entity]
+        }
+        return nil
+    }
+
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+        if let state = state as? ScreenState {
+            let previousState = state.previousState
+            var addedValues: [String : NSObject] = [:]
+            addedValues[kSPSvPreviousName] = previousState?.name as NSObject?
+            addedValues[kSPSvPreviousType] = previousState?.type as NSObject?
+            addedValues[kSPSvPreviousScreenId] = previousState?.screenId as NSObject?
+            return addedValues
+        }
+        return nil
+    }
+
+    // Private methods
+
+    func screenState(from screenView: ScreenView) -> ScreenState {
+        return ScreenState(
+            name: screenView.name,
+            type: screenView.type,
+            screenId: screenView.screenId.uuidString,
+            transitionType: screenView.transitionType,
+            topViewControllerClassName: screenView.topViewControllerClassName,
+            viewControllerClassName: screenView.viewControllerClassName)
+    }
+
+    func screenContext(from screenState: ScreenState) -> SelfDescribingJson? {
+        if let contextPayload = screenState.payload {
+            return SelfDescribingJson(schema: kSPScreenContextSchema, andPayload: contextPayload)
+        }
+        return nil
+    }
+}
diff --git a/Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift b/Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift
new file mode 100644
index 000000000..4652b4afb
--- /dev/null
+++ b/Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift
@@ -0,0 +1,188 @@
+//
+//  UIKitScreenViewTracking.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+class UIKitScreenViewTracking {
+    private static var initialized = false
+
+    class func setup() {
+        if (initialized) { return }
+        initialized = true
+        
+        swizzle()
+    }
+
+    private class func swizzle() {
+        #if os(iOS) || os(tvOS)
+        let originalSelector = #selector(UIViewController.viewDidAppear(_:))
+        let swizzledSelector = #selector(UIViewController.sp_viewDidAppear(_:))
+        let forClass = UIViewController.self
+
+        let originalMethod = class_getInstanceMethod(forClass, originalSelector)
+        let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
+
+        var didAddMethod = false
+        if let swizzledMethod = swizzledMethod {
+            didAddMethod = class_addMethod(
+                forClass,
+                originalSelector,
+                method_getImplementation(swizzledMethod),
+                method_getTypeEncoding(swizzledMethod))
+        }
+
+        if didAddMethod {
+            if let originalMethod = originalMethod {
+                class_replaceMethod(
+                    forClass,
+                    swizzledSelector,
+                    method_getImplementation(originalMethod),
+                    method_getTypeEncoding(originalMethod))
+            }
+        } else {
+            if let originalMethod = originalMethod,
+               let swizzledMethod = swizzledMethod {
+                method_exchangeImplementations(originalMethod, swizzledMethod)
+            }
+        }
+        #endif
+    }
+}
+
+#if os(iOS) || os(tvOS)
+
+import ObjectiveC
+import UIKit
+
+extension UIViewController {
+    
+    // MARK: - Method Swizzling
+
+    @objc func sp_viewDidAppear(_ animated: Bool) {
+        sp_viewDidAppear(animated)
+
+        let bundle = Bundle(for: self.classForCoder)
+        if !bundle.bundlePath.hasPrefix(Bundle.main.bundlePath) {
+            // Ignore view controllers that don't start with the main bundle path
+            return
+        }
+        
+        guard let activeController = _SP_top() else { return }
+        let top = _SP_topViewController(activeController)
+        
+        // Construct userInfo
+        var userInfo: [AnyHashable : Any] = [:]
+        userInfo["viewControllerClassName"] = String(describing: self.classForCoder)
+        userInfo["topViewControllerClassName"] = String(describing: top.self.classForCoder)
+        
+        // `name` is set to snowplowId class instance variable if it exists (hence no @"id" in userInfo)
+        userInfo["name"] = _SP_getName(self) ?? _SP_getName(top) ?? "Unknown"
+
+        userInfo["type"] = NSNumber(value: _SP_getType(top).rawValue)
+
+        // Send notification to tracker
+        NotificationCenter.default.post(
+            name: NSNotification.Name("SPScreenViewDidAppear"),
+            object: self,
+            userInfo: userInfo)
+    }
+
+    func _SP_validate(_ string: String) -> Bool {
+        return string.count > 0
+    }
+
+    func _SP_getSnowplowId() -> String? {
+        let propertyName = "snowplowId"
+        let selector = NSSelectorFromString(propertyName)
+        let propertyExists = responds(to: selector)
+        if propertyExists {
+            if let value = self.value(forKey: propertyName) as? String {
+                return value
+            }
+        }
+        return nil
+    }
+
+    func _SP_getName(_ viewController: UIViewController) -> String? {
+        // check if there's an instance variable snowplowId
+        if let viewControllerSnowplowId = viewController._SP_getSnowplowId() {
+            if _SP_validate(viewControllerSnowplowId) {
+                return viewControllerSnowplowId
+            }
+        }
+
+        // otherwise return the class name
+        let viewControllerClassName = NSStringFromClass(type(of: viewController).self)
+        if _SP_validate(viewControllerClassName) {
+            return viewControllerClassName
+        }
+
+        return nil
+    }
+
+    func _SP_getType(_ viewController: UIViewController) -> ScreenType {
+        if viewController is UINavigationController {
+            return .navigation
+        }
+        if viewController is UITabBarController {
+            return .tabBar
+        }
+        if viewController.presentedViewController != nil {
+            return .modal
+        }
+        if viewController is UIPageViewController {
+            return .pageView
+        }
+        if viewController is UISplitViewController {
+            return .splitView
+        }
+        // TODO: this was taken over from Obj-C, how would it ever occur?
+//        if viewController is UIPopoverPresentationController {
+//            return .popoverPresentation
+//        }
+        return .default
+    }
+
+    func _SP_top() -> UIViewController? {
+       if let rootViewController = viewIfLoaded?.window?.rootViewController {
+            return rootViewController
+        }
+        return nil
+    }
+
+    func _SP_topViewController(_ rootViewController: UIViewController) -> UIViewController {
+        if let navigationController = rootViewController as? UINavigationController,
+           let visible = navigationController.visibleViewController {
+            return _SP_topViewController(visible)
+        }
+
+        if let tabBarController = rootViewController as? UITabBarController,
+           let controller = tabBarController.selectedViewController {
+            return _SP_topViewController(controller)
+        }
+
+        if let presentedViewController = rootViewController.presentedViewController {
+            return _SP_topViewController(presentedViewController)
+        }
+
+        return rootViewController
+    }
+}
+
+#endif
diff --git a/Sources/Core/Session/Session.swift b/Sources/Core/Session/Session.swift
new file mode 100644
index 000000000..baeb2c2b2
--- /dev/null
+++ b/Sources/Core/Session/Session.swift
@@ -0,0 +1,254 @@
+//
+//  Session.swift
+//  Snowplow
+//
+//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+#if os(iOS) || os(tvOS)
+import UIKit
+#endif
+
+class Session {
+    /// Whether the application is in the background or foreground
+    private(set) var inBackground = false
+    /// The session's userId
+    private(set) var userId: String
+    /// The foreground index count
+    private(set) var foregroundIndex = 0
+    /// The background index count
+    private(set) var backgroundIndex = 0
+    /// The event index
+    private(set) var eventIndex = 0
+    /// The current tracker associated with the session
+    private(set) var tracker: Tracker?
+    /// Returns the current session state
+    private(set) var state: SessionState?
+    /// Callback to be called when the session is updated
+    public var onSessionStateUpdate: ((_ sessionState: SessionState) -> Void)?
+    
+    /// The currently set Foreground Timeout in milliseconds
+    public var foregroundTimeout = TrackerDefaults.foregroundTimeout
+    /// The currently set Background Timeout in milliseconds
+    public var backgroundTimeout = TrackerDefaults.backgroundTimeout
+
+    private var isNewSession = true
+    private var isSessionCheckerEnabled = false
+    private var lastSessionCheck: NSNumber = Utilities.getTimestamp()
+    private var dataPersistence: DataPersistence?
+
+    /// Initializes a newly allocated SnowplowSession
+    /// - Parameters:
+    ///   - foregroundTimeout: the session timeout while it is in the foreground
+    ///   - backgroundTimeout: the session timeout while it is in the background
+    /// - Returns: a SnowplowSession
+    convenience init(foregroundTimeout: Int, andBackgroundTimeout backgroundTimeout: Int) {
+        self.init(foregroundTimeout: foregroundTimeout, andBackgroundTimeout: backgroundTimeout, andTracker: nil)
+    }
+
+    /// Initializes a newly allocated SnowplowSession
+    /// - Parameters:
+    ///   - foregroundTimeout: the session timeout while it is in the foreground
+    ///   - backgroundTimeout: the session timeout while it is in the background
+    ///   - tracker: reference to the associated tracker of the session
+    /// - Returns: a SnowplowSession
+    init(foregroundTimeout: Int, andBackgroundTimeout backgroundTimeout: Int, andTracker tracker: Tracker?) {
+        
+        self.foregroundTimeout = foregroundTimeout * 1000
+        self.backgroundTimeout = backgroundTimeout * 1000
+        self.tracker = tracker
+        if let namespace = tracker?.trackerNamespace {
+            dataPersistence = DataPersistence.getFor(namespace: namespace)
+        }
+        let storedSessionDict = dataPersistence?.session
+        userId = Session.retrieveUserId(withSessionDict: storedSessionDict)
+        if var storedSessionDict = storedSessionDict {
+            storedSessionDict[kSPSessionUserId] = userId as NSObject?
+            state = SessionState(storedState: storedSessionDict)
+            dataPersistence?.session = storedSessionDict
+        }
+        if state == nil {
+            logDiagnostic(message: "No previous session info available")
+        }
+        
+        // Start session check
+        startChecker()
+        
+        // Trigger notification for view changes
+#if os(iOS) || os(tvOS)
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(updateInBackground),
+            name: UIApplication.willResignActiveNotification,
+            object: nil)
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(updateInForeground),
+            name: UIApplication.didBecomeActiveNotification,
+            object: nil)
+#endif
+    }
+
+    // MARK: - Public
+
+    /// Starts the recurring timer check for sessions
+    func startChecker() {
+        isSessionCheckerEnabled = true
+    }
+
+    /// Stops the recurring timer check for sessions
+    func stopChecker() {
+        isSessionCheckerEnabled = false
+    }
+
+    /// Expires the current session and starts a new one
+    func startNewSession() {
+        // TODO: when the sesssion has been renewed programmatically, it has to be reported in the session context to the collector.
+        isNewSession = true
+    }
+
+    /// Returns the session dictionary
+    /// - Parameters:
+    ///   - firstEventId: The potential first event id of the session
+    ///   - firstEventTimestamp: Device created timestamp of the first event of the session
+    ///   - userAnonymisation: Whether to anonymise user identifiers
+    /// - Returns: a SnowplowPayload containing the session dictionary
+    func getDictWithEventId(_ eventId: String?, eventTimestamp: Int64, userAnonymisation: Bool) -> [String : NSObject]? {
+        var context: [String : NSObject]? = nil
+        objc_sync_enter(self)
+        if isSessionCheckerEnabled {
+            if shouldUpdate() {
+                update(withEventId: eventId, eventTimestamp: eventTimestamp)
+                if let onSessionStateUpdate = onSessionStateUpdate, let state = state {
+                    DispatchQueue.global(qos: .default).async {
+                        onSessionStateUpdate(state)
+                    }
+                }
+            }
+            lastSessionCheck = Utilities.getTimestamp()
+        }
+
+        eventIndex += 1
+
+        context = state?.sessionContext
+        context?[kSPSessionEventIndex] = NSNumber(value: eventIndex)
+        objc_sync_exit(self)
+
+        if userAnonymisation {
+            // mask the user identifier
+            var copy = context
+            copy?[kSPSessionUserId] = kSPSessionAnonymousUserId as NSObject
+            copy?[kSPSessionPreviousId] = nil
+            return copy
+        } else {
+            return context
+        }
+    }
+
+
+    // MARK: - Private
+
+    private static func retrieveUserId(withSessionDict sessionDict: [String : NSObject]?) -> String {
+        var userId = sessionDict?[kSPSessionUserId] as? String ?? Utilities.getUUIDString()
+        // Session_UserID is available only if the session context is enabled.
+        // In a future version we would like to make it available even if the session context is disabled.
+        // For this reason, we store the Session_UserID in a separate storage (decoupled by session values)
+        // calling it Installation_UserID in order to remark that it isn't related to the session context.
+        // Although, for legacy, we need to copy its value in the Session_UserID of the session context
+        // as the session context schema (and related data modelling) requires it.
+        // For further details: https://discourse.snowplow.io/t/rfc-mobile-trackers-v2-0
+        let userDefaults = UserDefaults.standard
+        let storedUserId = userDefaults.string(forKey: kSPInstallationUserId)
+        if let storedUserId = storedUserId {
+            userId = storedUserId
+        } else {
+            userDefaults.set(userId, forKey: kSPInstallationUserId)
+        }
+        return userId
+    }
+
+    private func shouldUpdate() -> Bool {
+        if isNewSession {
+            return true
+        }
+        let lastAccess = lastSessionCheck.int64Value
+        let now = Utilities.getTimestamp().int64Value
+        let timeout = inBackground ? backgroundTimeout : foregroundTimeout
+        return now < lastAccess || Int(now - lastAccess) > timeout
+    }
+
+    private func update(withEventId eventId: String?, eventTimestamp: Int64) {
+        isNewSession = false
+        let sessionIndex = (state?.sessionIndex ?? 0) + 1
+        let eventISOTimestamp = Utilities.timestamp(toISOString: eventTimestamp)
+        state = SessionState(
+            firstEventId: eventId,
+            firstEventTimestamp: eventISOTimestamp,
+            currentSessionId: Utilities.getUUIDString(),
+            previousSessionId: state?.sessionId,
+            sessionIndex: sessionIndex,
+            userId: userId,
+            storage: "LOCAL_STORAGE")
+        var sessionToPersist = state?.sessionContext
+        // Remove previousSessionId if nil because dictionaries with nil values aren't plist serializable
+        // and can't be stored with SPDataPersistence.
+        if state?.previousSessionId == nil {
+            var sessionCopy = sessionToPersist
+            sessionCopy?.removeValue(forKey: kSPSessionPreviousId)
+            sessionToPersist = sessionCopy
+        }
+        dataPersistence?.session = sessionToPersist
+        eventIndex = 0
+    }
+
+    @objc func updateInBackground() {
+        if !inBackground && tracker?.lifecycleEvents ?? false {
+            backgroundIndex += 1
+            sendBackgroundEvent()
+            inBackground = true
+        }
+    }
+
+    @objc func updateInForeground() {
+        if inBackground && tracker?.lifecycleEvents ?? false {
+            foregroundIndex += 1
+            sendForegroundEvent()
+            inBackground = false
+        }
+    }
+
+    func sendBackgroundEvent() {
+        if let tracker = tracker {
+            let backgroundEvent = Background(index: backgroundIndex)
+            let _ = tracker.track(backgroundEvent)
+        }
+    }
+
+    func sendForegroundEvent() {
+        if let tracker = tracker {
+            let foregroundEvent = Foreground(index: foregroundIndex)
+            let _ = tracker.track(foregroundEvent)
+        }
+    }
+
+    deinit {
+        #if os(iOS)
+        NotificationCenter.default.removeObserver(self)
+        #endif
+    }
+}
diff --git a/Sources/Core/Session/SessionConfigurationUpdate.swift b/Sources/Core/Session/SessionConfigurationUpdate.swift
new file mode 100644
index 000000000..e5d0f8d43
--- /dev/null
+++ b/Sources/Core/Session/SessionConfigurationUpdate.swift
@@ -0,0 +1,60 @@
+//
+//  SPSessionConfigurationUpdate.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class SessionConfigurationUpdate: SessionConfiguration {
+    var sourceConfig: SessionConfiguration?
+    var isPaused = false
+    var foregroundTimeoutInSecondsUpdated = false
+    var backgroundTimeoutInSecondsUpdated = false
+    var onSessionStateUpdateUpdated = false
+
+    override var foregroundTimeoutInSeconds: Int {
+        get {
+            return ((sourceConfig == nil || foregroundTimeoutInSecondsUpdated) ? super.foregroundTimeoutInSeconds : sourceConfig?.foregroundTimeoutInSeconds) ?? 1800
+        }
+        set {
+            super.foregroundTimeoutInSeconds = newValue
+            foregroundTimeoutInSecondsUpdated = true
+        }
+    }
+
+    override var backgroundTimeoutInSeconds: Int {
+        get {
+            return ((sourceConfig == nil || backgroundTimeoutInSecondsUpdated) ? super.backgroundTimeoutInSeconds : sourceConfig?.backgroundTimeoutInSeconds) ?? 1800
+        }
+        set {
+            super.backgroundTimeoutInSeconds = newValue
+            backgroundTimeoutInSecondsUpdated = true
+        }
+    }
+
+    override var onSessionStateUpdate: ((_ sessionState: SessionState) -> Void)? {
+        get {
+            return ((sourceConfig == nil || onSessionStateUpdateUpdated) ? super.onSessionStateUpdate : sourceConfig?.onSessionStateUpdate)
+        }
+        set {
+            super.onSessionStateUpdate = newValue
+            onSessionStateUpdateUpdated = true
+        }
+    }
+}
diff --git a/Sources/Core/Session/SessionControllerImpl.swift b/Sources/Core/Session/SessionControllerImpl.swift
new file mode 100644
index 000000000..35c62647d
--- /dev/null
+++ b/Sources/Core/Session/SessionControllerImpl.swift
@@ -0,0 +1,177 @@
+//
+//  SPSessionControllerImpl.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class SessionControllerImpl: Controller, SessionController {
+    var isEnabled: Bool {
+        return session != nil
+    }
+
+    func pause() {
+        dirtyConfig.isPaused = true
+        session?.stopChecker()
+    }
+
+    func resume() {
+        dirtyConfig.isPaused = false
+        session?.startChecker()
+    }
+
+    func startNewSession() {
+        session?.startNewSession()
+    }
+
+    // MARK: - Properties
+
+    var foregroundTimeout: Measurement<UnitDuration> {
+        get {
+            return Measurement(value: Double(foregroundTimeoutInSeconds), unit: .seconds)
+        }
+        set {
+            let foreground = newValue.converted(to: .seconds)
+            foregroundTimeoutInSeconds = Int(foreground.value)
+        }
+    }
+
+    var foregroundTimeoutInSeconds: Int {
+        get {
+            if let session = session {
+                if isEnabled {
+                    return Int(session.foregroundTimeout / 1000)
+                } else {
+                    logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+                }
+            }
+            return -1
+        }
+        set {
+            dirtyConfig.foregroundTimeoutInSeconds = newValue
+            dirtyConfig.foregroundTimeoutInSecondsUpdated = true
+            session?.foregroundTimeout = newValue * 1000
+        }
+    }
+
+    var backgroundTimeout: Measurement<UnitDuration> {
+        get {
+            return Measurement(value: Double(backgroundTimeoutInSeconds), unit: .seconds)
+        }
+        set {
+            let background = newValue.converted(to: .seconds)
+            backgroundTimeoutInSeconds = Int(background.value)
+        }
+    }
+
+    var backgroundTimeoutInSeconds: Int {
+        get {
+            if let session = session {
+                if isEnabled {
+                    return Int(session.backgroundTimeout / 1000)
+                } else {
+                    logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+                }
+            }
+            return -1
+        }
+        set {
+            dirtyConfig.backgroundTimeoutInSeconds = newValue
+            dirtyConfig.backgroundTimeoutInSecondsUpdated = true
+            session?.backgroundTimeout = newValue * 1000
+        }
+    }
+
+    var onSessionStateUpdate: ((_ sessionState: SessionState) -> Void)? {
+        get {
+            if !isEnabled {
+                logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+                return nil
+            }
+            return session?.onSessionStateUpdate
+        }
+        set {
+            dirtyConfig.onSessionStateUpdate = newValue
+            dirtyConfig.onSessionStateUpdateUpdated = true
+            session?.onSessionStateUpdate = newValue
+        }
+    }
+
+    var sessionIndex: Int {
+        if !isEnabled {
+            logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+            return -1
+        }
+        return session?.state?.sessionIndex ?? -1
+    }
+
+    var sessionId: String? {
+        if !isEnabled {
+            logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+            return nil
+        }
+        return session?.state?.sessionId
+    }
+
+    var userId: String? {
+        if !isEnabled {
+            logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+            return nil
+        }
+        return session?.userId
+    }
+
+    var isInBackground: Bool {
+        if !isEnabled {
+            logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+            return false
+        }
+        return session?.inBackground ?? false
+    }
+
+    var backgroundIndex: Int {
+        if !isEnabled {
+            logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+            return -1
+        }
+        return session?.backgroundIndex ?? -1
+    }
+
+    var foregroundIndex: Int {
+        if !isEnabled {
+            logDiagnostic(message: "Attempt to access SessionController fields when disabled")
+            return -1
+        }
+        return session?.foregroundIndex ?? -1
+    }
+
+    // MARK: - Private methods
+
+    private var session: Session? {
+        get {
+            return serviceProvider.tracker.session
+        }
+    }
+
+    private var dirtyConfig: SessionConfigurationUpdate {
+        get {
+            return serviceProvider.sessionConfigurationUpdate
+        }
+    }
+}
diff --git a/Sources/Core/Storage/MemoryEventStore.swift b/Sources/Core/Storage/MemoryEventStore.swift
new file mode 100644
index 000000000..3ee6e93f1
--- /dev/null
+++ b/Sources/Core/Storage/MemoryEventStore.swift
@@ -0,0 +1,107 @@
+//
+//  SPMemoryEventStore.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class MemoryEventStore: NSObject, EventStore {
+
+    var sendLimit: UInt
+    var index: Int64
+    var orderedSet: NSMutableOrderedSet
+
+
+    convenience override init() {
+        self.init(limit: 250)
+    }
+
+    init(limit: UInt) {
+        orderedSet = NSMutableOrderedSet()
+        sendLimit = limit
+        index = 0
+    }
+
+    // Interface methods
+
+    func addEvent(_ payload: Payload) {
+        objc_sync_enter(self)
+        let item = EmitterEvent(payload: payload, storeId: index)
+        orderedSet.add(item)
+        objc_sync_exit(self)
+        index += 1
+    }
+
+    func count() -> UInt {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        return UInt(orderedSet.count)
+    }
+
+    func emittableEvents(withQueryLimit queryLimit: UInt) -> [EmitterEvent] {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        let setCount = (orderedSet).count
+        if setCount <= 0 {
+            return []
+        }
+        let len = min(Int(queryLimit), setCount)
+        _ = NSRange(location: 0, length: len)
+        var count = 0
+        let indexes = orderedSet.indexes { _, _, _ in
+            count += 1
+            return count <= queryLimit
+        }
+        let objects = orderedSet.objects(at: indexes)
+        var result: [EmitterEvent] = []
+        for object in objects {
+            if let event = object as? EmitterEvent {
+                result.append(event)
+            }
+        }
+        return result
+    }
+
+    func removeAllEvents() -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        orderedSet.removeAllObjects()
+        return true
+    }
+
+    func removeEvent(withId storeId: Int64) -> Bool {
+        return removeEvents(withIds: [NSNumber(value: storeId)])
+    }
+
+    func removeEvents(withIds storeIds: [NSNumber]) -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        var itemsToRemove: [EmitterEvent] = []
+        for item in orderedSet {
+            guard let item = item as? EmitterEvent else {
+                continue
+            }
+            if storeIds.contains(NSNumber(value: item.storeId)) {
+                itemsToRemove.append(item)
+            }
+        }
+        orderedSet.removeObjects(in: itemsToRemove)
+        return true
+    }
+}
diff --git a/Sources/Core/Storage/SQLiteEventStore.swift b/Sources/Core/Storage/SQLiteEventStore.swift
new file mode 100644
index 000000000..68dbd95b8
--- /dev/null
+++ b/Sources/Core/Storage/SQLiteEventStore.swift
@@ -0,0 +1,292 @@
+//
+//  SQLiteEventStore.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+#if os(iOS) || os(macOS)
+
+import FMDB
+import Foundation
+
+let _queryCreateTable = "CREATE TABLE IF NOT EXISTS 'events' (id INTEGER PRIMARY KEY, eventData BLOB, dateCreated TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"
+let _querySelectAll = "SELECT * FROM 'events'"
+let _querySelectCount = "SELECT Count(*) FROM 'events'"
+let _queryInsertEvent = "INSERT INTO 'events' (eventData) VALUES (?)"
+let _querySelectId = "SELECT * FROM 'events' WHERE id=?"
+let _queryDeleteId = "DELETE FROM 'events' WHERE id=?"
+let _queryDeleteIds = "DELETE FROM 'events' WHERE id IN (%@)"
+let _queryDeleteAll = "DELETE FROM 'events'"
+
+class SQLiteEventStore: NSObject, EventStore {
+    var namespace: String
+    var sqliteFilename: String
+    var dbPath: String
+    var queue: FMDatabaseQueue?
+    var sendLimit: Int
+
+    /// IMPORTANT: This method is for internal use only. It's signature and behaviour might change in any
+    /// future tracker release.
+    ///
+    /// Clears all the EventStores not associated at any of the namespaces passed as parameter.
+    ///
+    /// - Parameter allowedNamespaces: The namespace allowed. All the EventStores not associated at any of
+    ///                          the allowedNamespaces will be cleared.
+    /// - Returns: The list of namespaces that have been found with EventStores and have been cleared out.
+    class func removeUnsentEventsExcept(forNamespaces allowedNamespaces: [String]?) -> [String]? {
+        #if os(tvOS)
+        let libraryPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).map(\.path)[0]
+        #else
+        let libraryPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).map(\.path)[0]
+        #endif
+        let snowplowDirPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplow").path
+        var files: [String]? = nil
+        do {
+            files = try FileManager.default.contentsOfDirectory(atPath: snowplowDirPath)
+        } catch {
+        }
+        var allowedFiles: [String]? = []
+        for namespace in allowedNamespaces ?? [] {
+            var regex: NSRegularExpression? = nil
+            do {
+                regex = try NSRegularExpression(pattern: "[^a-zA-Z0-9_]+", options: [])
+            } catch {
+            }
+            let sqliteSuffix = regex?.stringByReplacingMatches(in: namespace, options: [], range: NSRange(location: 0, length: namespace.count), withTemplate: "-")
+            let sqliteFilename = "snowplowEvents-\(sqliteSuffix ?? "").sqlite"
+            allowedFiles?.append(sqliteFilename)
+        }
+        var removedFiles: [String]? = []
+        for file in files ?? [] {
+            if !(allowedFiles?.contains(file) ?? false) {
+                let pathToRemove = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent(file).path
+                try? FileManager.default.removeItem(atPath: pathToRemove)
+                removedFiles?.append(file)
+            }
+        }
+        return removedFiles
+    }
+
+    ///  Basic initializer that creates a database event table (if one does not exist) and then closes the connection.
+    convenience init(namespace: String?) {
+        self.init(namespace: namespace, limit: 250)
+    }
+
+    init(namespace: String?, limit: Int) {
+        self.namespace = namespace ?? ""
+        sendLimit = limit
+
+        #if os(tvOS)
+        let libraryPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).map(\.path)[0]
+        #else
+        let libraryPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).map(\.path)[0]
+        #endif
+        // Create snowplow subdirectory if it doesn't exist
+        let snowplowDirPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplow").path
+        try? FileManager.default.createDirectory(atPath: snowplowDirPath, withIntermediateDirectories: true, attributes: nil)
+
+        // Create path for the database
+        var regex: NSRegularExpression? = nil
+        do {
+            regex = try NSRegularExpression(pattern: "[^a-zA-Z0-9_]+", options: [])
+        } catch {
+        }
+        let sqliteSuffix = regex?.stringByReplacingMatches(in: self.namespace, options: [], range: NSRange(location: 0, length: namespace?.count ?? 0), withTemplate: "-")
+        sqliteFilename = "snowplowEvents-\(sqliteSuffix ?? "").sqlite"
+        dbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent(sqliteFilename).path
+
+        // Migrate old database if it exists
+        let oldDbPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplowEvents.sqlite").path
+        if FileManager.default.fileExists(atPath: oldDbPath) {
+            try? FileManager.default.moveItem(atPath: oldDbPath, toPath: dbPath)
+        }
+
+        // Create database
+        queue = FMDatabaseQueue(path: dbPath)
+        super.init()
+        _ = createTable()
+    }
+
+    deinit {
+        queue?.close()
+    }
+
+    // MARK: SPEventStore implementation methods
+
+    func addEvent(_ payload: Payload) {
+        _ = insertDictionaryData(payload.dictionary)
+    }
+
+    func removeEvent(withId storeId: Int64) -> Bool {
+        var res = false
+        queue?.inDatabase({ db in
+            if db.open() {
+                logDebug(message: String(format: "Removing %d from database now.", storeId))
+                res = db.executeUpdate(_queryDeleteId, withArgumentsIn: [storeId])
+            }
+        })
+        return res
+    }
+
+    func removeEvents(withIds storeIds: [NSNumber]) -> Bool {
+        var res = false
+        queue?.inDatabase({ db in
+            if db.open() && storeIds.count != 0 {
+                let ids = storeIds.map { $0.stringValue }.joined(separator: ",")
+                logDebug(message: String(format: "Removing [%@] from database now.", ids))
+                let query = String(format: _queryDeleteIds, ids)
+                res = db.executeUpdate(query, withArgumentsIn: [])
+            }
+        })
+        return res
+    }
+
+    func removeAllEvents() -> Bool {
+        var res = false
+        queue?.inDatabase({ db in
+            if db.open() {
+                logDebug(message: "Removing all events from database now.")
+                res = db.executeUpdate(_queryDeleteAll, withArgumentsIn: [])
+            }
+        })
+        return res
+    }
+
+    func count() -> UInt {
+        var num: UInt = 0
+        queue?.inDatabase({ db in
+            if db.open() {
+                if let s = db.executeQuery(_querySelectCount, withArgumentsIn: []) {
+                    while s.next() {
+                        num = NSNumber(value: s.int(forColumnIndex: 0)).uintValue
+                    }
+                    s.close()
+                }
+            }
+        })
+        return num
+    }
+
+    func emittableEvents(withQueryLimit queryLimit: UInt) -> [EmitterEvent] {
+        return getAllEventsLimited(UInt(sendLimit)) ?? []
+    }
+
+    // MARK: SPSQLiteEventStore methods
+
+    func createTable() -> Bool {
+        var res = false
+        queue?.inDatabase({ db in
+            if db.open() {
+                res = db.executeStatements(_queryCreateTable)
+            }
+        })
+        return res
+    }
+
+    ///  Inserts events into the sqlite table for the app identified with it's bundleId (appId).
+    ///  - Parameter payload: A SnowplowPayload instance to be inserted into the database.
+    ///  - Returns: If the insert was successful, we return the rowId of the inserted entry, otherwise -1. We explicitly do this in the case of an error, sqlite would return the previous successful insert leading to incorrect data removals.
+    func insertEvent(_ payload: Payload?) -> Int64 {
+        return insertDictionaryData(payload?.dictionary)
+    }
+
+    func insertDictionaryData(_ dict: [AnyHashable : Any]?) -> Int64 {
+        var res: Int64 = -1
+        if dict == nil {
+            return res
+        }
+        queue?.inDatabase({ db in
+            if db.open() {
+                if let dict = dict,
+                   let data = try? JSONSerialization.data(withJSONObject: dict) {
+                    try? db.executeUpdate(_queryInsertEvent, values: [data])
+                    res = db.lastInsertRowId
+                }
+            }
+        })
+        return res
+    }
+
+    ///  Finds the row in the event table with the supplied ID.
+    ///  - Parameter id_: Unique ID of the row in the events table to be returned.
+    ///  - Returns: A dictionary containing data with keys: 'ID', 'eventData', and 'dateCreated'.
+    func getEventWithId(_ id_: Int64) -> EmitterEvent? {
+        var event: EmitterEvent? = nil
+        queue?.inDatabase({ db in
+            if db.open() {
+                if let s = try? db.executeQuery(_querySelectId, values: [id_]) {
+                    while s.next() {
+                        if let data = s.data(forColumn: "eventData"),
+                           let dict = try? JSONSerialization.jsonObject(with: data) as? [String: NSObject] {
+                            let payload = Payload(dictionary: dict)
+                            event = EmitterEvent(payload: payload, storeId: id_)
+                        }
+                    }
+                    s.close()
+                }
+            }
+        })
+        return event
+    }
+
+    ///  Returns all the events in an array of dictionaries.
+    ///  - Returns: An array with each dictionary element containing key-value pairs of 'date', 'data', 'ID'.
+    func getAllEvents() -> [EmitterEvent]? {
+        return self.getAllEvents(withQuery: _querySelectAll)
+    }
+
+    ///  Returns limited number the events that are NOT pending in an array of dictionaries.
+    ///  - Returns: An array with each dictionary element containing key-value pairs of 'date', 'data', 'ID'.
+    func getAllEventsLimited(_ limit: UInt) -> [EmitterEvent]? {
+        let query = "\(_querySelectAll) LIMIT \((NSNumber(value: limit)).stringValue)"
+        return getAllEvents(withQuery: query)
+    }
+
+    func getAllEvents(withQuery query: String) -> [EmitterEvent]? {
+        var res: [EmitterEvent] = []
+        queue?.inDatabase({ db in
+            if db.open() {
+                if let s = try? db.executeQuery(query, values: []) {
+                    while s.next() {
+                        let index = s.longLongInt(forColumn: "ID")
+                        if let data = s.data(forColumn: "eventData"),
+                           let dict = try? JSONSerialization.jsonObject(with: data) as? [String: NSObject] {
+                            let payload = Payload(dictionary: dict)
+                            let event = EmitterEvent(payload: payload, storeId: index)
+                            res.append(event)
+                        }
+                    }
+                    s.close()
+                }
+            }
+        })
+        return res
+    }
+
+    ///  The row ID of the last insert made.
+    ///  - Returns: The row ID of the last insert made.
+    func getLastInsertedRowId() -> Int64 {
+        var res: Int64 = -1
+        queue?.inDatabase({ db in
+            res = db.lastInsertRowId
+        })
+        return res
+    }
+}
+
+#endif
diff --git a/Sources/Core/Subject/PlatformContext.swift b/Sources/Core/Subject/PlatformContext.swift
new file mode 100644
index 000000000..7e40816dd
--- /dev/null
+++ b/Sources/Core/Subject/PlatformContext.swift
@@ -0,0 +1,153 @@
+//
+//  PlatformContext.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+#if os(iOS)
+import UIKit
+#endif
+
+/// @class PlatformContext
+/// Manages a dictionary (Payload) with platform context. Some properties for mobile platforms are updated on fetch in set intervals.
+class PlatformContext {
+    private var platformDict: Payload = Payload()
+    private var mobileDictUpdateFrequency: TimeInterval = 0.0
+    private var networkDictUpdateFrequency: TimeInterval = 0.0
+    private var lastUpdatedEphemeralMobileDict: TimeInterval = 0.0
+    private var lastUpdatedEphemeralNetworkDict: TimeInterval = 0.0
+    private var deviceInfoMonitor: DeviceInfoMonitor
+
+    /// Initializes a newly allocated PlatformContext object with default update frequency
+    /// - Returns: a PlatformContext object
+    convenience init() {
+        self.init(mobileDictUpdateFrequency: 0.1, networkDictUpdateFrequency: 10.0)
+    }
+
+    /// Initializes a newly allocated PlatformContext object with custom update frequency for mobile and network properties
+    /// - Parameters:
+    ///   - mobileDictUpdateFrequency: Minimal gap between subsequent updates of mobile platform information
+    ///   - networkDictUpdateFrequency: Minimal gap between subsequent updates of network platform information
+    /// - Returns: a PlatformContext object
+    convenience init(mobileDictUpdateFrequency: TimeInterval, networkDictUpdateFrequency: TimeInterval) {
+        self.init(mobileDictUpdateFrequency: mobileDictUpdateFrequency,
+                  networkDictUpdateFrequency: networkDictUpdateFrequency,
+                  deviceInfoMonitor: DeviceInfoMonitor())
+    }
+
+    /// Initializes a newly allocated PlatformContext object with custom update frequency for mobile and network properties and a custom device info monitor
+    /// - Parameters:
+    ///   - mobileDictUpdateFrequency: Minimal gap between subsequent updates of mobile platform information
+    ///   - networkDictUpdateFrequency: Minimal gap between subsequent updates of network platform information
+    ///   - deviceInfoMonitor: Device monitor for fetching platform information
+    /// - Returns: a PlatformContext object
+    init(mobileDictUpdateFrequency: TimeInterval, networkDictUpdateFrequency: TimeInterval, deviceInfoMonitor: DeviceInfoMonitor) {
+        self.mobileDictUpdateFrequency = mobileDictUpdateFrequency
+        self.networkDictUpdateFrequency = networkDictUpdateFrequency
+        self.deviceInfoMonitor = deviceInfoMonitor
+        #if os(iOS)
+        UIDevice.current.isBatteryMonitoringEnabled = true
+        #endif
+        setPlatformDict()
+    }
+
+    /// Updates and returns payload dictionary with device context information.
+    /// - Parameter userAnonymisation: Whether to anonymise user identifiers (IDFA values)
+    func fetchPlatformDict(withUserAnonymisation userAnonymisation: Bool) -> Payload {
+        #if os(iOS)
+        objc_sync_enter(self)
+        let now = Date().timeIntervalSince1970
+        if now - lastUpdatedEphemeralMobileDict >= mobileDictUpdateFrequency {
+            setEphemeralMobileDict()
+        }
+        if now - lastUpdatedEphemeralNetworkDict >= networkDictUpdateFrequency {
+            setEphemeralNetworkDict()
+        }
+        objc_sync_exit(self)
+        #endif
+        if userAnonymisation {
+            // mask user identifiers
+            let copy = Payload(dictionary: platformDict.dictionary ?? [:])
+            copy.addValueToPayload(nil, forKey: kSPMobileAppleIdfa)
+            copy.addValueToPayload(nil, forKey: kSPMobileAppleIdfv)
+            return copy
+        } else {
+            return platformDict
+        }
+    }
+
+    // MARK: - Private methods
+
+    func setPlatformDict() {
+        platformDict = Payload()
+        platformDict.addValueToPayload(deviceInfoMonitor.osType, forKey: kSPPlatformOsType)
+        platformDict.addValueToPayload(deviceInfoMonitor.osVersion, forKey: kSPPlatformOsVersion)
+        platformDict.addValueToPayload(deviceInfoMonitor.deviceVendor, forKey: kSPPlatformDeviceManu)
+        platformDict.addValueToPayload(deviceInfoMonitor.deviceModel, forKey: kSPPlatformDeviceModel)
+
+        #if os(iOS)
+        setMobileDict()
+        #endif
+    }
+
+    func setMobileDict() {
+        platformDict.addValueToPayload(deviceInfoMonitor.carrierName, forKey: kSPMobileCarrier)
+        if let totalStorage = deviceInfoMonitor.totalStorage {
+            platformDict.addNumericValueToPayload(NSNumber(value: totalStorage), forKey: kSPMobileTotalStorage)
+        }
+        platformDict.addNumericValueToPayload(NSNumber(value: deviceInfoMonitor.physicalMemory), forKey: kSPMobilePhysicalMemory)
+        
+        setEphemeralMobileDict()
+        setEphemeralNetworkDict()
+    }
+
+    func setEphemeralMobileDict() {
+        lastUpdatedEphemeralMobileDict = Date().timeIntervalSince1970
+
+        if let currentDict = platformDict.dictionary {
+            if currentDict[kSPMobileAppleIdfa] == nil {
+                platformDict.addValueToPayload(deviceInfoMonitor.appleIdfa, forKey: kSPMobileAppleIdfa)
+            }
+            if currentDict[kSPMobileAppleIdfv] == nil {
+                platformDict.addValueToPayload(deviceInfoMonitor.appleIdfv, forKey: kSPMobileAppleIdfv)
+            }
+            
+            if let batteryLevel = deviceInfoMonitor.batteryLevel {
+                platformDict.addNumericValueToPayload(NSNumber(value: batteryLevel), forKey: kSPMobileBatteryLevel)
+            }
+            platformDict.addValueToPayload(deviceInfoMonitor.batteryState, forKey: kSPMobileBatteryState)
+            if let isLowPowerModeEnabled = deviceInfoMonitor.isLowPowerModeEnabled {
+                platformDict.addNumericValueToPayload(NSNumber(value: isLowPowerModeEnabled), forKey: kSPMobileLowPowerMode)
+            }
+            if let availableStorage = deviceInfoMonitor.availableStorage {
+                platformDict.addNumericValueToPayload(NSNumber(value: availableStorage), forKey: kSPMobileAvailableStorage)
+            }
+            if let appAvailableMemory = deviceInfoMonitor.appAvailableMemory {
+                platformDict.addNumericValueToPayload(NSNumber(value: appAvailableMemory), forKey: kSPMobileAppAvailableMemory)
+            }
+        }
+    }
+
+    func setEphemeralNetworkDict() {
+        lastUpdatedEphemeralNetworkDict = Date().timeIntervalSince1970
+
+        platformDict.addValueToPayload(deviceInfoMonitor.networkTechnology, forKey: kSPMobileNetworkTech)
+        platformDict.addValueToPayload(deviceInfoMonitor.networkType, forKey: kSPMobileNetworkType)
+    }
+}
diff --git a/Sources/Core/Subject/Subject.swift b/Sources/Core/Subject/Subject.swift
new file mode 100644
index 000000000..07152ce16
--- /dev/null
+++ b/Sources/Core/Subject/Subject.swift
@@ -0,0 +1,338 @@
+//
+//  Subject.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// @class Subject
+/// This class is used to access and persist user information, it represents the current user being tracked.
+class Subject : NSObject {
+    private var standardDict = Payload()
+    private var platformContextManager = PlatformContext()
+    private var geoLocationDict: [String : NSObject] = [:]
+
+    var platformContext = false
+    var geoLocationContext = false
+    
+    // MARK: - Standard Dictionary
+    
+    /// Sets the standard pairs for the Subject, called automatically on object creation.
+
+    private var _userId: String?
+    /// The user's ID.
+    var userId: String? {
+        get {
+            _userId
+        }
+        set(uid) {
+            _userId = uid
+            standardDict.addValueToPayload(uid, forKey: kSPUid)
+        }
+    }
+
+    private var _networkUserId: String?
+    var networkUserId: String? {
+        get {
+            _networkUserId
+        }
+        set(nuid) {
+            _networkUserId = nuid
+            standardDict.addValueToPayload(nuid, forKey: kSPNetworkUid)
+        }
+    }
+
+    private var _domainUserId: String?
+    /// The domain UID.
+    var domainUserId: String? {
+        get {
+            _domainUserId
+        }
+        set(duid) {
+            _domainUserId = duid
+            standardDict.addValueToPayload(duid, forKey: kSPDomainUid)
+        }
+    }
+
+    private var _useragent: String?
+    /// The user agent (also known as browser string).
+    var useragent: String? {
+        get {
+            _useragent
+        }
+        set(useragent) {
+            _useragent = useragent
+            standardDict.addValueToPayload(useragent, forKey: kSPUseragent)
+        }
+    }
+
+    private var _ipAddress: String?
+    /// The user's IP address.
+    var ipAddress: String? {
+        get {
+            _ipAddress
+        }
+        set(ip) {
+            _ipAddress = ip
+            standardDict.addValueToPayload(ip, forKey: kSPIpAddress)
+        }
+    }
+
+    private var _timezone: String?
+    /// The user's timezone.
+    var timezone: String? {
+        get {
+            _timezone
+        }
+        set(timezone) {
+            _timezone = timezone
+            standardDict.addValueToPayload(timezone, forKey: kSPTimezone)
+        }
+    }
+
+    private var _language: String?
+    /// The user's language.
+    var language: String? {
+        get {
+            _language
+        }
+        set(lang) {
+            _language = lang
+            standardDict.addValueToPayload(lang, forKey: kSPLanguage)
+        }
+    }
+
+    private var _colorDepth: NSNumber?
+    /// The user's color depth.
+    var colorDepth: NSNumber? {
+        get {
+            _colorDepth
+        }
+        set(depth) {
+            _colorDepth = depth
+            let res = "\(depth?.stringValue ?? "")"
+            standardDict.addValueToPayload(res, forKey: kSPColorDepth)
+        }
+    }
+
+    var _screenResolution: SPSize?
+    var screenResolution: SPSize? {
+        get {
+            _screenResolution
+        }
+        set {
+            _screenResolution = newValue
+            if let size = newValue {
+                let res = "\((NSNumber(value: size.width)).stringValue)x\((NSNumber(value: size.height)).stringValue)"
+                standardDict.addValueToPayload(res, forKey: kSPResolution)
+            } else {
+                standardDict.addValueToPayload(nil, forKey: kSPResolution)
+            }
+        }
+    }
+
+    var _screenViewPort: SPSize?
+    var screenViewPort: SPSize? {
+        get {
+            _screenViewPort
+        }
+        set {
+            _screenViewPort = newValue
+            if let size = newValue {
+                let res = "\((NSNumber(value: size.width)).stringValue)x\((NSNumber(value: size.height)).stringValue)"
+                standardDict.addValueToPayload(res, forKey: kSPViewPort)
+            } else {
+                standardDict.addValueToPayload(nil, forKey: kSPViewPort)
+            }
+            
+        }
+    }
+    
+    // MARK: - GeoLocation Dictionary
+
+    /// Optional geolocation context, if run will allocate memory for the geolocation context
+
+    /// Latitude value for the geolocation context.
+    var geoLatitude: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoLatitude] as? NSNumber
+        }
+        set(latitude) {
+            geoLocationDict[kSPGeoLatitude] = latitude
+        }
+    }
+
+    /// Longitude value for the geo context.
+    var geoLongitude: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoLongitude] as? NSNumber
+        }
+        set(longitude) {
+            geoLocationDict[kSPGeoLongitude] = longitude
+        }
+    }
+
+    /// LatitudeLongitudeAccuracy value for the geolocation context.
+    var geoLatitudeLongitudeAccuracy: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoLatLongAccuracy] as? NSNumber
+        }
+        set(latitudeLongitudeAccuracy) {
+            geoLocationDict[kSPGeoLatLongAccuracy] = latitudeLongitudeAccuracy
+        }
+    }
+
+    /// Altitude value for the geolocation context.
+    var geoAltitude: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoAltitude] as? NSNumber
+        }
+        set(altitude) {
+            geoLocationDict[kSPGeoAltitude] = altitude
+        }
+    }
+
+    /// AltitudeAccuracy value for the geolocation context.
+    var geoAltitudeAccuracy: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoAltitudeAccuracy] as? NSNumber
+        }
+        set(altitudeAccuracy) {
+            geoLocationDict[kSPGeoAltitudeAccuracy] = altitudeAccuracy
+        }
+    }
+
+    var geoBearing: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoBearing] as? NSNumber
+        }
+        set(bearing) {
+            geoLocationDict[kSPGeoBearing] = bearing
+        }
+    }
+
+    /// Speed value for the geolocation context.
+    var geoSpeed: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoSpeed] as? NSNumber
+        }
+        set(speed) {
+            geoLocationDict[kSPGeoSpeed] = speed
+        }
+    }
+
+    /// Timestamp value for the geolocation context.
+    var geoTimestamp: NSNumber? {
+        get {
+            return geoLocationDict[kSPGeoTimestamp] as? NSNumber
+        }
+        set(timestamp) {
+            geoLocationDict[kSPGeoTimestamp] = timestamp
+        }
+    }
+
+    /// Creates a subject which optionally adds platform and geolocation pairs.
+    /// - Parameters:
+    ///   - platformContext: Whether to enable the platform context.
+    ///   - geoContext: Whether to enabled the geolocation context.
+    /// - Returns: A new SPSubject.
+    convenience init(platformContext: Bool = false, andGeoContext geoContext: Bool = false) {
+        self.init(platformContext: platformContext, geoLocationContext: geoContext, subjectConfiguration: nil)
+    }
+
+    /// @warning Internal method - do not use in production
+    init(platformContext: Bool, geoLocationContext geoContext: Bool, subjectConfiguration config: SubjectConfiguration?) {
+        super.init()
+        
+        self.platformContext = platformContext
+        geoLocationContext = geoContext
+        
+        screenResolution = Utilities.resolution
+        screenViewPort = Utilities.viewPort
+        language = Utilities.language
+        timezone = Utilities.timezone
+        
+        if let config = config {
+            if let v = config.userId { userId = v }
+            if let v = config.networkUserId { networkUserId = v }
+            if let v = config.domainUserId { domainUserId = v }
+            if let v = config.useragent { useragent = v }
+            if let v = config.ipAddress { ipAddress = v }
+            if let v = config.timezone { timezone = v }
+            if let v = config.language { language = v }
+            if let v = config.screenResolution { screenResolution = v }
+            if let v = config.screenViewPort { screenViewPort = v }
+            if let v = config.colorDepth { colorDepth = v }
+            
+            // geolocation
+            if let v = config.geoLatitude { geoLatitude = v }
+            if let v = config.geoLongitude { geoLongitude = v }
+            if let v = config.geoLatitudeLongitudeAccuracy { geoLatitudeLongitudeAccuracy = v }
+            if let v = config.geoAltitude { geoAltitude = v }
+            if let v = config.geoAltitudeAccuracy { geoAltitudeAccuracy = v }
+            if let v = config.geoSpeed { geoSpeed = v }
+            if let v = config.geoBearing { geoBearing = v }
+            if let v = config.geoTimestamp { geoTimestamp = v }
+        }
+    }
+
+    /// Gets all standard dictionary pairs to decorate the event with.
+    /// - Parameter userAnonymisation: Whether to anonymise user identifiers
+    /// - Returns: A SPPayload with all standard pairs.
+
+    //#pragma clang diagnostic pop
+
+    func getStandardDict(withUserAnonymisation userAnonymisation: Bool) -> Payload? {
+        if userAnonymisation {
+            var copy = standardDict.dictionary ?? [:]
+            copy.removeValue(forKey: kSPUid)
+            copy.removeValue(forKey: kSPDomainUid)
+            copy.removeValue(forKey: kSPNetworkUid)
+            copy.removeValue(forKey: kSPIpAddress)
+            return Payload(dictionary: copy)
+        }
+        return standardDict
+    }
+
+    /// Gets all platform dictionary pairs to decorate event with. Returns nil if not enabled.
+    /// - Parameter userAnonymisation: Whether to anonymise user identifiers
+    /// - Returns: A SPPayload with all platform specific pairs.
+    func getPlatformDict(withUserAnonymisation userAnonymisation: Bool) -> Payload? {
+        if platformContext {
+            return platformContextManager.fetchPlatformDict(withUserAnonymisation: userAnonymisation)
+        } else {
+            return nil
+        }
+    }
+
+    /// Gets the geolocation dictionary if the required keys are available. Returns nil if not enabled.
+    /// - Returns: A dictionary with key-value pairs of the geolocation context.
+    func getGeoLocationDict() -> [String : NSObject]? {
+        if geoLocationContext {
+            if geoLocationDict[kSPGeoLatitude] != nil && geoLocationDict[kSPGeoLongitude] != nil {
+                return geoLocationDict
+            } else {
+                logDebug(message: "GeoLocation missing required fields; cannot get.")
+                return nil
+            }
+        } else {
+            return nil
+        }
+    }
+}
diff --git a/Sources/Core/Subject/SubjectConfigurationUpdate.swift b/Sources/Core/Subject/SubjectConfigurationUpdate.swift
new file mode 100644
index 000000000..4fb8a362b
--- /dev/null
+++ b/Sources/Core/Subject/SubjectConfigurationUpdate.swift
@@ -0,0 +1,135 @@
+//  SPSubjectConfigurationUpdate.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class SubjectConfigurationUpdate: SubjectConfiguration {
+    var sourceConfig: SubjectConfiguration?
+    var userIdUpdated = false
+    var networkUserIdUpdated = false
+    var domainUserIdUpdated = false
+    var useragentUpdated = false
+    var ipAddressUpdated = false
+    var timezoneUpdated = false
+    var languageUpdated = false
+    var screenResolutionUpdated = false
+    var screenViewPortUpdated = false
+    var colorDepthUpdated = false
+
+    override var userId: String? {
+        get {
+            return ((sourceConfig == nil || userIdUpdated) ? super.userId : sourceConfig?.userId)
+        }
+        set {
+            super.userId = newValue
+            userIdUpdated = true
+        }
+    }
+
+    override var networkUserId: String? {
+        get {
+            return ((sourceConfig == nil || networkUserIdUpdated) ? super.networkUserId : sourceConfig?.networkUserId)
+        }
+        set {
+            super.networkUserId = newValue
+            networkUserIdUpdated = true
+        }
+    }
+
+    override var domainUserId: String? {
+        get {
+            return ((sourceConfig == nil || domainUserIdUpdated) ? super.domainUserId : sourceConfig?.domainUserId)
+        }
+        set {
+            super.domainUserId = newValue
+            domainUserIdUpdated = true
+        }
+    }
+
+    override var useragent: String? {
+        get {
+            return ((sourceConfig == nil || useragentUpdated) ? super.useragent : sourceConfig?.useragent)
+        }
+        set {
+            super.useragent = newValue
+            useragentUpdated = true
+        }
+    }
+
+    override var ipAddress: String? {
+        get {
+            return ((sourceConfig == nil || ipAddressUpdated) ? super.ipAddress : sourceConfig?.ipAddress)
+        }
+        set {
+            super.ipAddress = newValue
+            ipAddressUpdated = true
+        }
+    }
+
+    override var timezone: String? {
+        get {
+            return ((sourceConfig == nil || timezoneUpdated) ? super.timezone : sourceConfig?.timezone)
+        }
+        set {
+            super.timezone = newValue
+            timezoneUpdated = true
+        }
+    }
+
+    override var language: String? {
+        get {
+            return ((sourceConfig == nil || languageUpdated) ? super.language : sourceConfig?.language)
+        }
+        set {
+            super.language = newValue
+            languageUpdated = true
+        }
+    }
+
+    override var screenResolution: SPSize? {
+        get {
+            return ((sourceConfig == nil || screenResolutionUpdated) ? super.screenResolution : sourceConfig?.screenResolution)
+        }
+        set {
+            super.screenResolution = newValue
+            screenResolutionUpdated = true
+        }
+    }
+
+    override var screenViewPort: SPSize? {
+        get {
+            return ((sourceConfig == nil || screenViewPortUpdated) ? super.screenViewPort : sourceConfig?.screenViewPort)
+        }
+        set {
+            super.screenViewPort = newValue
+            screenViewPortUpdated = true
+        }
+    }
+
+    override var colorDepth: NSNumber? {
+        get {
+            return ((sourceConfig == nil || colorDepthUpdated) ? super.colorDepth : sourceConfig?.colorDepth)
+        }
+        set {
+            super.colorDepth = newValue
+            colorDepthUpdated = true
+        }
+    }
+}
diff --git a/Sources/Core/Subject/SubjectControllerImpl.swift b/Sources/Core/Subject/SubjectControllerImpl.swift
new file mode 100644
index 000000000..773e35d98
--- /dev/null
+++ b/Sources/Core/Subject/SubjectControllerImpl.swift
@@ -0,0 +1,225 @@
+//
+//  SubjectControllerImpl.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class SubjectControllerImpl: Controller, SubjectController {
+    // MARK: - Properties
+
+    var userId: String? {
+        get {
+            return subject?.userId
+        }
+        set {
+            dirtyConfig.userId = newValue
+            dirtyConfig.userIdUpdated = true
+            subject?.userId = newValue
+        }
+    }
+
+    var networkUserId: String? {
+        get {
+            return subject?.networkUserId
+        }
+        set {
+            dirtyConfig.networkUserId = newValue
+            dirtyConfig.networkUserIdUpdated = true
+            subject?.networkUserId = newValue
+        }
+    }
+
+    var domainUserId: String? {
+        get {
+            return subject?.domainUserId
+        }
+        set {
+            dirtyConfig.domainUserId = newValue
+            dirtyConfig.domainUserIdUpdated = true
+            subject?.domainUserId = newValue
+        }
+    }
+
+    var useragent: String? {
+        get {
+            return subject?.useragent
+        }
+        set {
+            dirtyConfig.useragent = newValue
+            dirtyConfig.useragentUpdated = true
+            subject?.useragent = newValue
+        }
+    }
+
+    var ipAddress: String? {
+        get {
+            return subject?.ipAddress
+        }
+        set {
+            dirtyConfig.ipAddress = newValue
+            dirtyConfig.ipAddressUpdated = true
+            subject?.ipAddress = newValue
+        }
+    }
+
+    var timezone: String? {
+        get {
+            return subject?.timezone
+        }
+        set {
+            dirtyConfig.timezone = newValue
+            dirtyConfig.timezoneUpdated = true
+            subject?.timezone = newValue
+        }
+    }
+
+    var language: String? {
+        get {
+            return subject?.language
+        }
+        set {
+            dirtyConfig.language = newValue
+            dirtyConfig.languageUpdated = true
+            subject?.language = newValue
+        }
+    }
+
+    var screenResolution: SPSize? {
+        get {
+            return subject?.screenResolution
+        }
+        set {
+            dirtyConfig.screenResolution = newValue
+            dirtyConfig.screenResolutionUpdated = true
+            subject?.screenResolution = newValue
+        }
+    }
+
+    var screenViewPort: SPSize? {
+        get {
+            return subject?.screenViewPort
+        }
+        set {
+            dirtyConfig.screenViewPort = newValue
+            dirtyConfig.screenViewPortUpdated = true
+            subject?.screenViewPort = newValue
+        }
+    }
+
+    var colorDepth: NSNumber? {
+        get {
+            if let subject = subject {
+                return subject.colorDepth
+            }
+            return nil
+        }
+        set {
+            dirtyConfig.colorDepth = newValue
+            dirtyConfig.colorDepthUpdated = true
+            subject?.colorDepth = newValue
+        }
+    }
+
+    // MARK: - GeoLocalization
+
+    var geoLatitude: NSNumber? {
+        get {
+            return subject?.geoLatitude
+        }
+        set {
+            subject?.geoLatitude = newValue
+        }
+    }
+
+    var geoLongitude: NSNumber? {
+        get {
+            return subject?.geoLongitude
+        }
+        set {
+            subject?.geoLongitude = newValue
+        }
+    }
+
+    var geoLatitudeLongitudeAccuracy: NSNumber? {
+        get {
+            return subject?.geoLatitudeLongitudeAccuracy
+        }
+        set {
+            subject?.geoLatitudeLongitudeAccuracy = newValue
+        }
+    }
+
+    var geoAltitude: NSNumber? {
+        get {
+            return subject?.geoAltitude
+        }
+        set {
+            subject?.geoAltitude = newValue
+        }
+    }
+
+    var geoAltitudeAccuracy: NSNumber? {
+        get {
+            return subject?.geoAltitudeAccuracy
+        }
+        set {
+            subject?.geoAltitudeAccuracy = newValue
+        }
+    }
+
+    var geoSpeed: NSNumber? {
+        get {
+            return subject?.geoSpeed
+        }
+        set {
+            subject?.geoSpeed = newValue
+        }
+    }
+
+    var geoBearing: NSNumber? {
+        get {
+            return subject?.geoBearing
+        }
+        set {
+            subject?.geoBearing = newValue
+        }
+    }
+
+    var geoTimestamp: NSNumber? {
+        get {
+            return subject?.geoTimestamp
+        }
+        set {
+            subject?.geoTimestamp = newValue
+        }
+    }
+
+    // MARK: - Private methods
+
+    private var subject: Subject? {
+        get {
+            return serviceProvider.tracker.subject
+        }
+    }
+
+    private var dirtyConfig: SubjectConfigurationUpdate {
+        return serviceProvider.subjectConfigurationUpdate
+    }
+}
diff --git a/Snowplow/Internal/GDPR/SPGDPRControllerImpl.h b/Sources/Core/Tracker/DeepLinkState.swift
similarity index 73%
rename from Snowplow/Internal/GDPR/SPGDPRControllerImpl.h
rename to Sources/Core/Tracker/DeepLinkState.swift
index 9b450e56d..778f700ea 100644
--- a/Snowplow/Internal/GDPR/SPGDPRControllerImpl.h
+++ b/Sources/Core/Tracker/DeepLinkState.swift
@@ -1,5 +1,5 @@
 //
-//  SPGDPRControllerImpl.h
+//  SPDeepLinkState.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,15 +19,15 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPGDPRController.h"
-#import "SPController.h"
+import Foundation
 
-NS_ASSUME_NONNULL_BEGIN
+class DeepLinkState: NSObject, State {
+    private(set) var url: String
+    private(set) var referrer: String?
+    var readyForOutput = false
 
-NS_SWIFT_NAME(GDPRControllerImpl)
-@interface SPGDPRControllerImpl : SPController <SPGDPRController>
-
-@end
-
-NS_ASSUME_NONNULL_END
+    init(url: String, referrer: String?) {
+        self.url = url
+        self.referrer = referrer
+    }
+}
diff --git a/Sources/Core/Tracker/DeepLinkStateMachine.swift b/Sources/Core/Tracker/DeepLinkStateMachine.swift
new file mode 100644
index 000000000..746783741
--- /dev/null
+++ b/Sources/Core/Tracker/DeepLinkStateMachine.swift
@@ -0,0 +1,83 @@
+//
+//  DeepLinkStateMachine.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class DeepLinkStateMachine: StateMachineProtocol {
+    /*
+     States: Init, DeepLink, ReadyForOutput
+     Events: DL (DeepLinkReceived), SV (ScreenView)
+     Transitions:
+      - Init (DL) DeepLink
+      - DeepLink (SV) ReadyForOutput
+      - ReadyForOutput (DL) DeepLink
+      - ReadyForOutput (SV) Init
+     Entity Generation:
+      - ReadyForOutput
+     */
+    
+    static var identifier: String { return "DeepLinkContext" }
+    var identifier: String { return DeepLinkStateMachine.identifier }
+
+    var subscribedEventSchemasForTransitions: [String] {
+        return [DeepLinkReceived.schema, kSPScreenViewSchema]
+    }
+
+    var subscribedEventSchemasForEntitiesGeneration: [String] {
+        return [kSPScreenViewSchema]
+    }
+
+    var subscribedEventSchemasForPayloadUpdating: [String] {
+        return []
+    }
+
+    func transition(from event: Event, state: State?) -> State? {
+        if let dlEvent = event as? DeepLinkReceived {
+            return DeepLinkState(url: dlEvent.url, referrer: dlEvent.referrer)
+        } else {
+            if let dlState = state as? DeepLinkState {
+                if dlState.readyForOutput {
+                    return nil
+                }
+                let currentState = DeepLinkState(url: dlState.url, referrer: dlState.referrer)
+                currentState.readyForOutput = true
+                return currentState
+            }
+        }
+        return nil
+    }
+
+    func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]? {
+        if let deepLinkState = state as? DeepLinkState {
+            if !(deepLinkState.readyForOutput) {
+                return nil
+            }
+            let entity = DeepLinkEntity(url: deepLinkState.url)
+            entity.referrer = deepLinkState.referrer
+            return [entity]
+        }
+        return nil
+    }
+
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+        return nil
+    }
+}
diff --git a/Sources/Core/Tracker/InstallTracker.swift b/Sources/Core/Tracker/InstallTracker.swift
new file mode 100644
index 000000000..1c596203d
--- /dev/null
+++ b/Sources/Core/Tracker/InstallTracker.swift
@@ -0,0 +1,71 @@
+//
+//  InstallTracker.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class InstallTracker: NSObject {
+    /// Installation status
+    var isNewInstall = false
+
+    var previousInstallTimestamp: Date? {
+        let userDefaults = UserDefaults.standard
+        let value = userDefaults.object(forKey: kSPInstallTimestamp)
+        if value == nil {
+            return nil
+        } else if value is Date {
+            // v2.0 format
+            return value as? Date
+        } else if value is NSNumber {
+            // v1.7 format
+            let timeInterval = TimeInterval((value as? NSNumber)?.doubleValue ?? 0.0 / 1000)
+            return Date(timeIntervalSince1970: timeInterval)
+        }
+        return nil
+    }
+
+    override init() {
+        let userDefaults = UserDefaults.standard
+        if userDefaults.object(forKey: kSPInstalledBefore) == nil {
+            // mark the install if there's no value in userDefaults
+            userDefaults.set(NSNumber(value: true), forKey: kSPInstalledBefore)
+            userDefaults.set(Date(), forKey: kSPInstallTimestamp)
+            // since the value was missing in userDefaults, we're assuming this is a new install
+            isNewInstall = true
+        } else {
+            // if there's an object in standardUserDefaults - someone has been there!
+            isNewInstall = false
+        }
+    }
+
+    func clearPreviousInstallTimestamp() {
+        let userDefaults = UserDefaults.standard
+        userDefaults.removeObject(forKey: kSPInstallTimestamp)
+    }
+
+    func saveBuildAndVersion() {
+        if let build = Utilities.appBuild,
+           let version = Utilities.appVersion {
+            let userDefaults = UserDefaults.standard
+            userDefaults.set(build, forKey: kSPPreviousInstallBuild)
+            userDefaults.set(version, forKey: kSPPreviousInstallVersion)
+        }
+    }
+}
diff --git a/Snowplow/Internal/Tracker/SPLifecycleStateMachine.h b/Sources/Core/Tracker/LifecycleState.swift
similarity index 64%
rename from Snowplow/Internal/Tracker/SPLifecycleStateMachine.h
rename to Sources/Core/Tracker/LifecycleState.swift
index c331c686d..7e8264990 100644
--- a/Snowplow/Internal/Tracker/SPLifecycleStateMachine.h
+++ b/Sources/Core/Tracker/LifecycleState.swift
@@ -1,5 +1,5 @@
 //
-//  SPLifecycleStateMachine.h
+//  SPLifecycleState.swift
 //  Snowplow
 //
 // Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -18,13 +18,21 @@
 // License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPStateMachineProtocol.h"
+import Foundation
 
-NS_ASSUME_NONNULL_BEGIN
+class LifecycleState: NSObject, State {
+    private(set) var isForeground = false
+    private(set) var index: NSNumber?
 
-@interface SPLifecycleStateMachine : NSObject <SPStateMachineProtocol>
+    init(asForegroundWithIndex index: NSNumber?) {
+        super.init()
+        isForeground = true
+        self.index = index
+    }
 
-@end
-
-NS_ASSUME_NONNULL_END
+    init(asBackgroundWithIndex index: NSNumber?) {
+        super.init()
+        isForeground = false
+        self.index = index
+    }
+}
diff --git a/Sources/Core/Tracker/LifecycleStateMachine.swift b/Sources/Core/Tracker/LifecycleStateMachine.swift
new file mode 100644
index 000000000..c1d4e31d7
--- /dev/null
+++ b/Sources/Core/Tracker/LifecycleStateMachine.swift
@@ -0,0 +1,66 @@
+//
+//  SPLifecycleStateMachine.swift
+//  Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+import Foundation
+
+class LifecycleStateMachine: StateMachineProtocol {
+    static var identifier: String { return "Lifecycle" }
+    var identifier: String { return LifecycleStateMachine.identifier }
+
+    var subscribedEventSchemasForTransitions: [String] {
+        return [kSPBackgroundSchema, kSPForegroundSchema]
+    }
+
+    func transition(from event: Event, state currentState: State?) -> State? {
+        if let e = event as? Foreground {
+            return LifecycleState(asForegroundWithIndex: NSNumber(value: e.index))
+        }
+        if let e = event as? Background {
+            return LifecycleState(asBackgroundWithIndex: NSNumber(value: e.index))
+        }
+        return nil
+    }
+
+    var subscribedEventSchemasForEntitiesGeneration: [String] {
+        return ["*"]
+    }
+
+    func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]? {
+        if state == nil {
+            let entity = LifecycleEntity(isVisible: true)
+            entity.index = 0
+            return [entity]
+        }
+        if let s = state as? LifecycleState {
+            let entity = LifecycleEntity(isVisible: s.isForeground)
+            entity.index = s.index
+            return [entity]
+        }
+        return nil
+    }
+
+    var subscribedEventSchemasForPayloadUpdating: [String] {
+        return []
+    }
+
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+        return nil
+    }
+}
diff --git a/Sources/Core/Tracker/ServiceProvider.swift b/Sources/Core/Tracker/ServiceProvider.swift
new file mode 100644
index 000000000..b3426dc04
--- /dev/null
+++ b/Sources/Core/Tracker/ServiceProvider.swift
@@ -0,0 +1,354 @@
+//
+//  SPServiceProvider.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class ServiceProvider: NSObject, ServiceProviderProtocol {
+    private(set) var namespace: String
+    
+    var isTrackerInitialized: Bool { return _tracker != nil }
+
+    // Internal services
+    private var _subject: Subject?
+    var subject: Subject {
+        if let subject = _subject { return subject }
+        let subject = makeSubject()
+        _subject = subject
+        return subject
+    }
+
+    private var _emitter: Emitter?
+    var emitter: Emitter {
+        if let emitter = _emitter { return emitter }
+        let emitter = makeEmitter()
+        _emitter = emitter
+        return emitter
+    }
+
+    private var _tracker: Tracker?
+    var tracker: Tracker {
+        if let tracker = _tracker { return tracker }
+        let tracker = makeTracker()
+        _tracker = tracker
+        return tracker
+    }
+
+    // Controllers
+
+    private var _trackerController: TrackerControllerImpl?
+    var trackerController: TrackerControllerImpl {
+        if let controller = _trackerController { return controller }
+        let trackerController = makeTrackerController()
+        _trackerController = trackerController
+        return trackerController
+    }
+
+    private var _sessionController: SessionControllerImpl?
+    var sessionController: SessionControllerImpl {
+        if let controller = _sessionController { return controller }
+        let sessionController = makeSessionController()
+        _sessionController = sessionController
+        return sessionController
+    }
+
+    private var _emitterController: EmitterControllerImpl?
+    var emitterController: EmitterControllerImpl {
+        if let controller = _emitterController { return controller }
+        let emitterController = makeEmitterController()
+        _emitterController = emitterController
+        return emitterController
+    }
+
+    private var _gdprController: GDPRControllerImpl?
+    var gdprController: GDPRControllerImpl {
+        if let controller = _gdprController { return controller }
+        let gdprController = makeGDPRController()
+        _gdprController = gdprController
+        return gdprController
+    }
+
+    private var _globalContextsController: GlobalContextsControllerImpl?
+    var globalContextsController: GlobalContextsControllerImpl {
+        if let controller = _globalContextsController {
+            return controller
+        }
+        let globalContextsController = makeGlobalContextsController()
+        _globalContextsController = globalContextsController
+        return globalContextsController
+    }
+
+    private var _subjectController: SubjectControllerImpl?
+    var subjectController: SubjectControllerImpl {
+        if let controller = _subjectController { return controller }
+        let subjectController = makeSubjectController()
+        _subjectController = subjectController
+        return subjectController
+    }
+
+    private var _networkController: NetworkControllerImpl?
+    var networkController: NetworkControllerImpl {
+        if let controller = _networkController { return controller }
+        let networkController = makeNetworkController()
+        _networkController = networkController
+        return networkController
+    }
+
+    // Original configurations
+    private var globalContextConfiguration: GlobalContextsConfiguration?
+
+    // Configuration updates
+    private(set) var networkConfigurationUpdate = NetworkConfigurationUpdate()
+    private(set) var trackerConfigurationUpdate = TrackerConfigurationUpdate()
+    private(set) var emitterConfigurationUpdate = EmitterConfigurationUpdate()
+    private(set) var subjectConfigurationUpdate = SubjectConfigurationUpdate()
+    private(set) var sessionConfigurationUpdate = SessionConfigurationUpdate()
+    private(set) var gdprConfigurationUpdate = GDPRConfigurationUpdate()
+    
+    // MARK: - Init
+
+    init(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [Configuration]) {
+        self.namespace = namespace
+        super.init()
+        
+        networkConfigurationUpdate.sourceConfig = networkConfiguration
+        processConfigurations(configurations)
+        if trackerConfigurationUpdate.sourceConfig == nil {
+            trackerConfigurationUpdate.sourceConfig = TrackerConfiguration()
+        }
+        let _ = tracker // Build tracker to initialize NotificationCenter receivers
+    }
+
+    func reset(withConfigurations configurations: [Configuration]) {
+        stopServices()
+        resetConfigurationUpdates()
+        processConfigurations(configurations)
+        resetServices()
+        let _ = tracker
+    }
+
+    func shutdown() {
+        tracker.pauseEventTracking()
+        stopServices()
+        resetServices()
+        resetControllers()
+        initializeConfigurationUpdates()
+    }
+
+    // MARK: - Private methods
+
+    func processConfigurations(_ configurations: [Configuration]) {
+        for configuration in configurations {
+            if let configuration = configuration as? NetworkConfiguration {
+                networkConfigurationUpdate.sourceConfig = configuration
+            } else if let configuration = configuration as? TrackerConfiguration {
+                trackerConfigurationUpdate.sourceConfig = configuration
+            } else if let configuration = configuration as? SubjectConfiguration {
+                subjectConfigurationUpdate.sourceConfig = configuration
+            } else if let configuration = configuration as? SessionConfiguration {
+                sessionConfigurationUpdate.sourceConfig = configuration
+            } else if let configuration = configuration as? EmitterConfiguration {
+                emitterConfigurationUpdate.sourceConfig = configuration
+            } else if let configuration = configuration as? GDPRConfiguration {
+                gdprConfigurationUpdate.sourceConfig = configuration
+            } else if let configuration = configuration as? GlobalContextsConfiguration {
+                globalContextConfiguration = configuration
+            }
+        }
+    }
+
+    func stopServices() {
+        emitter.pauseTimer()
+    }
+
+    func resetServices() {
+        _emitter = nil
+        _subject = nil
+        _tracker = nil
+    }
+
+    func resetControllers() {
+        _trackerController = nil
+        _sessionController = nil
+        _emitterController = nil
+        _gdprController = nil
+        _globalContextsController = nil
+        _subjectController = nil
+        _networkController = nil
+    }
+
+    func resetConfigurationUpdates() {
+        // Don't reset networkConfiguration as it's needed in case it's not passed in the new configurations.
+        // Set a default trackerConfiguration to reset to default if not passed.
+        trackerConfigurationUpdate.sourceConfig = TrackerConfiguration()
+        emitterConfigurationUpdate.sourceConfig = nil
+        subjectConfigurationUpdate.sourceConfig = nil
+        sessionConfigurationUpdate.sourceConfig = nil
+        gdprConfigurationUpdate.sourceConfig = nil
+    }
+
+    func initializeConfigurationUpdates() {
+        networkConfigurationUpdate = NetworkConfigurationUpdate()
+        trackerConfigurationUpdate = TrackerConfigurationUpdate()
+        emitterConfigurationUpdate = EmitterConfigurationUpdate()
+        subjectConfigurationUpdate = SubjectConfigurationUpdate()
+        sessionConfigurationUpdate = SessionConfigurationUpdate()
+        gdprConfigurationUpdate = GDPRConfigurationUpdate()
+    }
+
+    // MARK: - Getters
+
+    // MARK: - Factories
+
+    //#pragma clang diagnostic push
+    //#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+    func makeSubject() -> Subject {
+        return Subject(
+            platformContext: trackerConfigurationUpdate.platformContext,
+            geoLocationContext: trackerConfigurationUpdate.geoLocationContext,
+            subjectConfiguration: subjectConfigurationUpdate)
+    }
+
+    func makeEmitter() -> Emitter {
+        let networkConfig = networkConfigurationUpdate
+        let emitterConfig = emitterConfigurationUpdate
+        
+        let builder = { (emitter: Emitter) in
+            if let method = networkConfig.method { emitter.method = method }
+            if let prtcl = networkConfig.protocol { emitter.protocol = prtcl }
+            emitter.customPostPath = networkConfig.customPostPath
+            emitter.requestHeaders = networkConfig.requestHeaders
+            emitter.emitThreadPoolSize = emitterConfig.threadPoolSize
+            emitter.byteLimitGet = emitterConfig.byteLimitGet
+            emitter.byteLimitPost = emitterConfig.byteLimitPost
+            emitter.serverAnonymisation = emitterConfig.serverAnonymisation
+            emitter.emitRange = emitterConfig.emitRange
+            emitter.bufferOption = emitterConfig.bufferOption
+            emitter.eventStore = emitterConfig.eventStore
+            emitter.callback = emitterConfig.requestCallback
+            emitter.customRetryForStatusCodes = emitterConfig.customRetryForStatusCodes
+        }
+
+        let emitter: Emitter
+        if let networkConnection = networkConfig.networkConnection {
+            emitter = Emitter(networkConnection: networkConnection, builder: builder)
+        } else {
+            emitter = Emitter(urlEndpoint: networkConfig.endpoint!, builder: builder)
+        }
+        
+        if emitterConfig.isPaused {
+            emitter.pauseEmit()
+        }
+        return emitter
+    }
+
+    func makeTracker() -> Tracker {
+        let emitter = self.emitter
+        let subject = self.subject
+        
+        let trackerConfig = trackerConfigurationUpdate
+        let sessionConfig = sessionConfigurationUpdate
+        let gcConfig = globalContextConfiguration
+        let gdprConfig = gdprConfigurationUpdate
+        
+        let tracker = Tracker(
+            trackerNamespace: namespace,
+            appId: trackerConfig.appId,
+            emitter: emitter
+        ) { tracker in
+            if let suffix = trackerConfig.trackerVersionSuffix {
+                tracker.trackerVersionSuffix = suffix
+            }
+            tracker.sessionContext = trackerConfig.sessionContext
+            tracker.foregroundTimeout = sessionConfig.foregroundTimeoutInSeconds
+            tracker.backgroundTimeout = sessionConfig.backgroundTimeoutInSeconds
+            tracker.exceptionEvents = trackerConfig.exceptionAutotracking
+            tracker.subject = subject
+            tracker.base64Encoded = trackerConfig.base64Encoding
+            tracker.logLevel = trackerConfig.logLevel
+            tracker.loggerDelegate = trackerConfig.loggerDelegate
+            tracker.devicePlatform = trackerConfig.devicePlatform
+            tracker.applicationContext = trackerConfig.applicationContext
+            tracker.deepLinkContext = trackerConfig.deepLinkContext
+            tracker.screenContext = trackerConfig.screenContext
+            tracker.autotrackScreenViews = trackerConfig.screenViewAutotracking
+            tracker.lifecycleEvents = trackerConfig.lifecycleAutotracking
+            tracker.installEvent = trackerConfig.installAutotracking
+            tracker.trackerDiagnostic = trackerConfig.diagnosticAutotracking
+            tracker.userAnonymisation = trackerConfig.userAnonymisation
+            if let config = gcConfig {
+                tracker.globalContextGenerators = config.contextGenerators
+            }
+            if gdprConfig.sourceConfig != nil {
+                tracker.gdprContext = GDPRContext(
+                    basis: gdprConfig.basisForProcessing,
+                    documentId: gdprConfig.documentId,
+                    documentVersion: gdprConfig.documentVersion,
+                    documentDescription: gdprConfig.documentDescription)
+            }
+        }
+        
+        if trackerConfigurationUpdate.isPaused {
+            tracker.pauseEventTracking()
+        }
+        if let session = tracker.session {
+            if sessionConfigurationUpdate.isPaused {
+                session.stopChecker()
+            }
+            if let callback = sessionConfigurationUpdate.onSessionStateUpdate {
+                session.onSessionStateUpdate = callback
+            }
+        }
+        return tracker
+    }
+
+    func makeTrackerController() -> TrackerControllerImpl {
+        return TrackerControllerImpl(serviceProvider: self)
+    }
+
+    func makeSessionController() -> SessionControllerImpl {
+        return SessionControllerImpl(serviceProvider: self)
+    }
+
+    func makeEmitterController() -> EmitterControllerImpl {
+        return EmitterControllerImpl(serviceProvider: self)
+    }
+
+    func makeGDPRController() -> GDPRControllerImpl {
+        let controller = GDPRControllerImpl(serviceProvider: self)
+        if let gdpr = tracker.gdprContext {
+            controller.reset(basis: gdpr.basis, documentId: gdpr.documentId, documentVersion: gdpr.documentVersion, documentDescription: gdpr.documentDescription)
+        }
+        return controller
+    }
+
+    func makeGlobalContextsController() -> GlobalContextsControllerImpl {
+        return GlobalContextsControllerImpl(serviceProvider: self)
+    }
+
+    func makeSubjectController() -> SubjectControllerImpl {
+        return SubjectControllerImpl(serviceProvider: self)
+    }
+
+    func makeNetworkController() -> NetworkControllerImpl {
+        return NetworkControllerImpl(serviceProvider: self)
+    }
+}
diff --git a/Sources/Core/Tracker/ServiceProviderProtocol.swift b/Sources/Core/Tracker/ServiceProviderProtocol.swift
new file mode 100644
index 000000000..e445fd7b5
--- /dev/null
+++ b/Sources/Core/Tracker/ServiceProviderProtocol.swift
@@ -0,0 +1,43 @@
+//
+//  SPServiceProviderProtocol.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+protocol ServiceProviderProtocol: AnyObject {
+    var namespace: String { get }
+    var isTrackerInitialized: Bool { get }
+    var tracker: Tracker { get }
+    var emitter: Emitter { get }
+    var subject: Subject { get }
+    var trackerController: TrackerControllerImpl { get }
+    var emitterController: EmitterControllerImpl { get }
+    var networkController: NetworkControllerImpl { get }
+    var gdprController: GDPRControllerImpl { get }
+    var globalContextsController: GlobalContextsControllerImpl { get }
+    var subjectController: SubjectControllerImpl { get }
+    var sessionController: SessionControllerImpl { get }
+    var networkConfigurationUpdate: NetworkConfigurationUpdate { get }
+    var trackerConfigurationUpdate: TrackerConfigurationUpdate { get }
+    var emitterConfigurationUpdate: EmitterConfigurationUpdate { get }
+    var subjectConfigurationUpdate: SubjectConfigurationUpdate { get }
+    var sessionConfigurationUpdate: SessionConfigurationUpdate { get }
+    var gdprConfigurationUpdate: GDPRConfigurationUpdate { get }
+}
diff --git a/Sources/Core/Tracker/StateFuture.swift b/Sources/Core/Tracker/StateFuture.swift
new file mode 100644
index 000000000..8e20ebf28
--- /dev/null
+++ b/Sources/Core/Tracker/StateFuture.swift
@@ -0,0 +1,54 @@
+//
+//  StateFuture.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// StateFuture represents the placeholder of a future computation.
+/// The proper state value is computed when it's observed. Until that moment the StateFuture keeps the elements
+/// (event, previous StateFuture, StateMachine) needed to calculate the real state value.
+/// For this reason, the StateFuture can be the head of StateFuture chain which will collapse once the StateFuture
+/// head is asked to get the real state value.
+class StateFuture: NSObject {
+    var state: State? {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        if computedState == nil {
+            if let stateMachine = stateMachine, let event = event {
+                computedState = stateMachine.transition(from: event, state: previousState?.state)
+            }
+            event = nil
+            previousState = nil
+            stateMachine = nil
+        }
+        return computedState
+    }
+    private var event: Event?
+    private var previousState: StateFuture?
+    private var stateMachine: StateMachineProtocol?
+    private var computedState: State?
+
+    init(event: Event, previousState: StateFuture?, stateMachine: StateMachineProtocol) {
+        super.init()
+        self.event = event
+        self.previousState = previousState
+        self.stateMachine = stateMachine
+    }
+}
diff --git a/Sources/Core/Tracker/StateManager.swift b/Sources/Core/Tracker/StateManager.swift
new file mode 100644
index 000000000..db750dd1e
--- /dev/null
+++ b/Sources/Core/Tracker/StateManager.swift
@@ -0,0 +1,165 @@
+//
+//  StateManager.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class StateManager: NSObject {
+    private var identifierToStateMachine: [String : StateMachineProtocol] = [:]
+    private var eventSchemaToStateMachine: [String : [StateMachineProtocol]] = [:]
+    private var eventSchemaToEntitiesGenerator: [String : [StateMachineProtocol]] = [:]
+    private var eventSchemaToPayloadUpdater: [String : [StateMachineProtocol]] = [:]
+    private var trackerState = TrackerState()
+
+    func addOrReplaceStateMachine(_ stateMachine: StateMachineProtocol) {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        if let previousStateMachine = identifierToStateMachine[stateMachine.identifier] {
+            if type(of: stateMachine) == type(of: previousStateMachine) {
+                return
+            }
+            let _ = removeStateMachine(stateMachine.identifier)
+        }
+        identifierToStateMachine[stateMachine.identifier] = stateMachine
+        add(
+            toSchemaRegistry: &eventSchemaToStateMachine,
+            schemas: stateMachine.subscribedEventSchemasForTransitions,
+            stateMachine: stateMachine)
+        add(
+            toSchemaRegistry: &eventSchemaToEntitiesGenerator,
+            schemas: stateMachine.subscribedEventSchemasForEntitiesGeneration,
+            stateMachine: stateMachine)
+        add(
+            toSchemaRegistry: &eventSchemaToPayloadUpdater,
+            schemas: stateMachine.subscribedEventSchemasForPayloadUpdating,
+            stateMachine: stateMachine)
+    }
+
+    func removeStateMachine(_ stateMachineIdentifier: String) -> Bool {
+        guard let stateMachine = identifierToStateMachine[stateMachineIdentifier] else {
+            return false
+        }
+        identifierToStateMachine.removeValue(forKey: stateMachineIdentifier)
+        trackerState.remove(withIdentifier: stateMachineIdentifier)
+        remove(
+            fromSchemaRegistry: &eventSchemaToStateMachine,
+            schemas: stateMachine.subscribedEventSchemasForTransitions,
+            stateMachine: stateMachine)
+        remove(
+            fromSchemaRegistry: &eventSchemaToEntitiesGenerator,
+            schemas: stateMachine.subscribedEventSchemasForEntitiesGeneration,
+            stateMachine: stateMachine)
+        remove(
+            fromSchemaRegistry: &eventSchemaToPayloadUpdater,
+            schemas: stateMachine.subscribedEventSchemasForPayloadUpdating,
+            stateMachine: stateMachine)
+        return true
+    }
+    
+    func trackerState(forProcessedEvent event: Event) -> TrackerStateSnapshot? {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        if let sdEvent = event as? SelfDescribingAbstract {
+            var stateMachines = Array(eventSchemaToStateMachine[sdEvent.schema] ?? [])
+            stateMachines.append(contentsOf: eventSchemaToStateMachine["*"] ?? [])
+            
+            for stateMachine in stateMachines {
+                let previousStateFuture = trackerState.stateFuture(withIdentifier: stateMachine.identifier)
+                let currentStateFuture = StateFuture(
+                    event: sdEvent,
+                    previousState: previousStateFuture,
+                    stateMachine: stateMachine)
+                
+                trackerState.setStateFuture(currentStateFuture, identifier: stateMachine.identifier)
+                // TODO: Remove early state computation.
+                /*
+                 The early state-computation causes low performance as it's executed synchronously on
+                 the track method thread. Ideally, the state computation should be executed only on
+                 entities generation or payload updating (outputs). In that case there are two problems
+                 to address:
+                 - long chains of StateFuture filling the memory (in case the outputs are not generated)
+                 - event object reuse by the user (the event object in the StateFuture could be modified
+                 externally)
+                 Remove the early state-computation only when these two problems are fixed.
+                 */
+                _ = currentStateFuture.state // Early state-computation
+            }
+        }
+        return trackerState.snapshot()
+    }
+
+    func entities(forProcessedEvent event: InspectableEvent) -> [SelfDescribingJson] {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        guard let schema = event.schema else { return [] }
+        var result: [SelfDescribingJson] = []
+        var stateMachines = eventSchemaToEntitiesGenerator[schema] ?? []
+        stateMachines.append(contentsOf: eventSchemaToEntitiesGenerator["*"] ?? [])
+        
+        for stateMachine in stateMachines {
+            let state = event.state.state(withIdentifier: stateMachine.identifier)
+            if let entities = stateMachine.entities(from: event, state: state) {
+                result.append(contentsOf: entities)
+            }
+        }
+        return result
+    }
+
+    func addPayloadValues(to event: InspectableEvent) -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        guard let schema = event.schema else { return true }
+        var failures = 0
+        var stateMachines = eventSchemaToPayloadUpdater[schema] ?? []
+        stateMachines.append(contentsOf: eventSchemaToPayloadUpdater["*"] ?? [])
+        for stateMachine in stateMachines {
+            let state = event.state.state(withIdentifier: stateMachine.identifier)
+            if let payloadValues = stateMachine.payloadValues(from: event, state: state) {
+                if !event.addPayloadValues(payloadValues) {
+                    failures += 1
+                }
+            }
+        }
+        return failures == 0
+    }
+
+    // MARK: - Private methods
+
+    func add(toSchemaRegistry schemaRegistry: inout [String : [StateMachineProtocol]], schemas: [String], stateMachine: StateMachineProtocol?) {
+        for eventSchema in schemas {
+            var array = schemaRegistry[eventSchema] ?? []
+            if let stateMachine = stateMachine {
+                array.append(stateMachine)
+            }
+            schemaRegistry[eventSchema] = array
+        }
+    }
+
+    func remove(fromSchemaRegistry schemaRegistry: inout [String : [StateMachineProtocol]], schemas: [String], stateMachine: StateMachineProtocol) {
+        for eventSchema in schemas {
+            var array = schemaRegistry[eventSchema]
+            array?.removeAll { $0.identifier == stateMachine.identifier }
+            schemaRegistry[eventSchema] = array
+        }
+    }
+}
diff --git a/Sources/Core/Tracker/Tracker.swift b/Sources/Core/Tracker/Tracker.swift
new file mode 100644
index 000000000..f5c296f83
--- /dev/null
+++ b/Sources/Core/Tracker/Tracker.swift
@@ -0,0 +1,682 @@
+//
+//  Tracker.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+func uncaughtExceptionHandler(_ exception: NSException) {
+    let symbols = exception.callStackSymbols
+    let stacktrace = "Stacktrace:\n\(symbols)"
+    let message = exception.reason
+    DispatchQueue.global(qos: .default).sync {
+        guard let message = message else { return }
+        if message.count == 0 { return }
+    
+        // Construct userInfo
+        var userInfo: [String : NSObject] = [:]
+        userInfo["message"] = message as NSObject
+        userInfo["stacktrace"] = stacktrace as NSObject
+    
+        // Send notification to tracker
+        NotificationCenter.default.post(
+            name: NSNotification.Name("SPCrashReporting"),
+            object: nil,
+            userInfo: userInfo)
+    
+        Thread.sleep(forTimeInterval: 2.0)
+    }
+}
+
+/// This class is used for tracking events, and delegates them to other classes responsible for sending, storage, etc.
+class Tracker: NSObject {
+    private var platformContextSchema: String = ""
+    private var dataCollection = true
+
+    private var builderFinished = false
+
+
+    /// The object used for sessionization, i.e. it characterizes user activity.
+    private(set) var session: Session?
+    /// Previous screen view state.
+    private(set) var previousScreenState: ScreenState?
+    /// Current screen view state.
+    private(set) var currentScreenState: ScreenState?
+    /// List of tags associated to global contexts.
+    
+    private var trackerData: [String : NSObject]? = nil
+    func setTrackerData() {
+        var trackerVersion = kSPVersion
+        if trackerVersionSuffix.count != 0 {
+            var allowedCharSet = CharacterSet.alphanumerics
+            allowedCharSet.formUnion(CharacterSet(charactersIn: ".-"))
+            let suffix = trackerVersionSuffix.components(separatedBy: allowedCharSet.inverted).joined(separator: "")
+            if suffix.count != 0 {
+                trackerVersion = "\(trackerVersion) \(suffix)"
+            }
+        }
+        trackerData = [
+            kSPTrackerVersion : trackerVersion as NSObject,
+            kSPNamespace : trackerNamespace as NSObject,
+            kSPAppId : appId as NSObject
+        ]
+    }
+
+    // MARK: - Setter
+
+    private var _emitter: Emitter
+    /// The emitter used to send events.
+    var emitter: Emitter {
+        get {
+            return _emitter
+        }
+        set(emitter) {
+            _emitter = emitter
+        }
+    }
+
+    /// The subject used to represent the current user and persist user information.
+    var subject: Subject?
+    
+    /// Whether to use Base64 encoding for events.
+    var base64Encoded = TrackerDefaults.base64Encoded
+    
+    /// A unique identifier for an application.
+    private var _appId: String
+    var appId: String {
+        get {
+            return _appId
+        }
+        set(appId) {
+            _appId = appId
+            if builderFinished && trackerData != nil {
+                setTrackerData()
+            }
+        }
+    }
+    
+    private(set) var _trackerNamespace: String
+    /// The identifier for the current tracker.
+    var trackerNamespace: String {
+        get {
+            return _trackerNamespace
+        }
+        set(trackerNamespace) {
+            _trackerNamespace = trackerNamespace
+            if builderFinished && trackerData != nil {
+                setTrackerData()
+            }
+        }
+    }
+    
+    /// Version suffix for tracker wrappers.
+    private var _trackerVersionSuffix: String = TrackerDefaults.trackerVersionSuffix
+    var trackerVersionSuffix: String {
+        get {
+            return _trackerVersionSuffix
+        }
+        set(trackerVersionSuffix) {
+            _trackerVersionSuffix = trackerVersionSuffix
+            if builderFinished && trackerData != nil {
+                setTrackerData()
+            }
+        }
+    }
+    
+    var devicePlatform: DevicePlatform = TrackerDefaults.devicePlatform
+
+    var logLevel: LogLevel {
+        get {
+            return Logger.logLevel
+        }
+        set {
+            Logger.logLevel = newValue
+        }
+    }
+
+    var loggerDelegate: LoggerDelegate? {
+        get {
+            return Logger.delegate
+        }
+        set(delegate) {
+            Logger.delegate = delegate
+        }
+    }
+    
+    private var _sessionContext = false
+    var sessionContext: Bool {
+        get {
+            return _sessionContext
+        }
+        set(sessionContext) {
+            _sessionContext = sessionContext
+            if session != nil && !sessionContext {
+                session?.stopChecker()
+                session = nil
+            } else if builderFinished && session == nil && sessionContext {
+                session = Session(
+                    foregroundTimeout: foregroundTimeout,
+                    andBackgroundTimeout: backgroundTimeout,
+                    andTracker: self)
+            }
+        }
+    }
+    
+    private var _deepLinkContext = false
+    var deepLinkContext: Bool {
+        get {
+            return _deepLinkContext
+        }
+        set(deepLinkContext) {
+            objc_sync_enter(self)
+            _deepLinkContext = deepLinkContext
+            if deepLinkContext {
+                stateManager.addOrReplaceStateMachine(DeepLinkStateMachine())
+            } else {
+                _ = stateManager.removeStateMachine(DeepLinkStateMachine.identifier)
+            }
+            objc_sync_exit(self)
+        }
+    }
+    
+    private var _screenContext = false
+    var screenContext: Bool {
+        get {
+            return _screenContext
+        }
+        set(screenContext) {
+            objc_sync_enter(self)
+            _screenContext = screenContext
+            if screenContext {
+                stateManager.addOrReplaceStateMachine(ScreenStateMachine())
+            } else {
+                _ = stateManager.removeStateMachine(ScreenStateMachine.identifier)
+            }
+            objc_sync_exit(self)
+        }
+    }
+    
+    var applicationContext = TrackerDefaults.applicationContext
+    
+    var autotrackScreenViews = TrackerDefaults.autotrackScreenViews
+    
+    private var _foregroundTimeout = TrackerDefaults.foregroundTimeout
+    var foregroundTimeout: Int {
+        get {
+            return _foregroundTimeout
+        }
+        set(foregroundTimeout) {
+            _foregroundTimeout = foregroundTimeout
+            if builderFinished && session != nil {
+                session?.foregroundTimeout = foregroundTimeout
+            }
+        }
+    }
+    
+    private var _backgroundTimeout = TrackerDefaults.backgroundTimeout
+    var backgroundTimeout: Int {
+        get {
+            return _backgroundTimeout
+        }
+        set(backgroundTimeout) {
+            _backgroundTimeout = backgroundTimeout
+            if builderFinished && session != nil {
+                session?.backgroundTimeout = backgroundTimeout
+            }
+        }
+    }
+    
+    private var _lifecycleEvents = false
+    /// Returns whether lifecyle events is enabled.
+    /// - Returns: Whether background and foreground events are sent.
+    var lifecycleEvents: Bool {
+        get {
+            return _lifecycleEvents
+        }
+        set(lifecycleEvents) {
+            objc_sync_enter(self)
+            _lifecycleEvents = lifecycleEvents
+            if lifecycleEvents {
+                stateManager.addOrReplaceStateMachine(LifecycleStateMachine())
+            } else {
+                _ = stateManager.removeStateMachine(LifecycleStateMachine.identifier)
+            }
+            objc_sync_exit(self)
+        }
+    }
+    
+    var exceptionEvents = TrackerDefaults.exceptionEvents
+    var installEvent = TrackerDefaults.installEvent
+    var trackerDiagnostic = TrackerDefaults.trackerDiagnostic
+    
+    private var _userAnonymisation = TrackerDefaults.userAnonymisation
+    var userAnonymisation: Bool {
+        get {
+            return _userAnonymisation
+        }
+        set(userAnonymisation) {
+            if _userAnonymisation != userAnonymisation {
+                _userAnonymisation = userAnonymisation
+                if let session = session { session.startNewSession() }
+            }
+        }
+    }
+
+    var globalContextTags: [String] {
+        return Array(globalContextGenerators.keys)
+    }
+    /// Dictionary of global contexts generators.
+    var globalContextGenerators: [String : GlobalContext] = [:]
+
+    /// GDPR context
+    /// You can enable or disable the context by setting this property
+    var gdprContext: GDPRContext?
+    
+    private var stateManager = StateManager()
+
+    var inBackground: Bool {
+        return session?.inBackground ?? false
+    }
+
+    var isTracking: Bool {
+        return dataCollection
+    }
+
+    init(trackerNamespace: String,
+         appId: String?,
+         emitter: Emitter,
+         builder: ((Tracker) -> (Void))) {
+        self._emitter = emitter
+        self._appId = appId ?? ""
+        self._trackerNamespace = trackerNamespace
+        
+        super.init()
+        builder(self)
+        
+        #if os(iOS)
+        platformContextSchema = kSPMobileContextSchema
+        #else
+        platformContextSchema = kSPDesktopContextSchema
+        #endif
+        
+        setup()
+        checkInstall()
+    }
+
+    private func setup() {
+        emitter.namespace = trackerNamespace // Needed to correctly send events to the right EventStore
+        setTrackerData()
+        
+        if sessionContext {
+            session = Session(
+                foregroundTimeout: foregroundTimeout,
+                andBackgroundTimeout: backgroundTimeout,
+                andTracker: self)
+        }
+
+        UIKitScreenViewTracking.setup()
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(receiveScreenViewNotification(_:)),
+            name: NSNotification.Name("SPScreenViewDidAppear"),
+            object: nil)
+
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(receiveDiagnosticNotification(_:)),
+            name: NSNotification.Name("SPTrackerDiagnostic"),
+            object: nil)
+
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(receiveCrashReporting(_:)),
+            name: NSNotification.Name("SPCrashReporting"),
+            object: nil)
+
+        if exceptionEvents {
+            NSSetUncaughtExceptionHandler(uncaughtExceptionHandler)
+        }
+
+        builderFinished = true
+    }
+
+    private func checkInstall() {
+        if installEvent {
+            let installTracker = InstallTracker()
+            let previousTimestamp = installTracker.previousInstallTimestamp
+            installTracker.clearPreviousInstallTimestamp()
+            if !installTracker.isNewInstall && previousTimestamp == nil {
+                return
+            }
+            let data: [String: NSObject] = [:]
+            let installEvent = SelfDescribingJson(schema: kSPApplicationInstallSchema, andDictionary: data)
+            let event = SelfDescribing(eventData: installEvent)
+            event.trueTimestamp = previousTimestamp // it can be nil
+            let _ = track(event)
+        }
+    }
+    
+
+    /// Add new generator for global contexts associated with a string tag.
+    /// If the string tag has been already set the new global context is not assigned.
+    /// - Parameters:
+    ///   - generator: The global context generator.
+    ///   - tag: The tag associated to the global context.
+    /// - Returns: Weather the global context has been added.
+    func add(_ generator: GlobalContext, tag: String) -> Bool {
+        if (globalContextGenerators)[tag] != nil {
+            return false
+        }
+        (globalContextGenerators)[tag] = generator
+        return true
+    }
+
+    /// Remove the global context associated with the string tag.
+    /// If the string tag exist it returns the global context generator associated with.
+    /// - Parameter tag: The tag associated to the global context.
+    /// - Returns: The global context associated with the tag or `nil` in case of any entry with that string tag.
+    func removeGlobalContext(_ tag: String) -> GlobalContext? {
+        let toDelete = (globalContextGenerators)[tag]
+        if toDelete != nil {
+            globalContextGenerators.removeValue(forKey: tag)
+        }
+        return toDelete
+    }
+
+    /// Pauses all event tracking, storage and session checking.
+
+    // MARK: - Extra Functions
+
+    func pauseEventTracking() {
+        dataCollection = false
+        emitter.pauseTimer()
+        session?.stopChecker()
+    }
+
+    func resumeEventTracking() {
+        dataCollection = true
+        emitter.resumeTimer()
+        session?.startChecker()
+    }
+
+    /// Returns whether the application is in the background or foreground.
+    /// - Returns: Whether application is suspended.
+
+    // MARK: - Notifications management
+
+    @objc func receiveScreenViewNotification(_ notification: Notification) {
+        guard let name = notification.userInfo?["name"] as? String else { return }
+        
+        var type: String?
+        if let typeId = (notification.userInfo?["type"] as? NSNumber)?.intValue,
+           let screenType = ScreenType(rawValue: typeId) {
+            type = ScreenView.stringWithScreenType(screenType)
+        }
+        
+        let topViewControllerClassName = notification.userInfo?["topViewControllerClassName"] as? String
+        let viewControllerClassName = notification.userInfo?["viewControllerClassName"] as? String
+
+        if autotrackScreenViews {
+            let event = ScreenView(name: name, screenId: nil)
+            event.type = type
+            event.viewControllerClassName = viewControllerClassName
+            event.topViewControllerClassName = topViewControllerClassName
+            let _ = track(event)
+        }
+    }
+
+    @objc func receiveDiagnosticNotification(_ notification: Notification) {
+        let userInfo = notification.userInfo
+        guard let tag = userInfo?["tag"] as? String,
+              let message = userInfo?["message"] as? String else { return }
+        let error = userInfo?["error"] as? Error
+        let exception = userInfo?["exception"] as? NSException
+
+        if trackerDiagnostic {
+            let event = TrackerError(source: tag, message: message, error: error, exception: exception)
+            let _ = track(event)
+        }
+    }
+
+    @objc func receiveCrashReporting(_ notification: Notification) {
+        let userInfo = notification.userInfo
+        guard let message = userInfo?["message"] as? String else { return }
+        let stacktrace = userInfo?["stacktrace"] as? String
+
+        if exceptionEvents {
+            let event = SNOWError(message: message)
+            event.stackTrace = stacktrace
+            let _ = track(event)
+        }
+    }
+
+    // MARK: - Events tracking methods
+
+    /// Tracks an event despite its specific type.
+    /// - Parameter event: The event to track
+    /// - Returns: The event ID or nil in case tracking is paused
+
+    // MARK: - Event Tracking Functions
+
+    func track(_ event: Event) -> UUID? {
+        if !dataCollection {
+            return nil
+        }
+        event.beginProcessing(withTracker: self)
+        let eventId = processEvent(event)
+        event.endProcessing(withTracker: self)
+        return eventId
+    }
+
+    // MARK: - Event Decoration
+
+    func processEvent(_ event: Event) -> UUID {
+        objc_sync_enter(self)
+        let stateSnapshot = stateManager.trackerState(forProcessedEvent: event)
+        objc_sync_exit(self)
+        let trackerEvent = TrackerEvent(event: event, state: stateSnapshot)
+        transformEvent(trackerEvent)
+        let payload = self.payload(with: trackerEvent)
+        emitter.addPayload(toBuffer: payload)
+        return trackerEvent.eventId
+    }
+
+    func transformEvent(_ event: TrackerEvent) {
+        // Application_install event needs the timestamp to the real installation event.
+        if (event.schema == kSPApplicationInstallSchema) {
+            if let trueTimestamp = event.trueTimestamp {
+                event.timestamp = Int64(trueTimestamp.timeIntervalSince1970 * 1000)
+                event.trueTimestamp = nil
+            }
+        }
+        // Payload can be optionally updated with values based on internal state
+        let _ = stateManager.addPayloadValues(to: event)
+    }
+
+    func payload(with event: TrackerEvent) -> Payload {
+        let payload = Payload()
+        payload.allowDiagnostic = !event.isService
+
+        addBasicProperties(to: payload, event: event)
+        if event.isPrimitive {
+            addPrimitiveProperties(to: payload, event: event)
+        } else {
+            addSelfDescribingProperties(to: payload, event: event)
+        }
+        var contexts = event.contexts
+        addBasicContexts(toContexts: &contexts, event: event)
+        addGlobalContexts(toContexts: &contexts, event: event)
+        addStateMachineEntities(toContexts: &contexts, event: event)
+        wrapContexts(contexts, to: payload)
+        if !event.isPrimitive {
+            // TODO: To remove when Atomic table refactoring is finished
+            workaround(forCampaignAttributionEnrichment: payload, event: event, contexts: &contexts)
+        }
+        return payload
+    }
+
+    func addBasicProperties(to payload: Payload, event: TrackerEvent) {
+        payload.addValueToPayload(event.eventId.uuidString, forKey: kSPEid)
+        payload.addValueToPayload(String(format: "%lld", event.timestamp), forKey: kSPTimestamp)
+        if let trueTimestamp = event.trueTimestamp {
+            let ttInMilliSeconds = Int64(trueTimestamp.timeIntervalSince1970 * 1000)
+            payload.addValueToPayload(String(format: "%lld", ttInMilliSeconds), forKey: kSPTrueTimestamp)
+        }
+        payload.addDictionaryToPayload(trackerData)
+        if let subjectDict = subject?.getStandardDict(withUserAnonymisation: userAnonymisation)?.dictionary {
+            payload.addDictionaryToPayload(subjectDict)
+        }
+        payload.addValueToPayload(devicePlatformToString(devicePlatform), forKey: kSPPlatform)
+    }
+
+    func addPrimitiveProperties(to payload: Payload, event: TrackerEvent) {
+        payload.addValueToPayload(event.eventName, forKey: kSPEvent)
+        payload.addDictionaryToPayload(event.payload)
+    }
+
+    func addSelfDescribingProperties(to payload: Payload, event: TrackerEvent) {
+        payload.addValueToPayload(kSPEventUnstructured, forKey: kSPEvent)
+
+        if let schema = event.schema {
+            let eventPayload = event.payload
+            let data = SelfDescribingJson(schema: schema, andData: eventPayload as NSObject)
+            if let data = data.dictionary as NSObject? {
+                let unstructuredEventPayload: [String : NSObject] = [
+                    kSPSchema: kSPUnstructSchema as NSObject,
+                    kSPData: data
+                ]
+                payload.addDictionaryToPayload(
+                    unstructuredEventPayload,
+                    base64Encoded: base64Encoded,
+                    typeWhenEncoded: kSPUnstructuredEncoded,
+                    typeWhenNotEncoded: kSPUnstructured)
+            }
+        }
+    }
+
+    /*
+     This is needed because the campaign-attribution-enrichment (in the pipeline) is able to parse
+     the `url` and `referrer` only if they are part of a PageView event.
+     The PageView event is an atomic event but the DeepLinkReceived and ScreenView are SelfDescribing events.
+     For this reason we copy these two fields in the atomic fields in order to let the enrichment
+     to process correctly the fields even if the event is not a PageView and it's a SelfDescribing event.
+     This is a hack that should be removed once the atomic event table is dismissed and all the events
+     will be SelfDescribing.
+     */
+    func workaround(forCampaignAttributionEnrichment payload: Payload, event: TrackerEvent, contexts: inout [SelfDescribingJson]) {
+        var url: String?
+        var referrer: String?
+
+        if event.schema == DeepLinkReceived.schema {
+            url = event.payload[DeepLinkReceived.paramUrl] as? String
+            referrer = event.payload[DeepLinkReceived.paramReferrer] as? String
+        } else if event.schema == kSPScreenViewSchema {
+            for entity in contexts {
+                if entity.schema == DeepLinkEntity.schema {
+                    let data = entity.data as? [AnyHashable : Any]
+                    url = data?[DeepLinkEntity.paramUrl] as? String
+                    referrer = data?[DeepLinkEntity.paramReferrer] as? String
+                    break
+                }
+            }
+        }
+
+        if let url = url {
+            payload.addValueToPayload(url, forKey: kSPPageUrl)
+        }
+        if let referrer = referrer {
+            payload.addValueToPayload(referrer, forKey: kSPPageRefr)
+        }
+    }
+
+    func addBasicContexts(toContexts contexts: inout [SelfDescribingJson], event: TrackerEvent) {
+        addBasicContexts(toContexts: &contexts, eventId: event.eventId.uuidString, eventTimestamp: event.timestamp, isService: event.isService)
+    }
+
+    func addBasicContexts(toContexts contexts: inout [SelfDescribingJson], eventId: String, eventTimestamp: Int64, isService: Bool) {
+        if subject != nil {
+            if let platformDict = subject?.getPlatformDict(withUserAnonymisation: userAnonymisation)?.dictionary {
+                contexts.append(SelfDescribingJson(schema: platformContextSchema, andDictionary: platformDict))
+            }
+            if let geoLocationDict = subject?.getGeoLocationDict() {
+                contexts.append(SelfDescribingJson(schema: kSPGeoContextSchema, andDictionary: geoLocationDict))
+            }
+        }
+
+        if applicationContext {
+            if let contextJson = Utilities.applicationContext {
+                contexts.append(contextJson)
+            }
+        }
+
+        if isService {
+            return
+        }
+
+        // Add session
+        if let session = session {
+            if let sessionDict = session.getDictWithEventId(eventId, eventTimestamp: eventTimestamp, userAnonymisation: userAnonymisation) {
+                contexts.append(SelfDescribingJson(schema: kSPSessionContextSchema, andDictionary: sessionDict))
+            } else {
+                logDiagnostic(message: String(format: "Unable to get session context for eventId: %@", eventId))
+            }
+        }
+
+        // Add GDPR context
+        if let gdprContext = gdprContext?.context {
+            contexts.append(gdprContext)
+        }
+    }
+
+    func addGlobalContexts(toContexts contexts: inout [SelfDescribingJson], event: InspectableEvent) {
+        for (_, generator) in globalContextGenerators {
+            contexts.append(contentsOf: generator.contexts(from: event))
+        }
+    }
+
+    func addStateMachineEntities(toContexts contexts: inout [SelfDescribingJson], event: InspectableEvent) {
+        let stateManagerEntities = stateManager.entities(forProcessedEvent: event)
+        contexts.append(contentsOf: stateManagerEntities)
+    }
+
+    func wrapContexts(_ contexts: [SelfDescribingJson], to payload: Payload) {
+        if contexts.count == 0 {
+            return
+        }
+        var data: [[String : NSObject]] = []
+        for context in contexts {
+            if let dict = context.dictionary {
+                data.append(dict)
+            }
+        }
+
+        let finalContext = SelfDescribingJson(schema: kSPContextSchema, andData: data as NSObject)
+        if let dict = finalContext.dictionary {
+            payload.addDictionaryToPayload(
+                dict,
+                base64Encoded: base64Encoded,
+                typeWhenEncoded: kSPContextEncoded,
+                typeWhenNotEncoded: kSPContext)
+        }
+    }
+
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+}
diff --git a/Sources/Core/Tracker/TrackerConfigurationUpdate.swift b/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
new file mode 100644
index 000000000..958ba2e40
--- /dev/null
+++ b/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
@@ -0,0 +1,222 @@
+//
+//  SPTrackerConfigurationUpdate.h
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class TrackerConfigurationUpdate: TrackerConfiguration {
+    var sourceConfig: TrackerConfiguration?
+    var isPaused = false
+    var appIdUpdated = false
+    var devicePlatformUpdated = false
+    var base64EncodingUpdated = false
+    var logLevelUpdated = false
+    var loggerDelegateUpdated = false
+    var applicationContextUpdated = false
+    var platformContextUpdated = false
+    var geoLocationContextUpdated = false
+    var deepLinkContextUpdated = false
+    var sessionContextUpdated = false
+    var screenContextUpdated = false
+    var screenViewAutotrackingUpdated = false
+    var lifecycleAutotrackingUpdated = false
+    var installAutotrackingUpdated = false
+    var exceptionAutotrackingUpdated = false
+    var diagnosticAutotrackingUpdated = false
+    var userAnonymisationUpdated = false
+    var trackerVersionSuffixUpdated = false
+
+    override var appId: String {
+        get {
+            return ((sourceConfig == nil) || appIdUpdated) ? super.appId : sourceConfig?.appId ?? ""
+        }
+        set { super.appId = newValue }
+    }
+
+    override var devicePlatform: DevicePlatform {
+        get {
+            return ((sourceConfig == nil) || devicePlatformUpdated) ? super.devicePlatform : sourceConfig?.devicePlatform ?? TrackerDefaults.devicePlatform
+        }
+        set {
+            super.devicePlatform = newValue
+            devicePlatformUpdated = true
+        }
+    }
+
+    override var base64Encoding: Bool {
+        get {
+            return ((sourceConfig == nil) || base64EncodingUpdated) ? super.base64Encoding : sourceConfig?.base64Encoding ?? TrackerDefaults.base64Encoded
+        }
+        set {
+            super.base64Encoding = newValue
+            base64EncodingUpdated = true
+        }
+    }
+
+    override var logLevel: LogLevel {
+        get {
+            return ((sourceConfig == nil) || logLevelUpdated) ? super.logLevel : sourceConfig?.logLevel ?? LogLevel.off
+        }
+        set {
+            super.logLevel = newValue
+            logLevelUpdated = true
+        }
+    }
+
+    override var loggerDelegate: LoggerDelegate? {
+        get {
+            return ((sourceConfig == nil) || loggerDelegateUpdated) ? super.loggerDelegate : sourceConfig?.loggerDelegate
+        }
+        set {
+            super.loggerDelegate = newValue
+            loggerDelegateUpdated = true
+        }
+    }
+
+    override var applicationContext: Bool {
+        get {
+            return ((sourceConfig == nil) || applicationContextUpdated) ? super.applicationContext : sourceConfig?.applicationContext ?? TrackerDefaults.applicationContext
+        }
+        set {
+            super.applicationContext = newValue
+            applicationContextUpdated = true
+        }
+    }
+
+    override var platformContext: Bool {
+        get {
+            return ((sourceConfig == nil) || platformContextUpdated) ? super.platformContext : sourceConfig?.platformContext ?? TrackerDefaults.platformContext
+        }
+        set {
+            super.platformContext = newValue
+            platformContextUpdated = true
+        }
+    }
+
+    override var geoLocationContext: Bool {
+        get {
+            return ((sourceConfig == nil) || geoLocationContextUpdated) ? super.geoLocationContext : sourceConfig?.geoLocationContext ?? TrackerDefaults.geoLocationContext
+        }
+        set {
+            super.geoLocationContext = newValue
+            geoLocationContextUpdated = true
+        }
+    }
+
+    override var deepLinkContext: Bool {
+        get {
+            return ((sourceConfig == nil) || deepLinkContextUpdated) ? super.deepLinkContext : sourceConfig?.deepLinkContext ?? TrackerDefaults.deepLinkContext
+        }
+        set {
+            super.deepLinkContext = newValue
+            deepLinkContextUpdated = true
+        }
+    }
+
+    override var sessionContext: Bool {
+        get {
+            return ((sourceConfig == nil) || sessionContextUpdated) ? super.sessionContext : sourceConfig?.sessionContext ?? TrackerDefaults.sessionContext
+        }
+        set {
+            super.sessionContext = newValue
+            sessionContextUpdated = true
+        }
+    }
+
+    override var screenContext: Bool {
+        get {
+            return ((sourceConfig == nil) || screenContextUpdated) ? super.screenContext : sourceConfig?.screenContext ?? TrackerDefaults.screenContext
+        }
+        set {
+            super.screenContext = newValue
+            screenContextUpdated = true
+        }
+    }
+
+    override var screenViewAutotracking: Bool {
+        get {
+            return ((sourceConfig == nil) || screenViewAutotrackingUpdated) ? super.screenViewAutotracking : sourceConfig?.screenViewAutotracking ?? TrackerDefaults.autotrackScreenViews
+        }
+        set {
+            super.screenViewAutotracking = newValue
+            screenViewAutotrackingUpdated = true
+        }
+    }
+
+    override var lifecycleAutotracking: Bool {
+        get {
+            return ((sourceConfig == nil) || lifecycleAutotrackingUpdated) ? super.lifecycleAutotracking : sourceConfig?.lifecycleAutotracking ?? TrackerDefaults.lifecycleEvents
+        }
+        set {
+            super.lifecycleAutotracking = newValue
+            lifecycleAutotrackingUpdated = true
+        }
+    }
+
+    override var installAutotracking: Bool {
+        get {
+            return ((sourceConfig == nil) || installAutotrackingUpdated) ? super.installAutotracking : sourceConfig?.installAutotracking ?? TrackerDefaults.installEvent
+        }
+        set {
+            super.installAutotracking = newValue
+            installAutotrackingUpdated = true
+        }
+    }
+
+    override var exceptionAutotracking: Bool {
+        get {
+            return ((sourceConfig == nil) || exceptionAutotrackingUpdated) ? super.exceptionAutotracking : sourceConfig?.exceptionAutotracking ?? TrackerDefaults.exceptionEvents
+        }
+        set {
+            super.exceptionAutotracking = newValue
+            exceptionAutotrackingUpdated = true
+        }
+    }
+
+    override var diagnosticAutotracking: Bool {
+        get {
+            return ((sourceConfig == nil) || diagnosticAutotrackingUpdated) ? super.diagnosticAutotracking : sourceConfig?.diagnosticAutotracking ?? TrackerDefaults.trackerDiagnostic
+        }
+        set {
+            super.diagnosticAutotracking = newValue
+            diagnosticAutotrackingUpdated = true
+        }
+    }
+
+    override var userAnonymisation: Bool {
+        get {
+            return ((sourceConfig == nil) || userAnonymisationUpdated) ? super.userAnonymisation : sourceConfig?.userAnonymisation ?? TrackerDefaults.userAnonymisation
+        }
+        set {
+            super.userAnonymisation = newValue
+            userAnonymisationUpdated = true
+        }
+    }
+
+    override var trackerVersionSuffix: String? {
+        get {
+            return ((sourceConfig == nil) || trackerVersionSuffixUpdated) ? super.trackerVersionSuffix : sourceConfig?.trackerVersionSuffix
+        }
+        set {
+            super.trackerVersionSuffix = newValue
+            trackerVersionSuffixUpdated = true
+        }
+    }
+}
diff --git a/Sources/Core/Tracker/TrackerControllerImpl.swift b/Sources/Core/Tracker/TrackerControllerImpl.swift
new file mode 100644
index 000000000..f376687f5
--- /dev/null
+++ b/Sources/Core/Tracker/TrackerControllerImpl.swift
@@ -0,0 +1,298 @@
+//
+//  TrackerController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class TrackerControllerImpl: Controller, TrackerController {
+    
+    // MARK: - Controllers
+
+    var network: NetworkController? {
+        return serviceProvider.networkController
+    }
+
+    var emitter: EmitterController? {
+        return serviceProvider.emitterController
+    }
+
+    var gdpr: GDPRController? {
+        return serviceProvider.gdprController
+    }
+
+    var globalContexts: GlobalContextsController? {
+        return serviceProvider.globalContextsController
+    }
+
+    var subject: SubjectController? {
+        return serviceProvider.subjectController
+    }
+
+    var sessionController: SessionControllerImpl {
+        return serviceProvider.sessionController
+    }
+
+    var session: SessionController? {
+        let sessionController = serviceProvider.sessionController
+        return sessionController.isEnabled ? sessionController : nil
+    }
+
+    // MARK: - Control methods
+
+    func pause() {
+        dirtyConfig.isPaused = true
+        tracker.pauseEventTracking()
+    }
+
+    func resume() {
+        dirtyConfig.isPaused = false
+        tracker.resumeEventTracking()
+    }
+
+    func track(_ event: Event) -> UUID? {
+        return tracker.track(event)
+    }
+
+    // MARK: - Properties' setters and getters
+
+    var appId: String {
+        get {
+            return tracker.appId
+        }
+        set {
+            dirtyConfig.appId = newValue
+            dirtyConfig.appIdUpdated = true
+            tracker.appId = newValue
+        }
+    }
+
+    var namespace: String {
+        return (tracker).trackerNamespace
+    }
+
+    var devicePlatform: DevicePlatform {
+        get {
+            return tracker.devicePlatform
+        }
+        set {
+            dirtyConfig.devicePlatform = newValue
+            dirtyConfig.devicePlatformUpdated = true
+            tracker.devicePlatform = newValue
+        }
+    }
+
+    var base64Encoding: Bool {
+        get {
+            return tracker.base64Encoded
+        }
+        set {
+            dirtyConfig.base64Encoding = newValue
+            dirtyConfig.base64EncodingUpdated = true
+            tracker.base64Encoded = newValue
+        }
+    }
+
+    var logLevel: LogLevel {
+        get {
+            return tracker.logLevel
+        }
+        set {
+            dirtyConfig.logLevel = newValue
+            dirtyConfig.logLevelUpdated = true
+            tracker.logLevel = newValue
+        }
+    }
+
+    var loggerDelegate: LoggerDelegate? {
+        get {
+            return Logger.delegate
+        }
+        set {
+            Logger.delegate = newValue
+        }
+    }
+
+    var applicationContext: Bool {
+        get {
+            return tracker.applicationContext
+        }
+        set {
+            dirtyConfig.applicationContext = newValue
+            dirtyConfig.applicationContextUpdated = true
+            tracker.applicationContext = newValue
+        }
+    }
+
+    var platformContext: Bool {
+        get {
+            return tracker.subject?.platformContext ?? false
+        }
+        set {
+            dirtyConfig.platformContext = newValue
+            dirtyConfig.platformContextUpdated = true
+            tracker.subject?.platformContext = newValue
+        }
+    }
+
+    var geoLocationContext: Bool {
+        get {
+            return tracker.subject?.geoLocationContext ?? false
+        }
+        set {
+            dirtyConfig.geoLocationContext = newValue
+            dirtyConfig.geoLocationContextUpdated = true
+            tracker.subject?.geoLocationContext = newValue
+        }
+    }
+
+    var diagnosticAutotracking: Bool {
+        get {
+            return tracker.trackerDiagnostic
+        }
+        set {
+            dirtyConfig.diagnosticAutotracking = newValue
+            dirtyConfig.diagnosticAutotrackingUpdated = true
+            tracker.trackerDiagnostic = newValue
+        }
+    }
+
+    var exceptionAutotracking: Bool {
+        get {
+            return tracker.exceptionEvents
+        }
+        set {
+            dirtyConfig.exceptionAutotracking = newValue
+            dirtyConfig.exceptionAutotrackingUpdated = true
+            tracker.exceptionEvents = newValue
+        }
+    }
+
+    var installAutotracking: Bool {
+        get {
+            return tracker.installEvent
+        }
+        set {
+            dirtyConfig.installAutotracking = newValue
+            dirtyConfig.installAutotrackingUpdated = true
+            tracker.installEvent = newValue
+        }
+    }
+
+    var lifecycleAutotracking: Bool {
+        get {
+            return tracker.lifecycleEvents
+        }
+        set {
+            dirtyConfig.lifecycleAutotracking = newValue
+            dirtyConfig.lifecycleAutotrackingUpdated = true
+            tracker.lifecycleEvents = newValue
+        }
+    }
+
+    var deepLinkContext: Bool {
+        get {
+            return tracker.deepLinkContext
+        }
+        set {
+            dirtyConfig.deepLinkContext = newValue
+            dirtyConfig.deepLinkContextUpdated = true
+            tracker.deepLinkContext = newValue
+        }
+    }
+
+    var screenContext: Bool {
+        get {
+            return tracker.screenContext
+        }
+        set {
+            dirtyConfig.screenContext = newValue
+            dirtyConfig.screenContextUpdated = true
+            tracker.screenContext = newValue
+        }
+    }
+
+    var screenViewAutotracking: Bool {
+        get {
+            return tracker.autotrackScreenViews
+        }
+        set {
+            dirtyConfig.screenViewAutotracking = newValue
+            dirtyConfig.screenViewAutotrackingUpdated = true
+            tracker.autotrackScreenViews = newValue
+        }
+    }
+
+    var trackerVersionSuffix: String? {
+        get {
+            return tracker.trackerVersionSuffix
+        }
+        set {
+            dirtyConfig.trackerVersionSuffix = newValue
+            dirtyConfig.trackerVersionSuffixUpdated = true
+            if let value = newValue {
+                tracker.trackerVersionSuffix = value
+            }
+        }
+    }
+
+    var sessionContext: Bool {
+        get {
+            return tracker.sessionContext
+        }
+        set {
+            dirtyConfig.sessionContext = newValue
+            dirtyConfig.sessionContextUpdated = true
+            tracker.sessionContext = newValue
+        }
+    }
+
+    var userAnonymisation: Bool {
+        get {
+            return tracker.userAnonymisation
+        }
+        set {
+            dirtyConfig.userAnonymisation = newValue
+            dirtyConfig.userAnonymisationUpdated = true
+            tracker.userAnonymisation = newValue
+        }
+    }
+
+    var isTracking: Bool {
+        return tracker.isTracking
+    }
+
+    var version: String {
+        return kSPVersion
+    }
+
+    // MARK: - Private methods
+
+    private var tracker: Tracker {
+        if !serviceProvider.isTrackerInitialized {
+            // TODO: return nil
+            logError(message: "Recreating tracker instance after it was removed. This will not be supported in future versions.")
+        }
+        return serviceProvider.tracker
+    }
+
+    private var dirtyConfig: TrackerConfigurationUpdate {
+        return serviceProvider.trackerConfigurationUpdate
+    }
+}
diff --git a/Sources/Core/Tracker/TrackerDefaults.swift b/Sources/Core/Tracker/TrackerDefaults.swift
new file mode 100644
index 000000000..f43571855
--- /dev/null
+++ b/Sources/Core/Tracker/TrackerDefaults.swift
@@ -0,0 +1,42 @@
+//
+//  TrackerDefaults.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class TrackerDefaults {
+    private(set) static var base64Encoded = true
+    private(set) static var trackerVersionSuffix = ""
+    private(set) static var devicePlatform: DevicePlatform = Utilities.platform
+    private(set) static var foregroundTimeout = 1800
+    private(set) static var backgroundTimeout = 1800
+    private(set) static var sessionContext = true
+    private(set) static var deepLinkContext = true
+    private(set) static var screenContext = true
+    private(set) static var applicationContext = true
+    private(set) static var autotrackScreenViews = true
+    private(set) static var lifecycleEvents = false
+    private(set) static var exceptionEvents = true
+    private(set) static var installEvent = true
+    private(set) static var trackerDiagnostic = false
+    private(set) static var userAnonymisation = false
+    private(set) static var platformContext = true
+    private(set) static var geoLocationContext = false
+}
diff --git a/Sources/Core/Tracker/TrackerEvent.swift b/Sources/Core/Tracker/TrackerEvent.swift
new file mode 100644
index 000000000..f546fbc5b
--- /dev/null
+++ b/Sources/Core/Tracker/TrackerEvent.swift
@@ -0,0 +1,83 @@
+//
+//  SPTrackerEvent.h
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class TrackerEvent : InspectableEvent {
+    
+    private var _payload: [String: NSObject]
+    var payload: [String: NSObject] {
+        get { return _payload }
+        set { _payload = newValue }
+    }
+    private var _schema: String?
+    var schema: String? {
+        get { return _schema }
+        set { _schema = newValue }
+    }
+    private var _eventName: String?
+    var eventName: String? {
+        get { return _eventName }
+        set { _eventName = newValue }
+    }
+    var eventId: UUID
+    var timestamp: Int64
+    var trueTimestamp: Date?
+    var contexts: [SelfDescribingJson]
+    private var _state: TrackerStateSnapshot
+    var state: TrackerStateSnapshot {
+        get { return _state }
+        set { _state = newValue }
+    }
+
+    var isPrimitive: Bool
+    var isService: Bool
+    
+    init(event: Event, state: TrackerStateSnapshot? = nil) {
+        eventId = UUID()
+        timestamp = Int64(Date().timeIntervalSince1970 * 1000)
+        trueTimestamp = event.trueTimestamp
+        contexts = event.contexts
+        _payload = event.payload
+        _state = state ?? TrackerState()
+
+        isService = (event is TrackerError)
+        if let abstractEvent = event as? PrimitiveAbstract {
+            _eventName = abstractEvent.eventName
+            isPrimitive = true
+        } else {
+            _schema = (event as! SelfDescribingAbstract).schema
+            isPrimitive = false
+        }
+    }
+
+    func addPayloadValues(_ payload: [String : NSObject]) -> Bool {
+        var result = true
+        for (key, obj) in payload {
+            if self.payload[key] == nil {
+                self.payload[key] = obj
+            } else {
+                result = false
+            }
+        }
+        return result
+    }
+}
diff --git a/Sources/Core/Tracker/TrackerState.swift b/Sources/Core/Tracker/TrackerState.swift
new file mode 100644
index 000000000..245dc6bff
--- /dev/null
+++ b/Sources/Core/Tracker/TrackerState.swift
@@ -0,0 +1,67 @@
+//
+//  TrackerState.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// The global tracker state which collects all the state generated by the tracker state machines.
+class TrackerState: NSObject, TrackerStateSnapshot {
+    private var trackerState: [String: StateFuture] = [:]
+
+    /// Set a future computable state with a specific state identifier
+    func setStateFuture(_ state: StateFuture, identifier stateIdentifier: String) {
+        objc_sync_enter(self)
+        trackerState[stateIdentifier] = state
+        objc_sync_exit(self)
+    }
+
+    /// Get a future computable state associated with a state identifier
+    func stateFuture(withIdentifier stateIdentifier: String) -> StateFuture? {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        return trackerState[stateIdentifier]
+    }
+
+    func remove(withIdentifier stateIdentifer: String) {
+        objc_sync_enter(self)
+        trackerState.removeValue(forKey: stateIdentifer)
+        objc_sync_exit(self)
+    }
+
+    /// Get an immutable copy of the whole tracker state
+    func snapshot() -> TrackerStateSnapshot? {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        let newTrackerState = TrackerState()
+        newTrackerState.trackerState = trackerState
+        return newTrackerState
+    }
+
+    // Protocol SPTrackerStateSnapshot
+
+    func state(withIdentifier stateIdentifier: String) -> State? {
+        return stateFuture(withIdentifier: stateIdentifier)?.state
+    }
+
+    func state(withStateMachine stateMachine: StateMachineProtocol) -> State? {
+        return state(withIdentifier: stateMachine.identifier)
+    }
+}
diff --git a/Sources/Core/Tracker/WebViewMessageHandler.swift b/Sources/Core/Tracker/WebViewMessageHandler.swift
new file mode 100644
index 000000000..d9a38c750
--- /dev/null
+++ b/Sources/Core/Tracker/WebViewMessageHandler.swift
@@ -0,0 +1,171 @@
+//
+//  WebViewMessageHandler.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+#if os(iOS) || os(macOS)
+import WebKit
+
+/// Handler for messages from the JavaScript library embedded in Web views.
+///
+/// The handler parses messages from the JavaScript library calls and forwards the tracked events to be tracked by the mobile tracker.
+class WebViewMessageHandler: NSObject, WKScriptMessageHandler {
+    /// Callback called when the message handler receives a new message.
+    ///
+    /// The message dictionary should contain three properties:
+    /// 1. "event" with a dictionary containing the event information (structure depends on the tracked event)
+    /// 2. "context" (optional) with a list of self-describing JSONs
+    /// 3. "trackers" (optional) with a list of tracker namespaces to track the event with
+    func userContentController(
+        _ userContentController: WKUserContentController,
+        didReceive message: WKScriptMessage
+    ) {
+        receivedMesssage(message)
+    }
+    
+    func receivedMesssage(_ message: WKScriptMessage) {
+        if let body = message.body as? [AnyHashable : Any],
+           let event = body["event"] as? [AnyHashable : Any],
+           let command = body["command"] as? String {
+            let context = body["context"] as? [[AnyHashable : Any]] ?? []
+            let trackers = body["trackers"] as? [String] ?? []
+            
+            if command == "trackSelfDescribingEvent" {
+                trackSelfDescribing(event, withContext: context, andTrackers: trackers)
+            } else if command == "trackStructEvent" {
+                trackStructEvent(event, withContext: context, andTrackers: trackers)
+            } else if command == "trackPageView" {
+                trackPageView(event, withContext: context, andTrackers: trackers)
+            } else if command == "trackScreenView" {
+                trackScreenView(event, withContext: context, andTrackers: trackers)
+            }
+        }
+    }
+
+    func trackSelfDescribing(_ event: [AnyHashable : Any], withContext context: [[AnyHashable : Any]], andTrackers trackers: [String]) {
+        if let schema = event["schema"] as? String,
+           let payload = event["data"] as? [String : NSObject] {
+            let selfDescribing = SelfDescribing(schema: schema, payload: payload)
+            track(selfDescribing, withContext: context, andTrackers: trackers)
+        }
+    }
+
+    func trackStructEvent(_ event: [AnyHashable : Any], withContext context: [[AnyHashable : Any]], andTrackers trackers: [String]) {
+        let category = event["category"] as? String
+        let action = event["action"] as? String
+        let label = event["label"] as? String
+        let property = event["property"] as? String
+        let value = event["value"] as? NSNumber
+
+        if let category = category, let action = action {
+            let structured = Structured(category: category, action: action)
+            if let label = label {
+                structured.label = label
+            }
+            if let property = property {
+                structured.property = property
+            }
+            if let value = value {
+                structured.value = value
+            }
+            track(structured, withContext: context, andTrackers: trackers)
+        }
+    }
+
+    func trackPageView(_ event: [AnyHashable : Any], withContext context: [[AnyHashable : Any]], andTrackers trackers: [String]) {
+        let url = event["url"] as? String
+        let title = event["title"] as? String
+        let referrer = event["referrer"] as? String
+
+        if let url = url {
+            let pageView = PageView(pageUrl: url)
+            if let title = title {
+                pageView.pageTitle = title
+            }
+            if let referrer = referrer {
+                pageView.referrer = referrer
+            }
+            track(pageView, withContext: context, andTrackers: trackers)
+        }
+    }
+
+    func trackScreenView(_ event: [AnyHashable : Any], withContext context: [[AnyHashable : Any]], andTrackers trackers: [String]) {
+        let name = event["name"] as? String
+        let screenId = event["id"] as? String
+        let type = event["type"] as? String
+        let previousName = event["previousName"] as? String
+        let previousId = event["previousId"] as? String
+        let previousType = event["previousType"] as? String
+        let transitionType = event["transitionType"] as? String
+
+        if let name = name, let screenId = screenId {
+            let screenUuid = UUID(uuidString: screenId)
+            let screenView = ScreenView(name: name, screenId: screenUuid)
+            if let type = type {
+                screenView.type = type
+            }
+            if let previousName = previousName {
+                screenView.previousName = previousName
+            }
+            if let previousId = previousId {
+                screenView.previousId = previousId
+            }
+            if let previousType = previousType {
+                screenView.previousType = previousType
+            }
+            if let transitionType = transitionType {
+                screenView.transitionType = transitionType
+            }
+            track(screenView, withContext: context, andTrackers: trackers)
+        }
+    }
+
+    func track(_ event: Event, withContext context: [[AnyHashable : Any]], andTrackers trackers: [String]) {
+        event.contexts = parseContext(context)
+        if trackers.count > 0 {
+            for namespace in trackers {
+                if let tracker = Snowplow.tracker(namespace: namespace) {
+                    _ = tracker.track(event)
+                }
+            }
+        } else {
+            _ = Snowplow.defaultTracker()?.track(event)
+        }
+    }
+
+    func parseContext(_ context: [[AnyHashable : Any]]) -> [SelfDescribingJson] {
+        var contextEntities: [SelfDescribingJson] = []
+
+        for entityJson in context {
+            if let schema = entityJson["schema"] as? String,
+               let payload = entityJson["data"] as? [String : NSObject] {
+                let entity = SelfDescribingJson(schema: schema, andDictionary: payload)
+                contextEntities.append(entity)
+            }
+        }
+
+        return contextEntities
+    }
+}
+
+
+
+#endif
diff --git a/Sources/Core/TrackerConstants.swift b/Sources/Core/TrackerConstants.swift
new file mode 100644
index 000000000..be678473d
--- /dev/null
+++ b/Sources/Core/TrackerConstants.swift
@@ -0,0 +1,272 @@
+//
+//  TrackerConstants.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+// --- Version
+#if os(iOS)
+let kSPVersion = "ios-4.1.0"
+#elseif os(tvOS)
+let kSPVersion = "tvos-4.1.0"
+#elseif os(watchOS)
+let kSPVersion = "watchos-4.1.0"
+#else
+let kSPVersion = "osx-4.1.0"
+#endif
+
+// --- Session Dictionary keys
+let kSPInstallationUserId = "SPInstallationUserId"
+
+// --- Emitter
+let kSPContentTypeHeader = "application/json; charset=utf-8"
+let kSPAcceptContentHeader = "text/html, application/x-www-form-urlencoded, text/plain, image/gif"
+let kSPDefaultBufferTimeout = 60
+let kSPEndpointPost = "/com.snowplowanalytics.snowplow/tp2"
+let kSPEndpointGet = "/i"
+
+// --- Schema Paths
+let kSPIglu = "iglu"
+let kSPSnowplowVendor = "com.snowplowanalytics.snowplow"
+let kSPSchemaTag = "jsonschema"
+let kSPPayloadDataSchema = "iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4"
+let kSPUserTimingsSchema = "iglu:com.snowplowanalytics.snowplow/timing/jsonschema/1-0-0"
+let kSPScreenViewSchema = "iglu:com.snowplowanalytics.mobile/screen_view/jsonschema/1-0-0"
+let kSPUnstructSchema = "iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0"
+let kSPContextSchema = "iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-1"
+let kSPMobileContextSchema = "iglu:com.snowplowanalytics.snowplow/mobile_context/jsonschema/1-0-2"
+let kSPDesktopContextSchema = "iglu:com.snowplowanalytics.snowplow/desktop_context/jsonschema/1-0-0"
+let kSPSessionContextSchema = "iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2"
+let kSPScreenContextSchema = "iglu:com.snowplowanalytics.mobile/screen/jsonschema/1-0-0"
+let kSPGeoContextSchema = "iglu:com.snowplowanalytics.snowplow/geolocation_context/jsonschema/1-1-0"
+let kSPConsentDocumentSchema = "iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0"
+let kSPConsentGrantedSchema = "iglu:com.snowplowanalytics.snowplow/consent_granted/jsonschema/1-0-0"
+let kSPConsentWithdrawnSchema = "iglu:com.snowplowanalytics.snowplow/consent_withdrawn/jsonschema/1-0-0"
+let kSPPushNotificationSchema = "iglu:com.apple/notification_event/jsonschema/1-0-1"
+let kSPApplicationContextSchema = "iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0"
+let kSPForegroundSchema = "iglu:com.snowplowanalytics.snowplow/application_foreground/jsonschema/1-0-0"
+let kSPBackgroundSchema = "iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0"
+let kSPErrorSchema = "iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-2"
+let kSPApplicationInstallSchema = "iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0"
+let kSPGdprContextSchema = "iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0"
+let kSPDiagnosticErrorSchema = "iglu:com.snowplowanalytics.snowplow/diagnostic_error/jsonschema/1-0-0"
+
+// --- Event Keys
+let kSPEventPageView = "pv"
+let kSPEventStructured = "se"
+let kSPEventUnstructured = "ue"
+let kSPEventEcomm = "tr"
+let kSPEventEcommItem = "ti"
+
+// --- General Keys
+let kSPSchema = "schema"
+let kSPData = "data"
+let kSPEvent = "e"
+let kSPEid = "eid"
+let kSPTimestamp = "dtm"
+let kSPTrueTimestamp = "ttm"
+let kSPSentTimestamp = "stm"
+let kSPTrackerVersion = "tv"
+let kSPAppId = "aid"
+let kSPNamespace = "tna"
+let kSPUid = "uid"
+let kSPContext = "co"
+let kSPContextEncoded = "cx"
+let kSPUnstructured = "ue_pr"
+let kSPUnstructuredEncoded = "ue_px"
+
+// --- Subject
+let kSPPlatform = "p"
+let kSPResolution = "res"
+let kSPViewPort = "vp"
+let kSPColorDepth = "cd"
+let kSPTimezone = "tz"
+let kSPLanguage = "lang"
+let kSPIpAddress = "ip"
+let kSPUseragent = "ua"
+let kSPNetworkUid = "tnuid"
+let kSPDomainUid = "duid"
+
+// --- Platform Generic
+let kSPPlatformOsType = "osType"
+let kSPPlatformOsVersion = "osVersion"
+let kSPPlatformDeviceManu = "deviceManufacturer"
+let kSPPlatformDeviceModel = "deviceModel"
+
+// --- Mobile Context
+let kSPMobileCarrier = "carrier"
+let kSPMobileAppleIdfa = "appleIdfa"
+let kSPMobileAppleIdfv = "appleIdfv"
+let kSPMobileNetworkType = "networkType"
+let kSPMobileNetworkTech = "networkTechnology"
+let kSPMobilePhysicalMemory = "physicalMemory"
+let kSPMobileAppAvailableMemory = "appAvailableMemory"
+let kSPMobileBatteryLevel = "batteryLevel"
+let kSPMobileBatteryState = "batteryState"
+let kSPMobileLowPowerMode = "lowPowerMode"
+let kSPMobileAvailableStorage = "availableStorage"
+let kSPMobileTotalStorage = "totalStorage"
+
+// --- Application Context
+let kSPApplicationVersion = "version"
+let kSPApplicationBuild = "build"
+
+// --- Session Context
+let kSPSessionUserId = "userId"
+let kSPSessionId = "sessionId"
+let kSPSessionPreviousId = "previousSessionId"
+let kSPSessionIndex = "sessionIndex"
+let kSPSessionStorage = "storageMechanism"
+let kSPSessionFirstEventId = "firstEventId"
+let kSPSessionFirstEventTimestamp = "firstEventTimestamp"
+let kSPSessionEventIndex = "eventIndex"
+let kSPSessionAnonymousUserId = "00000000-0000-0000-0000-000000000000"
+
+// --- Geo-Location Context
+let kSPGeoLatitude = "latitude"
+let kSPGeoLongitude = "longitude"
+let kSPGeoLatLongAccuracy = "latitudeLongitudeAccuracy"
+let kSPGeoAltitude = "altitude"
+let kSPGeoAltitudeAccuracy = "altitudeAccuracy"
+let kSPGeoBearing = "bearing"
+let kSPGeoSpeed = "speed"
+let kSPGeoTimestamp = "timestamp"
+
+// --- Screen Context
+let kSPScreenName = "name"
+let kSPScreenType = "type"
+let kSPScreenId = "id"
+let kSPScreenViewController = "viewController"
+let kSPScreenTopViewController = "topViewController"
+
+// --- Page View Event
+let kSPPageUrl = "url"
+let kSPPageTitle = "page"
+let kSPPageRefr = "refr"
+
+// --- Structured Event
+let kSPStuctCategory = "se_ca"
+let kSPStuctAction = "se_ac"
+let kSPStuctLabel = "se_la"
+let kSPStuctProperty = "se_pr"
+let kSPStuctValue = "se_va"
+
+// --- E-commerce Transaction Event
+let kSPEcommId = "tr_id"
+let kSPEcommTotal = "tr_tt"
+let kSPEcommAffiliation = "tr_af"
+let kSPEcommTax = "tr_tx"
+let kSPEcommShipping = "tr_sh"
+let kSPEcommCity = "tr_ci"
+let kSPEcommState = "tr_st"
+let kSPEcommCountry = "tr_co"
+let kSPEcommCurrency = "tr_cu"
+
+// --- E-commerce Transaction Item Event
+let kSPEcommItemId = "ti_id"
+let kSPEcommItemSku = "ti_sk"
+let kSPEcommItemName = "ti_nm"
+let kSPEcommItemCategory = "ti_ca"
+let kSPEcommItemPrice = "ti_pr"
+let kSPEcommItemQuantity = "ti_qu"
+let kSPEcommItemCurrency = "ti_cu"
+
+// --- Consent Granted Event
+let KSPCgExpiry = "expiry"
+
+// --- Consent Withdrawn Event
+let KSPCwAll = "all"
+
+// --- Consent Document Event
+let kSPCdId = "id"
+let kSPCdVersion = "version"
+let kSPCdName = "name"
+let KSPCdDescription = "description"
+
+// --- Screen View Event
+let kSPSvName = "name"
+let kSPSvType = "type"
+let kSPSvScreenId = "id"
+let kSPSvPreviousName = "previousName"
+let kSPSvPreviousType = "previousType"
+let kSPSvPreviousScreenId = "previousId"
+let kSPSvTransitionType = "transitionType"
+let kSPSvViewController = "viewController"
+let kSPSvTopViewController = "topViewController"
+
+// --- User Timing Event
+let kSPUtCategory = "category"
+let kSPUtVariable = "variable"
+let kSPUtTiming = "timing"
+let kSPUtLabel = "label"
+
+// --- Push Notification Event
+let kSPPushAction = "action"
+let kSPPushTrigger = "trigger"
+let kSPPushDeliveryDate = "deliveryDate"
+let kSPPushCategoryId = "categoryIdentifier"
+let kSPPushThreadId = "threadIdentifier"
+let kSPPushNotificationParam = "notification"
+let kSPPnTitle = "title"
+let kSPPnSubtitle = "subtitle"
+let kSPPnBody = "body"
+let kSPPnBadge = "badge"
+let kSPPnSound = "sound"
+let kSPPnLaunchImageName = "launchImageName"
+let kSPPnUserInfo = "userInfo"
+let kSPPnAttachments = "attachments"
+let kSPPnAttachmentId = "identifier"
+let kSPPnAttachmentUrl = "url"
+let kSPPnAttachmentType = "type"
+
+// --- Foreground Event
+let kSPBackgroundIndex = "backgroundIndex"
+
+// --- Background Event
+let kSPForegroundIndex = "foregroundIndex"
+
+// --- Error Event
+let kSPErrorName = "exceptionName"
+let kSPErrorStackTrace = "stackTrace"
+let kSPErrorLanguage = "programmingLanguage"
+let kSPErrorMessage = "message"
+
+// --- Error Event - Tracker Settings Storage
+let kSPErrorTrackerUrl = "url"
+let kSPErrorTrackerProtocol = "protocol"
+let kSPErrorTrackerMethod = "method"
+
+// --- Install tracking
+let kSPInstalledBefore = "SPInstalledBefore"
+let kSPInstallTimestamp = "SPInstallTimestamp"
+let kSPPreviousInstallVersion = "SPInstallVersion"
+let kSPPreviousInstallBuild = "SPInstallBuild"
+
+// --- GDPR Context
+let kSPBasisForProcessing = "basisForProcessing"
+let kSPDocumentId = "documentId"
+let kSPDocumentVersion = "documentVersion"
+let kSPDocumentDescription = "documentDescription"
+
+// --- Tracker Diagnostic
+let kSPDiagnosticErrorMessage = "message"
+let kSPDiagnosticErrorStack = "stackTrace"
+let kSPDiagnosticErrorClassName = "className"
+let kSPDiagnosticErrorExceptionName = "exceptionName"
diff --git a/Sources/Core/Utils/DataPersistence.swift b/Sources/Core/Utils/DataPersistence.swift
new file mode 100644
index 000000000..186233cad
--- /dev/null
+++ b/Sources/Core/Utils/DataPersistence.swift
@@ -0,0 +1,209 @@
+//
+//  SPDataPersistence.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+var instances: [String : DataPersistence]? = nil
+let kSPSessionDictionaryPrefix = "SPSessionDictionary"
+let kFilename = "namespace"
+let kFilenameExt = "dict"
+let kSessionFilenameV1 = "session.dict"
+let kSessionFilenamePrefixV2_2 = "session"
+var sessionKey = "session"
+
+class DataPersistence: NSObject {
+    var data: [String : [String : NSObject]] {
+        get {
+            objc_sync_enter(self)
+            defer { objc_sync_exit(self) }
+            if !isStoredOnFile {
+                return ((UserDefaults.standard.dictionary(forKey: userDefaultsKey) ?? [:]) as? [String : [String : NSObject]]) ?? [:]
+            }
+            var result: [String : [String : NSObject]]? = nil
+            if let fileUrl = fileUrl {
+                result = NSDictionary(contentsOf: fileUrl) as Dictionary? as? [String : [String : NSObject]]
+            }
+
+            if result == nil {
+                // Initialise
+                result = [:]
+                var sessionDict: [AnyHashable : Any] = [:]
+                // Add missing fields
+                sessionDict[kSPSessionFirstEventId] = ""
+                sessionDict[kSPSessionStorage] = "LOCAL_STORAGE"
+                // Wrap up
+                result?[sessionKey] = sessionDict as? [String : NSObject]
+                if let result = result, let fileUrl = fileUrl {
+                    let _ = storeDictionary(result, fileURL: fileUrl)
+                }
+            }
+
+            return result ?? [:]
+        }
+        set(data) {
+            objc_sync_enter(self)
+            if let fileUrl = fileUrl {
+                let _ = storeDictionary(data, fileURL: fileUrl)
+            } else {
+                UserDefaults.standard.set(data, forKey: userDefaultsKey)
+            }
+            objc_sync_exit(self)
+        }
+    }
+
+    var session: [String : NSObject]? {
+        get {
+            return (data)[sessionKey]
+        }
+        set(session) {
+            objc_sync_enter(self)
+            var data = self.data
+            data[sessionKey] = session
+            self.data = data
+            objc_sync_exit(self)
+        }
+    }
+
+    var isStoredOnFile: Bool {
+        return fileUrl != nil
+    }
+    private var escapedNamespace: String = ""
+    private var userDefaultsKey: String = ""
+    private var directoryUrl: URL?
+    private var fileUrl: URL?
+
+    init(namespace escapedNamespace: String, storedOnFile isStoredOnFile: Bool) {
+        super.init()
+        self.escapedNamespace = escapedNamespace
+        userDefaultsKey = "\(kSPSessionDictionaryPrefix)_\(escapedNamespace)"
+#if !(os(tvOS) || os(watchOS))
+        if isStoredOnFile {
+            let directoryUrl = createDirectoryUrl()
+            let filename = "\(kFilename)_\(escapedNamespace).\(kFilenameExt)"
+            fileUrl = directoryUrl?.appendingPathComponent(filename)
+            self.directoryUrl = directoryUrl
+        }
+#endif
+    }
+
+    class func getFor(namespace: String, storedOnFile isStoredOnFile: Bool = true) -> DataPersistence? {
+        let escapedNamespace = DataPersistence.string(fromNamespace: namespace)
+        if escapedNamespace.count <= 0 {
+            return nil
+        }
+        objc_sync_enter(DataPersistence.self)
+        defer { objc_sync_exit(DataPersistence.self) }
+        
+        if let instances = instances {
+            if let instance = instances[escapedNamespace] {
+                return instance
+            }
+        } else {
+            instances = [:]
+        }
+        
+        let instance = DataPersistence(namespace: escapedNamespace, storedOnFile: isStoredOnFile)
+        instances?[escapedNamespace] = instance
+        return instance
+    }
+
+    class func remove(withNamespace namespace: String) -> Bool {
+        if let instance = DataPersistence.getFor(namespace: namespace) {
+            objc_sync_enter(DataPersistence.self)
+            instances?.removeValue(forKey: instance.escapedNamespace)
+            objc_sync_exit(DataPersistence.self)
+            let _ = instance.removeAll()
+        }
+        return true
+    }
+
+    class func string(fromNamespace namespace: String) -> String {
+        var regex: NSRegularExpression? = nil
+        do {
+            regex = try NSRegularExpression(pattern: "[^a-zA-Z0-9_]+", options: [])
+        } catch {
+        }
+        return regex?.stringByReplacingMatches(in: namespace, options: [], range: NSRange(location: 0, length: namespace.count), withTemplate: "-") ?? ""
+    }
+
+    // MARK: - Private instance methods
+
+
+    func removeAll() -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        UserDefaults.standard.removeObject(forKey: userDefaultsKey)
+        
+        if let fileUrl = fileUrl {
+            do {
+                try FileManager.default.removeItem(at: fileUrl)
+            } catch let error {
+                logError(message: error.localizedDescription)
+                return false
+            }
+        }
+        return true
+    }
+
+    func createDirectoryUrl() -> URL? {
+        let fileManager = FileManager.default
+        var url = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).last
+        url = url?.appendingPathComponent("snowplow", isDirectory: true)
+        
+        var error: Error? = nil
+        do {
+            if try url?.checkResourceIsReachable() ?? false {
+                return url
+            }
+        } catch let e {
+            error = e
+        }
+        
+        do {
+            if let url = url {
+                try fileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
+                return url
+            }
+        } catch let e {
+            error = e
+        }
+        
+        if let error = error {
+            logError(message: String(format: "Unable to create directory for tracker data persistence: %@", error.localizedDescription))
+        }
+        return nil
+    }
+
+    func storeDictionary(_ dictionary: [AnyHashable : Any], fileURL fileUrl: URL) -> Bool {
+        if #available(iOS 11.0, macOS 10.13, watchOS 4.0, *) {
+            do {
+                try (dictionary as NSDictionary).write(to: fileUrl)
+                return true
+            } catch let error {
+                logError(message: String(format: "Unable to write file for sessions: %@", error.localizedDescription))
+            }
+        } else {
+            return (dictionary as NSDictionary).write(to: fileUrl, atomically: true)
+        }
+        return false
+    }
+}
diff --git a/Sources/Core/Utils/DeviceInfoMonitor.swift b/Sources/Core/Utils/DeviceInfoMonitor.swift
new file mode 100644
index 000000000..64fb72779
--- /dev/null
+++ b/Sources/Core/Utils/DeviceInfoMonitor.swift
@@ -0,0 +1,341 @@
+//
+//  SPDeviceInfoMonitor.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+import os
+#if os(watchOS)
+import WatchKit
+#endif
+#if os(iOS)
+import CoreTelephony
+#endif
+#if os(iOS) || os(tvOS)
+import UIKit
+#endif
+
+class DeviceInfoMonitor {
+    /// Returns a generated string unique to each device, used only for serving advertisements. This works only if you have the AdSupport library in your project and you enable the compiler flag <code>SNOWPLOW_IDFA_ENABLED</code> to your build settings.
+    /// - Returns: A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
+    /*
+     The IDFA can be retrieved using selectors rather than proper instance methods because
+     the compiler would complain about the missing AdSupport framework.
+     As stated in the header file, this only works if you have the AdSupport library in your project.
+     If you have it and you want to use IDFA, add the compiler flag <code>SNOWPLOW_IDFA_ENABLED</code> to your build settings.
+     If you haven't AdSupport framework in your project or SNOWPLOW_IDFA_ENABLED it's not set, it just compiles returning a nil advertisingIdentifier.
+
+     Note that `advertisingIdentifier` returns a sequence of 0s when used in the simulator.
+     Use a real device if you want a proper IDFA.
+     */
+    var appleIdfa: String? {
+        #if os(iOS) || os(tvOS)
+        #if SNOWPLOW_IDFA_ENABLED
+        var errorMsg = "ASIdentifierManager not found. Please, add the AdSupport.framework if you want to use it."
+        let identifierManagerClass: AnyClass? = NSClassFromString("ASIdentifierManager")
+        if identifierManagerClass == nil {
+            logError(message: errorMsg)
+            return nil
+        }
+
+        let sharedManagerSelector = NSSelectorFromString("sharedManager")
+        if !(identifierManagerClass?.responds(to: sharedManagerSelector) ?? false) {
+            logError(message: errorMsg)
+            return nil
+        }
+
+        let identifierManager = (identifierManagerClass?.method(for: sharedManagerSelector))(identifierManagerClass, sharedManagerSelector)
+
+        if #available(iOS 14.0, *) {
+            errorMsg = "ATTrackingManager not found. Please, add the AppTrackingTransparency.framework if you want to use it."
+            let trackingManagerClass: AnyClass? = NSClassFromString("ATTrackingManager")
+            if trackingManagerClass == nil {
+                logError(message: errorMsg)
+                return nil
+            }
+
+            let trackingStatusSelector = NSSelectorFromString("trackingAuthorizationStatus")
+            if !(trackingManagerClass?.responds(to: trackingStatusSelector) ?? false) {
+                logError(message: errorMsg)
+                return nil
+            }
+
+            //notDetermined = 0, restricted = 1, denied = 2, authorized = 3
+            let authorizationStatus = (Int(trackingManagerClass?.method(for: trackingStatusSelector) ?? 0))(trackingManagerClass, trackingStatusSelector)
+
+            if authorizationStatus != 3 {
+                logDebug(message: String(format: "The user didn't let tracking of IDFA. Authorization status is: %d", authorizationStatus))
+                return nil
+            }
+        } else {
+            let isAdvertisingTrackingEnabledSelector = NSSelectorFromString("isAdvertisingTrackingEnabled")
+            if !(identifierManager?.responds(to: isAdvertisingTrackingEnabledSelector) ?? false) {
+                logError(message: errorMsg)
+                return nil
+            }
+
+            let isAdvertisingTrackingEnabled = (Bool(identifierManager?.method(for: isAdvertisingTrackingEnabledSelector) ?? false))(identifierManager, isAdvertisingTrackingEnabledSelector)
+            if !isAdvertisingTrackingEnabled {
+                logError(message: "The user didn't let tracking of IDFA.")
+                return nil
+            }
+        }
+
+        let advertisingIdentifierSelector = NSSelectorFromString("advertisingIdentifier")
+        if !(identifierManager?.responds(to: advertisingIdentifierSelector) ?? false) {
+            logError(message: "ASIdentifierManager doesn't respond to selector `advertisingIdentifier`.")
+            return nil
+        }
+
+        if let uuid = (identifierManager?.method(for: advertisingIdentifierSelector) as? UUID)(identifierManager, advertisingIdentifierSelector) {
+            return uuid.uuidString
+        }
+        #endif
+        #endif
+        return nil
+    }
+
+    /// Returns the generated identifier for vendors. More info can be found in UIDevice's identifierForVendor documentation. If you do not want to use IDFV, add the comiler flag <code>SNOWPLOW_NO_IDFV</code> to your build settings.
+    /// - Returns: A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
+    var appleIdfv: String? {
+        #if os(iOS) || os(tvOS)
+        #if !SNOWPLOW_NO_IDFV
+        if let idfv = UIDevice.current.identifierForVendor?.uuidString {
+            return idfv
+        }
+        #endif
+        #endif
+        return nil
+    }
+
+    /// Returns the current device's vendor in the form of a string.
+    /// - Returns: A string with vendor, i.e. "Apple Inc."
+    var deviceVendor: String? {
+        return "Apple Inc."
+    }
+
+    /// Returns the current device's model in the form of a string.
+    /// - Returns: A string with device model.
+    var deviceModel: String? {
+        let simulatorModel = (ProcessInfo.processInfo.environment)["SIMULATOR_MODEL_IDENTIFIER"]
+        if simulatorModel != nil {
+            return simulatorModel
+        }
+        
+        var size = 0
+        sysctlbyname("hw.machine", nil, &size, nil, 0)
+        var machine = [CChar](repeating: 0, count: size)
+        sysctlbyname("hw.machine", &machine, &size, nil, 0)
+        return String(cString: machine)
+    }
+
+    /// This is to detect what the version of mobile OS of the current device.
+    /// - Returns: The current device's OS version type as a string.
+    var osVersion: String? {
+        #if os(iOS) || os(tvOS)
+        return UIDevice.current.systemVersion
+        #elseif os(watchOS)
+        return WKInterfaceDevice.current().systemVersion
+        #else
+        var osxMajorVersion: Int32
+        var osxMinorVersion: Int32
+        var osxPatchFixVersion: Int32
+        let info = ProcessInfo.processInfo
+        let systemVersion = info.operatingSystemVersion
+        osxMajorVersion = Int32(systemVersion.majorVersion)
+        osxMinorVersion = Int32(systemVersion.minorVersion)
+        osxPatchFixVersion = Int32(systemVersion.patchVersion)
+        let versionString = "\(osxMajorVersion).\(osxMinorVersion).\(osxPatchFixVersion)"
+        return versionString
+        #endif
+    }
+
+    var osType: String? {
+        #if os(iOS)
+        return "ios"
+        #elseif os(tvOS)
+        return "tvos"
+        #elseif os(watchOS)
+        return "watchos"
+        #else
+        return "osx"
+        #endif
+    }
+
+    /// Returns the carrier of the SIM inserted in the device.
+    /// - Returns: A string containing the carrier name of the service provider.
+    var carrierName: String? {
+        #if os(iOS)
+        let networkInfo = CTTelephonyNetworkInfo()
+        var carrier: CTCarrier?
+        if #available(iOS 12.1, *) {
+            // `serviceSubscribersCellularProviders` has a bug in the iOS 12.0 so we use it from iOS 12.1
+            let carrierKey = self.carrierKey
+            let services = networkInfo.serviceSubscriberCellularProviders
+            carrier = services?[carrierKey]
+        } else {
+            carrier = networkInfo.subscriberCellularProvider
+        }
+        return carrier?.carrierName
+        #else
+        return nil
+        #endif
+    }
+
+    /// Returns the Network Technology the device is using.
+    /// - Returns: A string containing the Network Technology.
+    var networkTechnology: String? {
+        #if os(iOS)
+        let networkInfo = CTTelephonyNetworkInfo()
+        if #available(iOS 12.1, *) {
+            // `serviceCurrentRadioAccessTechnology` has a bug in the iOS 12.0 so we use it from iOS 12.1
+            let carrierKey = self.carrierKey
+            let services = networkInfo.serviceCurrentRadioAccessTechnology
+            return services?[carrierKey]
+        } else {
+            return networkInfo.currentRadioAccessTechnology
+        }
+        #else
+        return nil
+        #endif
+    }
+
+    var carrierKey: String {
+        #if os(iOS)
+        if #available(iOS 12.1, *) {
+            let networkInfo = CTTelephonyNetworkInfo()
+            // `serviceSubscribersCellularProviders` has a bug in the iOS 12.0 so we use it from iOS 12.1
+            let services = networkInfo.serviceSubscriberCellularProviders
+            let carrierKeys = services?.keys
+            // From iOS 12, iPhones with eSIMs can return multiple carrier providers.
+            // We can't prefer anyone of them so we track the first reported.
+            return carrierKeys?.first ?? ""
+        }
+        #endif
+        return ""
+    }
+
+    /// Returns the Network Type the device is connected to.
+    /// - Returns: A string containing the Network Type.
+    var networkType: String? {
+        #if os(iOS)
+        let networkStatus = SNOWReachability.forInternetConnection()?.networkStatus
+        switch networkStatus {
+        case .offline:
+            return "offline"
+        case .wifi:
+            return "wifi"
+        case .wwan:
+            return "mobile"
+        default:
+            break
+        }
+        #endif
+        return "offline"
+    }
+
+    /// Returns remaining battery level as an integer percentage of total battery capacity.
+    /// - Returns: Battery level.
+    var batteryLevel: Int? {
+        #if os(iOS)
+        let batteryLevel = UIDevice.current.batteryLevel
+        if batteryLevel != Float(UIDevice.BatteryState.unknown.rawValue) && batteryLevel >= 0 {
+            return Int(batteryLevel * 100)
+        }
+        #endif
+        return nil
+    }
+
+    /// Returns battery state for the device.
+    /// - Returns: One of "charging", "full", "unplugged" or NULL
+    var batteryState: String {
+        #if os(iOS)
+        switch UIDevice.current.batteryState {
+        case .charging:
+            return "charging"
+        case .full:
+            return "full"
+        case .unplugged:
+            return "unplugged"
+        default:
+            return ""
+        }
+        #else
+        return ""
+        #endif
+    }
+
+    /// Returns whether low power mode is activated.
+    /// - Returns: Boolean indicating the state of low power mode.
+    var isLowPowerModeEnabled: Bool? {
+        #if os(iOS)
+        return ProcessInfo.processInfo.isLowPowerModeEnabled
+        #else
+        return nil
+        #endif
+    }
+
+    /// Returns total physical system memory in bytes.
+    /// - Returns: Total physical system memory in bytes.
+    var physicalMemory: UInt64 {
+        return ProcessInfo.processInfo.physicalMemory
+    }
+
+    /// Returns the amount of memory in bytes available to the current app (iOS 13+).
+    /// - Returns: Amount of memory in bytes available to the current app (or 0 if not supported).
+    var appAvailableMemory: Int? {
+        // TODO: couldn't find a way to import <os/proc.h>
+//        #if os(iOS)
+//        if #available(iOS 13.0, *) {
+//            return os_proc_available_memory()
+//        }
+//        #endif
+        return nil
+    }
+
+    /// Returns number of bytes of storage remaining. The information is requested from the home directory.
+    /// - Returns: Bytes of storage remaining.
+    var availableStorage: Int64? {
+        #if os(iOS)
+        let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
+        do {
+            let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
+            return values.volumeAvailableCapacityForImportantUsage
+        } catch {
+            logError(message: "Failed to read available storage size: \(error.localizedDescription)")
+        }
+        #endif
+        return nil
+    }
+
+    /// Returns the total number of bytes of storage. The information is requested from the home directory.
+    /// - Returns: Total size of storage in bytes.
+    var totalStorage: Int? {
+        #if os(iOS)
+        let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
+        do {
+            let values = try fileURL.resourceValues(forKeys: [.volumeTotalCapacityKey])
+            return values.volumeTotalCapacity
+        } catch {
+            logError(message: "Failed to read available storage size: \(error.localizedDescription)")
+        }
+        #endif
+        return nil
+    }
+}
diff --git a/Sources/Core/Utils/SNOWReachability.swift b/Sources/Core/Utils/SNOWReachability.swift
new file mode 100644
index 000000000..2ffcaf465
--- /dev/null
+++ b/Sources/Core/Utils/SNOWReachability.swift
@@ -0,0 +1,101 @@
+//
+//  SNOWReachability.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+#if os(iOS)
+import Foundation
+import SystemConfiguration
+
+enum SNOWNetworkStatus : Int {
+    case offline
+    case wifi
+    case wwan
+}
+
+class SNOWReachability: NSObject {
+    private var reachability: SCNetworkReachability
+
+    var networkStatus: SNOWNetworkStatus! {
+        var flags = SCNetworkReachabilityFlags()
+        if !SCNetworkReachabilityGetFlags(reachability, &flags) {
+            return .offline
+        }
+        return reachabilityStatus(for: flags)
+    }
+
+    init(reachability: SCNetworkReachability) {
+        self.reachability = reachability
+    }
+
+    class func forInternetConnection() -> SNOWReachability? {
+        var zeroAddress = sockaddr_in()
+        zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
+        zeroAddress.sin_family = sa_family_t(AF_INET)
+        
+        guard let reachability = withUnsafePointer(to: &zeroAddress, {
+            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
+                SCNetworkReachabilityCreateWithAddress(nil, $0)
+            }
+        }) else {
+            return nil
+        }
+
+        return SNOWReachability(reachability: reachability)
+    }
+
+    // MARK: - Private methods
+
+    private func reachabilityStatus(for flags: SCNetworkReachabilityFlags) -> SNOWNetworkStatus {
+        let isReachable = (flags.rawValue & SCNetworkReachabilityFlags.reachable.rawValue) != 0
+        let isConnectionRequired = (flags.rawValue & SCNetworkReachabilityFlags.connectionRequired.rawValue) != 0
+        let isOnDemand = (flags.rawValue & SCNetworkReachabilityFlags.connectionOnDemand.rawValue) != 0
+        let isOnTraffic = (flags.rawValue & SCNetworkReachabilityFlags.connectionOnTraffic.rawValue) != 0
+        let isInterventionRequired = (flags.rawValue & SCNetworkReachabilityFlags.interventionRequired.rawValue) != 0
+
+        if !isReachable {
+            return .offline
+        }
+
+        #if os(iOS)
+        let isWWAN = (flags.rawValue & SCNetworkReachabilityFlags.isWWAN.rawValue) == SCNetworkReachabilityFlags.isWWAN.rawValue
+        if isWWAN {
+            return .wwan
+        }
+        #endif
+
+        var returnValue: SNOWNetworkStatus = .offline
+        if !isConnectionRequired {
+            // If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi
+            returnValue = .wifi
+        }
+        if isOnDemand || isOnTraffic {
+            //... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
+            if !isInterventionRequired {
+                //... and no [user] intervention is needed...
+                returnValue = .wifi
+            }
+        }
+        return returnValue
+    }
+
+    deinit {
+    }
+}
+#endif
diff --git a/Sources/Core/Utils/Utilities.swift b/Sources/Core/Utils/Utilities.swift
new file mode 100644
index 000000000..3292f2aef
--- /dev/null
+++ b/Sources/Core/Utils/Utilities.swift
@@ -0,0 +1,254 @@
+//
+//  Utilities.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+#if os(iOS)
+import UIKit
+#elseif os(macOS)
+import AppKit
+#elseif os(tvOS)
+import UIKit
+#elseif os(watchOS)
+import WatchKit
+#endif
+
+/// This is a class that contains utility functions used throughout the tracker.
+class Utilities {
+    /// Returns the system timezone region.
+    /// - Returns: A string of the timezone region (e.g. 'Toronto/Canada').
+    class var timezone: String? {
+        return TimeZone.current.identifier
+    }
+
+    /// Returns the system language currently used on the device.
+    /// - Returns: A string of the current language.
+    class var language: String? {
+        return Locale.preferredLanguages.first
+    }
+
+    /// Returns the platform type of the device..
+    /// - Returns: A string of the platform type.
+    class var platform: DevicePlatform {
+        #if os(iOS)
+        return .mobile
+        #else
+        return .desktop
+        #endif
+    }
+
+    /// Returns a randomly generated UUID (type 4).
+    /// - Returns: A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
+    class func getUUIDString() -> String {
+        // Generates type 4 UUID
+        return UUID().uuidString.lowercased()
+    }
+
+    /// Check if the value is a valid UUID (type 4).
+    /// - Parameter uuidString: UUID string to validate.
+    /// - Returns: Weither is a valid UUID string.
+    class func isUUIDString(_ uuidString: String) -> Bool {
+        return UUID(uuidString: uuidString) != nil
+    }
+
+    /// Returns the timestamp (in milliseconds) generated at the point it was called.
+    /// - Returns: A double of the timestamp from when the method was called.
+    class func getTimestamp() -> NSNumber {
+        let time = Date()
+        return NSNumber(value: time.timeIntervalSince1970 * 1000)
+    }
+
+    class func timestamp(toISOString timestamp: Int64) -> String? {
+        let eventDate = Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
+        let formatter = DateFormatter()
+        formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
+        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
+        formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone
+        return formatter.string(from: eventDate)
+    }
+
+    /// Calculates the resolution of the screen in-terms of actual pixels of the device. This doesn't count Retine-pixels which are technically subpixels.
+    /// - Returns: A formatted string with resolution 'width' and 'height'.
+    class var resolution: SPSize? {
+        var mainScreen: CGRect?
+        var screenScale: CGFloat?
+        #if os(iOS) || os(tvOS)
+        mainScreen = UIScreen.main.bounds
+        screenScale = UIScreen.main.scale
+        #elseif os(watchOS)
+        mainScreen = WKInterfaceDevice.current().screenBounds
+        screenScale = WKInterfaceDevice.current().screenScale
+        #elseif os(macOS)
+        mainScreen = NSScreen.main?.frame
+        screenScale = NSScreen.main?.backingScaleFactor ?? 0.0
+        #endif
+        if let mainScreen = mainScreen, let screenScale = screenScale {
+            let screenWidth = mainScreen.size.width * screenScale
+            let screenHeight = mainScreen.size.height * screenScale
+            return SPSize(width: Int(screenWidth), height: Int(screenHeight))
+        }
+        return nil
+    }
+
+    /// Calculates the viewport of the app as it is on the screen. Currently, returns the same value as getResolution.
+    /// - Returns: A formatted string with viewport width and height.
+    class var viewPort: SPSize? {
+        // This probably doesn't change as well
+        return self.resolution
+    }
+
+    /// Returns the Application ID
+    /// - Returns: The device bundle application id
+    class var appId: String? {
+        return Bundle.main.bundleIdentifier
+    }
+    
+    /// @brief URL encodes a dictionary as key=value pairs separated by &, so that it can be used in a query-string.
+    /// This method can encode string, numbers, and bool values, and not embedded arrays or dictionaries.
+    /// It encodes bool as 1 and 0.
+    /// @return The url encoded string of the dictionary.
+    class func urlEncode(_ dictionary: [String : NSObject]) -> String {
+        return dictionary.map { (key: String, value: NSObject) in
+            "\(self.urlEncode(key))=\(self.urlEncode(value.description))"
+        }.joined(separator: "&")
+    }
+
+    /// URL encodes a string so that it is suitable to use in a query-string. A nil s returns @"".
+    /// - Returns: The url encoded string
+    class func urlEncode(_ string: String) -> String {
+        var allowedCharSet = CharacterSet.urlQueryAllowed
+        allowedCharSet.remove(charactersIn: "!*'\"();:@&=+$,/?%#[]% ")
+        return string.addingPercentEncoding(withAllowedCharacters: allowedCharSet) ?? string
+    }
+
+    /// Removes all entries which have a value of NSNull from the dictionary.
+    /// - Parameter dict: An NSDictionary to be cleaned.
+    /// - Returns: The same NSDictionary without any Null values.
+    class func removeNullValuesFromDict(withDict dict: [AnyHashable : Any]) -> [AnyHashable : Any] {
+        var cleanDictionary: [AnyHashable : Any] = [:]
+        for key in dict.keys {
+            guard let key = key as? String else {
+                continue
+            }
+            if let aDict = dict[key] {
+                if aDict is NSNull { continue }
+                cleanDictionary[key] = aDict
+            }
+        }
+        return cleanDictionary
+    }
+
+    /// Converts a kebab-case string keys into a camel-case string keys.
+    /// - Parameter dict: The dictionary to convert.
+    /// - Returns: A dictionary.
+    class func replaceHyphenatedKeys(withCamelcase dict: [String : Any]) -> [String : Any] {
+        var newDictionary: [String : Any] = [:]
+        for key in dict.keys {
+            if self.string(key, contains: "-") {
+                if let aDict = dict[key] as? [String : Any] {
+                    let replaceHyphenatedKeys = self.replaceHyphenatedKeys(withCamelcase: aDict)
+                    newDictionary[self.camelcaseParsedKey(key) ?? ""] = replaceHyphenatedKeys
+                } else {
+                    if let aDict = dict[key] {
+                        newDictionary[self.camelcaseParsedKey(key) ?? ""] = aDict
+                    }
+                }
+            } else {
+                if let aDict = dict[key] as? [String : Any] {
+                    let replaceHyphenatedKeys = self.replaceHyphenatedKeys(withCamelcase: aDict)
+                    newDictionary[key] = replaceHyphenatedKeys
+                } else {
+                    if let aDict = dict[key] {
+                        newDictionary[key] = aDict
+                    }
+                }
+            }
+        }
+
+        return newDictionary
+    }
+
+    class func string(_ string: String, contains subString: String) -> Bool {
+        return string.contains(subString)
+    }
+
+    /// Converts a kebab-case string into a camel-case string.
+    /// - Parameter key: A kebab-case key.
+    /// - Returns: A camel-case string.
+    class func camelcaseParsedKey(_ key: String) -> String? {
+        let words = key.split(separator: "-")
+
+        if words.count == 0 {
+            return nil
+        } else if words.count == 1 {
+            return words[0].lowercased()
+        } else {
+            var camelcaseKey = words[0].lowercased()
+            for word in words[1..<words.count] {
+                camelcaseKey += word.capitalized
+            }
+            return camelcaseKey
+        }
+    }
+
+    /// Return nil if value is empty string, otherwise return string.
+    /// - Parameter aString: Some string
+    /// - Returns: A string or nil
+    class func validate(_ aString: String) -> String? {
+        if aString.count == 0 {
+            return nil
+        }
+        return aString
+    }
+
+    /// Returns the application build and version as a payload to be used in the application context.
+    /// - Returns: A context SDJ.
+    class var applicationContext: SelfDescribingJson? {
+        if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String,
+           let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String {
+            return self.getApplicationContext(withVersion: version, andBuild: build)
+        }
+        return nil
+    }
+
+    /// Returns the application build and version as a payload to be used in the application context.
+    /// - Parameters:
+    ///   - version: The application version
+    ///   - build: The application build
+    /// - Returns: A context SDJ.
+    class func getApplicationContext(withVersion version: String, andBuild build: String) -> SelfDescribingJson {
+        let payload = Payload()
+        payload.addValueToPayload(build, forKey: kSPApplicationBuild)
+        payload.addValueToPayload(version, forKey: kSPApplicationVersion)
+        return SelfDescribingJson(schema: kSPApplicationContextSchema, andPayload: payload)
+    }
+
+    /// Returns the app version.
+    /// - Returns: App version string.
+    class var appVersion: String? {
+        return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
+    }
+
+    /// Returns the app build.
+    /// - Returns: App build string.
+    class var appBuild: String? {
+        return Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String
+    }
+}
+
diff --git a/Snowplow/Internal/Emitter/SPEmitterEvent.m b/Sources/Snowplow/Configurations/Configuration.swift
similarity index 59%
rename from Snowplow/Internal/Emitter/SPEmitterEvent.m
rename to Sources/Snowplow/Configurations/Configuration.swift
index 26476a498..1691da887 100644
--- a/Snowplow/Internal/Emitter/SPEmitterEvent.m
+++ b/Sources/Snowplow/Configurations/Configuration.swift
@@ -1,5 +1,4 @@
-//
-//  SPEmitterEvent.m
+//  Configuration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,27 +18,29 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPEmitterEvent.h"
-
-@interface SPEmitterEvent ()
+import Foundation
 
-@property (nonatomic, readwrite) SPPayload *payload;
-@property (nonatomic, readwrite) long long storeId;
-
-@end
+@objc(SPConfiguration)
+public class Configuration: NSObject, NSCopying, NSSecureCoding {
+    @objc
+    public convenience init?(dictionary: [String : NSObject]) {
+        self.init()
+    }
 
-@implementation SPEmitterEvent
+    @objc
+    public func copy(with zone: NSZone? = nil) -> Any {
+        return Configuration()
+    }
 
-- (instancetype)initWithPayload:(SPPayload *)payload storeId:(long long)storeId {
-    if (self = [super init]) {
-        self.payload = payload;
-        self.storeId = storeId;
+    @objc
+    public func encode(with coder: NSCoder) {
     }
-    return self;
-}
 
-- (NSString *)description {
-    return [NSString stringWithFormat:@"EmitterEvent{ %lld }", self.storeId];
+    @objc
+    public class var supportsSecureCoding: Bool { return true }
+    
+    @objc
+    required convenience public init?(coder: NSCoder) {
+        self.init()
+    }
 }
-
-@end
diff --git a/Sources/Snowplow/Configurations/ConfigurationBundle.swift b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
new file mode 100644
index 000000000..8d20b5d54
--- /dev/null
+++ b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
@@ -0,0 +1,125 @@
+//  SPConfigurationBundle.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// This class represents the default configuration applied in place of the remote configuration.
+@objc(SPConfigurationBundle)
+public class ConfigurationBundle: Configuration {
+    @objc
+    private(set) public var namespace: String
+    @objc
+    public var networkConfiguration: NetworkConfiguration?
+    @objc
+    public var trackerConfiguration: TrackerConfiguration?
+    @objc
+    public var subjectConfiguration: SubjectConfiguration?
+    @objc
+    public var sessionConfiguration: SessionConfiguration?
+
+    @objc
+    public var configurations: [Configuration] {
+        var array: [Configuration] = []
+        if let networkConfiguration = networkConfiguration {
+            array.append(networkConfiguration)
+        }
+        if let trackerConfiguration = trackerConfiguration {
+            array.append(trackerConfiguration)
+        }
+        if let subjectConfiguration = subjectConfiguration {
+            array.append(subjectConfiguration)
+        }
+        if let sessionConfiguration = sessionConfiguration {
+            array.append(sessionConfiguration)
+        }
+        return array
+    }
+
+    @objc
+    public convenience init(namespace: String) {
+        self.init(namespace: namespace, networkConfiguration: nil)
+    }
+
+    @objc
+    public init(namespace: String, networkConfiguration: NetworkConfiguration?) {
+        self.namespace = namespace
+        self.networkConfiguration = networkConfiguration
+    }
+
+    @objc
+    public init?(dictionary: [String : NSObject]) {
+        if let namespace = dictionary["namespace"] as? String {
+            self.namespace = namespace
+        } else {
+            logDebug(message: "Error assigning: namespace")
+            return nil
+        }
+        if let config = dictionary["networkConfiguration"] as? [String : NSObject] {
+            networkConfiguration = NetworkConfiguration(dictionary: config)
+        }
+        if let config = dictionary["trackerConfiguration"] as? [String : NSObject] {
+            trackerConfiguration = TrackerConfiguration(dictionary: config)
+        }
+        if let config = dictionary["subjectConfiguration"] as? [String : NSObject] {
+            subjectConfiguration = SubjectConfiguration(dictionary: config)
+        }
+        if let config = dictionary["sessionConfiguration"] as? [String: NSObject] {
+            sessionConfiguration = SessionConfiguration(dictionary: config)
+        }
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    override public func copy(with zone: NSZone? = nil) -> Any {
+        let copy = ConfigurationBundle(namespace: namespace)
+        copy.networkConfiguration = networkConfiguration?.copy(with: zone) as? NetworkConfiguration
+        copy.trackerConfiguration = trackerConfiguration?.copy(with: zone) as? TrackerConfiguration
+        copy.subjectConfiguration = subjectConfiguration?.copy(with: zone) as? SubjectConfiguration
+        copy.sessionConfiguration = sessionConfiguration?.copy(with: zone) as? SessionConfiguration
+        return copy
+    }
+
+    // MARK: - NSSecureCoding
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+
+    @objc
+    override public func encode(with coder: NSCoder) {
+        coder.encode(namespace, forKey: "namespace")
+        coder.encode(networkConfiguration, forKey: "networkConfiguration")
+        coder.encode(trackerConfiguration, forKey: "trackerConfiguration")
+        coder.encode(subjectConfiguration, forKey: "subjectConfiguration")
+        coder.encode(sessionConfiguration, forKey: "sessionConfiguration")
+    }
+
+    required init?(coder: NSCoder) {
+        if let namespace = coder.decodeObject(forKey: "namespace") as? String {
+            self.namespace = namespace
+        } else {
+            return nil
+        }
+        networkConfiguration = coder.decodeObject(forKey: "networkConfiguration") as? NetworkConfiguration
+        trackerConfiguration = coder.decodeObject(forKey: "trackerConfiguration") as? TrackerConfiguration
+        subjectConfiguration = coder.decodeObject(forKey: "subjectConfiguration") as? SubjectConfiguration
+        sessionConfiguration = coder.decodeObject(forKey: "sessionConfiguration") as? SessionConfiguration
+    }
+}
diff --git a/Snowplow iOSTests/Utils/SPMockWKScriptMessage.m b/Sources/Snowplow/Configurations/ConfigurationState.swift
similarity index 64%
rename from Snowplow iOSTests/Utils/SPMockWKScriptMessage.m
rename to Sources/Snowplow/Configurations/ConfigurationState.swift
index 21aa4a773..4a0c87517 100644
--- a/Snowplow iOSTests/Utils/SPMockWKScriptMessage.m	
+++ b/Sources/Snowplow/Configurations/ConfigurationState.swift
@@ -1,5 +1,5 @@
 //
-//  SPMockWKScriptMessage.m
+//  SPConfigurationState.h
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,29 +19,15 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPMockWKScriptMessage.h"
-
-@interface SPMockWKScriptMessage()
-
-@property (nonatomic, strong) id messageBody;
-
-@end
-
-@implementation SPMockWKScriptMessage
-
-- (id) initWithBody:(id)body {
-    self = [super init];
-    
-    if (self) {
-        _messageBody = body;
-    }
-    
-    return self;
+import Foundation
+
+/// State of retrieved remote configuration that states where the configuration was retrieved from.
+@objc(SPConfigurationState)
+public enum ConfigurationState: Int {
+    /// The default configuration was used.
+    case `default`
+    /// The configuration was retrieved from local cache.
+    case cached
+    /// The configuration was retrieved from the remote configuration endpoint.
+    case fetched
 }
-
-- (id) body {
-    return self.messageBody;
-}
-
-
-@end
diff --git a/Sources/Snowplow/Configurations/EmitterConfiguration.swift b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
new file mode 100644
index 000000000..e828ec392
--- /dev/null
+++ b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
@@ -0,0 +1,155 @@
+//  SPEmitterConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPEmitterConfigurationProtocol)
+public protocol EmitterConfigurationProtocol: AnyObject {
+    /// Sets whether the buffer should send events instantly or after the buffer
+    /// has reached it's limit. By default, this is set to BufferOption Default.
+    @objc
+    var bufferOption: BufferOption { get set }
+    /// Maximum number of events collected from the EventStore to be sent in a request.
+    @objc
+    var emitRange: Int { get set }
+    /// Maximum number of threads working in parallel in the tracker to send requests.
+    @objc
+    var threadPoolSize: Int { get set }
+    /// Maximum amount of bytes allowed to be sent in a payload in a GET request.
+    @objc
+    var byteLimitGet: Int { get set }
+    /// Maximum amount of bytes allowed to be sent in a payload in a POST request.
+    @objc
+    var byteLimitPost: Int { get set }
+    /// Callback called for each request performed by the tracker to the collector.
+    @objc
+    var requestCallback: RequestCallback? { get set }
+    ///  Custom retry rules for HTTP status codes returned from the Collector.
+    ///  The dictionary is a mapping of integers (status codes) to booleans (true for retry and false for not retry).
+    @objc
+    var customRetryForStatusCodes: [Int : Bool]? { get set }
+    /// Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
+    @objc
+    var serverAnonymisation: Bool { get set }
+}
+
+/// It allows the tracker configuration from the emission perspective.
+/// The EmitterConfiguration can be used to setup details about how the tracker should treat the events
+/// to emit to the collector.
+@objc(SPEmitterConfiguration)
+public class EmitterConfiguration: Configuration, EmitterConfigurationProtocol {
+    /// Sets whether the buffer should send events instantly or after the buffer
+    /// has reached it's limit. By default, this is set to BufferOption Default.
+    @objc
+    public var bufferOption: BufferOption = EmitterDefaults.bufferOption
+
+    /// Maximum number of events collected from the EventStore to be sent in a request.
+    @objc
+    public var emitRange: Int = EmitterDefaults.emitRange
+
+    /// Maximum number of threads working in parallel in the tracker to send requests.
+    @objc
+    public var threadPoolSize: Int = EmitterDefaults.emitThreadPoolSize
+
+    /// Maximum amount of bytes allowed to be sent in a payload in a GET request.
+    @objc
+    public var byteLimitGet: Int = EmitterDefaults.byteLimitGet
+
+    /// Maximum amount of bytes allowed to be sent in a payload in a POST request.
+    @objc
+    public var byteLimitPost: Int = EmitterDefaults.byteLimitPost
+
+    /// Callback called for each request performed by the tracker to the collector.
+    @objc
+    public var requestCallback: RequestCallback?
+
+    /// Custom retry rules for HTTP status codes returned from the Collector.
+    /// The dictionary is a mapping of integers (status codes) to booleans (true for retry and false for not retry).
+    @objc
+    public var customRetryForStatusCodes: [Int : Bool]?
+
+    /// Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
+    @objc
+    public var serverAnonymisation: Bool = EmitterDefaults.serverAnonymisation
+
+    /// Custom component with full ownership for persisting events before to be sent to the collector.
+    /// If it's not set the tracker will use a SQLite database as default EventStore.
+    @objc
+    public var eventStore: EventStore?
+
+    /// It sets a default EmitterConfiguration.
+    /// Default values:
+    ///         bufferOption = BufferOption.Single;
+    ///         emitRange = 150;
+    ///         threadPoolSize = 15;
+    ///         byteLimitGet = 40000;
+    ///         byteLimitPost = 40000;
+    ///         serverAnonymisation = false;
+    @objc
+    public override init() {
+        super.init()
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    public override func copy(with zone: NSZone? = nil) -> Any {
+        let copy = EmitterConfiguration()
+        copy.bufferOption = bufferOption
+        copy.emitRange = emitRange
+        copy.threadPoolSize = threadPoolSize
+        copy.byteLimitGet = byteLimitGet
+        copy.byteLimitPost = byteLimitPost
+        copy.requestCallback = requestCallback
+        copy.eventStore = eventStore
+        copy.customRetryForStatusCodes = customRetryForStatusCodes
+        copy.serverAnonymisation = serverAnonymisation
+        return copy
+    }
+
+    // MARK: - NSSecureCoding
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+
+    @objc
+    public override func encode(with coder: NSCoder) {
+        coder.encode(bufferOption, forKey: "bufferOption")
+        coder.encode(emitRange, forKey: "emitRange")
+        coder.encode(threadPoolSize, forKey: "threadPoolSize")
+        coder.encode(byteLimitGet, forKey: "byteLimitGet")
+        coder.encode(byteLimitPost, forKey: "byteLimitPost")
+        coder.encode(customRetryForStatusCodes, forKey: "customRetryForStatusCodes")
+        coder.encode(serverAnonymisation, forKey: "serverAnonymisation")
+    }
+
+    required init?(coder: NSCoder) {
+        super.init()
+        if let bufferOption = BufferOption(rawValue: coder.decodeInteger(forKey: "bufferOption")) {
+            self.bufferOption = bufferOption
+        }
+        emitRange = coder.decodeInteger(forKey: "emitRange")
+        threadPoolSize = coder.decodeInteger(forKey: "threadPoolSize")
+        byteLimitGet = coder.decodeInteger(forKey: "byteLimitGet")
+        byteLimitPost = coder.decodeInteger(forKey: "byteLimitPost")
+        customRetryForStatusCodes = coder.decodeObject(forKey: "customRetryForStatusCodes") as? [Int : Bool]
+        serverAnonymisation = coder.decodeBool(forKey: "serverAnonymisation")
+    }
+}
diff --git a/Sources/Snowplow/Configurations/GDPRConfiguration.swift b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
new file mode 100644
index 000000000..c937b7ee5
--- /dev/null
+++ b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
@@ -0,0 +1,110 @@
+//  SPGDPRConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPGDPRConfigurationProtocol)
+public protocol GDPRConfigurationProtocol: AnyObject {
+    /// Basis for processing.
+    @objc
+    var basisForProcessing: GDPRProcessingBasis { get }
+    /// ID of a GDPR basis document.
+    @objc
+    var documentId: String? { get }
+    /// Version of the document.
+    @objc
+    var documentVersion: String? { get }
+    /// Description of the document.
+    @objc
+    var documentDescription: String? { get }
+}
+
+/// This class allows the GDPR configuration of the tracker.
+@objc(SPGDPRConfiguration)
+public class GDPRConfiguration: Configuration, GDPRConfigurationProtocol {
+    /// Basis for processing.
+    @objc
+    public var basisForProcessing: GDPRProcessingBasis
+    /// ID of a GDPR basis document.
+    @objc
+    public var documentId: String?
+    /// Version of the document.
+    @objc
+    public var documentVersion: String?
+    /// Description of the document.
+    @objc
+    public var documentDescription: String?
+
+    /// Enables GDPR context to be sent with each event.
+    /// - Parameters:
+    ///   - basisForProcessing: GDPR Basis for processing.
+    ///   - documentId: ID of a GDPR basis document.
+    ///   - documentVersion: Version of the document.
+    ///   - documentDescription: Description of the document.
+    @objc
+    public init(
+        basis basisForProcessing: GDPRProcessingBasis,
+        documentId: String?,
+        documentVersion: String?,
+        documentDescription: String?
+    ) {
+        self.basisForProcessing = basisForProcessing
+        self.documentId = documentId ?? ""
+        self.documentVersion = documentVersion ?? ""
+        self.documentDescription = documentDescription ?? ""
+        super.init()
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    public override func copy(with zone: NSZone? = nil) -> Any {
+        let copy = GDPRConfiguration(basis: basisForProcessing,
+                                     documentId: documentId,
+                                     documentVersion: documentVersion,
+                                     documentDescription: documentDescription)
+        return copy
+    }
+
+    // MARK: - NSSecureCodin
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+
+    @objc
+    public override func encode(with coder: NSCoder) {
+        coder.encode(basisForProcessing.rawValue, forKey: "basisForProcessing")
+        coder.encode(documentId, forKey: "documentId")
+        coder.encode(documentVersion, forKey: "documentVersion")
+        coder.encode(documentDescription, forKey: "documentDescription")
+    }
+
+    required init?(coder: NSCoder) {
+        if let basisForProcessing = GDPRProcessingBasis(rawValue: coder.decodeInteger(forKey: "basisForProcessing")) {
+            self.basisForProcessing = basisForProcessing
+            self.documentId = coder.decodeObject(forKey: "documentId") as? String ?? ""
+            self.documentVersion = coder.decodeObject(forKey: "documentVersion") as? String ?? ""
+            self.documentDescription = coder.decodeObject(forKey: "documentDescription") as? String ?? ""
+            super.init()
+        } else {
+            return nil
+        }
+    }
+}
diff --git a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
new file mode 100644
index 000000000..4a9ca468b
--- /dev/null
+++ b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
@@ -0,0 +1,75 @@
+//  SPGlobalContextsConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPGlobalContextsConfigurationProtocol)
+public protocol GlobalContextsConfigurationProtocol: AnyObject {
+    @objc
+    var contextGenerators: [String : GlobalContext] { get set }
+    /// Add a GlobalContext generator to the configuration of the tracker.
+    /// - Parameters:
+    ///   - tag: The label identifying the generator in the tracker.
+    ///   - generator: The GlobalContext generator.
+    /// - Returns: Whether the adding operation has succeeded.
+    @objc
+    func add(tag: String, contextGenerator generator: GlobalContext) -> Bool
+    /// Remove a GlobalContext generator from the configuration of the tracker.
+    /// - Parameter tag: The label identifying the generator in the tracker.
+    /// - Returns: Whether the removing operation has succeded.
+    @objc
+    func remove(tag: String) -> GlobalContext?
+}
+
+/// This class allows the setup of Global Contexts which are attached to selected events.
+@objc(SPGlobalContextsConfiguration)
+public class GlobalContextsConfiguration: Configuration, GlobalContextsConfigurationProtocol {
+    @objc
+    public var contextGenerators: [String : GlobalContext] = [:]
+
+    @objc
+    public func add(tag: String, contextGenerator generator: GlobalContext) -> Bool {
+        if (contextGenerators)[tag] != nil {
+            return false
+        }
+        (contextGenerators)[tag] = generator
+        return true
+    }
+
+    @objc
+    public func remove(tag: String) -> GlobalContext? {
+        let toDelete = (contextGenerators)[tag]
+        if toDelete != nil {
+            contextGenerators.removeValue(forKey: tag)
+        }
+        return toDelete
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    public override func copy(with zone: NSZone? = nil) -> Any {
+        let copy = GlobalContextsConfiguration()
+        copy.contextGenerators = contextGenerators
+        return copy
+    }
+
+    // MARK: - NSCoding (No coding possible as we can't encode and decode the contextGenerators)
+}
diff --git a/Sources/Snowplow/Configurations/NetworkConfiguration.swift b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
new file mode 100644
index 000000000..64a5b034b
--- /dev/null
+++ b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
@@ -0,0 +1,132 @@
+//  SPNetworkConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Represents the network communication configuration
+/// allowing the tracker to be able to send events to the Snowplow collector.
+@objc(SPNetworkConfiguration)
+public class NetworkConfiguration: Configuration {
+    /// URL (without schema/protocol) used to send events to the collector.
+    @objc
+    private(set) public var endpoint: String?
+    /// Method used to send events to the collector.
+    @objc
+    private(set) public var method: HttpMethodOptions
+    /// Method used to send events to the collector.
+    /// Protocol used to send events to the collector.
+    @objc
+    private(set) public var `protocol`: ProtocolOptions
+    /// See `NetworkConfiguration(NetworkConnection)`
+    @objc
+    public var networkConnection: NetworkConnection?
+    /// A custom path which will be added to the endpoint URL to specify the
+    /// complete URL of the collector when paired with the POST method.
+    @objc
+    public var customPostPath: String?
+    ///  Custom headers for http requests.
+    @objc
+    public var requestHeaders: [String : String]?
+
+    // TODO: add -> @property () NSInteger timeout;
+
+    /// Allow endpoint and method only.
+    @objc
+    public convenience init?(dictionary: [String : NSObject]) {
+        if let endpoint = dictionary["endpoint"] as? String {
+            let method = dictionary["method"] as? String
+            let httpMethod = (method == "get") ? HttpMethodOptions.get : HttpMethodOptions.post
+            self.init(endpoint: endpoint, method: httpMethod)
+        } else {
+            return nil
+        }
+    }
+
+    /// - Parameters:
+    ///   - endpoint: URL of the collector that is going to receive the events tracked by the tracker.
+    ///                 The URL can include the schema/protocol (e.g.: `http://collector-url.com`).
+    ///                 In case the URL doesn't include the schema/protocol, the HTTPS protocol is
+    ///                 automatically selected.
+    ///   - method: The method used to send the requests (GET or POST).
+    @objc
+    public init(endpoint: String, method: HttpMethodOptions = EmitterDefaults.httpMethod) {
+        let url = URL(string: endpoint)
+        if url?.scheme == "https" {
+            self.protocol = ProtocolOptions.https
+            self.endpoint = endpoint
+        } else if url?.scheme == "http" {
+            self.protocol = ProtocolOptions.http
+            self.endpoint = endpoint
+        } else {
+            self.protocol = ProtocolOptions.https
+            self.endpoint = "https://\(endpoint)"
+        }
+        self.method = method
+        networkConnection = nil
+        customPostPath = nil
+    }
+
+    /// - Parameter networkConnection: The NetworkConnection component which will control the
+    ///                          communication between the tracker and the collector.
+    @objc
+    public init(networkConnection: NetworkConnection?) {
+        endpoint = nil
+        self.protocol = .https
+        method = .post
+        self.networkConnection = networkConnection
+        customPostPath = nil
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    public override func copy(with zone: NSZone? = nil) -> Any {
+        var copy: NetworkConfiguration?
+        if let connection = networkConnection {
+            copy = NetworkConfiguration(networkConnection: connection)
+        } else {
+            copy = NetworkConfiguration(endpoint: endpoint ?? "", method: method )
+        }
+        copy?.customPostPath = customPostPath
+        return copy!
+    }
+
+    // MARK: - NSSecureCoding
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+
+    @objc
+    public override func encode(with coder: NSCoder) {
+        coder.encode(endpoint, forKey: "endpoint")
+        coder.encode(self.protocol.rawValue, forKey: "protocol")
+        coder.encode(method.rawValue, forKey: "method")
+        coder.encode(customPostPath, forKey: "customPostPath")
+        coder.encode(requestHeaders, forKey: "requestHeaders")
+    }
+
+    required init?(coder: NSCoder) {
+        endpoint = coder.decodeObject(forKey: "endpoint") as? String
+        self.protocol = ProtocolOptions(rawValue: coder.decodeInteger(forKey: "protocol")) ?? .https
+        method = HttpMethodOptions(rawValue: coder.decodeInteger(forKey: "method")) ?? .post
+        customPostPath = coder.decodeObject(forKey: "customPostPath") as? String
+        requestHeaders = coder.decodeObject(forKey: "requestHeaders") as? [String : String]
+    }
+}
diff --git a/Sources/Snowplow/Configurations/RemoteConfiguration.swift b/Sources/Snowplow/Configurations/RemoteConfiguration.swift
new file mode 100644
index 000000000..5ea96af5b
--- /dev/null
+++ b/Sources/Snowplow/Configurations/RemoteConfiguration.swift
@@ -0,0 +1,88 @@
+//  SPRemoteConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Represents the configuration for fetching configurations from a remote source.
+/// For details on the correct format of a remote configuration see the official documentation.
+@objc(SPRemoteConfiguration)
+public class RemoteConfiguration: Configuration {
+    /// URL of the remote configuration.
+    @objc
+    private(set) public var endpoint: String
+    /// The method used to send the request.
+    private(set) public var method: HttpMethodOptions?
+    
+    /// - Parameters:
+    ///   - endpoint: URL of the remote configuration.
+    ///                 The URL can include the schema/protocol (e.g.: `http://remote-config-url.xyz`).
+    ///                 In case the URL doesn't include the schema/protocol, the HTTPS protocol is
+    ///                 automatically selected.
+    @objc
+    public init(endpoint: String) {
+        let url = URL(string: endpoint)
+        if url?.scheme != nil && ["https", "http"].contains(url?.scheme ?? "") {
+            self.endpoint = endpoint
+        } else {
+            self.endpoint = "https://\(endpoint)"
+        }
+    }
+
+    /// - Parameters:
+    ///   - endpoint: URL of the remote configuration.
+    ///                 The URL can include the schema/protocol (e.g.: `http://remote-config-url.xyz`).
+    ///                 In case the URL doesn't include the schema/protocol, the HTTPS protocol is
+    ///                 automatically selected.
+    ///   - method: The method used to send the requests (GET or POST).
+    @objc
+    public convenience init(endpoint: String, method: HttpMethodOptions) {
+        self.init(endpoint: endpoint)
+        self.method = method
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    override public func copy(with zone: NSZone? = nil) -> Any {
+        let copy = RemoteConfiguration(endpoint: endpoint)
+        copy.method = method
+        return copy
+    }
+
+    // MARK: - NSSecureCoding
+
+    @objc
+    override public func encode(with coder: NSCoder) {
+        coder.encode(endpoint, forKey: "endpoint")
+        coder.encode(method?.rawValue, forKey: "method")
+    }
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+    
+    required init?(coder: NSCoder) {
+        if let endpoint = coder.decodeObject(forKey: "endpoint") as? String {
+            self.endpoint = endpoint
+            method = HttpMethodOptions(rawValue: coder.decodeInteger(forKey: "method"))
+        } else {
+            return nil
+        }
+    }
+}
diff --git a/Sources/Snowplow/Configurations/SessionConfiguration.swift b/Sources/Snowplow/Configurations/SessionConfiguration.swift
new file mode 100644
index 000000000..75f7206de
--- /dev/null
+++ b/Sources/Snowplow/Configurations/SessionConfiguration.swift
@@ -0,0 +1,162 @@
+//  SPSessionConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPSessionConfigurationProtocol)
+public protocol SessionConfigurationProtocol: AnyObject {
+    /// The amount of time that can elapse before the
+    /// session id is updated while the app is in the
+    /// foreground.
+    @objc
+    var foregroundTimeoutInSeconds: Int { get set }
+    /// The amount of time that can elapse before the
+    /// session id is updated while the app is in the
+    /// background.
+    @objc
+    var backgroundTimeoutInSeconds: Int { get set }
+    /// The amount of time that can elapse before the
+    /// session id is updated while the app is in the
+    /// foreground.
+    @objc
+    var foregroundTimeout: Measurement<UnitDuration> { get set }
+    /// The amount of time that can elapse before the
+    /// session id is updated while the app is in the
+    /// background.
+    @objc
+    var backgroundTimeout: Measurement<UnitDuration> { get set }
+    /// The callback called everytime the session is updated.
+    @objc
+    var onSessionStateUpdate: ((_ sessionState: SessionState) -> Void)? { get set }
+}
+
+/// This class represents the configuration from of the applications session.
+/// The SessionConfiguration can be used to setup the behaviour of sessions.
+///
+/// A session is a context which is appended to each event sent.
+/// The values it brings can change based on:
+/// - the timeout set for the inactivity of app when in foreground;
+/// - the timeout set for the inactivity of app when in background.
+///
+/// Session data is maintained for the life of the application being installed on a device.
+/// A new session will be created if the session information is not accessed within a configurable timeout.
+@objc(SPSessionConfiguration)
+public class SessionConfiguration: Configuration, SessionConfigurationProtocol {
+    @objc
+    public var backgroundTimeoutInSeconds: Int
+    @objc
+    public var foregroundTimeoutInSeconds: Int
+    @objc
+    public var onSessionStateUpdate: ((_ sessionState: SessionState) -> Void)?
+
+    @objc
+    public convenience override init() {
+        self.init(foregroundTimeoutInSeconds: 1800, backgroundTimeoutInSeconds: 1800)
+    }
+
+    @objc
+    public convenience init?(dictionary: [String : NSObject]) {
+        let foregroundTimeout = dictionary["foregroundTimeout"] as? Int ?? TrackerDefaults.foregroundTimeout
+        let backgroundTimeout = dictionary["backgroundTimeout"] as? Int ?? TrackerDefaults.backgroundTimeout
+        self.init(foregroundTimeoutInSeconds: foregroundTimeout, backgroundTimeoutInSeconds: backgroundTimeout)
+    }
+
+    /// This will setup the session behaviour of the tracker.
+    /// - Parameters:
+    ///   - foregroundTimeout: The timeout set for the inactivity of app when in foreground.
+    ///   - backgroundTimeout: The timeout set for the inactivity of app when in background.
+    @objc
+    public convenience init(foregroundTimeout: Measurement<UnitDuration>, backgroundTimeout: Measurement<UnitDuration>) {
+        let foreground = foregroundTimeout.converted(to: .seconds)
+        let foregroundInSeconds = Int(floor(foreground.value))
+        let background = backgroundTimeout.converted(to: .seconds)
+        let backgroundInSeconds = Int(floor(background.value))
+        self.init(foregroundTimeoutInSeconds: foregroundInSeconds, backgroundTimeoutInSeconds: Int(backgroundInSeconds))
+    }
+
+    /// This will setup the session behaviour of the tracker.
+    /// - Parameters:
+    ///   - foregroundTimeout: The timeout set for the inactivity of app when in foreground.
+    ///   - backgroundTimeout: The timeout set for the inactivity of app when in background.
+    @objc
+    public init(foregroundTimeoutInSeconds foregroundTimeout: Int, backgroundTimeoutInSeconds backgroundTimeout: Int) {
+        self.backgroundTimeoutInSeconds = backgroundTimeout
+        self.foregroundTimeoutInSeconds = foregroundTimeout
+    }
+
+    @objc
+    public var foregroundTimeout: Measurement<UnitDuration> {
+        get {
+            return Measurement(value: Double(foregroundTimeoutInSeconds), unit: .seconds)
+        }
+        set(foregroundTimeout) {
+            let foreground = foregroundTimeout.converted(to: .seconds)
+            foregroundTimeoutInSeconds = Int(floor(foreground.value))
+        }
+    }
+
+    @objc
+    public var backgroundTimeout: Measurement<UnitDuration> {
+        get {
+            return Measurement(value: Double(backgroundTimeoutInSeconds), unit: .seconds)
+        }
+        set(backgroundTimeout) {
+            let background = backgroundTimeout.converted(to: .seconds)
+            backgroundTimeoutInSeconds = Int(floor(background.value))
+        }
+    }
+
+    /// The callback called everytime the session is updated.
+
+    // MARK: - Builders
+
+    func onSessionStateUpdate(_ value: ((_ sessionState: SessionState) -> Void)?) -> Self {
+        onSessionStateUpdate = value
+        return self
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    public override func copy(with zone: NSZone? = nil) -> Any {
+        let copy = SessionConfiguration()
+        copy.backgroundTimeoutInSeconds = backgroundTimeoutInSeconds
+        copy.foregroundTimeoutInSeconds = foregroundTimeoutInSeconds
+        copy.onSessionStateUpdate = onSessionStateUpdate
+        return copy
+    }
+
+    // MARK: - NSSecureCoding
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+    
+    @objc
+    public override func encode(with coder: NSCoder) {
+        coder.encode(backgroundTimeoutInSeconds, forKey: "backgroundTimeoutInSeconds")
+        coder.encode(foregroundTimeoutInSeconds, forKey: "foregroundTimeoutInSeconds")
+    }
+
+    required init?(coder: NSCoder) {
+        backgroundTimeoutInSeconds = coder.decodeInteger(forKey: "backgroundTimeoutInSeconds")
+        foregroundTimeoutInSeconds = coder.decodeInteger(forKey: "foregroundTimeoutInSeconds")
+        super.init()
+    }
+}
diff --git a/Sources/Snowplow/Configurations/SubjectConfiguration.swift b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
new file mode 100644
index 000000000..a96ca9edd
--- /dev/null
+++ b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
@@ -0,0 +1,274 @@
+//  SPSubjectConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import CoreGraphics
+import Foundation
+
+@objc(SPSubjectConfigurationProtocol)
+public protocol SubjectConfigurationProtocol: AnyObject {
+    /// The custom UserID.
+    @objc
+    var userId: String? { get set }
+    /// The network UserID.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    var networkUserId: String? { get set }
+    /// The domain UserID.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    var domainUserId: String? { get set }
+    /// The user-agent.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    var useragent: String? { get set }
+    /// The IP address.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    var ipAddress: String? { get set }
+    /// The current timezone.
+    @objc
+    var timezone: String? { get set }
+    /// The language set in the device.
+    @objc
+    var language: String? { get set }
+    /// The screen resolution.
+    @objc
+    var screenResolution: SPSize? { get set }
+    /// The screen viewport.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    var screenViewPort: SPSize? { get set }
+    /// The color depth.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    var colorDepth: NSNumber? { get set }
+    
+    // MARK: - GeoLocation
+
+    /// Latitude value for the geolocation context.
+    @objc
+    var geoLatitude: NSNumber? { get set }
+    /// Longitude value for the geo context.
+    @objc
+    var geoLongitude: NSNumber? { get set }
+    /// LatitudeLongitudeAccuracy value for the geolocation context.
+    @objc
+    var geoLatitudeLongitudeAccuracy: NSNumber? { get set }
+    /// Altitude value for the geolocation context.
+    @objc
+    var geoAltitude: NSNumber? { get set }
+    /// AltitudeAccuracy value for the geolocation context.
+    @objc
+    var geoAltitudeAccuracy: NSNumber? { get set }
+    /// Bearing value for the geolocation context.
+    @objc
+    var geoBearing: NSNumber? { get set }
+    /// Speed value for the geolocation context.
+    @objc
+    var geoSpeed: NSNumber? { get set }
+    /// Timestamp value for the geolocation context.
+    @objc
+    var geoTimestamp: NSNumber? { get set }
+}
+
+/// This class represents the configuration of the subject.
+/// The SubjectConfiguration can be used to setup the tracker with the basic information about the
+/// user and the app which will be attached on all the events as contexts.
+/// The contexts to track can be enabled in the `TrackerConfiguration` class.
+@objc(SPSubjectConfiguration)
+public class SubjectConfiguration: Configuration, SubjectConfigurationProtocol {
+    convenience init(dictionary: [String : NSObject]) {
+        self.init()
+        if let userId = dictionary["userId"] as? String { self.userId = userId }
+        if let networkUserId = dictionary["networkUserId"] as? String { self.networkUserId = networkUserId }
+        if let domainUserId = dictionary["domainUserId"] as? String { self.domainUserId = domainUserId }
+        if let useragent = dictionary["useragent"] as? String { self.useragent = useragent }
+        if let ipAddress = dictionary["ipAddress"] as? String { self.ipAddress = ipAddress }
+        if let timezone = dictionary["timezone"] as? String { self.timezone = timezone }
+        if let language = dictionary["language"] as? String { self.language = language }
+    }
+
+    @objc
+    public override init() {
+        super.init()
+    }
+
+    /// The custom UserID.
+
+    // MARK: - Builder
+
+    @objc
+    public var userId: String?
+
+    /// The network UserID.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public var networkUserId: String?
+
+    /// The domain UserID.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public var domainUserId: String?
+
+    /// The user-agent.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public var useragent: String?
+
+    /// The IP address.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public var ipAddress: String?
+
+    /// The current timezone.
+    @objc
+    public var timezone: String?
+
+    /// The language set in the device.
+    @objc
+    public var language: String?
+
+    /// The screen resolution.
+    @objc
+    public var screenResolution: SPSize?
+
+    /// The screen viewport.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public var screenViewPort: SPSize?
+
+    /// The color depth.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public var colorDepth: NSNumber?
+
+    // GeoLocation builders
+
+    /// Latitude value for the geolocation context.
+    @objc
+    public var geoLatitude: NSNumber?
+
+    /// Longitude value for the geo context.
+    @objc
+    public var geoLongitude: NSNumber?
+
+    /// LatitudeLongitudeAccuracy value for the geolocation context.
+    @objc
+    public var geoLatitudeLongitudeAccuracy: NSNumber?
+
+    /// Altitude value for the geolocation context.
+    @objc
+    public var geoAltitude: NSNumber?
+
+    /// AltitudeAccuracy value for the geolocation context.
+    @objc
+    public var geoAltitudeAccuracy: NSNumber?
+
+    /// Bearing value for the geolocation context.
+    @objc
+    public var geoBearing: NSNumber?
+
+    /// Speed value for the geolocation context.
+    @objc
+    public var geoSpeed: NSNumber?
+
+    /// Timestamp value for the geolocation context.
+    @objc
+    public var geoTimestamp: NSNumber?
+
+    // MARK: - NSCopying
+
+    @objc
+    public override func copy(with zone: NSZone? = nil) -> Any {
+        let copy = SubjectConfiguration()
+        copy.userId = userId
+        copy.networkUserId = networkUserId
+        copy.domainUserId = domainUserId
+        copy.useragent = useragent
+        copy.ipAddress = ipAddress
+        copy.timezone = timezone
+        copy.language = language
+        copy.screenResolution = screenResolution
+        copy.screenViewPort = screenViewPort
+        copy.colorDepth = colorDepth
+
+        // geolocation
+        copy.geoLatitude = geoLatitude
+        copy.geoLongitude = geoLongitude
+        copy.geoLatitudeLongitudeAccuracy = geoLatitudeLongitudeAccuracy
+        copy.geoAltitude = geoAltitude
+        copy.geoAltitudeAccuracy = geoAltitudeAccuracy
+        copy.geoSpeed = geoSpeed
+        copy.geoBearing = geoBearing
+        copy.geoTimestamp = geoTimestamp
+        return copy
+    }
+
+    // MARK: - NSSecureCoding
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+
+    @objc
+    public override func encode(with coder: NSCoder) {
+        coder.encode(userId, forKey: "userId")
+        coder.encode(networkUserId, forKey: "networkUserId")
+        coder.encode(domainUserId, forKey: "domainUserId")
+        coder.encode(useragent, forKey: "useragent")
+        coder.encode(ipAddress, forKey: "ipAddress")
+        coder.encode(timezone, forKey: "timezone")
+        coder.encode(language, forKey: "language")
+        coder.encode(screenResolution, forKey: "screenResolution")
+        coder.encode(screenViewPort, forKey: "screenViewPort")
+        coder.encode(colorDepth, forKey: "colorDepth")
+        // geolocation
+        coder.encode(geoLatitude, forKey: "geoLatitude")
+        coder.encode(geoLongitude, forKey: "geoLongitude")
+        coder.encode(geoLatitudeLongitudeAccuracy, forKey: "geoLatitudeLongitudeAccuracy")
+        coder.encode(geoAltitude, forKey: "geoAltitude")
+        coder.encode(geoAltitudeAccuracy, forKey: "geoAltitudeAccuracy")
+        coder.encode(geoSpeed, forKey: "geoSpeed")
+        coder.encode(geoBearing, forKey: "geoBearing")
+        coder.encode(geoTimestamp, forKey: "geoTimestamp")
+    }
+
+    required init?(coder: NSCoder) {
+        super.init()
+        userId = coder.decodeObject(forKey: "userId") as? String
+        networkUserId = coder.decodeObject(forKey: "networkUserId") as? String
+        domainUserId = coder.decodeObject(forKey: "domainUserId") as? String
+        useragent = coder.decodeObject(forKey: "useragent") as? String
+        ipAddress = coder.decodeObject(forKey: "ipAddress") as? String
+        timezone = coder.decodeObject(forKey: "timezone") as? String
+        language = coder.decodeObject(forKey: "language") as? String
+        screenResolution = coder.decodeObject(forKey: "screenResolution") as? SPSize
+        screenViewPort = coder.decodeObject(forKey: "screenViewPort") as? SPSize
+        colorDepth = coder.decodeObject(forKey: "colorDepth") as? NSNumber
+        // geolocation
+        geoLatitude = coder.decodeObject(forKey: "geoLatitude") as? NSNumber
+        geoLongitude = coder.decodeObject(forKey: "geoLongitude") as? NSNumber
+        geoLatitudeLongitudeAccuracy = coder.decodeObject(forKey: "geoLatitudeLongitudeAccuracy") as? NSNumber
+        geoAltitude = coder.decodeObject(forKey: "geoAltitude") as? NSNumber
+        geoAltitudeAccuracy = coder.decodeObject(forKey: "geoAltitudeAccuracy") as? NSNumber
+        geoSpeed = coder.decodeObject(forKey: "geoSpeed") as? NSNumber
+        geoBearing = coder.decodeObject(forKey: "geoBearing") as? NSNumber
+        geoTimestamp = coder.decodeObject(forKey: "geoTimestamp") as? NSNumber
+    }
+}
diff --git a/Sources/Snowplow/Configurations/TrackerConfiguration.swift b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
new file mode 100644
index 000000000..7e31c9d28
--- /dev/null
+++ b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
@@ -0,0 +1,297 @@
+//
+//  SPTrackerConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPTrackerConfigurationProtocol)
+public protocol TrackerConfigurationProtocol: AnyObject {
+    /// Identifer of the app.
+    @objc
+    var appId: String { get set }
+    /// It sets the device platform the tracker is running on.
+    @objc
+    var devicePlatform: DevicePlatform { get set }
+    /// It indicates whether the JSON data in the payload should be base64 encoded.
+    @objc
+    var base64Encoding: Bool { get set }
+    /// It sets the log level of tracker logs.
+    @objc
+    var logLevel: LogLevel { get set }
+    /// It sets the logger delegate that receive logs from the tracker.
+    @objc
+    var loggerDelegate: LoggerDelegate? { get set }
+    /// Whether application context is sent with all the tracked events.
+    @objc
+    var applicationContext: Bool { get set }
+    /// Whether mobile/platform context is sent with all the tracked events.
+    @objc
+    var platformContext: Bool { get set }
+    /// Whether geo-location context is sent with all the tracked events.
+    @objc
+    var geoLocationContext: Bool { get set }
+    /// Whether session context is sent with all the tracked events.
+    @objc
+    var sessionContext: Bool { get set }
+    /// Whether deepLink context is sent with all the ScreenView events.
+    @objc
+    var deepLinkContext: Bool { get set }
+    /// Whether screen context is sent with all the tracked events.
+    @objc
+    var screenContext: Bool { get set }
+    /// Whether enable automatic tracking of ScreenView events.
+    @objc
+    var screenViewAutotracking: Bool { get set }
+    /// Whether enable automatic tracking of background and foreground transitions.
+    @objc
+    var lifecycleAutotracking: Bool { get set }
+    /// Whether enable automatic tracking of install event.
+    @objc
+    var installAutotracking: Bool { get set }
+    /// Whether enable crash reporting.
+    @objc
+    var exceptionAutotracking: Bool { get set }
+    /// Whether enable diagnostic reporting.
+    @objc
+    var diagnosticAutotracking: Bool { get set }
+    /// Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
+    /// Setting this property on a running tracker instance starts a new session (if sessions are tracked).
+    @objc
+    var userAnonymisation: Bool { get set }
+    /// Decorate the v_tracker field in the tracker protocol.
+    /// @note Do not use. Internal use only.
+    @objc
+    var trackerVersionSuffix: String? { get set }
+}
+
+/// This class represents the configuration of the tracker and the core tracker properties.
+/// The TrackerConfiguration can be used to setup the tracker behaviour indicating what should be
+/// tracked in term of automatic tracking and contexts/entities to track with the events.
+@objc(SPTrackerConfiguration)
+public class TrackerConfiguration: Configuration, TrackerConfigurationProtocol {
+    /// Identifer of the app.
+    @objc
+    public var appId = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? ""
+    /// It sets the device platform the tracker is running on.
+    @objc
+    public var devicePlatform = DevicePlatform.mobile
+    /// It indicates whether the JSON data in the payload should be base64 encoded.
+    @objc
+    public var base64Encoding = true
+    /// It sets the log level of tracker logs.
+    @objc
+    public var logLevel = LogLevel.off
+    /// It sets the logger delegate that receive logs from the tracker.
+    @objc
+    public var loggerDelegate: LoggerDelegate?
+    /// Whether application context is sent with all the tracked events.
+    @objc
+    public var applicationContext = TrackerDefaults.applicationContext
+    /// Whether mobile/platform context is sent with all the tracked events.
+    @objc
+    public var platformContext = TrackerDefaults.platformContext
+    /// Whether geo-location context is sent with all the tracked events.
+    @objc
+    public var geoLocationContext = TrackerDefaults.geoLocationContext
+    /// Whether session context is sent with all the tracked events.
+    @objc
+    public var sessionContext = TrackerDefaults.sessionContext
+    /// Whether deepLink context is sent with all the ScreenView events.
+    @objc
+    public var deepLinkContext = TrackerDefaults.deepLinkContext
+    /// Whether screen context is sent with all the tracked events.
+    @objc
+    public var screenContext = TrackerDefaults.screenContext
+    /// Whether enable automatic tracking of ScreenView events.
+    @objc
+    public var screenViewAutotracking = TrackerDefaults.autotrackScreenViews
+    /// Whether enable automatic tracking of background and foreground transitions.
+    @objc
+    public var lifecycleAutotracking = TrackerDefaults.lifecycleEvents
+    /// Whether enable automatic tracking of install event.
+    @objc
+    public var installAutotracking = TrackerDefaults.installEvent
+    /// Whether enable crash reporting.
+    @objc
+    public var exceptionAutotracking = TrackerDefaults.exceptionEvents
+    /// Whether enable diagnostic reporting.
+    @objc
+    public var diagnosticAutotracking: Bool = TrackerDefaults.trackerDiagnostic
+    /// Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
+    /// Setting this property on a running tracker instance starts a new session (if sessions are tracked).
+    @objc
+    public var userAnonymisation: Bool = TrackerDefaults.userAnonymisation
+    /// Decorate the v_tracker field in the tracker protocol.
+    /// @note Do not use. Internal use only.
+    @objc
+    public var trackerVersionSuffix: String?
+
+    @objc
+    public override init() {
+        super.init()
+    }
+    
+    @objc
+    public convenience init(appId: String) {
+        self.init()
+        self.appId = appId
+    }
+
+    @objc
+    public convenience init?(dictionary: [String : NSObject]) {
+        self.init()
+        if let appId = dictionary["appId"] as? String {
+            self.appId = appId
+        }
+        if let devicePlatform = dictionary["devicePlatform"] as? String {
+            self.devicePlatform = stringToDevicePlatform(devicePlatform) ?? .mobile
+        }
+        // TODO: Uniform "base64encoding" string on both Android and iOS trackers
+        if let base64Encoding = dictionary["base64encoding"] as? Bool {
+            self.base64Encoding = base64Encoding
+        }
+        if let logLevelValue = dictionary["logLevel"] as? String,
+           let index = ["off", "error", "debug", "verbose"].firstIndex(of: logLevelValue),
+           let logLevel = LogLevel(rawValue: index) {
+            self.logLevel = logLevel
+        }
+        if let sessionContext = dictionary["sessionContext"] as? Bool {
+            self.sessionContext = sessionContext
+        }
+        if let applicationContext = dictionary["applicationContext"] as? Bool {
+            self.applicationContext = applicationContext
+        }
+        if let platformContext = dictionary["platformContext"] as? Bool {
+            self.platformContext = platformContext
+        }
+        if let geoLocationContext = dictionary["geoLocationContext"] as? Bool {
+            self.geoLocationContext = geoLocationContext
+        }
+        if let deepLinkContext = dictionary["deepLinkContext"] as? Bool {
+            self.deepLinkContext = deepLinkContext
+        }
+        if let screenContext = dictionary["screenContext"] as? Bool {
+            self.screenContext = screenContext
+        }
+        if let screenViewAutotracking = dictionary["screenViewAutotracking"] as? Bool {
+            self.screenViewAutotracking = screenViewAutotracking
+        }
+        if let lifecycleAutotracking = dictionary["lifecycleAutotracking"] as? Bool {
+            self.lifecycleAutotracking = lifecycleAutotracking
+        }
+        if let installAutotracking = dictionary["installAutotracking"] as? Bool {
+            self.installAutotracking = installAutotracking
+        }
+        if let exceptionAutotracking = dictionary["exceptionAutotracking"] as? Bool {
+            self.exceptionAutotracking = exceptionAutotracking
+        }
+        if let diagnosticAutotracking = dictionary["diagnosticAutotracking"] as? Bool {
+            self.diagnosticAutotracking = diagnosticAutotracking
+        }
+        if let userAnonymisation = dictionary["userAnonymisation"] as? Bool {
+            self.userAnonymisation = userAnonymisation
+        }
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    override public func copy(with zone: NSZone? = nil) -> Any {
+        let copy = TrackerConfiguration()
+        copy.appId = appId
+        copy.devicePlatform = devicePlatform
+        copy.base64Encoding = base64Encoding
+        copy.logLevel = logLevel
+        copy.loggerDelegate = loggerDelegate
+        copy.sessionContext = sessionContext
+        copy.applicationContext = applicationContext
+        copy.platformContext = platformContext
+        copy.geoLocationContext = geoLocationContext
+        copy.deepLinkContext = deepLinkContext
+        copy.screenContext = screenContext
+        copy.screenViewAutotracking = screenViewAutotracking
+        copy.lifecycleAutotracking = lifecycleAutotracking
+        copy.installAutotracking = installAutotracking
+        copy.exceptionAutotracking = exceptionAutotracking
+        copy.diagnosticAutotracking = diagnosticAutotracking
+        copy.trackerVersionSuffix = trackerVersionSuffix
+        copy.userAnonymisation = userAnonymisation
+        return copy
+    }
+
+    // MARK: - NSSecureCoding
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+
+    @objc
+    public override func encode(with coder: NSCoder) {
+        coder.encode(appId, forKey: "appId")
+        coder.encode(devicePlatform, forKey: "devicePlatform")
+        coder.encode(base64Encoding, forKey: "base64Encoding")
+        coder.encode(logLevel, forKey: "logLevel")
+        coder.encode(loggerDelegate, forKey: "loggerDelegate")
+        coder.encode(sessionContext, forKey: "sessionContext")
+        coder.encode(applicationContext, forKey: "applicationContext")
+        coder.encode(platformContext, forKey: "platformContext")
+        coder.encode(geoLocationContext, forKey: "geoLocationContext")
+        coder.encode(deepLinkContext, forKey: "deepLinkContext")
+        coder.encode(screenContext, forKey: "screenContext")
+        coder.encode(screenViewAutotracking, forKey: "screenViewAutotracking")
+        coder.encode(lifecycleAutotracking, forKey: "lifecycleAutotracking")
+        coder.encode(installAutotracking, forKey: "installAutotracking")
+        coder.encode(exceptionAutotracking, forKey: "exceptionAutotracking")
+        coder.encode(diagnosticAutotracking, forKey: "diagnosticAutotracking")
+        coder.encode(trackerVersionSuffix, forKey: "trackerVersionSuffix")
+        coder.encode(userAnonymisation, forKey: "userAnonymisation")
+    }
+
+    required init?(coder: NSCoder) {
+        super.init()
+        if let appId = coder.decodeObject(forKey: "appId") as? String {
+            self.appId = appId
+        }
+        if let devicePlatform = DevicePlatform(rawValue: coder.decodeInteger(forKey: "devicePlatform")) {
+            self.devicePlatform = devicePlatform
+        }
+        base64Encoding = coder.decodeBool(forKey: "base64Encoding")
+        if let logLevel = LogLevel(rawValue: coder.decodeInteger(forKey: "logLevel")) {
+            self.logLevel = logLevel
+        }
+        if let loggerDelegate = coder.decodeObject(forKey: "loggerDelegate") as? LoggerDelegate {
+            self.loggerDelegate = loggerDelegate
+        }
+        sessionContext = coder.decodeBool(forKey: "sessionContext")
+        applicationContext = coder.decodeBool(forKey: "applicationContext")
+        platformContext = coder.decodeBool(forKey: "platformContext")
+        geoLocationContext = coder.decodeBool(forKey: "geoLocationContext")
+        deepLinkContext = coder.decodeBool(forKey: "deepLinkContext")
+        screenContext = coder.decodeBool(forKey: "screenContext")
+        screenViewAutotracking = coder.decodeBool(forKey: "screenViewAutotracking")
+        lifecycleAutotracking = coder.decodeBool(forKey: "lifecycleAutotracking")
+        installAutotracking = coder.decodeBool(forKey: "installAutotracking")
+        exceptionAutotracking = coder.decodeBool(forKey: "exceptionAutotracking")
+        diagnosticAutotracking = coder.decodeBool(forKey: "diagnosticAutotracking")
+        if let trackerVersionSuffix = coder.decodeObject(forKey: "trackerVersionSuffix") as? String {
+            self.trackerVersionSuffix = trackerVersionSuffix
+        }
+        userAnonymisation = coder.decodeBool(forKey: "userAnonymisation")
+    }
+}
diff --git a/Snowplow/Internal/Emitter/SPEmitterControllerImpl.h b/Sources/Snowplow/Controllers/Controller.swift
similarity index 73%
rename from Snowplow/Internal/Emitter/SPEmitterControllerImpl.h
rename to Sources/Snowplow/Controllers/Controller.swift
index 1da83770e..8d05504b4 100644
--- a/Snowplow/Internal/Emitter/SPEmitterControllerImpl.h
+++ b/Sources/Snowplow/Controllers/Controller.swift
@@ -1,5 +1,4 @@
-//
-//  SPEmitterControllerImpl.h
+//  SPController.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,15 +18,14 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPEmitterController.h"
-#import "SPController.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(EmitterControllerImpl)
-@interface SPEmitterControllerImpl : SPController <SPEmitterController>
+import Foundation
 
-@end
+@objc(SPController)
+public class Controller: NSObject {
+    private(set) var serviceProvider: ServiceProviderProtocol
 
-NS_ASSUME_NONNULL_END
+    init(serviceProvider: ServiceProviderProtocol) {
+        self.serviceProvider = serviceProvider
+        super.init()
+    }
+}
diff --git a/Sources/Snowplow/Controllers/EmitterController.swift b/Sources/Snowplow/Controllers/EmitterController.swift
new file mode 100644
index 000000000..738fb4801
--- /dev/null
+++ b/Sources/Snowplow/Controllers/EmitterController.swift
@@ -0,0 +1,43 @@
+//
+//  SPEmitterController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPEmitterController)
+public protocol EmitterController: EmitterConfigurationProtocol {
+    /// Number of events recorded in the EventStore.
+    @objc
+    var dbCount: Int { get }
+    /// Whether the emitter is currently sending events.
+    @objc
+    var isSending: Bool { get }
+    @objc
+    func flush()
+    /// Pause emitting events.
+    /// Emitting events will be suspended until resumed again.
+    /// Suitable for low bandwidth situations.
+    @objc
+    func pause()
+    /// Resume emitting events if previously paused.
+    /// The emitter will resume emitting events again.
+    @objc
+    func resume()
+}
diff --git a/Sources/Snowplow/Controllers/GDPRController.swift b/Sources/Snowplow/Controllers/GDPRController.swift
new file mode 100644
index 000000000..d075d9244
--- /dev/null
+++ b/Sources/Snowplow/Controllers/GDPRController.swift
@@ -0,0 +1,48 @@
+//
+//  GDPRController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPGDPRController)
+public protocol GDPRController: GDPRConfigurationProtocol {
+    /// Whether the recorded GDPR context is enabled and will be attached as context.
+    @objc
+    var isEnabled: Bool { get }
+    /// Reset GDPR context to be sent with each event.
+    /// - Parameters:
+    ///   - basisForProcessing: GDPR Basis for processing.
+    ///   - documentId: ID of a GDPR basis document.
+    ///   - documentVersion: Version of the document.
+    ///   - documentDescription: Description of the document.
+    @objc
+    func reset(
+            basis basisForProcessing: GDPRProcessingBasis,
+            documentId: String?,
+            documentVersion: String?,
+            documentDescription: String?
+        )
+    /// Enable the GDPR context recorded.
+    @objc
+    func enable() -> Bool
+    /// Disable the GDPR context recorded.
+    @objc
+    func disable()
+}
diff --git a/Snowplow/Internal/Tracker/SPDeepLinkStateMachine.h b/Sources/Snowplow/Controllers/GlobalContextsController.swift
similarity index 78%
rename from Snowplow/Internal/Tracker/SPDeepLinkStateMachine.h
rename to Sources/Snowplow/Controllers/GlobalContextsController.swift
index a764ebe00..3c94b60cf 100644
--- a/Snowplow/Internal/Tracker/SPDeepLinkStateMachine.h
+++ b/Sources/Snowplow/Controllers/GlobalContextsController.swift
@@ -1,5 +1,5 @@
 //
-//  SPDeepLinkStateMachine.h
+//  SPGlobalContextsController.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,13 +19,10 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPStateMachineProtocol.h"
+import Foundation
 
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPDeepLinkStateMachine : NSObject <SPStateMachineProtocol>
-
-@end
-
-NS_ASSUME_NONNULL_END
+@objc(SPGlobalContextsController)
+public protocol GlobalContextsController: GlobalContextsConfigurationProtocol {
+    @objc
+    var tags: [String] { get }
+}
diff --git a/Sources/Snowplow/Controllers/NetworkController.swift b/Sources/Snowplow/Controllers/NetworkController.swift
new file mode 100644
index 000000000..9ed30a1bf
--- /dev/null
+++ b/Sources/Snowplow/Controllers/NetworkController.swift
@@ -0,0 +1,39 @@
+//
+//  SPNetworkController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPNetworkController)
+public protocol NetworkController: AnyObject {
+    /// URL used to send events to the collector.
+    @objc
+    var endpoint: String? { get set }
+    /// Method used to send events to the collector.
+    @objc
+    var method: HttpMethodOptions { get set }
+    /// A custom path which will be added to the endpoint URL to specify the
+    /// complete URL of the collector when paired with the POST method.
+    @objc
+    var customPostPath: String? { get set }
+    /// Custom headers for http requests.
+    @objc
+    var requestHeaders: [String : String]? { get set }
+}
diff --git a/Sources/Snowplow/Controllers/SessionController.swift b/Sources/Snowplow/Controllers/SessionController.swift
new file mode 100644
index 000000000..20eba6a00
--- /dev/null
+++ b/Sources/Snowplow/Controllers/SessionController.swift
@@ -0,0 +1,59 @@
+//
+//  SPSessionController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPSessionController)
+public protocol SessionController: SessionConfigurationProtocol {
+    /// The session index.
+    /// An increasing number which helps to order the sequence of sessions.
+    @objc
+    var sessionIndex: Int { get }
+    /// The session identifier.
+    /// A unique identifier which is used to identify the session.
+    @objc
+    var sessionId: String? { get }
+    /// The session user identifier.
+    /// It identifies this app installation and it doesn't change for the life of the app.
+    /// It will change only when the app is uninstalled and installed again.
+    /// An app update doesn't change the value.
+    @objc
+    var userId: String? { get }
+    /// Whether the app is currently in background state or in foreground state.
+    @objc
+    var isInBackground: Bool { get }
+    /// Count the number of background transitions in the current session.
+    @objc
+    var backgroundIndex: Int { get }
+    /// Count the number of foreground transitions in the current session.
+    @objc
+    var foregroundIndex: Int { get }
+    /// Pause the session tracking.
+    /// Meanwhile the session is paused it can't expire and can't be updated.
+    @objc
+    func pause()
+    /// Resume the session tracking.
+    @objc
+    func resume()
+    /// Expire the current session also if the timeout is not triggered.
+    @objc
+    func startNewSession()
+}
diff --git a/Sources/Snowplow/Controllers/SubjectController.swift b/Sources/Snowplow/Controllers/SubjectController.swift
new file mode 100644
index 000000000..094f1a13a
--- /dev/null
+++ b/Sources/Snowplow/Controllers/SubjectController.swift
@@ -0,0 +1,26 @@
+//
+//  SPSubjectController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPSubjectController)
+public protocol SubjectController: SubjectConfigurationProtocol {
+}
diff --git a/Sources/Snowplow/Controllers/TrackerController.swift b/Sources/Snowplow/Controllers/TrackerController.swift
new file mode 100644
index 000000000..9ee816e9b
--- /dev/null
+++ b/Sources/Snowplow/Controllers/TrackerController.swift
@@ -0,0 +1,77 @@
+//
+//  SPTrackerController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPTrackerController)
+public protocol TrackerController: TrackerConfigurationProtocol {
+    /// Version of the tracker.
+    @objc
+    var version: String { get }
+    /// Whether the tracker is running and able to collect/send events.
+    /// See `pause()` and `resume()`.
+    @objc
+    var isTracking: Bool { get }
+    /// Namespace of the tracker.
+    /// It is used to identify the tracker among multiple trackers running in the same app.
+    @objc
+    var namespace: String { get }
+    /// SubjectController.
+    /// @apiNote Don't retain the reference. It may change on tracker reconfiguration.
+    @objc
+    var subject: SubjectController? { get }
+    /// SessionController.
+    /// @apiNote Don't retain the reference. It may change on tracker reconfiguration.
+    @objc
+    var session: SessionController? { get }
+    /// NetworkController.
+    /// @apiNote Don't retain the reference. It may change on tracker reconfiguration.
+    @objc
+    var network: NetworkController? { get }
+    /// EmitterController.
+    /// @apiNote Don't retain the reference. It may change on tracker reconfiguration.
+    @objc
+    var emitter: EmitterController? { get }
+    /// GdprController.
+    /// @apiNote Don't retain the reference. It may change on tracker reconfiguration.
+    @objc
+    var gdpr: GDPRController? { get }
+    /// GlobalContextsController.
+    /// @apiNote Don't retain the reference. It may change on tracker reconfiguration.
+    @objc
+    var globalContexts: GlobalContextsController? { get }
+    /// Track the event.
+    /// The tracker will take care to process and send the event assigning `event_id` and `device_timestamp`.
+    /// - Parameter event: The event to track.
+    /// - Returns: The event ID or nil in case tracking is paused
+    @objc
+    func track(_ event: Event) -> UUID?
+    /// Pause the tracker.
+    /// The tracker will stop any new activity tracking but it will continue to send remaining events
+    /// already tracked but not sent yet.
+    /// Calling a track method will not have any effect and event tracked will be lost.
+    @objc
+    func pause()
+    /// Resume the tracker.
+    /// The tracker will start tracking again.
+    @objc
+    func resume()
+}
diff --git a/Sources/Snowplow/Emitter/BufferOption.swift b/Sources/Snowplow/Emitter/BufferOption.swift
new file mode 100644
index 000000000..209112a42
--- /dev/null
+++ b/Sources/Snowplow/Emitter/BufferOption.swift
@@ -0,0 +1,35 @@
+//  BufferOption.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// An enum for buffer options.
+@objc(SPBufferOption)
+public enum BufferOption : Int {
+    /// Sends both GET and POST requests with only a single event.  Can cause a spike in
+    /// network traffic if used in correlation with a large amount of events.
+    case single = 1
+    /// Sends POST requests in groups of 10 events.  This is the default amount of events too
+    /// package into a POST.  All GET requests will still emit one at a time.
+    case defaultGroup = 10
+    /// Sends POST requests in groups of 25 events.  Useful for situations where many events
+    /// need to be sent.  All GET requests will still emit one at a time.
+    case largeGroup = 25
+}
diff --git a/Snowplow/Internal/Tracker/SPWebViewMessageHandler.h b/Sources/Snowplow/Emitter/EmitterDefaults.swift
similarity index 57%
rename from Snowplow/Internal/Tracker/SPWebViewMessageHandler.h
rename to Sources/Snowplow/Emitter/EmitterDefaults.swift
index 35367a415..8043453be 100644
--- a/Snowplow/Internal/Tracker/SPWebViewMessageHandler.h
+++ b/Sources/Snowplow/Emitter/EmitterDefaults.swift
@@ -1,5 +1,5 @@
 //
-//  SPWebViewMessageHandler.h
+//  EmitterDefaults.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,24 +19,15 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-
-#import "SPTrackerConstants.h"
-
-#if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_OSX
-@import WebKit;
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * Handler for messages from the JavaScript library embedded in Web views.
- *
- * The handler parses messages from the JavaScript library calls and forwards the tracked events to be tracked by the mobile tracker.
- */
-@interface SPWebViewMessageHandler : NSObject <WKScriptMessageHandler>
-
-@end
-
-NS_ASSUME_NONNULL_END
-
-#endif
+import Foundation
+
+public class EmitterDefaults {
+    public private(set) static var httpMethod: HttpMethodOptions = .post
+    public private(set) static var httpProtocol: ProtocolOptions = .https
+    public private(set) static var emitRange = 150
+    public private(set) static var emitThreadPoolSize = 15
+    public private(set) static var byteLimitGet = 40000
+    public private(set) static var byteLimitPost = 40000
+    public private(set) static var serverAnonymisation = false
+    public private(set) static var bufferOption: BufferOption = .defaultGroup
+}
diff --git a/Snowplow/Internal/Tracker/SPDeepLinkState.m b/Sources/Snowplow/Emitter/EmitterEvent.swift
similarity index 65%
rename from Snowplow/Internal/Tracker/SPDeepLinkState.m
rename to Sources/Snowplow/Emitter/EmitterEvent.swift
index 3d429d579..6fdbab83e 100644
--- a/Snowplow/Internal/Tracker/SPDeepLinkState.m
+++ b/Sources/Snowplow/Emitter/EmitterEvent.swift
@@ -1,5 +1,5 @@
 //
-//  SPDeepLinkState.m
+//  SPEmitterEvent.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,25 +19,20 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPDeepLinkState.h"
+import Foundation
 
-@interface SPDeepLinkState ()
+@objc(SPEmitterEvent)
+public class EmitterEvent: NSObject {
+    private(set) var payload: Payload
+    private(set) var storeId: Int64
 
-@property (readwrite) NSString *url;
-@property (readwrite) NSString *referrer;
-
-@end
-
-
-@implementation SPDeepLinkState
+    init(payload: Payload, storeId: Int64) {
+        self.payload = payload
+        self.storeId = storeId
+    }
 
-- (instancetype)initWithUrl:(NSString *)url referrer:(NSString *)referrer {
-    if (self = [super init]) {
-        self.url = url;
-        self.referrer = referrer;
-        self.readyForOutput = NO;
+    @objc
+    override public var description: String {
+        return String(format: "EmitterEvent{ %lld }", storeId)
     }
-    return self;
 }
-
-@end
diff --git a/Sources/Snowplow/Emitter/EventStore.swift b/Sources/Snowplow/Emitter/EventStore.swift
new file mode 100644
index 000000000..fbe89fb19
--- /dev/null
+++ b/Sources/Snowplow/Emitter/EventStore.swift
@@ -0,0 +1,53 @@
+//
+//  SPEventStore.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPEventStore)
+public protocol EventStore: NSObjectProtocol {
+    /// Adds an event to the store.
+    /// - Parameter payload: the payload to be added
+    @objc
+    func addEvent(_ payload: Payload)
+    /// Removes an event from the store.
+    /// - Parameter storeId: the identifier of the event in the store.
+    /// - Returns: a boolean of success to remove.
+    @objc
+    func removeEvent(withId storeId: Int64) -> Bool
+    /// Removes a range of events from the store.
+    /// - Parameter storeIds: the events' identifiers in the store.
+    /// - Returns: a boolean of success to remove.
+    @objc
+    func removeEvents(withIds storeIds: [NSNumber]) -> Bool
+    /// Empties the store of all the events.
+    /// - Returns: a boolean of success to remove.
+    @objc
+    func removeAllEvents() -> Bool
+    /// Returns amount of events currently in the store.
+    /// - Returns: the count of events in the store.
+    @objc
+    func count() -> UInt
+    /// Returns a list of EmitterEvent objects which contains events and related ids.
+    /// - Parameter queryLimit: is the maximum number of events returned.
+    /// - Returns: EmitterEvent objects containing storeIds and event payloads.
+    @objc
+    func emittableEvents(withQueryLimit queryLimit: UInt) -> [EmitterEvent]
+}
diff --git a/Sources/Snowplow/Entities/DeepLinkEntity.swift b/Sources/Snowplow/Entities/DeepLinkEntity.swift
new file mode 100644
index 000000000..8c84ff69e
--- /dev/null
+++ b/Sources/Snowplow/Entities/DeepLinkEntity.swift
@@ -0,0 +1,54 @@
+//
+// SPDeepLinkEntity.swift
+// Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Entity that indicates a deep-link has been received and processed.
+@objc(SPDeepLinkEntity)
+public class DeepLinkEntity: SelfDescribingJson {
+    @objc
+    public static let schema = "iglu:com.snowplowanalytics.mobile/deep_link/jsonschema/1-0-0"
+    @objc
+    public static let paramReferrer = "referrer"
+    @objc
+    public static let paramUrl = "url"
+    
+    @objc
+    public var url: String
+    @objc
+    public var referrer: String?
+
+    @objc
+    public init(url: String) {
+        self.url = url
+        super.init(schema: DeepLinkEntity.schema, andData: nil)
+    }
+
+    @objc
+    override public var data: NSObject? {
+        get {
+            var data: [String: NSObject] = [:]
+            data[DeepLinkEntity.paramUrl] = url as NSObject
+            data[DeepLinkEntity.paramReferrer] = referrer as NSObject?
+            return data as NSObject
+        }
+        set {}
+    }
+}
diff --git a/Sources/Snowplow/Entities/LifecycleEntity.swift b/Sources/Snowplow/Entities/LifecycleEntity.swift
new file mode 100644
index 000000000..2ea704f58
--- /dev/null
+++ b/Sources/Snowplow/Entities/LifecycleEntity.swift
@@ -0,0 +1,54 @@
+//
+// SPLifecycleEntity.swift
+// Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Entity that indicates the state of the app is visible (foreground) when the event is tracked.
+let kSPLifecycleEntitySchema = "iglu:com.snowplowanalytics.mobile/application_lifecycle/jsonschema/1-0-0"
+let kSPLifecycleEntityParamIndex = "index"
+let kSPLifecycleEntityParamIsVisible = "isVisible"
+
+@objc(SPLifecycleEntity)
+public class LifecycleEntity: SelfDescribingJson {
+
+    @objc
+    public init(isVisible: Bool) {
+        var parameters: [String : NSObject] = [:]
+        parameters[kSPLifecycleEntityParamIsVisible] = NSNumber(value: isVisible)
+        super.init(schema: kSPLifecycleEntitySchema, andData: parameters as NSObject)
+    }
+
+    @objc
+    public var index: NSNumber? {
+        set {
+            if let data = data,
+               var parameters = data as? [String : NSObject] {
+                parameters[kSPLifecycleEntityParamIndex] = newValue
+            }
+        }
+        get {
+            if let data = data,
+               let parameters = data as? [String : NSObject] {
+                return parameters[kSPLifecycleEntityParamIndex] as? NSNumber
+            }
+            return nil
+        }
+    }
+}
diff --git a/Snowplow/Internal/Events/SPBackground.h b/Sources/Snowplow/Events/Background.swift
similarity index 55%
rename from Snowplow/Internal/Events/SPBackground.h
rename to Sources/Snowplow/Events/Background.swift
index 435556423..608d2e905 100644
--- a/Snowplow/Internal/Events/SPBackground.h
+++ b/Sources/Snowplow/Events/Background.swift
@@ -1,5 +1,5 @@
 //
-//  SPBackground.h
+//  Background.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,25 +19,29 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPEventBase.h"
-
-NS_ASSUME_NONNULL_BEGIN
+import Foundation
 
 /// A background transition event.
-NS_SWIFT_NAME(Background)
-@interface SPBackground : SPSelfDescribingAbstract
-
-/// Index indicating the current transition.
-@property (readonly) NSNumber *index;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Creates a brackground transition event.
- @param index indicate the current transition.
- */
-- (instancetype)initWithIndex:(NSNumber *)index NS_SWIFT_NAME(init(index:));
-
-@end
-
-NS_ASSUME_NONNULL_END
+@objc(SPBackground)
+public class Background: SelfDescribingAbstract {
+    /// Index indicating the current transition.
+    @objc
+    public var index: Int
+
+    /// Creates a brackground transition event.
+    /// - Parameter index: indicate the current transition.
+    @objc
+    public init(index: Int) {
+        self.index = index
+    }
+
+    override public var schema: String {
+        return kSPBackgroundSchema
+    }
+
+    override public var payload: [String : NSObject] {
+        var payload: [AnyHashable : Any] = [:]
+        payload[kSPBackgroundIndex] = NSNumber(value: index)
+        return payload as? [String : NSObject] ?? [:]
+    }
+}
diff --git a/Sources/Snowplow/Events/ConsentDocument.swift b/Sources/Snowplow/Events/ConsentDocument.swift
new file mode 100644
index 000000000..d3debbf8c
--- /dev/null
+++ b/Sources/Snowplow/Events/ConsentDocument.swift
@@ -0,0 +1,65 @@
+//
+//  ConsentDocument.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A consent document event.
+@objc(SPConsentDocument)
+public class ConsentDocument: NSObject {
+    /// Identifier of the document.
+    @objc
+    public var documentId: String
+    /// Version of the document.
+    @objc
+    public var version: String
+    /// Name of the document.
+    @objc
+    public var name: String?
+    /// Description of the document.
+    @objc
+    public var documentDescription: String?
+
+    /// Create a consent document event.
+    /// - Parameters:
+    ///   - documentId: identifier of the document.
+    ///   - version: version of the document.
+    @objc
+    public init(documentId: String, version: String) {
+        self.documentId = documentId
+        self.version = version
+    }
+
+    /// Returns the payload.
+    public var payload: SelfDescribingJson {
+        var event: [String : String] = [:]
+        event[kSPCdId] = documentId
+        event[kSPCdVersion] = version
+        if (name?.count ?? 0) != 0 {
+            event[kSPCdName] = name ?? ""
+        }
+        if (documentDescription?.count ?? 0) != 0 {
+            event[KSPCdDescription] = documentDescription ?? ""
+        }
+        return SelfDescribingJson(
+            schema: kSPConsentDocumentSchema,
+            andDictionary: event)
+    }
+}
diff --git a/Sources/Snowplow/Events/ConsentGranted.swift b/Sources/Snowplow/Events/ConsentGranted.swift
new file mode 100644
index 000000000..d420489c6
--- /dev/null
+++ b/Sources/Snowplow/Events/ConsentGranted.swift
@@ -0,0 +1,91 @@
+//
+//  ConsentGranted.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A consent granted event.
+@objc(SPConsentGranted)
+public class ConsentGranted: SelfDescribingAbstract {
+    /// Expiration of the consent.
+    @objc
+    public var expiry: String
+    /// Identifier of the first document.
+    @objc
+    public var documentId: String
+    /// Version of the first document.
+    @objc
+    public var version: String
+    /// Name of the first document.
+    @objc
+    public var name: String?
+    /// Description of the first document.
+    @objc
+    public var documentDescription: String?
+    /// Other attached documents.
+    @objc
+    public var documents: [SelfDescribingJson]?
+
+    /// Creates a consent granted event with a first document.
+    /// - Parameters:
+    ///   - expiry: consent expiration.
+    ///   - documentId: identifier of the first document.
+    ///   - version: version of the first document.
+    @objc
+    public init(expiry: String, documentId: String, version: String) {
+        self.expiry = expiry
+        self.documentId = documentId
+        self.version = version
+    }
+
+    override public var schema: String {
+        return kSPConsentGrantedSchema
+    }
+
+    override public var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[KSPCgExpiry] = expiry as NSObject
+        return payload
+    }
+
+    /// Retuns the full list of attached documents.
+    @objc
+    public var allDocuments: [SelfDescribingJson] {
+        var results: [SelfDescribingJson] = []
+
+        let document = ConsentDocument(documentId: documentId, version: version)
+        if (name?.count ?? 0) != 0 {
+            document.name = name
+        }
+        if documentDescription != nil {
+            document.documentDescription = documentDescription
+        }
+
+        results.append(document.payload)
+        if let documents = documents {
+            results.append(contentsOf: documents)
+        }
+        return results
+    }
+
+    override func beginProcessing(withTracker tracker: Tracker) {
+        contexts.append(contentsOf: allDocuments) // TODO: Only the user should modify the public contexts property
+    }
+}
diff --git a/Sources/Snowplow/Events/ConsentWithdrawn.swift b/Sources/Snowplow/Events/ConsentWithdrawn.swift
new file mode 100644
index 000000000..d947f1a5e
--- /dev/null
+++ b/Sources/Snowplow/Events/ConsentWithdrawn.swift
@@ -0,0 +1,79 @@
+//
+//  ConsentWithdrawn.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A consent withdrawn event.
+@objc(SPConsentWithdrawn)
+public class ConsentWithdrawn: SelfDescribingAbstract {
+    /// Consent to all.
+    @objc
+    public var all = false
+    /// Identifier of the first document.
+    @objc
+    public var documentId: String?
+    /// Version of the first document.
+    @objc
+    public var version: String?
+    /// Name of the first document.
+    @objc
+    public var name: String?
+    /// Description of the first document.
+    @objc
+    public var documentDescription: String?
+    /// Other documents.
+    @objc
+    public var documents: [SelfDescribingJson]?
+
+    public override var schema: String {
+        return kSPConsentWithdrawnSchema
+    }
+
+    public override var payload: [String : NSObject] {
+        return [
+            KSPCwAll: all ? NSNumber(value: true) : NSNumber(value: false)
+        ]
+    }
+
+    @objc
+    public var allDocuments: [SelfDescribingJson] {
+        var results: [SelfDescribingJson] = []
+        guard let documentId = documentId, let version = version else { return results }
+
+        let document = ConsentDocument(documentId: documentId, version: version)
+        if let name = name {
+            document.name = name
+        }
+        if let documentDescription = documentDescription {
+            document.documentDescription = documentDescription
+        }
+
+        results.append(document.payload)
+        if let documents = documents {
+            results.append(contentsOf: documents)
+        }
+        return results
+    }
+
+    override func beginProcessing(withTracker tracker: Tracker) {
+        contexts.append(contentsOf: allDocuments)
+    }
+}
diff --git a/Sources/Snowplow/Events/DeepLinkReceived.swift b/Sources/Snowplow/Events/DeepLinkReceived.swift
new file mode 100644
index 000000000..09404729c
--- /dev/null
+++ b/Sources/Snowplow/Events/DeepLinkReceived.swift
@@ -0,0 +1,67 @@
+//
+// DeepLinkReceived.swift
+// Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A deep-link received in the app.
+@objc(SPDeepLinkReceived)
+public class DeepLinkReceived: SelfDescribingAbstract {
+    /// Referrer URL, source of this deep-link.
+    @objc
+    public var referrer: String?
+    /// URL in the received deep-link.
+    @objc
+    public var url: String
+
+    /// Creates a deep-link received event.
+    /// @param url URL in the received deep-link.
+    @objc
+    public init(url: String) {
+        self.url = url
+    }
+    
+    @objc
+    public class var schema: String {
+        return "iglu:com.snowplowanalytics.mobile/deep_link_received/jsonschema/1-0-0"
+    }
+
+    @objc
+    public class var paramUrl: String {
+        return "url"
+    }
+
+    @objc
+    public class var paramReferrer: String {
+        return "referrer"
+    }
+
+    public override var schema: String {
+        return DeepLinkReceived.schema
+    }
+
+    public override var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        if let referrer = referrer {
+            payload[DeepLinkReceived.paramReferrer] = referrer as NSObject
+        }
+        payload[DeepLinkReceived.paramUrl] = url as NSObject
+        return payload
+    }
+}
diff --git a/Sources/Snowplow/Events/Ecommerce.swift b/Sources/Snowplow/Events/Ecommerce.swift
new file mode 100644
index 000000000..3b36affd0
--- /dev/null
+++ b/Sources/Snowplow/Events/Ecommerce.swift
@@ -0,0 +1,93 @@
+//
+//  Ecommerce.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPEcommerce)
+public class Ecommerce : PrimitiveAbstract {
+    /// Identifier of the order.
+    @objc
+    public var orderId: String
+    /// Total amount of the order.
+    @objc
+    public var totalValue: Double
+    /// Items purchased.
+    @objc
+    public var items: [EcommerceItem]
+    /// Identifies an affiliation.
+    @objc
+    public var affiliation: String?
+    /// Taxes applied to the purchase.
+    @objc
+    public var taxValue: NSNumber?
+    /// Shipping number.
+    @objc
+    public var shipping: NSNumber?
+    /// City for shipping.
+    @objc
+    public var city: String?
+    /// State for shipping.
+    @objc
+    public var state: String?
+    /// Country for shipping.
+    @objc
+    public var country: String?
+    /// Currency used for totalValue and taxValue.
+    @objc
+    public var currency: String?
+
+    @objc
+    public init(orderId: String, totalValue: Double, items: [EcommerceItem]?) {
+        self.orderId = orderId
+        self.totalValue = totalValue
+        self.items = items ?? []
+    }
+
+    @objc
+    override public var eventName: String {
+        return kSPEventEcomm
+    }
+
+    override public var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPEcommTotal] = String(format: "%.02f", totalValue) as NSObject
+        if let taxValue = taxValue {
+            payload[kSPEcommTax] = String(format: "%.02f", taxValue.doubleValue) as NSObject
+        }
+        if let shipping = shipping {
+            payload[kSPEcommShipping] = String(format: "%.02f", shipping.doubleValue) as NSObject
+        }
+        payload[kSPEcommId] = orderId as NSObject
+        payload[kSPEcommAffiliation] = affiliation as NSObject?
+        payload[kSPEcommCity] = city as NSObject?
+        payload[kSPEcommState] = state as NSObject?
+        payload[kSPEcommCountry] = country as NSObject?
+        payload[kSPEcommCurrency] = currency as NSObject?
+        return payload
+    }
+
+    override func endProcessing(withTracker tracker: Tracker?) {
+        for item in items {
+            item.orderId = orderId
+            _ = tracker?.track(item)
+        }
+    }
+}
diff --git a/Sources/Snowplow/Events/EcommerceItem.swift b/Sources/Snowplow/Events/EcommerceItem.swift
new file mode 100644
index 000000000..d1d7ef3c6
--- /dev/null
+++ b/Sources/Snowplow/Events/EcommerceItem.swift
@@ -0,0 +1,71 @@
+//
+//  EcommerceItem.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPEcommerceItem)
+public class EcommerceItem : PrimitiveAbstract {
+    /// Stock Keeping Unit of the item.
+    @objc
+    public var sku: String
+    /// Price of the item.
+    @objc
+    public var price: Double
+    /// Quantity of the item.
+    @objc
+    public var quantity: Int
+    /// Name of the item.
+    @objc
+    public var name: String?
+    /// Category of the item.
+    @objc
+    public var category: String?
+    /// Currency used for the price of the item.
+    @objc
+    public var currency: String?
+    /// OrderID of the order that contains this item.
+    @objc
+    public var orderId: String?
+
+    @objc
+    public init(sku: String, price: Double, quantity: Int) {
+        self.sku = sku
+        self.price = price
+        self.quantity = quantity
+    }
+
+    @objc
+    override public var eventName: String {
+        return kSPEventEcommItem
+    }
+
+    override public var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPEcommItemId] = orderId as NSObject?
+        payload[kSPEcommItemSku] = sku as NSObject
+        payload[kSPEcommItemName] = name as NSObject?
+        payload[kSPEcommItemCategory] = category as NSObject?
+        payload[kSPEcommItemCurrency] = currency as NSObject?
+        payload[kSPEcommItemPrice] = String(format: "%.02f", price) as NSObject
+        payload[kSPEcommItemQuantity] = String(format: "%ld", quantity) as NSObject
+        return payload
+    }
+}
diff --git a/Sources/Snowplow/Events/EventBase.swift b/Sources/Snowplow/Events/EventBase.swift
new file mode 100644
index 000000000..165a8752f
--- /dev/null
+++ b/Sources/Snowplow/Events/EventBase.swift
@@ -0,0 +1,78 @@
+//
+//  EventBase.swift
+//  Snowplow
+//
+//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// This class has the basic functionality needed to represent all events
+@objc(SPEvent)
+public class Event: NSObject {
+    /// The user event timestamp in milliseconds (epoch time).
+    @objc
+    public var trueTimestamp: Date?
+    /// The contexts attached to the event.
+    @objc
+    public var contexts: [SelfDescribingJson] = []
+    /// The payload of the event.
+    public var payload: [String : NSObject] {
+        NSException(
+            name: .internalInconsistencyException,
+            reason: "You must override \(NSStringFromSelector(#function)) in a subclass",
+            userInfo: nil).raise()
+        abort()
+    }
+
+    /// Hook method called just before the event processing in order to execute special operations.
+    /// @note Internal use only - Don't use in production, it can change without notice.
+    func beginProcessing(withTracker tracker: Tracker) {
+    }
+
+    /// Hook method called just after the event processing in order to execute special operations.
+    /// @note Internal use only - Don't use in production, it can change without notice.
+    func endProcessing(withTracker tracker: Tracker) {
+    }
+}
+
+/// The properties for all the self-describing events.
+@objc(SPSelfDescribingAbstract)
+public class SelfDescribingAbstract: Event {
+    /// The schema of the event.
+    @objc
+    public var schema: String {
+        NSException(
+            name: .internalInconsistencyException,
+            reason: "You must override \(NSStringFromSelector(#function)) in a subclass",
+            userInfo: nil).raise()
+        abort()
+    }
+}
+
+/// The properties for all the self-describing events.
+@objc(SPPrimitiveAbstract)
+public class PrimitiveAbstract: Event {
+    /// The name of the event.
+    var eventName: String {
+        NSException(
+            name: .internalInconsistencyException,
+            reason: "You must override \(NSStringFromSelector(#function)) in a subclass",
+            userInfo: nil).raise()
+        abort()
+    }
+}
diff --git a/Snowplow/Internal/Events/SPForeground.h b/Sources/Snowplow/Events/Foreground.swift
similarity index 56%
rename from Snowplow/Internal/Events/SPForeground.h
rename to Sources/Snowplow/Events/Foreground.swift
index 2af9641d3..fa8abcc45 100644
--- a/Snowplow/Internal/Events/SPForeground.h
+++ b/Sources/Snowplow/Events/Foreground.swift
@@ -1,5 +1,5 @@
 //
-//  SPForeground.h
+//  Foreground.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,26 +19,29 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPEventBase.h"
-#import "SPSelfDescribingJson.h"
-
-NS_ASSUME_NONNULL_BEGIN
+import Foundation
 
 /// A foreground transition event.
-NS_SWIFT_NAME(Foreground)
-@interface SPForeground : SPSelfDescribingAbstract
-
-/// Indicate the current transition.
-@property (readonly) NSNumber *index;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-/**
- Creates a foreground transition event.
- @param index indicate the current transition.
- */
-- (instancetype)initWithIndex:(NSNumber *)index NS_SWIFT_NAME(init(index:));
-
-@end
-
-NS_ASSUME_NONNULL_END
+@objc(SPForeground)
+public class Foreground: SelfDescribingAbstract {
+    /// Indicate the current transition.
+    @objc
+    public var index: Int
+
+    /// Creates a foreground transition event.
+    /// - Parameter index: indicate the current transition.
+    @objc
+    public init(index: Int) {
+        self.index = index
+    }
+
+    override public var schema: String {
+        return kSPForegroundSchema
+    }
+
+    override public var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPForegroundIndex] = NSNumber(value: index)
+        return payload
+    }
+}
diff --git a/Sources/Snowplow/Events/MessageNotification.swift b/Sources/Snowplow/Events/MessageNotification.swift
new file mode 100644
index 000000000..a0b8c838a
--- /dev/null
+++ b/Sources/Snowplow/Events/MessageNotification.swift
@@ -0,0 +1,194 @@
+//
+// MessageNotification.m
+// Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+import Foundation
+
+let kSPMessageNotificationSchema = "iglu:com.snowplowanalytics.mobile/message_notification/jsonschema/1-0-0"
+let kSPMessageNotificationParamAction = "action"
+let kSPMessageNotificationParamMessageNotificationAttachments = "attachments"
+let kSPMessageNotificationParamBody = "body"
+let kSPMessageNotificationParamBodyLocArgs = "bodyLocArgs"
+let kSPMessageNotificationParamBodyLocKey = "bodyLocKey"
+let kSPMessageNotificationParamCategory = "category"
+let kSPMessageNotificationParamContentAvailable = "contentAvailable"
+let kSPMessageNotificationParamGroup = "group"
+let kSPMessageNotificationParamIcon = "icon"
+let kSPMessageNotificationParamNotificationCount = "notificationCount"
+let kSPMessageNotificationParamNotificationTimestamp = "notificationTimestamp"
+let kSPMessageNotificationParamSound = "sound"
+let kSPMessageNotificationParamSubtitle = "subtitle"
+let kSPMessageNotificationParamTag = "tag"
+let kSPMessageNotificationParamThreadIdentifier = "threadIdentifier"
+let kSPMessageNotificationParamTitle = "title"
+let kSPMessageNotificationParamTitleLocArgs = "titleLocArgs"
+let kSPMessageNotificationParamTitleLocKey = "titleLocKey"
+let kSPMessageNotificationParamTrigger = "trigger"
+
+@objc
+public enum MessageNotificationTrigger: Int {
+    case push = 0
+    case location
+    case calendar
+    case timeInterval
+    case other
+}
+
+func triggerToString(_ trigger: MessageNotificationTrigger) -> String {
+    return [
+        "push", "location", "calendar", "timeInterval", "other"
+    ][trigger.rawValue]
+}
+
+/// An event that represents the reception of a push notification (or a locally generated one).
+@objc(SPMessageNotification)
+public class MessageNotification : SelfDescribingAbstract {
+    /// The action associated with the notification.
+    @objc
+    public var action: String?
+    /// Attachments added to the notification (they can be part of the data object).
+    @objc
+    public var attachments: [MessageNotificationAttachment]?
+    /// The notification's body.
+    @objc
+    public var body: String
+    /// Variable string values to be used in place of the format specifiers in bodyLocArgs to use to localize the body text to the user's current localization.
+    @objc
+    public var bodyLocArgs: [String]?
+    /// The key to the body string in the app's string resources to use to localize the body text to the user's current localization.
+    @objc
+    public var bodyLocKey: String?
+    /// The category associated to the notification.
+    @objc
+    public var category: String?
+    /// The application is notified of the delivery of the notification if it's in the foreground or background, the app will be woken up (iOS only).
+    public var contentAvailable: Bool?
+    /// The group which this notification is part of.
+    @objc
+    public var group: String?
+    /// The icon associated to the notification (Android only).
+    @objc
+    public var icon: String?
+    /// The number of items this notification represents. It's the badge number on iOS.
+    public var notificationCount: Int?
+    /// The time when the event of the notification occurred.
+    @objc
+    public var notificationTimestamp: String?
+    /// The sound played when the device receives the notification.
+    @objc
+    public var sound: String?
+    /// The notification's subtitle. (iOS only)
+    @objc
+    public var subtitle: String?
+    /// An identifier similar to 'group' but usable for different purposes (Android only).
+    @objc
+    public var tag: String?
+    /// An identifier similar to 'group' but usable for different purposes (iOS only).
+    @objc
+    public var threadIdentifier: String?
+    /// The notification's title.
+    @objc
+    public var title: String
+    /// Variable string values to be used in place of the format specifiers in titleLocArgs to use to localize the title text to the user's current localization.
+    @objc
+    public var titleLocArgs: [String]?
+    /// The key to the title string in the app's string resources to use to localize the title text to the user's current localization.
+    @objc
+    public var titleLocKey: String?
+    /// The trigger that raised the notification message.
+    @objc
+    public var trigger: MessageNotificationTrigger
+    
+    /// Creates a Message Notification event that represents a push notification or a local notification.
+    /// @note The custom data of the push notification have to be tracked separately in custom entities that can be attached to this event.
+    /// @param title Title of message notification.
+    /// @param body Body content of the message notification.
+    /// @param trigger The trigger that raised this notification: remote notification (push), position related (location), date-time related (calendar, timeInterval) or app generated (other).
+    @objc
+    public init(title: String, body: String, trigger: MessageNotificationTrigger) {
+        self.title = title
+        self.body = body
+        self.trigger = trigger
+    }
+    
+    @objc
+    public class func messageNotification(userInfo: [String: NSObject], defaultTitle: String?, defaultBody: String?) -> MessageNotification? {
+        guard let aps = userInfo["aps"] as? [String : NSObject] else {
+            return nil
+        }
+        guard let alert = aps["alert"] as? [String : NSObject] else {
+            return nil
+        }
+        // alert fields
+        guard let title = alert["title"] as? String ?? defaultTitle,
+              let body = alert["body"] as? String ?? defaultBody else {
+            return nil
+        }
+        let event = MessageNotification(title: title, body: body, trigger: .push)
+        event.subtitle = alert["subtitle"] as? String
+        event.icon = alert["launch-image"] as? String
+        event.titleLocKey = alert["title-loc-key"] as? String
+        event.titleLocArgs = alert["title-loc-args"] as? [String]
+        event.bodyLocKey = alert["loc-key"] as? String
+        event.bodyLocArgs = alert["loc-args"] as? [String]
+        // aps fields
+        event.notificationCount = aps["badge"] as? Int
+        event.sound = aps["sound"] as? String
+        event.contentAvailable = aps["content-available"] as? Bool
+        event.category = aps["category"] as? String
+        event.threadIdentifier = aps["thread-id"] as? String
+        return event
+    }
+    
+    public override var schema: String {
+        return kSPMessageNotificationSchema
+    }
+    
+    public override var payload: [String: NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPMessageNotificationParamAction] = action as NSObject?
+        if let attachments = attachments {
+            payload[kSPMessageNotificationParamMessageNotificationAttachments] = attachments.map { $0.data } as NSObject
+        }
+        payload[kSPMessageNotificationParamBody] = body as NSObject?
+        if let bodyLocArgs = bodyLocArgs {
+            payload[kSPMessageNotificationParamBodyLocArgs] = bodyLocArgs as NSObject
+        }
+        payload[kSPMessageNotificationParamBodyLocKey] = bodyLocKey as NSObject?
+        payload[kSPMessageNotificationParamCategory] = category as NSObject?
+        if let contentAvailable = contentAvailable {
+            payload[kSPMessageNotificationParamContentAvailable] = NSNumber(value: contentAvailable)
+        }
+        payload[kSPMessageNotificationParamGroup] = group as NSObject?
+        payload[kSPMessageNotificationParamIcon] = icon as NSObject?
+        payload[kSPMessageNotificationParamNotificationCount] = notificationCount as NSObject?
+        payload[kSPMessageNotificationParamNotificationTimestamp] = notificationTimestamp as NSObject?
+        payload[kSPMessageNotificationParamSound] = sound as NSObject?
+        payload[kSPMessageNotificationParamSubtitle] = subtitle as NSObject?
+        payload[kSPMessageNotificationParamTag] = tag as NSObject?
+        payload[kSPMessageNotificationParamThreadIdentifier] = threadIdentifier as NSObject?
+        payload[kSPMessageNotificationParamTitle] = title as NSObject?
+        if let titleLocArgs = titleLocArgs {
+            payload[kSPMessageNotificationParamTitleLocArgs] = titleLocArgs as NSObject
+        }
+        payload[kSPMessageNotificationParamTitleLocKey] = titleLocKey as NSObject?
+        payload[kSPMessageNotificationParamTrigger] = triggerToString(trigger) as NSObject
+        return payload
+    }
+}
diff --git a/Sources/Snowplow/Events/MessageNotificationAttachment.swift b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
new file mode 100644
index 000000000..095b05deb
--- /dev/null
+++ b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
@@ -0,0 +1,53 @@
+//
+// MessageNotificationAttachment.swift
+// Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+import Foundation
+
+let kSPMessageNotificationAttachmentParamIdentifier = "identifier"
+let kSPMessageNotificationAttachmentParamType = "type"
+let kSPMessageNotificationAttachmentParamUrl = "url"
+
+/// Attachment object that identify an attachment in the MessageNotification
+@objc(SPMessageNotificationAttachment)
+public class MessageNotificationAttachment : NSObject {
+    @objc
+    public var identifer: String
+    @objc
+    public var type: String
+    @objc
+    public var url: String
+    
+    /// Attachments added to the notification (they can be part of the data object).
+    @objc
+    public init(identifier: String, type: String, url: String) {
+        self.identifer = identifier
+        self.type = type
+        self.url = url
+    }
+    
+    @objc
+    public var data: [String : NSObject] {
+        return [
+            kSPMessageNotificationAttachmentParamIdentifier: identifer as NSObject,
+            kSPMessageNotificationAttachmentParamType: type as NSObject,
+            kSPMessageNotificationAttachmentParamUrl: url as NSObject
+        ]
+    }
+}
diff --git a/Sources/Snowplow/Events/PageView.swift b/Sources/Snowplow/Events/PageView.swift
new file mode 100644
index 000000000..5ba37f865
--- /dev/null
+++ b/Sources/Snowplow/Events/PageView.swift
@@ -0,0 +1,60 @@
+//
+//  PageView.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A pageview event.
+/// @deprecated This event has been designed for web trackers, not suitable for mobile apps. Use DeepLinkReceived event to track deep link received in the app.
+@objc(SPPageView)
+public class PageView : PrimitiveAbstract {
+    /// Page url.
+    @objc
+    public var pageUrl: String
+    /// Page title.
+    @objc
+    public var pageTitle: String?
+    /// Page referrer url.
+    @objc
+    public var referrer: String?
+
+    /// Creates a Page View event
+    /// @param pageUrl Page URL
+    /// @param pageTitle Page title
+    /// @param referrer Page referrer URL
+    @objc
+    public init(pageUrl: String) {
+        self.pageUrl = pageUrl
+    }
+    
+    @objc
+    override public var eventName: String {
+        return kSPEventPageView
+    }
+    
+    override public var payload: [String : NSObject] {
+        var payload: [String: NSObject] = [
+            kSPPageUrl: pageUrl as NSObject
+        ]
+        payload[kSPPageTitle] = pageTitle as NSObject?
+        payload[kSPPageRefr] = referrer as NSObject?
+        return payload
+    }
+}
diff --git a/Sources/Snowplow/Events/PushNotification.swift b/Sources/Snowplow/Events/PushNotification.swift
new file mode 100644
index 000000000..114526cc4
--- /dev/null
+++ b/Sources/Snowplow/Events/PushNotification.swift
@@ -0,0 +1,177 @@
+//  PushNotification.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+#if os(iOS)
+import UserNotifications
+#endif
+
+@objc(SPPushNotification)
+public class PushNotification : SelfDescribingAbstract {
+    @objc
+    public var date: String
+    @objc
+    public var action: String
+    @objc
+    public var trigger: String
+    @objc
+    public var category: String
+    @objc
+    public var thread: String
+    @objc
+    public var notification: NotificationContent?
+
+    @objc
+    public init(date: String, action: String, trigger: String, category: String, thread: String, notification: NotificationContent?) {
+        self.date = date
+        self.action = action
+        self.trigger = trigger
+        self.category = category
+        self.thread = thread
+        self.notification = notification
+    }
+
+    #if os(iOS)
+
+    @objc
+    public init(date: String, action: String, notificationTrigger trigger: UNNotificationTrigger?, category: String, thread: String, notification: NotificationContent?) {
+        self.date = date
+        self.action = action
+        self.trigger = PushNotification.string(from: trigger)
+        self.category = category
+        self.thread = thread
+        self.notification = notification
+    }
+
+    @objc
+    public class func string(from trigger: UNNotificationTrigger?) -> String {
+        var triggerType = "UNKNOWN"
+        if let trigger = trigger {
+            let triggerClass = NSStringFromClass(type(of: trigger).self)
+            if triggerClass == "UNTimeIntervalNotificationTrigger" {
+                triggerType = "TIME_INTERVAL"
+            } else if triggerClass == "UNCalendarNotificationTrigger" {
+                triggerType = "CALENDAR"
+            } else if triggerClass == "UNLocationNotificationTrigger" {
+                triggerType = "LOCATION"
+            } else if triggerClass == "UNPushNotificationTrigger" {
+                triggerType = "PUSH"
+            }
+        }
+        return triggerType
+    }
+
+    #endif
+
+    public override var schema: String {
+        return kSPPushNotificationSchema
+    }
+
+    public override var payload: [String : NSObject] {
+        var data: [String: NSObject] = [
+            kSPPushTrigger: trigger as NSObject,
+            kSPPushAction: action as NSObject,
+            kSPPushDeliveryDate: date as NSObject,
+            kSPPushCategoryId: category as NSObject,
+            kSPPushThreadId: thread as NSObject
+        ]
+        if let notification = notification?.payload { data[kSPPushNotificationParam] = notification as NSObject }
+        return data
+    }
+}
+
+// MARK:- SPNotificationContent
+
+@objc(SPNotificationContent)
+public class NotificationContent : NSObject {
+    @objc
+    public var title: String
+    @objc
+    public var body: String
+    @objc
+    public var badge: NSNumber?
+    @objc
+    public var subtitle: String?
+    @objc
+    public var sound: String?
+    @objc
+    public var launchImageName: String?
+    @objc
+    public var userInfo: [String : NSObject]?
+    @objc
+    public var attachments: [NSObject]?
+
+    @objc
+    public init(title: String, body: String, badge: NSNumber?) {
+        self.title = title
+        self.body = body
+        self.badge = badge
+    }
+
+    @objc
+    public var payload: [String : NSObject] {
+        var event: [String : NSObject] = [:]
+        event[kSPPnTitle] = title as NSObject
+        event[kSPPnBody] = body as NSObject
+        event[kSPPnBadge] = badge
+        if let subtitle = subtitle {
+            event[kSPPnSubtitle] = subtitle as NSObject
+        }
+        if let sound = sound {
+            event[kSPPnSound] = sound as NSObject
+        }
+        if let launchImageName = launchImageName {
+            event[kSPPnLaunchImageName] = launchImageName as NSObject
+        }
+        if let userInfo = userInfo {
+            // modify contentAvailable value "1" and "0" to @YES and @NO to comply with schema
+            if var aps = userInfo["aps"] as? [NSString : NSObject],
+               let contentAvailable = aps["contentAvailable"] as? NSNumber {
+
+                if contentAvailable == NSNumber(value: 1) {
+                    aps["contentAvailable"] = NSNumber(value: true)
+                } else if contentAvailable == NSNumber(value: 0) {
+                    aps["contentAvailable"] = NSNumber(value: false)
+                }
+                var newUserInfo = userInfo
+                newUserInfo["aps"] = aps as NSObject
+                event[kSPPnUserInfo] = newUserInfo as NSObject
+            }
+        }
+        if let attachments = attachments {
+            var converting: [[AnyHashable : Any]] = []
+            for attachment in attachments {
+                var newAttachment: [String : NSObject] = [:]
+                if let value = attachment.value(forKey: "identifier") as? NSObject {
+                    newAttachment[kSPPnAttachmentId] = value
+                }
+                if let value = attachment.value(forKey: "URL") as? NSObject {
+                    newAttachment[kSPPnAttachmentUrl] = value
+                }
+                if let value = attachment.value(forKey: "type") as? NSObject {
+                    newAttachment[kSPPnAttachmentType] = value
+                }
+                converting.append(newAttachment)
+            }
+            event[kSPPnAttachments] = converting as NSObject
+        }
+        return event // copyItems: true
+    }
+}
diff --git a/Snowplow/Internal/Events/SPBackground.m b/Sources/Snowplow/Events/SNOWError.swift
similarity index 52%
rename from Snowplow/Internal/Events/SPBackground.m
rename to Sources/Snowplow/Events/SNOWError.swift
index 08c2d8083..807afc999 100644
--- a/Snowplow/Internal/Events/SPBackground.m
+++ b/Sources/Snowplow/Events/SNOWError.swift
@@ -1,5 +1,5 @@
 //
-//  SPBackground.m
+//  SNOWError.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,38 +19,32 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPBackground.h"
-
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-
-@interface SPBackground ()
-
-@property (readwrite) NSNumber *index;
-
-@end
-
-@implementation SPBackground
-
-- (instancetype)initWithIndex:(NSNumber *)index {
-    if (self = [super init]) {
-        _index = index;
-        [SPUtilities checkArgument:(_index != nil) withMessage:@"Index cannot be nil or empty."];
+import Foundation
+
+@objc(SPSNOWError)
+public class SNOWError: SelfDescribingAbstract {
+    @objc
+    public var message: String
+    @objc
+    public var name: String?
+    @objc
+    public var stackTrace: String?
+    
+    @objc
+    public init(message: String) {
+        self.message = message
+    }
+    
+    override public var schema: String {
+        return kSPErrorSchema
     }
-    return self;
-}
-
-// --- Public Methods
-
-- (NSString *)schema {
-    return kSPBackgroundSchema;
-}
-
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_index forKey:kSPBackgroundIndex];
-    return payload;
 
+    override public var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPErrorMessage] = message as NSObject
+        payload[kSPErrorStackTrace] = stackTrace as NSObject?
+        payload[kSPErrorName] = name as NSObject?
+        payload[kSPErrorLanguage] = "SWIFT" as NSObject
+        return payload
+    }
 }
-
-@end
diff --git a/Sources/Snowplow/Events/ScreenView.swift b/Sources/Snowplow/Events/ScreenView.swift
new file mode 100644
index 000000000..a51078b65
--- /dev/null
+++ b/Sources/Snowplow/Events/ScreenView.swift
@@ -0,0 +1,108 @@
+//
+//  ScreenView.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// An enum for screen types.
+@objc(SPScreenType)
+public enum ScreenType : Int {
+    // sourced from `View Controller Catalog for iOS`
+    case `default`
+    case navigation
+    case tabBar
+    case pageView
+    case splitView
+    case popoverPresentation
+    case modal
+    case combined
+}
+
+/// A screenview event.
+@objc(SPScreenView)
+public class ScreenView: SelfDescribingAbstract {
+    /// Name of the screen.
+    @objc
+    public var name: String
+    /// Identifier of the screen.
+    @objc
+    public var screenId: UUID
+    /// Type of screen.
+    @objc
+    public var type: String?
+    /// Name of the previous screen.
+    @objc
+    public var previousName: String?
+    /// Identifier of the previous screen.
+    @objc
+    public var previousId: String?
+    /// Type of the previous screen.
+    @objc
+    public var previousType: String?
+    /// Type of transition between previous and current screen,
+    @objc
+    public var transitionType: String?
+    /// Name of the ViewController subclass.
+    @objc
+    public var viewControllerClassName: String?
+    /// Name of the top ViewController subclass.
+    @objc
+    public var topViewControllerClassName: String?
+
+    /// Creates a screenview event.
+    /// @param name Name of the screen.
+    /// @param screenId Identifier of the screen.
+    @objc
+    public init(name: String, screenId: UUID?) {
+        self.screenId = screenId ?? UUID()
+        self.name = name
+    }
+
+    public override var schema: String {
+        return kSPScreenViewSchema
+    }
+
+    public override var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPSvName] = name as NSObject
+        payload[kSPSvScreenId] = screenId.uuidString as NSObject
+        if let type = type { payload[kSPSvType] = type as NSObject }
+        if let previousName = previousName { payload[kSPSvPreviousName] = previousName as NSObject }
+        if let previousType = previousType { payload[kSPSvPreviousType] = previousType as NSObject }
+        if let previousId = previousId { payload[kSPSvPreviousScreenId] = previousId as NSObject }
+        if let transitionType = transitionType { payload[kSPSvTransitionType] = transitionType as NSObject }
+        return payload
+    }
+
+    @objc
+    public class func stringWithScreenType(_ screenType: ScreenType) -> String? {
+        let arr = [
+            "Default",
+            "Navigation",
+            "TabBar",
+            "PageView",
+            "SplitView",
+            "PopoverPresentation",
+            "Modal",
+            "Combined"
+        ];
+        return arr[screenType.rawValue];
+    }
+}
diff --git a/Sources/Snowplow/Events/SelfDescribing.swift b/Sources/Snowplow/Events/SelfDescribing.swift
new file mode 100644
index 000000000..5311ba3fa
--- /dev/null
+++ b/Sources/Snowplow/Events/SelfDescribing.swift
@@ -0,0 +1,62 @@
+//
+//  SelfDescribing.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A self-describing event.
+@objc(SPSelfDescribing)
+public class SelfDescribing: SelfDescribingAbstract {
+    @objc
+    public var eventData: SelfDescribingJson {
+        set {
+            schema = newValue.schema
+            payload = newValue.data as! [String : NSObject]
+        }
+        get {
+            return SelfDescribingJson(schema: schema, andDictionary: payload)
+        }
+    }
+    private var _schema: String
+    @objc
+    override public var schema: String {
+        get { return _schema }
+        set { _schema = newValue }
+    }
+    private var _payload: [String: NSObject]
+    @objc
+    override public var payload: [String : NSObject] {
+        get { return _payload }
+        set {
+            _payload = newValue
+        }
+    }
+
+    @objc
+    public convenience init(eventData: SelfDescribingJson) {
+        self.init(schema: eventData.schema, payload: eventData.data as! [String : NSObject])
+    }
+
+    @objc
+    public init(schema: String, payload: [String : NSObject]) {
+        self._schema = schema
+        self._payload = payload
+    }
+}
diff --git a/Sources/Snowplow/Events/Structured.swift b/Sources/Snowplow/Events/Structured.swift
new file mode 100644
index 000000000..c184a3896
--- /dev/null
+++ b/Sources/Snowplow/Events/Structured.swift
@@ -0,0 +1,60 @@
+//
+//  Structured.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A structured event.
+@objc(SPStructured)
+public class Structured: PrimitiveAbstract {
+    @objc
+    public var category: String
+    @objc
+    public var action: String
+    @objc
+    public var label: String?
+    @objc
+    public var property: String?
+    @objc
+    public var value: NSNumber?
+
+    @objc
+    public init(category: String, action: String) {
+        self.category = category
+        self.action = action
+    }
+
+    @objc
+    override public var eventName: String {
+        return kSPEventStructured
+    }
+
+    override public var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPStuctCategory] = category as NSObject
+        payload[kSPStuctAction] = action as NSObject
+        if let label = label { payload[kSPStuctLabel] = label as NSObject }
+        if let property = property { payload[kSPStuctProperty] = property as NSObject }
+        if let value = value {
+            payload[kSPStuctValue] = String(format: "%.17g", value.doubleValue) as NSObject
+        }
+        return payload
+    }
+}
diff --git a/Sources/Snowplow/Events/Timing.swift b/Sources/Snowplow/Events/Timing.swift
new file mode 100644
index 000000000..a1672d82b
--- /dev/null
+++ b/Sources/Snowplow/Events/Timing.swift
@@ -0,0 +1,63 @@
+//
+//  Timing.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// A timing event.
+@objc(SPTiming)
+public class Timing: SelfDescribingAbstract {
+    /// The timing category
+    @objc
+    public var category: String
+    /// The timing variable
+    @objc
+    public var variable: String
+    /// The time
+    @objc
+    public var timing: Int
+    /// The timing label
+    @objc
+    public var label: String?
+
+    /// Creates a timing event
+    /// @param category The timing category
+    /// @param variable The timing variable
+    /// @param timing The time
+    @objc
+    public init(category: String, variable: String, timing: Int) {
+        self.category = category
+        self.variable = variable
+        self.timing = timing
+    }
+
+    public override var schema: String {
+        return kSPUserTimingsSchema
+    }
+
+    public override var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPUtCategory] = category as NSObject
+        payload[kSPUtVariable] = variable as NSObject
+        payload[kSPUtTiming] = NSNumber(value: timing)
+        if let label = label { payload[kSPUtLabel] = label as NSObject }
+        return payload
+    }
+}
diff --git a/Sources/Snowplow/Events/TrackerError.swift b/Sources/Snowplow/Events/TrackerError.swift
new file mode 100644
index 000000000..6ed30f579
--- /dev/null
+++ b/Sources/Snowplow/Events/TrackerError.swift
@@ -0,0 +1,74 @@
+//
+//  TrackerError.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+let kMaxMessageLength = 2048
+let kMaxStackLength = 8192
+let kMaxExceptionNameLength = 1024
+
+import Foundation
+
+@objc(SPTrackerError)
+public class TrackerError : SelfDescribingAbstract {
+    @objc
+    public var source: String
+    @objc
+    public var message: String
+    @objc
+    public var error: Error?
+    @objc
+    public var exception: NSException?
+    
+    @objc
+    public init(source: String, message: String, error: Error? = nil, exception: NSException? = nil) {
+        self.source = source
+        self.message = message
+        self.error = error
+        self.exception = exception
+    }
+    
+    override public var schema: String {
+        return kSPDiagnosticErrorSchema
+    }
+    
+    override public var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[kSPDiagnosticErrorClassName] = source as NSObject
+        payload[kSPDiagnosticErrorMessage] = truncate(message, maxLength: kMaxMessageLength) as NSObject
+        if let error = error {
+            payload[kSPDiagnosticErrorExceptionName] = error as NSObject
+        }
+        if let exception = exception {
+            payload[kSPDiagnosticErrorExceptionName] = truncate(exception.name.rawValue, maxLength: kMaxExceptionNameLength) as NSObject
+            let symbols = (exception).callStackSymbols
+            if symbols.count != 0 {
+                let stackTrace = "Stacktrace:\n\(symbols)"
+                payload[kSPDiagnosticErrorStack] = truncate(stackTrace, maxLength: kMaxStackLength) as NSObject
+            }
+        }
+        return payload
+    }
+
+    // -- Private methods
+
+    private func truncate(_ s: String, maxLength: Int) -> String {
+        return String(s.prefix(maxLength))
+    }
+}
diff --git a/Sources/Snowplow/GlobalContexts/ContextGenerator.swift b/Sources/Snowplow/GlobalContexts/ContextGenerator.swift
new file mode 100644
index 000000000..fad7be0c1
--- /dev/null
+++ b/Sources/Snowplow/GlobalContexts/ContextGenerator.swift
@@ -0,0 +1,38 @@
+//
+//  ContextGenerator.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// @protocol SPContextGenerator
+/// A context generator used to generate global contexts.
+@objc(SPContextGenerator)
+public protocol ContextGenerator: NSObjectProtocol {
+    /// Takes event information and decide if the context needs to be generated.
+    /// - Parameter event: informations about the event to process.
+    /// - Returns: whether the context has to be generated.
+    @objc
+    func filter(from event: InspectableEvent) -> Bool
+    /// Takes event information and generates a context.
+    /// - Parameter event: informations about the event to process.
+    /// - Returns: a user-generated self-describing JSON.
+    @objc
+    func generator(from event: InspectableEvent) -> [SelfDescribingJson]?
+}
diff --git a/Sources/Snowplow/GlobalContexts/GlobalContext.swift b/Sources/Snowplow/GlobalContexts/GlobalContext.swift
new file mode 100644
index 000000000..b11739117
--- /dev/null
+++ b/Sources/Snowplow/GlobalContexts/GlobalContext.swift
@@ -0,0 +1,121 @@
+//
+//  GlobalContext.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Block signature for context generators, takes event information and generates a context.
+/// - Parameter event: informations about the event to process.
+/// - Returns: a user-generated self-describing JSON.
+public typealias GeneratorBlock = (InspectableEvent) -> [SelfDescribingJson]
+/// Block signature for context filtering, takes event information and decide if the context needs to be generated.
+/// - Parameter event: informations about the event to process.
+/// - Returns: whether the context has to be generated.
+public typealias FilterBlock = (InspectableEvent) -> Bool
+// MARK: - SPContextGenerator
+
+// MARK: - SPGlobalContext
+
+@objc(SPGlobalContext)
+public class GlobalContext: NSObject {
+    private var generator: GeneratorBlock
+    private var filter: FilterBlock?
+    
+    /// Initialize a Global Context generator with a custom SPContextGenerator.
+    /// - Parameter generator: Implementation of SPContextGenerator protocol.
+    @objc
+    public convenience init(contextGenerator generator: ContextGenerator) {
+        self.init(generator: { event in
+            return generator.generator(from: event) ?? []
+        }, filter: { event in
+            return generator.filter(from: event)
+        })
+    }
+
+    /// Initialize a Global Context generator with static contexts.
+    /// - Parameter staticContexts: Static contexts added to all the events.
+    @objc
+    public convenience init(staticContexts: [SelfDescribingJson]) {
+        self.init(generator: { event in
+            return staticContexts
+        })
+    }
+
+    /// Initialize a Global Context generator with a generator block.
+    /// - Parameter generator: Generator block able to generate multiple contexts.
+    @objc
+    public convenience init(generator: @escaping GeneratorBlock) {
+        self.init(generator: generator, filter: nil)
+    }
+
+    /// Initialize a Global Context generator with static contexts and a ruleset filter.
+    /// - Parameters:
+    ///   - staticContexts: Static contexts added to all the events conforming with `ruleset`.
+    ///   - ruleset: Rule set to apply to events to check weather or not the contexts have to be added.
+    @objc
+    public convenience init(staticContexts: [SelfDescribingJson], ruleset: SchemaRuleset) {
+        self.init(generator: { event in
+            return staticContexts
+        }, filter: ruleset.filterBlock)
+    }
+
+    /// Initialize a Global Context generator with static contexts and a ruleset filter.
+    /// - Parameters:
+    ///   - generator: Generator block able to generate multiple contexts.
+    ///   - ruleset: Rule set to apply to events to check weather or not the contexts have to be added.
+    @objc
+    public convenience init(generator: @escaping GeneratorBlock, ruleset: SchemaRuleset) {
+        self.init(generator: generator, filter: ruleset.filterBlock)
+    }
+
+    /// Initialize a Global Context generator with static contexts and a ruleset filter.
+    /// - Parameters:
+    ///   - staticContexts: Static contexts added to all the events conforming with `ruleset`.
+    ///   - filter: Filter to apply to events to check weather or not the contexts have to be added.
+    @objc
+    public convenience init(staticContexts: [SelfDescribingJson], filter: @escaping FilterBlock) {
+        self.init(generator: { event in
+            return staticContexts
+        }, filter: filter)
+    }
+
+    /// Initialize a Global Context generator with static contexts and a ruleset filter.
+    /// - Parameters:
+    ///   - generator: Generator block able to generate multiple contexts.
+    ///   - filter: Filter to apply to events to check weather or not the contexts have to be added.
+    @objc
+    public required init(generator: @escaping GeneratorBlock, filter: FilterBlock?) {
+        self.generator = generator
+        self.filter = filter
+    }
+
+    /// Generate contexts based on event details and internal filter and generator.
+    /// - Parameter event: Event details used to filter and generate contexts.
+    /// - Returns: Generated contexts.
+    @objc
+    public func contexts(from event: InspectableEvent) -> [SelfDescribingJson] {
+        if let filter = filter {
+            if !filter(event) {
+                return []
+            }
+        }
+        return generator(event)
+    }
+}
diff --git a/Sources/Snowplow/GlobalContexts/SchemaRuleset.swift b/Sources/Snowplow/GlobalContexts/SchemaRuleset.swift
new file mode 100644
index 000000000..43e8ca5d8
--- /dev/null
+++ b/Sources/Snowplow/GlobalContexts/SchemaRuleset.swift
@@ -0,0 +1,110 @@
+//
+//  SchemaRuleset.swift
+//  Snowplow-iOS
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPSchemaRuleset)
+public class SchemaRuleset: NSObject, NSCopying {
+    private var rulesDenied: [SchemaRule] = []
+    @objc
+    public var denied: [String] {
+        return rulesDenied.map { $0.rule }
+    }
+
+    private var rulesAllowed: [SchemaRule] = []
+    @objc
+    public var allowed: [String] {
+        return rulesAllowed.map { $0.rule }
+    }
+
+    @objc
+    public var filterBlock: FilterBlock {
+        return { [self] event in
+            if let schema = event.schema {
+                return match(withUri: schema)
+            }
+            return false
+        }
+    }
+
+    public func copy(with zone: NSZone? = nil) -> Any {
+        return SchemaRuleset(allowedList: allowed, andDeniedList: denied)
+    }
+
+    /// Generate a set of rules based on allowed and denied event schemas.
+    /// - Parameters:
+    ///   - allowed: Rules of allowed schemas.
+    ///   - denied: Rules of denied schemas.
+    @objc
+    public init(allowedList allowed: [String], andDeniedList denied: [String]) {
+        for rule in allowed {
+            if let schemaRule = SchemaRule(rule: rule) {
+                rulesAllowed.append(schemaRule)
+            }
+        }
+        
+        for rule in denied {
+            if let schemaRule = SchemaRule(rule: rule) {
+                rulesDenied.append(schemaRule)
+            }
+        }
+    }
+
+    /// Generate a set of rules based on allowed and denied event schemas.
+    /// - Parameter allowed: Rules of allowed schemas.
+    @objc
+    public convenience init(allowedList allowed: [String]) {
+        self.init(allowedList: allowed, andDeniedList: [])
+    }
+
+    /// Generate a set of rules based on allowed and denied event schemas.
+    /// - Parameter denied: Rules of denied schemas.
+    @objc
+    public convenience init(deniedList denied: [String]) {
+        self.init(allowedList: [], andDeniedList: denied)
+    }
+
+    /// Weather the `uri` match the stored rules.
+    /// - Parameter uri: URI to check.
+    /// - Returns: Weather the uri is allowed.
+    @objc
+    public func match(withUri uri: String) -> Bool {
+        for rule in rulesDenied {
+            if rule.match(withUri: uri) {
+                return false
+            }
+        }
+        if rulesAllowed.count == 0 {
+            return true
+        }
+        for rule in rulesAllowed {
+            if rule.match(withUri: uri) {
+                return true
+            }
+        }
+        return false
+    }
+
+    @objc
+    public override var description: String {
+        return "SchemaRuleset:\r\n  allowed:\(allowed)\r\n  denied:\(denied)\r\n"
+    }
+}
diff --git a/Sources/Snowplow/Network/DefaultNetworkConnection.swift b/Sources/Snowplow/Network/DefaultNetworkConnection.swift
new file mode 100644
index 000000000..63b49de69
--- /dev/null
+++ b/Sources/Snowplow/Network/DefaultNetworkConnection.swift
@@ -0,0 +1,242 @@
+//
+//  SPDefaultNetworkConnection.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPDefaultNetworkConnection)
+public class DefaultNetworkConnection: NSObject, NetworkConnection {
+    private var _protocol: ProtocolOptions = .https
+    // The protocol for connection to the collector
+    @objc
+    public var `protocol`: ProtocolOptions {
+        get {
+            return _protocol
+        }
+        set {
+            _protocol = newValue
+            if builderFinished { setup() }
+        }
+    }
+
+    private var _urlString: String
+    /// The collector endpoint.
+    @objc
+    public var urlString: String {
+        get {
+            return urlEndpoint?.absoluteString ?? _urlString
+        }
+        set {
+            _urlString = newValue
+            if builderFinished { setup() }
+        }
+    }
+    @objc
+    private(set) public var urlEndpoint: URL?
+
+    private var _httpMethod: HttpMethodOptions = .post
+    /// HTTP method, should be .get or .post.
+    @objc
+    public var httpMethod: HttpMethodOptions {
+        get {
+            return _httpMethod
+        }
+        set(method) {
+            _httpMethod = method
+            if builderFinished && urlEndpoint != nil {
+                setup()
+            }
+        }
+    }
+
+    private var _emitThreadPoolSize = 15
+    /// The number of threads used by the emitter.
+    @objc
+    public var emitThreadPoolSize: Int {
+        get {
+            return _emitThreadPoolSize
+        }
+        set(emitThreadPoolSize) {
+            self._emitThreadPoolSize = emitThreadPoolSize
+            if dataOperationQueue.maxConcurrentOperationCount != emitThreadPoolSize {
+                dataOperationQueue.maxConcurrentOperationCount = emitThreadPoolSize
+            }
+        }
+    }
+    /// Maximum event size for a GET request.
+    public var byteLimitGet: Int = 40000
+    /// Maximum event size for a POST request.
+    @objc
+    public var byteLimitPost = 40000
+    /// A custom path that is used on the endpoint to send requests.
+    @objc
+    public var customPostPath: String?
+    /// Custom headers (key, value) for http requests.
+    @objc
+    public var requestHeaders: [String : String]?
+    /// Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
+    @objc
+    public var serverAnonymisation = false
+    private var dataOperationQueue = OperationQueue()
+    private var builderFinished = false
+    
+    @objc
+    public init(urlString: String,
+                httpMethod: HttpMethodOptions = EmitterDefaults.httpMethod,
+                protocol: ProtocolOptions = EmitterDefaults.httpProtocol,
+                customPostPath: String? = nil) {
+        self._urlString = urlString
+        super.init()
+        self.httpMethod = httpMethod
+        self.protocol = `protocol`
+        self.customPostPath = customPostPath
+        setup()
+    }
+
+    // MARK: - Implement SPNetworkConnection protocol
+    
+    private func setup() {
+        // Decode url to extract protocol
+        let url = URL(string: _urlString)
+        var endpoint = _urlString
+        if url?.scheme == "https" {
+            `protocol` = .https
+        } else if url?.scheme == "http" {
+            `protocol` = .http
+        } else {
+            `protocol` = .https
+            endpoint = "https://\(_urlString)"
+        }
+
+        // Configure
+        let urlPrefix = `protocol` == .http ? "http://" : "https://"
+        var urlSuffix = _httpMethod == .get ? kSPEndpointGet : kSPEndpointPost
+        if _httpMethod == .post {
+            if let customPostPath = customPostPath { urlSuffix = customPostPath }
+        }
+
+        // Remove trailing slashes from endpoint to avoid double slashes when appending path
+        endpoint = endpoint.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
+
+        urlEndpoint = URL(string: endpoint)?.appendingPathComponent(urlSuffix)
+
+        // Log
+        if urlEndpoint?.scheme != nil && urlEndpoint?.host != nil {
+            logDebug(message: "Emitter URL created successfully '\(urlEndpoint?.absoluteString ?? "-")'")
+        } else {
+            logDebug(message: "Invalid emitter URL: '\(urlEndpoint?.absoluteString ?? "-")'")
+        }
+        let userDefaults = UserDefaults.standard
+        userDefaults.set(endpoint, forKey: kSPErrorTrackerUrl)
+        userDefaults.set(urlSuffix, forKey: kSPErrorTrackerProtocol)
+        userDefaults.set(urlPrefix, forKey: kSPErrorTrackerMethod)
+
+        builderFinished = true
+    }
+
+    @objc
+    public func sendRequests(_ requests: [Request]) -> [RequestResult] {
+        var results: [RequestResult] = []
+
+        for request in requests {
+            let urlRequest = _httpMethod == .get
+                ? buildGet(request)
+                : buildPost(request)
+
+            dataOperationQueue.addOperation({
+                //source: https://forums.developer.apple.com/thread/11519
+                var httpResponse: HTTPURLResponse? = nil
+                var connectionError: Error? = nil
+                var sem: DispatchSemaphore
+
+                sem = DispatchSemaphore(value: 0)
+
+                URLSession.shared.dataTask(with: urlRequest) { data, urlResponse, error in
+                    connectionError = error
+                    httpResponse = urlResponse as? HTTPURLResponse
+                    sem.signal()
+                }.resume()
+
+                let _ = sem.wait(timeout: .distantFuture)
+                var statusCode: NSNumber?
+                if let httpResponse = httpResponse { statusCode = NSNumber(value: httpResponse.statusCode) }
+
+                let result = RequestResult(statusCode: statusCode, oversize: request.oversize, storeIds: request.emitterEventIds)
+                if !result.isSuccessful {
+                    logError(message: "Connection error: " + (connectionError?.localizedDescription ?? "-"))
+                }
+
+                objc_sync_enter(self)
+                results.append(result)
+                objc_sync_exit(self)
+            })
+        }
+        dataOperationQueue.waitUntilAllOperationsAreFinished()
+        return results
+    }
+
+    // MARK: - Private methods
+
+    func buildPost(_ request: Request) -> URLRequest {
+        var requestData: Data? = nil
+        do {
+            requestData = try JSONSerialization.data(withJSONObject: request.payload?.dictionary ?? [:], options: [])
+        } catch {
+        }
+        let url = URL(string: urlEndpoint!.absoluteString)!
+        var urlRequest = URLRequest(url: url)
+        urlRequest.setValue("\(NSNumber(value: requestData?.count ?? 0).stringValue)", forHTTPHeaderField: "Content-Length")
+        urlRequest.setValue(kSPAcceptContentHeader, forHTTPHeaderField: "Accept")
+        urlRequest.setValue(kSPContentTypeHeader, forHTTPHeaderField: "Content-Type")
+        if serverAnonymisation {
+            urlRequest.setValue("*", forHTTPHeaderField: "SP-Anonymous")
+        }
+        if let requestHeaders = requestHeaders {
+            applyValuesAndHeaderFields(requestHeaders, to: &urlRequest)
+        }
+        urlRequest.httpMethod = "POST"
+        urlRequest.httpBody = requestData
+        return urlRequest
+    }
+
+    func buildGet(_ request: Request) -> URLRequest {
+        let payload = request.payload?.dictionary ?? [:]
+        let url = "\(urlEndpoint!.absoluteString)?\(Utilities.urlEncode(payload))"
+        let anUrl = URL(string: url)!
+        var urlRequest = URLRequest(url: anUrl)
+        urlRequest.setValue(kSPAcceptContentHeader, forHTTPHeaderField: "Accept")
+        if serverAnonymisation {
+            urlRequest.setValue("*", forHTTPHeaderField: "SP-Anonymous")
+        }
+        if let requestHeaders = requestHeaders {
+            applyValuesAndHeaderFields(requestHeaders, to: &urlRequest)
+        }
+        urlRequest.httpMethod = "GET"
+        return urlRequest
+    }
+
+    func applyValuesAndHeaderFields(_ requestHeaders: [String : String], to request: inout URLRequest) {
+        (requestHeaders as NSDictionary).enumerateKeysAndObjects({ key, obj, stop in
+            if let key = key as? String, let obj = obj as? String {
+                request.setValue(obj, forHTTPHeaderField: key)
+            }
+        })
+    }
+}
diff --git a/Snowplow/Internal/Storage/SPMemoryEventStore.h b/Sources/Snowplow/Network/HttpMethodOptions.swift
similarity index 78%
rename from Snowplow/Internal/Storage/SPMemoryEventStore.h
rename to Sources/Snowplow/Network/HttpMethodOptions.swift
index 6205eb5fb..4aedf0336 100644
--- a/Snowplow/Internal/Storage/SPMemoryEventStore.h
+++ b/Sources/Snowplow/Network/HttpMethodOptions.swift
@@ -1,5 +1,5 @@
 //
-//  SPMemoryEventStore.h
+//  HttpMethodOptions.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,14 +19,13 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPEventStore.h"
+import Foundation
 
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(MemoryEventStore)
-@interface SPMemoryEventStore : NSObject <SPEventStore>
-
-@end
-
-NS_ASSUME_NONNULL_END
+/// An enum for HTTP method types.
+@objc(SPHttpMethod)
+public enum HttpMethodOptions : Int {
+    /// GET request.
+    case get
+    /// POST request.
+    case post
+}
diff --git a/Snowplow/Internal/Configurations/SPConfiguration.m b/Sources/Snowplow/Network/NetworkConnection.swift
similarity index 56%
rename from Snowplow/Internal/Configurations/SPConfiguration.m
rename to Sources/Snowplow/Network/NetworkConnection.swift
index 9dc76c890..9c9e1263e 100644
--- a/Snowplow/Internal/Configurations/SPConfiguration.m
+++ b/Sources/Snowplow/Network/NetworkConnection.swift
@@ -1,5 +1,5 @@
 //
-//  SPConfiguration.m
+//  SPNetworkConnection.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,27 +19,21 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPConfiguration.h"
-
-@implementation SPConfiguration
-
-+ (BOOL)supportsSecureCoding {
-    return YES;
-}
-
-- (instancetype)initWithDictionary:(NSDictionary<NSString *,NSObject *> *)dictionary {
-    return [[SPConfiguration alloc] init];
-}
-
-- (nonnull instancetype)copyWithZone:(nullable NSZone *)zone {
-    return [[SPConfiguration allocWithZone:zone] init];
+import Foundation
+
+/// Interface for the component that
+/// sends events to the collector.
+@objc(SPNetworkConnection)
+public protocol NetworkConnection: NSObjectProtocol {
+    /// Send requests to the collector.
+    /// - Parameter requests: to send,
+    /// - Returns: results of the sending operation.
+    @objc
+    func sendRequests(_ requests: [Request]) -> [RequestResult]
+    /// - Returns: http method used to send requests to the collector.
+    @objc
+    var httpMethod: HttpMethodOptions { get }
+    /// - Returns: URL of the collector.
+    @objc
+    var urlEndpoint: URL? { get }
 }
-
-- (void)encodeWithCoder:(nonnull NSCoder *)coder {
-}
-
-- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
-    return [[SPConfiguration alloc] init];
-}
-
-@end
diff --git a/Sources/Snowplow/Network/ProtocolOptions.swift b/Sources/Snowplow/Network/ProtocolOptions.swift
new file mode 100644
index 000000000..17ba49c2b
--- /dev/null
+++ b/Sources/Snowplow/Network/ProtocolOptions.swift
@@ -0,0 +1,31 @@
+//
+//  ProtocolOptions.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// An enum for HTTP security.
+@objc(SPProtocol)
+public enum ProtocolOptions : Int {
+    /// Use HTTP.
+    case http
+    /// Use HTTP over TLS.
+    case https
+}
diff --git a/Sources/Snowplow/Network/Request.swift b/Sources/Snowplow/Network/Request.swift
new file mode 100644
index 000000000..d9d86617f
--- /dev/null
+++ b/Sources/Snowplow/Network/Request.swift
@@ -0,0 +1,69 @@
+//
+//  SPRequest.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPRequest)
+public class Request: NSObject {
+    @objc
+    public private(set) var payload: Payload?
+    @objc
+    public private(set) var emitterEventIds: [NSNumber]?
+    @objc
+    public private(set) var oversize = false
+    @objc
+    public private(set) var customUserAgent: String?
+
+    convenience init(payload: Payload, emitterEventId: Int64) {
+        self.init(payload: payload, emitterEventId: emitterEventId, oversize: false)
+    }
+
+    init(payload: Payload, emitterEventId: Int64, oversize: Bool) {
+        super.init()
+        self.payload = payload
+        emitterEventIds = [NSNumber(value: emitterEventId)]
+        customUserAgent = userAgent(from: payload)
+        self.oversize = oversize
+    }
+
+    init(payloads: [Payload], emitterEventIds: [NSNumber]) {
+        super.init()
+        var tempUserAgent: String? = nil
+        var payloadData: [[String : NSObject]] = []
+        for payload in payloads {
+            if let data = payload.dictionary {
+                payloadData.append(data)
+            }
+            tempUserAgent = userAgent(from: payload)
+        }
+        let payloadBundle = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: payloadData as NSObject)
+        if let payloadBundleDict = payloadBundle.dictionary {
+            payload = Payload(dictionary: payloadBundleDict)
+        }
+        self.emitterEventIds = emitterEventIds
+        customUserAgent = tempUserAgent
+        oversize = false
+    }
+
+    func userAgent(from payload: Payload) -> String? {
+        return (payload.dictionary?[kSPUseragent] as? String)
+    }
+}
diff --git a/Snowplow/Internal/Emitter/SPRequestCallback.h b/Sources/Snowplow/Network/RequestCallback.swift
similarity index 74%
rename from Snowplow/Internal/Emitter/SPRequestCallback.h
rename to Sources/Snowplow/Network/RequestCallback.swift
index 0a86e932d..f25e5a7dd 100644
--- a/Snowplow/Internal/Emitter/SPRequestCallback.h
+++ b/Sources/Snowplow/Network/RequestCallback.swift
@@ -1,5 +1,5 @@
 //
-//  SPRequestCallback.h
+//  SPRequestCallback.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,13 +19,12 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
+import Foundation
 
-NS_SWIFT_NAME(RequestCallback)
-@protocol SPRequestCallback <NSObject>
-
-- (void) onSuccessWithCount:(NSInteger)successCount;
-
-- (void) onFailureWithCount:(NSInteger)failureCount successCount:(NSInteger)successCount;
-
-@end
+@objc(SPRequestCallback)
+public protocol RequestCallback: NSObjectProtocol {
+    @objc
+    func onSuccess(withCount successCount: Int)
+    @objc
+    func onFailure(withCount failureCount: Int, successCount: Int)
+}
diff --git a/Sources/Snowplow/Network/RequestResult.swift b/Sources/Snowplow/Network/RequestResult.swift
new file mode 100644
index 000000000..ea484915c
--- /dev/null
+++ b/Sources/Snowplow/Network/RequestResult.swift
@@ -0,0 +1,85 @@
+//
+//  SPRequestResult.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPRequestResult)
+public class RequestResult: NSObject {
+    /// Returns the HTTP status code from Collector.
+    public private(set) var statusCode: Int?
+    /// Was the request oversize
+    @objc
+    public private(set) var isOversize: Bool
+    /// Returns the stored index array, needed to remove the events after sending.
+    @objc
+    public private(set) var storeIds: [NSNumber]?
+
+    @objc
+    public convenience override init() {
+        self.init(statusCode: -1, oversize: false, storeIds: [])
+    }
+
+    /// Creates a request result object
+    /// - Parameters:
+    ///   - statusCode: HTTP status code from collector response
+    ///   - storeIds: the event indexes in the database
+    @objc
+    public init(statusCode: NSNumber?, oversize isOversize: Bool, storeIds: [NSNumber]?) {
+        self.statusCode = statusCode?.intValue
+        self.isOversize = isOversize
+        self.storeIds = storeIds
+    }
+    
+    /// - Returns: Whether the events were successfuly sent to the Collector.
+    @objc
+    public var isSuccessful: Bool {
+        if let statusCode = statusCode {
+            return statusCode >= 200 && statusCode < 300
+        }
+        return false
+    }
+
+    /// - Parameter customRetryForStatusCodes: mapping of custom retry rules for HTTP status codes in Collector response.
+    /// - Returns: Whether sending the events to the Collector should be retried.
+    func shouldRetry(_ customRetryForStatusCodes: [Int : Bool]?) -> Bool {
+        // don't retry if successful
+        if isSuccessful {
+            return false
+        }
+
+        // don't retry if request is larger than max byte limit
+        if isOversize {
+            return false
+        }
+
+        // status code has a custom retry rule
+        if let statusCode = statusCode {
+            if let retryRule = customRetryForStatusCodes?[statusCode] {
+                return retryRule
+            }
+            
+            // retry if status code is not in the list of no-retry status codes
+            let dontRetryStatusCodes = [400, 401, 403, 410, 422]
+            return !dontRetryStatusCodes.contains(statusCode)
+        }
+        return true
+    }
+}
diff --git a/Sources/Snowplow/Payload/Payload.swift b/Sources/Snowplow/Payload/Payload.swift
new file mode 100644
index 000000000..40cd10db3
--- /dev/null
+++ b/Sources/Snowplow/Payload/Payload.swift
@@ -0,0 +1,201 @@
+//
+//  SPPayload.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPPayload)
+public class Payload: NSObject {
+    private var payload: [String : NSObject] = [:]
+    @objc
+    public var allowDiagnostic = true
+    
+    /// Returns the payload of that particular SPPayload object.
+    /// - Returns: NSDictionary of data in the object.
+    @objc
+    public var dictionary: [String : NSObject]? {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        return payload
+    }
+
+    /// Returns the byte size of a payload.
+    /// - Returns: A long representing the byte size of the payload.
+    @objc
+    public var byteSize: Int {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        if let data = try? JSONSerialization.data(withJSONObject: payload) {
+            return data.count
+        }
+        return 0
+    }
+
+    @objc
+    override public var description: String {
+        return dictionary?.description ?? ""
+    }
+
+    ///  Initializes a newly allocated SPPayload
+    ///  - Returns: A SnowplowPayload.
+    @objc
+    public override init() {
+        super.init()
+    }
+
+    ///  Initializes a newly allocated SPPayload with an existing object of type NSDictionary.
+    ///  - Parameter dictionary: An object of NSDictionary.
+    ///  - Returns: A SnowplowPayload.
+    @objc
+    public init(dictionary: [String : NSObject]) {
+        super.init()
+        payload = dictionary
+    }
+
+    /// Adds a simple name-value pair into the SPPayload intance.
+    /// - Parameters:
+    /// - value: A NSString value
+    /// - key: A key of type NSString
+    @objc
+    public func addValueToPayload(_ value: String?, forKey key: String) {
+        objc_sync_enter(self)
+        if value == nil || value?.count == 0 {
+            if payload[key] != nil {
+                payload.removeValue(forKey: key)
+            }
+        } else {
+            payload[key] = value as NSObject?
+        }
+        objc_sync_exit(self)
+    }
+
+    /// Adds a simple name-value pair into the SPPayload intance.
+    /// - Parameters:
+    /// - value: A NSNumber value
+    /// - key: A key of type NSString
+    @objc
+    public func addNumericValueToPayload(_ value: NSNumber?, forKey key: String) {
+        objc_sync_enter(self)
+        if let value = value {
+            payload[key] = value
+        } else if payload[key] != nil {
+            payload.removeValue(forKey: key)
+        }
+        objc_sync_exit(self)
+    }
+
+    ///  Adds a dictionary of attributes to be appended into the SPPayload instance. It does NOT overwrite the existing data in the object.
+    ///  All attribute values must be NSString types to be added; all others are discarded.
+    ///  - Parameter dictionary: An object of NSDictionary.
+    @objc
+    public func addDictionaryToPayload(_ dictionary: [String : NSObject]?) {
+        if dictionary == nil {
+            return
+        }
+        (dictionary as NSDictionary?)?.enumerateKeysAndObjects({ [self] key, value, stop in
+            if value is NSString {
+                if let key = key as? String {
+                    addValueToPayload(value as? String, forKey: key)
+                }
+            }
+        })
+    }
+
+    ///  Adds a dictionary of attributes to be appended into the SPPayload instance. Gives you the option to Base64 encode the data before adding it into the object.
+    ///  - Parameters:
+    ///  - json: NSData of JSON-compatible data to be added.
+    ///  - encode: Boolean option to choose whether the JSON data should be encoded.
+    ///  - typeEncoded: If the data is to be encoded, the result will be a value of the key in typeEncoded.
+    ///  - typeNotEncoded: If the data is NOT going to be encoded, the result will be a value of the key in typeWhenNotEncoded.
+    @objc
+    public func addJsonToPayload(
+        _ json: Data,
+        base64Encoded encode: Bool,
+        typeWhenEncoded typeEncoded: String?,
+        typeWhenNotEncoded typeNotEncoded: String?
+    ) {
+        guard let _ = try? JSONSerialization.jsonObject(with: json) as? [String : NSObject] else { return }
+        if encode {
+            guard let typeEncoded = typeEncoded else { return }
+            var encodedString = json.base64EncodedString(options: [])
+
+            // We need URL safe with no padding. Since there is no built-in way to do this, we transform
+            // the encoded payload to make it URL safe by replacing chars that are different in the URL-safe
+            // alphabet. Namely, 62 is - instead of +, and 63 _ instead of /.
+            // See: https://tools.ietf.org/html/rfc4648#section-5
+            encodedString = encodedString.replacingOccurrences(of: "/", with: "_").replacingOccurrences(
+                of: "+",
+                with: "-")
+
+            // There is also no padding since the length is implicitly known.
+            encodedString = encodedString.trimmingCharacters(in: CharacterSet(charactersIn: "="))
+
+            addValueToPayload(encodedString, forKey: typeEncoded)
+        } else {
+            guard let typeNotEncoded = typeNotEncoded else { return }
+            addValueToPayload(String(data: json, encoding: .utf8), forKey: typeNotEncoded)
+        }
+    }
+
+    ///  Adds a JSON string of attributes to be appended into the SPPayload instance. Gives you the option to Base64 encode the data before adding it into the object. This method converts the string to NSData and uses the data with addJsonStringToPayload:base64Encoded:typeWhenEncoded:typeWhenNotEncoded:
+    ///  - Parameters:
+    ///  - json: NSData of JSON-compatible data to be added.
+    ///  - encode: Boolean option to choose whether the JSON data should be encoded.
+    ///  - typeEncoded: If the data is to be encoded, the result will be a value of the key in typeEncoded.
+    ///  - typeNotEncoded: If the data is NOT going to be encoded, the result will be a value of the key in typeWhenNotEncoded.
+    @objc
+    public func addJsonStringToPayload(
+        _ json: String,
+        base64Encoded encode: Bool,
+        typeWhenEncoded typeEncoded: String?,
+        typeWhenNotEncoded typeNotEncoded: String?
+    ) {
+        guard let data = json.data(using: .utf8) else { return }
+
+        addJsonToPayload(
+            data,
+            base64Encoded: encode,
+            typeWhenEncoded: typeEncoded,
+            typeWhenNotEncoded: typeNotEncoded)
+
+    }
+
+    ///  Adds a dictionary of attributes to be appended into the SPPayload instance. Gives you the option to Base64 encode the data before adding it into the object. This method converts the dictionary to NSData and uses the data with addJsonStringToPayload:base64Encoded:typeWhenEncoded:typeWhenNotEncoded:
+    ///  - Parameters:
+    ///  - json: NSDictionary of JSON-compatible data to be added.
+    ///  - encode: Boolean option to choose whether the JSON data should be encoded.
+    ///  - typeEncoded: If the data is to be encoded, the result will be a value of the key in typeEncoded.
+    ///  - typeNotEncoded: If the data is NOT going to be encoded, the result will be a value of the key in typeWhenNotEncoded.
+    @objc
+    public func addDictionaryToPayload(
+        _ dictionary: [String : NSObject],
+        base64Encoded encode: Bool,
+        typeWhenEncoded typeEncoded: String?,
+        typeWhenNotEncoded typeNotEncoded: String?
+    ) {
+        guard let data = try? JSONSerialization.data(withJSONObject: dictionary) else { return }
+        
+        addJsonToPayload(
+            data,
+            base64Encoded: encode,
+            typeWhenEncoded: typeEncoded,
+            typeWhenNotEncoded: typeNotEncoded)
+    }
+}
diff --git a/Sources/Snowplow/Payload/SelfDescribingJson.swift b/Sources/Snowplow/Payload/SelfDescribingJson.swift
new file mode 100644
index 000000000..a453dcfea
--- /dev/null
+++ b/Sources/Snowplow/Payload/SelfDescribingJson.swift
@@ -0,0 +1,129 @@
+//
+//  SPSelfDescribingJson.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// @class SPSelfDescribingJson
+/// The class that represents self-describing JSONs.
+/// This class holds the information of a self-describing JSON.
+/// - seealso: SPPayload
+@objc(SPSelfDescribingJson)
+public class SelfDescribingJson: NSObject {
+    /// the schema URI for this self-describing JSON.
+    @objc
+    public var schema: String
+    
+    /// Data of the self-describing JSON.
+    @objc
+    public var data: NSObject?
+
+    /// Returns the internal NSDictionary of the self-describing JSON.
+    /// - Returns: The self-describing JSON as an NSDictionary.
+    @objc
+    public var dictionary: [String : NSObject]? {
+        if let data = data {
+            return [
+                kSPSchema: schema as NSObject,
+                kSPData: data
+            ]
+        }
+        return nil
+    }
+
+    /// Returns a string description of the internal dictionary.
+    /// - Returns: The description of the dictionary.
+    @objc
+    override public var description: String {
+        return dictionary?.description ?? ""
+    }
+
+    /// Initializes a newly allocated SPSelfDescribingJson.
+    /// - Parameters:
+    ///   - schema: A valid schema string.
+    ///   - data: Data to set for data field of the self-describing JSON, should be an NSDictionary.
+    /// - Returns: An SPSelfDescribingJson.
+    @objc
+    public init(schema: String, andData data: NSObject?) {
+        self.schema = schema
+        super.init()
+        setData(withObject: data)
+    }
+
+    /// Initializes a newly allocated SPSelfDescribingJson.
+    /// - Parameters:
+    ///   - schema: A valid schema string.
+    ///   - data: Dictionary to set for data field of the self-describing JSON.
+    /// - Returns: An SPSelfDescribingJson.
+    @objc
+    public convenience init(schema: String, andDictionary data: [String : NSObject]) {
+        self.init(schema: schema, andData: data as NSObject)
+    }
+
+    /// Initializes a newly allocated SPSelfDescribingJson.
+    /// - Parameters:
+    ///   - schema: A valid schema string.
+    ///   - data: Dictionary to set for data field of the self-describing JSON.
+    /// - Returns: An SPSelfDescribingJson.
+    public convenience init(schema: String, andDictionary data: [String : String]) {
+        self.init(schema: schema, andDictionary: data as [String : NSObject])
+    }
+
+    /// Initializes a newly allocated SPSelfDescribingJson.
+    /// - Parameters:
+    ///   - schema: A valid schema string.
+    ///   - data: Payload to set for data field of the self-describing JSON.
+    /// - Returns: An SPSelfDescribingJson.
+    @objc
+    public convenience init(schema: String, andPayload data: Payload) {
+        self.init(schema: schema, andData: data.dictionary as? NSObject)
+    }
+
+    /// Initializes a newly allocated SPSelfDescribingJson.
+    /// - Parameters:
+    ///   - schema: A valid schema URI.
+    ///   - data: Self-describing JSON to set for data field of the self-describing JSON.
+    /// - Returns: An SPSelfDescribingJson.
+    @objc
+    public convenience init(schema: String, andSelfDescribingJson data: SelfDescribingJson) {
+        self.init(schema: schema, andData: data.dictionary as? NSObject)
+    }
+
+    /// Sets the data field of the self-describing JSON.
+    /// - Parameter data: An NSObject to be nested into the data.
+    @objc
+    public func setData(withObject data: NSObject?) {
+        self.data = data
+    }
+
+    /// Sets the data field of the self-describing JSON.
+    /// - Parameter data: An SPPayload to be nested into the data.
+    @objc
+    public func setData(withPayload data: Payload) {
+        return setData(withObject: data.dictionary as? NSObject)
+    }
+
+    /// Sets the data field of the self-describing JSON.
+    /// - Parameter data: A self-describing JSON to be nested into the data.
+    @objc
+    public func setData(withSelfDescribingJson data: SelfDescribingJson) {
+        return setData(withObject: data.dictionary as? NSObject)
+    }
+}
diff --git a/Snowplow/Snowplow-Prefix.pch b/Sources/Snowplow/Snowplow-Prefix.pch
similarity index 100%
rename from Snowplow/Snowplow-Prefix.pch
rename to Sources/Snowplow/Snowplow-Prefix.pch
diff --git a/Sources/Snowplow/Snowplow.swift b/Sources/Snowplow/Snowplow.swift
new file mode 100644
index 000000000..0613f09f2
--- /dev/null
+++ b/Sources/Snowplow/Snowplow.swift
@@ -0,0 +1,317 @@
+//
+//  SPSnowplow.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+#if os(iOS) || os(macOS)
+import WebKit
+#endif
+
+/// Entry point to instance a new Snowplow tracker.
+@objc(SPSnowplow)
+public class Snowplow: NSObject {
+    private static var serviceProviderInstances: [String : ServiceProvider] = [:]
+    private static var configurationProvider: ConfigurationProvider?
+    private static var defaultServiceProvider: ServiceProvider?
+    
+    /// Remote Configuration
+
+    /// Setup a single or a set of tracker instances which will be used inside the app to track events.
+    /// The app can run multiple tracker instances which will be identified by string `namespaces`.
+    /// The trackers configuration is automatically download from the endpoint indicated in the `RemoteConfiguration`
+    /// passed as argument. For more details see `RemoteConfiguration`.
+    ///
+    /// The method is asynchronous and you can receive the list of the created trackers in the callbacks once the trackers are created.
+    /// The callback can be called multiple times in case a cached configuration is ready and later a fetched configuration is available.
+    /// You can also pass as argument a default configuration in case there isn't a cached configuration and it's not able to download
+    /// a new one. The downloaded configuration updates the cached one only if the configuration version is greater than the cached one.
+    /// Otherwise the cached one is kept and the callback is not called.
+    ///
+    /// IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
+    /// Those events are attached to the namespace.
+    /// If the tracker is removed or the app relaunched with a different namespace, those events can't
+    /// be sent to the collector and they remain in a zombie state inside the EventStore.
+    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
+    ///
+    /// - Parameters:
+    ///   - remoteConfiguration: The remote configuration used to indicate where to download the configuration from.
+    ///   - defaultBundles: The default configuration passed by default in case there isn't a cached version and it's able to download a new one.
+    ///   - onSuccess: The callback called when a configuration (cached or downloaded) is set.
+    ///                  It passes two arguments: list of the namespaces associated to the created trackers
+    ///                  and the state of the configuration – whether it was retrieved from cache or fetched over the network.
+    @objc
+    public class func setup(remoteConfiguration: RemoteConfiguration, defaultConfiguration defaultBundles: [ConfigurationBundle]?, onSuccess: @escaping (_ namespaces: [String]?, _ configurationState: ConfigurationState) -> Void) {
+        configurationProvider = ConfigurationProvider(remoteConfiguration: remoteConfiguration, defaultConfigurationBundles: defaultBundles)
+        configurationProvider?.retrieveConfigurationOnlyRemote(false, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            let bundles = fetchedConfigurationBundle.configurationBundle
+            let namespaces = createTrackers(configurationBundles: bundles)
+            onSuccess(namespaces, configurationState)
+        })
+    }
+
+    /// Reconfigure, create or delete the trackers based on the configuration downloaded remotely.
+    /// The trackers configuration is automatically download from the endpoint indicated in the `RemoteConfiguration`
+    /// previously used to setup the trackers.
+    ///
+    /// The method is asynchronous and you can receive the list of the created trackers in the callbacks once the trackers are created.
+    /// The downloaded configuration updates the cached one only if the configuration version is greater than the cached one.
+    /// Otherwise the cached one is kept and the callback is not called.
+    ///
+    /// IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
+    /// Those events are attached to the namespace.
+    /// If the tracker is removed or the app relaunched with a different namespace, those events can't
+    /// be sent to the collector and they remain in a zombie state inside the EventStore.
+    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
+    ///
+    /// - Parameter onSuccess: The callback called when a configuration (cached or downloaded) is set It passes the list of the namespaces associated
+    ///                  to the created trackers.
+    @objc
+    public class func refresh(onSuccess: @escaping (_ namespaces: [String]?, _ configurationState: ConfigurationState) -> Void) {
+        configurationProvider?.retrieveConfigurationOnlyRemote(true, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            let bundles = fetchedConfigurationBundle.configurationBundle
+            let namespaces = createTrackers(configurationBundles: bundles)
+            onSuccess(namespaces, configurationState)
+        })
+    }
+
+    /// Standard Configuration
+
+    /// Create a new tracker instance which will be used inside the app to track events.
+    /// The app can run multiple tracker instances which will be identified by string `namespaces`.
+    /// The tracker will be configured with default setting and only the collector endpoint URL need
+    /// to be passed for the configuration.
+    /// For the default configuration of the tracker see `TrackerConfiguration(String)`.
+    ///
+    /// To configure tracker with more details see `createTracker(Context, String, NetworkConfiguration, Configuration...)`
+    /// To use the tracker as singleton see `getDefaultTracker()`
+    ///
+    /// IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
+    /// Those events are attached to the namespace.
+    /// If the tracker is removed or the app relaunched with a different namespace, those events can't
+    /// be sent to the collector and they remain in a zombie state inside the EventStore.
+    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
+    ///
+    /// - Parameters:
+    ///   - namespace: The namespace used to identify the current tracker among the possible
+    ///                  multiple tracker instances.
+    ///   - endpoint: The URL of the collector.
+    ///   - method: The method for the requests to the collector (GET or POST).
+    /// - Returns: The tracker instance created.
+    @objc
+    public class func createTracker(namespace: String, endpoint: String, method: HttpMethodOptions) -> TrackerController? {
+        let networkConfiguration = NetworkConfiguration(endpoint: endpoint, method: method)
+        return createTracker(namespace: namespace, network: networkConfiguration, configurations: [])
+    }
+
+    /// Create a new tracker instance which will be used inside the app to track events.
+    /// The app can run multiple tracker instances which will be identified by string `namespaces`.
+    /// The tracker will be configured with default setting and only the collector endpoint URL need
+    /// to be passed for the configuration.
+    /// For the default configuration of the tracker see `TrackerConfiguration(String)`.
+    ///
+    /// To configure tracker with more details see `createTracker(Context, String, NetworkConfiguration, Configuration...)`
+    /// To use the tracker as singleton see `getDefaultTracker()`
+    ///
+    /// IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
+    /// Those events are attached to the namespace.
+    /// If the tracker is removed or the app relaunched with a different namespace, those events can't
+    /// be sent to the collector and they remain in a zombie state inside the EventStore.
+    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
+    ///
+    /// - Parameters:
+    ///   - namespace: The namespace used to identify the current tracker among the possible
+    ///                  multiple tracker instances.
+    ///   - networkConfiguration: The NetworkConfiguration object with settings for the communication with the
+    ///                collector.
+    /// - Returns: The tracker instance created.
+    @objc
+    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration) -> TrackerController? {
+        return createTracker(namespace: namespace, network: networkConfiguration, configurations: [])
+    }
+
+    /// Create a new tracker instance which will be used inside the app to track events.
+    /// The app can run multiple tracker instances which will be identified by string `namespaces`.
+    /// The tracker will be configured with default setting and only the collector endpoint URL need
+    /// to be passed for the configuration.
+    /// For the default configuration of the tracker see `TrackerConfiguration(String)`.
+    ///
+    /// To configure tracker with more details see `createTracker(Context, String, NetworkConfiguration, Configuration...)`
+    /// To use the tracker as singleton see `getDefaultTracker()`
+    ///
+    /// IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
+    /// Those events are attached to the namespace.
+    /// If the tracker is removed or the app relaunched with a different namespace, those events can't
+    /// be sent to the collector and they remain in a zombie state inside the EventStore.
+    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
+    ///
+    /// - Parameters:
+    ///   - namespace: The namespace used to identify the current tracker among the possible
+    ///                  multiple tracker instances.
+    ///   - networkConfiguration: The NetworkConfiguration object with settings for the communication with the
+    ///                collector.
+    ///   - configurations: All the configuration objects with the details about the fine tuning of
+    ///                       the tracker.
+    /// - Returns: The tracker instance created.
+    @objc
+    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [Configuration]) -> TrackerController? {
+        if let serviceProvider = serviceProviderInstances[namespace] {
+            serviceProvider.reset(withConfigurations: configurations + [networkConfiguration])
+            return serviceProvider.trackerController
+        } else {
+            let serviceProvider = ServiceProvider(namespace: namespace, network: networkConfiguration, configurations: configurations)
+            let _ = registerInstance(serviceProvider)
+            return serviceProvider.trackerController
+        }
+    }
+
+    /// The default tracker instance is the first created in the app, but that can be overridden programmatically
+    /// calling `setTrackerAsDefault(TrackerController)`.
+    @objc
+    public class func defaultTracker() -> TrackerController? {
+        return defaultServiceProvider?.trackerController
+    }
+
+    /// Using the namespace identifier is possible to get the trackerController if already instanced.
+    ///
+    /// - Parameter namespace: The namespace that identifies the tracker.
+    /// - Returns: The tracker if it exist with that namespace.
+    @objc
+    public class func tracker(namespace: String) -> TrackerController? {
+        return serviceProviderInstances[namespace]?.trackerController
+    }
+
+    /// Set the passed tracker as default tracker if it's registered as an active tracker in the app.
+    /// If the passed instance is of a tracker which is already removed (see `removeTracker`) then it can't become the new default tracker
+    /// and the operation fails.
+    ///
+    /// - Parameter trackerController: The new default tracker.
+    /// - Returns: Whether the tracker passed is registered among the active trackers of the app.
+    @objc
+    public class func setAsDefault(tracker trackerController: TrackerController?) -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        if let namespace = trackerController?.namespace,
+           let serviceProvider = serviceProviderInstances[namespace] {
+            defaultServiceProvider = serviceProvider
+            return true
+        }
+        return false
+    }
+
+    /// A tracker can be removed from the active trackers of the app.
+    /// Once it has been removed it can't be added again or set as default.
+    /// The unique way to resume a removed tracker is creating a new tracker with same namespace and
+    /// same configurations.
+    /// The removed tracker is always stopped.
+    ///
+    /// - Parameter trackerController: The tracker controller to remove.
+    /// - Returns: Whether it has been able to remove it.
+    @objc
+    public class func remove(tracker trackerController: TrackerController?) -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        if let namespace = trackerController?.namespace,
+           let serviceProvider = (serviceProviderInstances)[namespace] {
+            serviceProvider.shutdown()
+            serviceProviderInstances.removeValue(forKey: namespace)
+            if serviceProvider == defaultServiceProvider {
+                defaultServiceProvider = nil
+            }
+            return true
+        }
+        return false
+    }
+
+    /// Remove all the trackers.
+    /// The removed tracker is always stopped.
+    /// See `removeTracker(TrackerController)`
+    @objc
+    public class func removeAllTrackers() {
+        objc_sync_enter(self)
+        defaultServiceProvider = nil
+        let serviceProviders = serviceProviderInstances.values
+        serviceProviderInstances.removeAll()
+        for sp in serviceProviders {
+            sp.shutdown()
+        }
+        objc_sync_exit(self)
+    }
+
+    /// - Returns: Set of namespace of the active trackers in the app.
+    @objc
+    class public var instancedTrackerNamespaces: [String] {
+        return Array(serviceProviderInstances.keys)
+    }
+
+    #if os(iOS) || os(macOS)
+
+    /// Subscribe to events tracked in a Web view using the Snowplow WebView tracker JavaScript library.
+    /// - Parameter webViewConfiguration: Configuration of the Web view to subscribe to events from
+    @objc
+    public class func subscribeToWebViewEvents(with webViewConfiguration: WKWebViewConfiguration) {
+        let messageHandler = WebViewMessageHandler()
+
+        webViewConfiguration.userContentController.add(messageHandler, name: "snowplow")
+    }
+
+    #endif
+
+    // MARK: - Private methods
+
+    private class func registerInstance(_ serviceProvider: ServiceProvider) -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        let namespace = serviceProvider.namespace
+        let isOverriding = serviceProviderInstances[namespace] != nil
+        serviceProviderInstances[namespace] = serviceProvider
+        if defaultServiceProvider == nil {
+            defaultServiceProvider = serviceProvider
+        }
+        return isOverriding
+    }
+
+    private class func createTrackers(configurationBundles bundles: [ConfigurationBundle]) -> [String] {
+        var namespaces: [String]? = []
+        for bundle in bundles {
+            objc_sync_enter(self)
+            if let networkConfiguration = bundle.networkConfiguration {
+                if let _ = createTracker(
+                    namespace: bundle.namespace,
+                    network: networkConfiguration,
+                    configurations: bundle.configurations) {
+                    namespaces?.append(bundle.namespace)
+                }
+            } else {
+                // remove tracker if it exists
+                if let tracker = tracker(namespace: bundle.namespace) {
+                    let _ = remove(tracker: tracker)
+                }
+            }
+            objc_sync_exit(self)
+        }
+        return namespaces ?? []
+    }
+}
diff --git a/Sources/Snowplow/Tracker/DevicePlatform.swift b/Sources/Snowplow/Tracker/DevicePlatform.swift
new file mode 100644
index 000000000..2db8a3817
--- /dev/null
+++ b/Sources/Snowplow/Tracker/DevicePlatform.swift
@@ -0,0 +1,62 @@
+//
+//  SPDevicePlatform.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPDevicePlatform)
+public enum DevicePlatform : Int {
+    case web = 0
+    case mobile
+    case desktop
+    case serverSideApp
+    case general
+    case connectedTV
+    case gameConsole
+    case internetOfThings
+}
+
+func devicePlatformToString(_ devicePlatform: DevicePlatform) -> String? {
+    switch devicePlatform {
+    case .web:
+        return "web"
+    case .mobile:
+        return "mob"
+    case .desktop:
+        return "pc"
+    case .serverSideApp:
+        return "srv"
+    case .general:
+        return "app"
+    case .connectedTV:
+        return "tv"
+    case .gameConsole:
+        return "cnsl"
+    case .internetOfThings:
+        return "iot"
+    }
+}
+
+func stringToDevicePlatform(_ devicePlatformString: String) -> DevicePlatform? {
+    if let index = ["web", "mob", "pc", "srv", "app", "tv", "cnsl", "iot"].firstIndex(of: devicePlatformString) {
+        return DevicePlatform(rawValue: index)
+    }
+    return nil
+}
diff --git a/Sources/Snowplow/Tracker/InspectableEvent.swift b/Sources/Snowplow/Tracker/InspectableEvent.swift
new file mode 100644
index 000000000..fd31b0fb4
--- /dev/null
+++ b/Sources/Snowplow/Tracker/InspectableEvent.swift
@@ -0,0 +1,44 @@
+//
+//  InspectableEvent.swift
+//  Snowplow
+//
+//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// The inspectable properties of the event used to generate contexts.
+@objc(SPInspectableEvent)
+public protocol InspectableEvent {
+    /// The schema of the event
+    @objc
+    var schema: String? { get }
+    /// The name of the event
+    @objc
+    var eventName: String? { get }
+    /// The payload of the event
+    @objc
+    var payload: [String : NSObject] { get }
+    /// The tracker state at the time the event was sent.
+    @objc
+    var state: TrackerStateSnapshot { get }
+    /// Add payload values to the event.
+    /// @param payload Map of values to add to the event payload.
+    /// @return Whether or not the values have been successfully added to the event payload.
+    @objc
+    func addPayloadValues(_ payload: [String : NSObject]) -> Bool
+}
diff --git a/Sources/Snowplow/Tracker/LogLevel.swift b/Sources/Snowplow/Tracker/LogLevel.swift
new file mode 100644
index 000000000..61e3e03b0
--- /dev/null
+++ b/Sources/Snowplow/Tracker/LogLevel.swift
@@ -0,0 +1,30 @@
+//
+//  LogLevel.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPLogLevel)
+public enum LogLevel : Int {
+    case off = 0
+    case error
+    case debug
+    case verbose
+}
diff --git a/Snowplow iOSTests/Utils/SPMockEventStore.h b/Sources/Snowplow/Tracker/LoggerDelegate.swift
similarity index 68%
rename from Snowplow iOSTests/Utils/SPMockEventStore.h
rename to Sources/Snowplow/Tracker/LoggerDelegate.swift
index 6488a511b..438ea6d9d 100644
--- a/Snowplow iOSTests/Utils/SPMockEventStore.h	
+++ b/Sources/Snowplow/Tracker/LoggerDelegate.swift
@@ -1,5 +1,5 @@
 //
-//  SPMockEventStore.h
+//  LoggerDelegate.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,17 +19,15 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPEventStore.h"
-#import "SPPayload.h"
+import Foundation
 
-NS_ASSUME_NONNULL_BEGIN
-
-@interface SPMockEventStore : NSObject <SPEventStore>
-
-@property (atomic) NSMutableDictionary<NSNumber *, SPPayload *> *db;
-@property (atomic) long lastInsertedRow;
-
-@end
-
-NS_ASSUME_NONNULL_END
+/// Logger delegate to implement in oder to receive logs from the tracker.
+@objc(SPLoggerDelegate)
+public protocol LoggerDelegate: NSObjectProtocol {
+    @objc
+    func error(_ tag: String, message: String)
+    @objc
+    func debug(_ tag: String, message: String)
+    @objc
+    func verbose(_ tag: String, message: String)
+}
diff --git a/Sources/Snowplow/Tracker/SessionState.swift b/Sources/Snowplow/Tracker/SessionState.swift
new file mode 100644
index 000000000..6b8197ceb
--- /dev/null
+++ b/Sources/Snowplow/Tracker/SessionState.swift
@@ -0,0 +1,107 @@
+//
+//  SPSessionState.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPSessionState)
+public class SessionState: NSObject, State {
+    @objc
+    public private(set) var firstEventId: String?
+    @objc
+    public private(set) var firstEventTimestamp: String?
+    @objc
+    public private(set) var previousSessionId: String?
+    @objc
+    public private(set) var sessionId: String
+    @objc
+    public private(set) var sessionIndex = 0
+    @objc
+    public private(set) var storage: String
+    @objc
+    public private(set) var userId: String
+
+    var sessionContext: [String : NSObject] {
+        return sessionDictionary
+    }
+    private var sessionDictionary: [String : NSObject] = [:]
+
+    class func buildSessionDictionary(withFirstEventId firstEventId: String?, firstEventTimestamp: String?, currentSessionId: String, previousSessionId: String?, sessionIndex: Int, userId: String, storage: String) -> [String : NSObject] {
+        var dictionary: [String : NSObject] = [:]
+        dictionary[kSPSessionPreviousId] = previousSessionId as? NSObject ?? NSNull()
+        dictionary[kSPSessionId] = currentSessionId as NSObject
+        dictionary[kSPSessionFirstEventId] = firstEventId as? NSObject
+        dictionary[kSPSessionFirstEventTimestamp] = firstEventTimestamp as? NSObject
+        dictionary[kSPSessionIndex] = NSNumber(value: sessionIndex)
+        dictionary[kSPSessionStorage] = storage as NSObject
+        dictionary[kSPSessionUserId] = userId as NSObject
+        return dictionary
+    }
+
+    init(firstEventId: String?, firstEventTimestamp: String?, currentSessionId: String, previousSessionId: String?, sessionIndex: Int, userId: String, storage: String) {
+        self.firstEventId = firstEventId
+        self.firstEventTimestamp = firstEventTimestamp
+        sessionId = currentSessionId
+        self.previousSessionId = previousSessionId
+        self.sessionIndex = sessionIndex
+        self.userId = userId
+        self.storage = storage
+
+        sessionDictionary = SessionState.buildSessionDictionary(
+            withFirstEventId: firstEventId,
+            firstEventTimestamp: firstEventTimestamp,
+            currentSessionId: currentSessionId,
+            previousSessionId: previousSessionId,
+            sessionIndex: sessionIndex,
+            userId: userId,
+            storage: storage)
+    }
+
+    init?(storedState: [String : NSObject]) {
+        guard let sessionId = storedState[kSPSessionId] as? String,
+              let sessionIndex = storedState[kSPSessionIndex] as? Int,
+              let userId = storedState[kSPSessionUserId] as? String else {
+            return nil
+        }
+        
+        self.sessionId = sessionId
+        self.sessionIndex = sessionIndex
+        self.userId = userId
+
+        previousSessionId = storedState[kSPSessionPreviousId] as? String
+
+        // The FirstEventId should be stored in legacy persisted sessions even
+        // if it wasn't used. Anyway we provide a default value in order to be
+        // defensive and exclude any possible issue with a missing value.
+        firstEventId = storedState[kSPSessionFirstEventId] as? String ?? "00000000-0000-0000-0000-000000000000"
+        firstEventTimestamp = storedState[kSPSessionFirstEventTimestamp] as? String
+
+        storage = storedState[kSPSessionStorage] as? String ?? "LOCAL_STORAGE"
+
+        sessionDictionary = SessionState.buildSessionDictionary(
+            withFirstEventId: firstEventId,
+            firstEventTimestamp: firstEventTimestamp,
+            currentSessionId: sessionId,
+            previousSessionId: previousSessionId,
+            sessionIndex: sessionIndex,
+            userId: userId,
+            storage: storage)
+    }
+}
diff --git a/Snowplow/Internal/Tracker/SPState.h b/Sources/Snowplow/Tracker/State.swift
similarity index 90%
rename from Snowplow/Internal/Tracker/SPState.h
rename to Sources/Snowplow/Tracker/State.swift
index 6d81f5835..bc921cba7 100644
--- a/Snowplow/Internal/Tracker/SPState.h
+++ b/Sources/Snowplow/Tracker/State.swift
@@ -1,5 +1,5 @@
 //
-//  SPState.h
+//  State.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,7 +19,8 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
+import Foundation
 
-@protocol SPState <NSObject>
-@end
+@objc(SPState)
+public protocol State {
+}
diff --git a/Sources/Snowplow/Tracker/StateMachineProtocol.swift b/Sources/Snowplow/Tracker/StateMachineProtocol.swift
new file mode 100644
index 000000000..75f854594
--- /dev/null
+++ b/Sources/Snowplow/Tracker/StateMachineProtocol.swift
@@ -0,0 +1,40 @@
+//
+//  SPStateMachineProtocol.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPStateMachineProtocol)
+public protocol StateMachineProtocol {
+    @objc
+    var identifier: String { get }
+    @objc
+    var subscribedEventSchemasForTransitions: [String] { get }
+    @objc
+    var subscribedEventSchemasForEntitiesGeneration: [String] { get }
+    @objc
+    var subscribedEventSchemasForPayloadUpdating: [String] { get }
+    @objc
+    func transition(from event: Event, state: State?) -> State?
+    @objc
+    func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]?
+    @objc
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]?
+}
diff --git a/Snowplow/Internal/SPController.m b/Sources/Snowplow/Tracker/TrackerStateSnapshot.swift
similarity index 66%
rename from Snowplow/Internal/SPController.m
rename to Sources/Snowplow/Tracker/TrackerStateSnapshot.swift
index a20d606d9..1813af2e2 100644
--- a/Snowplow/Internal/SPController.m
+++ b/Sources/Snowplow/Tracker/TrackerStateSnapshot.swift
@@ -1,5 +1,5 @@
 //
-//  SPController.m
+//  TrackerStateSnapshot.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,22 +19,14 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPController.h"
+import Foundation
 
-
-@interface SPController ()
-
-@property (nonatomic, nonnull) id<SPServiceProviderProtocol> serviceProvider;
-
-@end
-
-@implementation SPController
-
-- (instancetype)initWithServiceProvider:(id<SPServiceProviderProtocol>)serviceProvider {
-    if (self = [super init]) {
-        self.serviceProvider = serviceProvider;
-    }
-    return self;
+@objc(SPTrackerStateSnapshot)
+public protocol TrackerStateSnapshot {
+    /// Get a computed state with a specific state identifier
+    @objc
+    func state(withIdentifier stateIdentifier: String) -> State?
+    /// Get a computed state with a specific state machine
+    @objc
+    func state(withStateMachine stateMachine: StateMachineProtocol) -> State?
 }
-
-@end
diff --git a/Snowplow/Internal/Subject/SPSubjectController.h b/Sources/Snowplow/Utils/GDPRProcessingBasis.swift
similarity index 74%
rename from Snowplow/Internal/Subject/SPSubjectController.h
rename to Sources/Snowplow/Utils/GDPRProcessingBasis.swift
index 37cff6502..b90c325b3 100644
--- a/Snowplow/Internal/Subject/SPSubjectController.h
+++ b/Sources/Snowplow/Utils/GDPRProcessingBasis.swift
@@ -1,5 +1,5 @@
 //
-//  SPSubjectController.h
+//  GDPRProcessingBasis.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,14 +19,14 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPSubjectConfiguration.h"
+import Foundation
 
-NS_ASSUME_NONNULL_BEGIN
-
-NS_SWIFT_NAME(SubjectController)
-@protocol SPSubjectController <SPSubjectConfigurationProtocol>
-
-@end
-
-NS_ASSUME_NONNULL_END
+@objc(SPGDPRProcessingBasis)
+public enum GDPRProcessingBasis : Int {
+    case consent = 0
+    case contract = 1
+    case legalObligation = 2
+    case vitalInterest = 3
+    case publicTask = 4
+    case legitimateInterests = 5
+}
diff --git a/Snowplow/Internal/Events/SPForeground.m b/Sources/Snowplow/Utils/SPSize.swift
similarity index 55%
rename from Snowplow/Internal/Events/SPForeground.m
rename to Sources/Snowplow/Utils/SPSize.swift
index 7c35b7e6c..d85972303 100644
--- a/Snowplow/Internal/Events/SPForeground.m
+++ b/Sources/Snowplow/Utils/SPSize.swift
@@ -1,5 +1,5 @@
 //
-//  SPForeground.m
+//  SPSubjectConfiguration.h
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,37 +19,30 @@
 //  License: Apache License Version 2.0
 //
 
-#import "SPForeground.h"
+import Foundation
 
-#import "SPTrackerConstants.h"
-#import "SPUtilities.h"
-#import "SPSelfDescribingJson.h"
+@objc
+public class SPSize: NSObject, NSCoding {
+    @objc
+    public private(set) var width = 0
+    @objc
+    public private(set) var height = 0
 
-@interface SPForeground ()
-
-@property (readwrite) NSNumber *index;
-
-@end
-
-@implementation SPForeground
-
-- (instancetype)initWithIndex:(NSNumber *)index {
-    if (self = [super init]) {
-        _index = index;
+    @objc
+    public init(width: Int, height: Int) {
+        super.init()
+        self.width = width
+        self.height = height
     }
-    return self;
-}
 
-// --- Public Methods
-
-- (NSString *)schema {
-    return kSPForegroundSchema;
-}
+    public func encode(with coder: NSCoder) {
+        coder.encode(width, forKey: "width")
+        coder.encode(height, forKey: "height")
+    }
 
-- (NSDictionary<NSString *, NSObject *> *)payload {
-    NSMutableDictionary *payload = [NSMutableDictionary dictionary];
-    [payload setValue:_index forKey:kSPForegroundIndex];
-    return payload;
+    required public init?(coder: NSCoder) {
+        super.init()
+        width = coder.decodeInteger(forKey: "width")
+        height = coder.decodeInteger(forKey: "height")
+    }
 }
-
-@end
diff --git a/Sources/Snowplow/ios.modulemap b/Sources/Snowplow/ios.modulemap
new file mode 100644
index 000000000..bd8bacd98
--- /dev/null
+++ b/Sources/Snowplow/ios.modulemap
@@ -0,0 +1,3 @@
+framework module SnowplowTracker {
+    export *
+}
diff --git a/Sources/Snowplow/watchos.modulemap b/Sources/Snowplow/watchos.modulemap
new file mode 100644
index 000000000..bd8bacd98
--- /dev/null
+++ b/Sources/Snowplow/watchos.modulemap
@@ -0,0 +1,3 @@
+framework module SnowplowTracker {
+    export *
+}
diff --git a/TestServiceProvider.m b/TestServiceProvider.m
deleted file mode 100644
index b4b78d908..000000000
--- a/TestServiceProvider.m
+++ /dev/null
@@ -1,108 +0,0 @@
-//
-//  TestServiceProvider.m
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#import <XCTest/XCTest.h>
-#import "SPSnowplow.h"
-#import "SPNetworkConfiguration.h"
-#import "SPTrackerConfiguration.h"
-#import "SPSession.h"
-#import "SPMockEventStore.h"
-#import "SPDataPersistence.h"
-#import "SPMockNetworkConnection.h"
-#import "SPLogger.h"
-#import "SPServiceProvider.h"
-#import "SPEmitterControllerImpl.h"
-#import "SPEmitterConfigurationUpdate.h"
-#import "SPTrackerController.h"
-#import "SPMockLoggerDelegate.h"
-
-
-@interface TestServiceProvider : XCTestCase
-
-@end
-
-@implementation TestServiceProvider
-
-- (void)setUp {
-    [super setUp];
-    [SPLogger setLogLevel:SPLogLevelVerbose];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testUpdatingConfigurationRetainsPausedEmitter {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:200];
-    SPEmitterConfiguration *emitterConfig = [[SPEmitterConfiguration alloc] init];
-    emitterConfig.eventStore = [SPMockEventStore new];
-    emitterConfig.bufferOption = SPBufferOptionSingle;
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"" method:SPHttpMethodPost];
-    networkConfig.networkConnection = networkConnection;
-    SPTrackerConfiguration *trackerConfig = [[SPTrackerConfiguration new] appId:@"appid"];
-    trackerConfig.installAutotracking = false;
-    trackerConfig.screenViewAutotracking = false;
-    trackerConfig.lifecycleAutotracking = false;
-    SPServiceProvider *serviceProvider = [[SPServiceProvider alloc] initWithNamespace:@"ns" network:networkConfig configurations:@[emitterConfig, trackerConfig]];
-    XCTAssertNotNil(serviceProvider);
-
-    // pause emitter
-    [[serviceProvider emitterController] pause];
-
-    // refresh configuration
-    [serviceProvider resetWithConfigurations:@[[[SPEmitterConfigurationUpdate alloc] init]]];
-
-    // track event and check that emitter is paused
-    [[serviceProvider trackerController] track:[[SPStructured alloc] initWithCategory:@"cat" action:@"act"]];
-    [NSThread sleepForTimeInterval:3];
-    XCTAssertEqual(1, [[serviceProvider emitter] getDbCount]);
-    XCTAssertEqual(0, [networkConnection sendingCount]);
-
-    // resume emitting
-    [[serviceProvider emitterController] resume];
-    [NSThread sleepForTimeInterval:3];
-    XCTAssertEqual(1, [networkConnection sendingCount]);
-    XCTAssertEqual(0, [[serviceProvider emitter] getDbCount]);
-}
-
-- (void)testLogsErrorWhenAccessingShutDownTracker {
-    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:200];
-    SPEmitterConfiguration *emitterConfig = [[SPEmitterConfiguration alloc] init];
-    emitterConfig.eventStore = [SPMockEventStore new];
-    emitterConfig.bufferOption = SPBufferOptionSingle;
-    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"" method:SPHttpMethodPost];
-    networkConfig.networkConnection = networkConnection;
-    SPServiceProvider *serviceProvider = [[SPServiceProvider alloc] initWithNamespace:@"ns" network:networkConfig configurations:@[emitterConfig]];
-    XCTAssertNotNil(serviceProvider);
-    
-    // listen for the error log
-    id<SPTrackerController> trackerController = [serviceProvider trackerController];
-    SPMockLoggerDelegate *logger = [SPMockLoggerDelegate new];
-    [trackerController setLoggerDelegate:logger];
-    
-    // shutting down and accessing the tracker should log the error
-    [serviceProvider shutdown];
-    [trackerController namespace];
-    XCTAssertEqual(1, [[logger errorLogs] count]);
-    XCTAssertTrue([[[logger errorLogs] objectAtIndex:0] containsString:@"Recreating tracker instance"]);
-}
-
-@end
diff --git a/Tests/Configurations/TestEmitterConfiguration.swift b/Tests/Configurations/TestEmitterConfiguration.swift
new file mode 100644
index 000000000..257c7584d
--- /dev/null
+++ b/Tests/Configurations/TestEmitterConfiguration.swift
@@ -0,0 +1,71 @@
+//
+//  TestEmitterConfiguration.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini, Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestEmitterConfiguration: XCTestCase {
+    override func setUp() {
+        super.setUp()
+        Logger.logLevel = .verbose
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testPauseEmitter() {
+        let networkConnection = MockNetworkConnection(requestOption: .post, statusCode: 200)
+        let emitterConfig = EmitterConfiguration()
+        emitterConfig.eventStore = MockEventStore()
+        emitterConfig.bufferOption = .single
+        let networkConfig = NetworkConfiguration(networkConnection: networkConnection)
+
+        let trackerConfig = TrackerConfiguration(appId: "appid")
+        trackerConfig.installAutotracking = false
+        trackerConfig.screenViewAutotracking = false
+        trackerConfig.lifecycleAutotracking = false
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig, emitterConfig])
+        XCTAssertNotNil(tracker)
+
+        tracker?.emitter?.pause()
+        _ = tracker?.track(Structured(category: "cat", action: "act"))
+        Thread.sleep(forTimeInterval: 3)
+        XCTAssertEqual(1, tracker?.emitter?.dbCount)
+        XCTAssertEqual(0, networkConnection.previousResults.count)
+
+        tracker?.emitter?.resume()
+        Thread.sleep(forTimeInterval: 3)
+        XCTAssertEqual(1, networkConnection.previousResults.count)
+        XCTAssertEqual(0, tracker?.emitter?.dbCount)
+    }
+
+    func testActivatesServerAnonymisationInEmitter() {
+        let emitterConfig = EmitterConfiguration()
+        emitterConfig.serverAnonymisation = true
+
+        let networkConfig = NetworkConfiguration(endpoint: "", method: .post)
+
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [emitterConfig])
+
+        XCTAssertTrue(tracker?.emitter?.serverAnonymisation ?? false)
+    }
+}
diff --git a/Tests/Configurations/TestMultipleInstances.swift b/Tests/Configurations/TestMultipleInstances.swift
new file mode 100644
index 000000000..98dcdf583
--- /dev/null
+++ b/Tests/Configurations/TestMultipleInstances.swift
@@ -0,0 +1,98 @@
+//
+//  TestMultipleInstances.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestMultipleInstances: XCTestCase {
+    override func setUp() {
+        Snowplow.removeAllTrackers()
+    }
+
+    override func tearDown() {
+        Snowplow.removeAllTrackers()
+    }
+
+    func testSingleInstanceIsReconfigurable() {
+        let t1 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        XCTAssertEqual(t1?.network?.endpoint, "https://snowplowanalytics.fake/com.snowplowanalytics.snowplow/tp2")
+        let t2 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake2"))
+        XCTAssertEqual(t2?.network?.endpoint, "https://snowplowanalytics.fake2/com.snowplowanalytics.snowplow/tp2")
+        XCTAssertEqual(["t1"], Snowplow.instancedTrackerNamespaces)
+        XCTAssertTrue(t1 === t2)
+    }
+
+    func testMultipleInstances() {
+        let t1 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        XCTAssertEqual(t1?.network?.endpoint, "https://snowplowanalytics.fake/com.snowplowanalytics.snowplow/tp2")
+        let t2 = Snowplow.createTracker(namespace: "t2", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake2"))
+        XCTAssertEqual(t2?.network?.endpoint, "https://snowplowanalytics.fake2/com.snowplowanalytics.snowplow/tp2")
+        XCTAssertFalse(t1 === t2)
+        let expectedNamespaces = Set<String>(["t1", "t2"])
+        XCTAssertEqual(expectedNamespaces, Set<String>(Snowplow.instancedTrackerNamespaces))
+    }
+
+    func testDefaultTracker() {
+        let t1 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        _ = Snowplow.createTracker(namespace: "t2", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake2"))
+        let td = Snowplow.defaultTracker()
+        XCTAssertEqual(t1?.namespace, td?.namespace)
+    }
+
+    func testUpdateDefaultTracker() {
+        _ = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        let t2 = Snowplow.createTracker(namespace: "t2", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake2"))
+        _ = Snowplow.setAsDefault(tracker: t2)
+        let td = Snowplow.defaultTracker()
+        XCTAssertEqual(t2?.namespace, td?.namespace)
+    }
+
+    func testRemoveTracker() {
+        let t1 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        let t2 = Snowplow.createTracker(namespace: "t2", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake2"))
+        _ = Snowplow.remove(tracker: t1)
+        XCTAssertNotNil(t2)
+        XCTAssertEqual(["t2"], Snowplow.instancedTrackerNamespaces)
+    }
+
+    func testRecreateTrackerWhichWasRemovedWithSameNamespace() {
+        let t1 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        _ = Snowplow.remove(tracker: t1)
+        let t2 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake2"))
+        XCTAssertFalse(t1 === t2)
+        XCTAssertEqual(["t1"], Snowplow.instancedTrackerNamespaces)
+    }
+
+    func testRemoveDefaultTracker() {
+        let t1 = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        _ = Snowplow.remove(tracker: t1)
+        let td = Snowplow.defaultTracker()
+        XCTAssertNil(td)
+        XCTAssertEqual([], Snowplow.instancedTrackerNamespaces)
+    }
+
+    func testRemoveAllTrackers() {
+        _ = Snowplow.createTracker(namespace: "t1", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake"))
+        _ = Snowplow.createTracker(namespace: "t2", network: NetworkConfiguration(endpoint: "snowplowanalytics.fake2"))
+        Snowplow.removeAllTrackers()
+        XCTAssertEqual([], Snowplow.instancedTrackerNamespaces)
+    }
+}
diff --git a/Tests/Configurations/TestRemoteConfiguration.swift b/Tests/Configurations/TestRemoteConfiguration.swift
new file mode 100644
index 000000000..11ac82aba
--- /dev/null
+++ b/Tests/Configurations/TestRemoteConfiguration.swift
@@ -0,0 +1,392 @@
+//
+//  TestRemoteConfiguration.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Nocilla
+import XCTest
+@testable import SnowplowTracker
+
+class TestRemoteConfiguration: XCTestCase {
+    func testJSONToConfigurations() {
+        let config = """
+            {"$schema":"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0","configurationVersion":12,"configurationBundle": [\
+                {"namespace": "default1",\
+                "networkConfiguration": {"endpoint":"https://fake.snowplow.io","method":"get"},\
+                "trackerConfiguration": {"applicationContext":false,"screenContext":false,},\
+                "sessionConfiguration": {"backgroundTimeout":60,"foregroundTimeout":60}\
+                },\
+                {"namespace": "default2",\
+                "subjectConfiguration": {"userId":"testUserId"}\
+                }\
+                ]}
+            """
+        guard let jsonData = config.data(using: .utf8) else {
+            return XCTFail()
+        }
+        guard let dictionary = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String : NSObject] else {
+            return XCTFail()
+        }
+        guard let fetchedConfigurationBundle = FetchedConfigurationBundle(dictionary: dictionary) else {
+            return XCTFail()
+        }
+        XCTAssertEqual("http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", fetchedConfigurationBundle.schema)
+        XCTAssertEqual(12, fetchedConfigurationBundle.configurationVersion)
+        XCTAssertEqual(2, fetchedConfigurationBundle.configurationBundle.count)
+
+        // Regular setup
+        var configurationBundle = fetchedConfigurationBundle.configurationBundle[0]
+        XCTAssertEqual("default1", configurationBundle.namespace)
+        XCTAssertNotNil(configurationBundle.networkConfiguration)
+        XCTAssertNotNil(configurationBundle.trackerConfiguration)
+        XCTAssertNotNil(configurationBundle.sessionConfiguration)
+        XCTAssertNil(configurationBundle.subjectConfiguration)
+        let networkConfiguration = configurationBundle.networkConfiguration
+        XCTAssertEqual(.get, networkConfiguration?.method)
+        guard let trackerConfiguration = configurationBundle.trackerConfiguration else { return XCTFail() }
+        XCTAssertFalse(trackerConfiguration.applicationContext)
+        let sessionConfiguration = configurationBundle.sessionConfiguration
+        XCTAssertEqual(60, sessionConfiguration?.backgroundTimeoutInSeconds)
+
+        // Regular setup without NetworkConfiguration
+        configurationBundle = fetchedConfigurationBundle.configurationBundle[1]
+        XCTAssertEqual("default2", configurationBundle.namespace)
+        XCTAssertNil(configurationBundle.networkConfiguration)
+        XCTAssertNotNil(configurationBundle.subjectConfiguration)
+        let subjectConfiguration = configurationBundle.subjectConfiguration
+        XCTAssertEqual("testUserId", subjectConfiguration?.userId)
+    }
+
+    func testDownloadConfiguration() {
+        let endpoint = "https://fake-snowplow.io/config.json"
+
+        LSNocilla.sharedInstance().start()
+        _ = stubRequest("GET", endpoint as NSString)?
+            .andReturn(200)?
+            .withHeaders(
+                [ "Content-Type": "application/json" ]
+            )?
+            .withBody(
+                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}" as NSString)
+        let expectation = XCTestExpectation()
+
+        let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
+        _ = ConfigurationFetcher(remoteSource: remoteConfig, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTAssertNotNil(fetchedConfigurationBundle)
+            XCTAssertEqual("http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", fetchedConfigurationBundle.schema)
+            XCTAssertEqual(.fetched, configurationState)
+            expectation.fulfill()
+        })
+
+        wait(for: [expectation], timeout: 10)
+        LSNocilla.sharedInstance().stop()
+    }
+
+    func testCache() {
+        let bundle = ConfigurationBundle(namespace: "namespace", networkConfiguration: NetworkConfiguration(endpoint: "endpoint"))
+        let expected = FetchedConfigurationBundle(schema: "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", configurationVersion: 12)
+        expected.configurationBundle = [bundle]
+
+        let remoteConfig = RemoteConfiguration(endpoint: "http://example.com", method: .get)
+
+        var cache = ConfigurationCache(remoteConfiguration: remoteConfig)
+        cache.clear()
+        cache.write(expected)
+
+        Thread.sleep(forTimeInterval: 5) // wait the config is written on cache.
+
+        cache = ConfigurationCache(remoteConfiguration: remoteConfig)
+        let config = cache.read()
+
+        XCTAssertEqual(expected.configurationVersion, config?.configurationVersion)
+        XCTAssertEqual(expected.schema, config?.schema)
+        XCTAssertEqual(expected.configurationBundle.count, config?.configurationBundle.count)
+        let expectedBundle = expected.configurationBundle[0]
+        let configBundle = config?.configurationBundle[0]
+        XCTAssertEqual(expectedBundle.networkConfiguration?.endpoint, configBundle?.networkConfiguration?.endpoint)
+        XCTAssertNil(configBundle?.trackerConfiguration)
+    }
+
+    func testProvider_notDownloading_fails() {
+        // prepare test
+        let endpoint = "https://fake-snowplow.io/config.json"
+        let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
+        let cache = ConfigurationCache(remoteConfiguration: remoteConfig)
+        cache.clear()
+        LSNocilla.sharedInstance().start()
+        _ = stubRequest("GET", endpoint as NSString).andReturn(404)
+        // test
+        let expectation = XCTestExpectation()
+        let provider = ConfigurationProvider(remoteConfiguration: remoteConfig)
+        provider.retrieveConfigurationOnlyRemote(false, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTFail()
+        })
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(XCTWaiter.Result.timedOut, result)
+
+        // close test
+        LSNocilla.sharedInstance().stop()
+    }
+
+    func testProvider_downloadOfWrongSchema_fails() {
+        // prepare test
+        let endpoint = "https://fake-snowplow.io/config.json"
+        let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
+        let cache = ConfigurationCache(remoteConfiguration: remoteConfig)
+        cache.clear()
+        LSNocilla.sharedInstance().start()
+        _ = stubRequest("GET", endpoint as NSString)?
+            .andReturn(200)?
+            .withHeaders([
+                "Content-Type": "application/json"
+            ])?
+            .withBody(
+                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/2-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}" as NSString)
+        
+        // test
+        let expectation = XCTestExpectation()
+        let provider = ConfigurationProvider(remoteConfiguration: remoteConfig)
+        provider.retrieveConfigurationOnlyRemote(false, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTFail()
+        })
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(XCTWaiter.Result.timedOut, result)
+
+        // close test
+        LSNocilla.sharedInstance().stop()
+    }
+
+    func testProvider_downloadSameConfigVersionThanCached_dontUpdate() {
+        // prepare test
+        let endpoint = "https://fake-snowplow.io/config.json"
+        let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
+
+        let cache = ConfigurationCache(remoteConfiguration: remoteConfig)
+        cache.clear()
+        let bundle = ConfigurationBundle(namespace: "namespace", networkConfiguration: NetworkConfiguration(endpoint: "endpoint"))
+        let cached = FetchedConfigurationBundle(schema: "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", configurationVersion: 1)
+        cached.configurationBundle = [bundle]
+        cache.write(cached)
+        Thread.sleep(forTimeInterval: 5) // wait to write on cache
+
+        LSNocilla.sharedInstance().start()
+        _ = stubRequest("GET", endpoint as NSString)?
+            .andReturn(200)?
+            .withHeaders([
+                "Content-Type": "application/json"
+            ] as [NSString: NSString])?
+            .withBody(
+                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}" as NSString)
+
+        // test
+        let expectation = XCTestExpectation()
+        let provider = ConfigurationProvider(remoteConfiguration: remoteConfig)
+        var i = 0
+        provider.retrieveConfigurationOnlyRemote(false, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTAssertEqual(.cached, configurationState)
+            if i == 1 || (fetchedConfigurationBundle.schema == "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0") {
+                XCTFail()
+            }
+            if i == 0 && (fetchedConfigurationBundle.schema == "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0") {
+                i += 1
+            }
+        })
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(XCTWaiter.Result.timedOut, result)
+        XCTAssertEqual(1, i)
+
+        // close test
+        LSNocilla.sharedInstance().stop()
+    }
+
+    func testProvider_downloadHigherConfigVersionThanCached_doUpdate() {
+        // prepare test
+        let endpoint = "https://fake-snowplow.io/config.json"
+        let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
+
+        let cache = ConfigurationCache(remoteConfiguration: remoteConfig)
+        cache.clear()
+        let bundle = ConfigurationBundle(namespace: "namespace", networkConfiguration: NetworkConfiguration(endpoint: "endpoint"))
+        let cached = FetchedConfigurationBundle(schema: "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", configurationVersion: 1)
+        cached.configurationBundle = [bundle]
+        cache.write(cached)
+        Thread.sleep(forTimeInterval: 5) // wait to write on cache
+
+        LSNocilla.sharedInstance().start()
+        _ = stubRequest("GET", endpoint as NSString)?
+            .andReturn(200)?
+            .withHeaders([
+                "Content-Type": "application/json"
+            ] as [NSString : NSString])?
+            .withBody(
+                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}" as NSString)
+
+        // test
+        let expectation = XCTestExpectation()
+        let provider = ConfigurationProvider(remoteConfiguration: remoteConfig)
+        var i = 0
+        provider.retrieveConfigurationOnlyRemote(false, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTAssertEqual(
+                i == 0 ? .cached : .fetched,
+                configurationState)
+            if i == 1 && (fetchedConfigurationBundle.schema == "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0") {
+                i += 1
+            }
+            if i == 0 && (fetchedConfigurationBundle.schema == "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0") {
+                i += 1
+            }
+        })
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(XCTWaiter.Result.timedOut, result)
+        XCTAssertEqual(2, i)
+
+        // close test
+        LSNocilla.sharedInstance().stop()
+    }
+
+    func testProvider_justRefresh_downloadSameConfigVersionThanCached_dontUpdate() {
+        // prepare test
+        let endpoint = "https://fake-snowplow.io/config.json"
+        let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
+
+        let cache = ConfigurationCache(remoteConfiguration: remoteConfig)
+        cache.clear()
+        let bundle = ConfigurationBundle(namespace: "namespace", networkConfiguration: NetworkConfiguration(endpoint: "endpoint"))
+        let cached = FetchedConfigurationBundle(schema: "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", configurationVersion: 1)
+        cached.configurationBundle = [bundle]
+        cache.write(cached)
+        Thread.sleep(forTimeInterval: 5) // wait to write on cache
+        
+        // stub request for configuration (return version 1)
+        LSNocilla.sharedInstance().start()
+        _ = stubRequest("GET", endpoint as NSString)?
+            .andReturn(200)?
+            .withHeaders([
+                "Content-Type": "application/json"
+            ] as [NSString : NSString])?
+            .withBody(
+                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}" as NSString)
+
+        // test
+        let provider = ConfigurationProvider(remoteConfiguration: remoteConfig)
+        var expectation = XCTestExpectation()
+        provider.retrieveConfigurationOnlyRemote(false, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTAssertEqual(.cached, configurationState)
+            expectation.fulfill()
+        })
+        wait(for: [expectation], timeout: 5)
+
+        expectation = XCTestExpectation()
+        provider.retrieveConfigurationOnlyRemote(true, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTFail()
+        })
+        let result = XCTWaiter.wait(for: [expectation], timeout: 5)
+        XCTAssertEqual(XCTWaiter.Result.timedOut, result)
+
+        // close test
+        LSNocilla.sharedInstance().stop()
+    }
+
+    func testDoesntUseCachedConfigurationIfDifferentRemoteEndpoint() {
+        // prepare test
+        let endpoint = "https://fake-snowplow.io/config.json"
+        let cachedRemoteConfig = RemoteConfiguration(endpoint: "https://cached-snowplow.io/config.json", method: .get)
+        let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
+
+        // write configuration (version 2) to cache
+        let cache = ConfigurationCache(remoteConfiguration: cachedRemoteConfig)
+        cache.clear()
+        let bundle = ConfigurationBundle(namespace: "namespace", networkConfiguration: NetworkConfiguration(endpoint: "endpoint"))
+        let cached = FetchedConfigurationBundle(schema: "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", configurationVersion: 2)
+        cached.configurationBundle = [bundle]
+        cache.write(cached)
+        Thread.sleep(forTimeInterval: 5) // wait to write on cache
+
+        // stub request for configuration (return version 1)
+        LSNocilla.sharedInstance().start()
+        _ = stubRequest("GET", endpoint as NSString)?
+            .andReturn(200)?
+            .withHeaders([
+                "Content-Type": "application/json"
+            ] as [NSString : NSString])?
+            .withBody(
+                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}" as NSString)
+
+        // initialize tracker with remote config
+        let expectation = XCTestExpectation()
+        _ = ConfigurationFetcher(remoteSource: remoteConfig, onFetchCallback: { fetchedConfigurationBundle, configurationState in
+            XCTAssertNotNil(fetchedConfigurationBundle)
+            // should be the non-cache configuration (version 1)
+            XCTAssertEqual("http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0", fetchedConfigurationBundle.schema)
+            XCTAssertEqual(1, fetchedConfigurationBundle.configurationVersion)
+            expectation.fulfill()
+        })
+
+        wait(for: [expectation], timeout: 10)
+        LSNocilla.sharedInstance().stop()
+    }
+    
+    // TODO: Replace LSNocilla as it's unreliable and unsupported. It causes this test failure.
+    /*
+    - (void)testConfigurationProvider_justRefresh_downloadHigherConfigVersionThanCached_doUpdate {
+        // prepare test
+        NSString *endpoint = @"https://fake-snowplow.io/config.json";
+        SPRemoteConfiguration *remoteConfig = [[SPRemoteConfiguration alloc] initWithEndpoint:endpoint method:SPHttpMethodGet];
+
+        SPConfigurationCache *cache = [[SPConfigurationCache alloc] initWithRemoteConfiguration:remoteConfig];
+        [cache clearCache];
+        SPConfigurationBundle *bundle = [[SPConfigurationBundle alloc] init];
+        bundle.networkConfiguration = [[SPNetworkConfiguration alloc] initWithEndpoint:@"endpoint"];
+        SPFetchedConfigurationBundle *cached = [[SPFetchedConfigurationBundle alloc] init];
+        cached.schema = @"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0";
+        cached.configurationVersion = 1;
+        cached.configurationBundle = @[bundle];
+        [cache writeCache:cached];
+        [NSThread sleepForTimeInterval:5]; // wait to write on cache
+
+        SPConfigurationProvider *provider = [[SPConfigurationProvider alloc] initWithRemoteConfiguration:remoteConfig];
+        XCTestExpectation *expectation = [XCTestExpectation new];
+        [provider retrieveConfigurationOnlyRemote:NO onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle) {
+            [expectation fulfill];
+        }];
+        [self waitForExpectations:@[expectation] timeout:5];
+
+        [[LSNocilla sharedInstance] start];
+        stubRequest(@"GET", endpoint)
+        .andReturn(200)
+        .withHeaders(@{@"Content-Type": @"application/json"})
+        .withBody(@"{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}");
+
+        // test
+        expectation = [XCTestExpectation new];
+        __block int i = 0;
+        [provider retrieveConfigurationOnlyRemote:YES onFetchCallback:^(SPFetchedConfigurationBundle * _Nonnull fetchedConfigurationBundle) {
+            if ([fetchedConfigurationBundle.schema isEqualToString:@"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0"]) {
+                i++;
+            }
+        }];
+        XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:5];
+        XCTAssertEqual(XCTWaiterResultTimedOut, result);
+        XCTAssertEqual(1, i);
+
+        // close test
+        [[LSNocilla sharedInstance] stop];
+    }
+    */
+}
diff --git a/Tests/Configurations/TestTrackerConfiguration.swift b/Tests/Configurations/TestTrackerConfiguration.swift
new file mode 100644
index 000000000..b75068813
--- /dev/null
+++ b/Tests/Configurations/TestTrackerConfiguration.swift
@@ -0,0 +1,400 @@
+//
+//  TestTrackerConfiguration.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestTrackerConfiguration: XCTestCase {
+    func testNetworkConfiguration_EmptyEndpoint_Fails() {
+        let networkConfig = NetworkConfiguration(endpoint: "", method: .post)
+        XCTAssertEqual("https://", networkConfig.endpoint)
+        XCTAssertEqual(.https, networkConfig.protocol)
+        XCTAssertEqual(.post, networkConfig.method)
+
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "appid"
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNotNil(tracker)
+    }
+
+    func testNetworkConfiguration_EndpointWithoutProtocol_SuccessWithHttps() {
+        let networkConfig = NetworkConfiguration(endpoint: "fake-url.com", method: .get)
+        XCTAssertEqual("https://fake-url.com", networkConfig.endpoint)
+        XCTAssertEqual(.https, networkConfig.protocol)
+        XCTAssertEqual(.get, networkConfig.method)
+
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "appid"
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNotNil(tracker)
+    }
+
+    func testNetworkConfiguration_EndpointWithHttpsProtocol_SuccessWithHttps() {
+        let networkConfig = NetworkConfiguration(endpoint: "https://fake-url.com", method: .get)
+        XCTAssertEqual("https://fake-url.com", networkConfig.endpoint)
+        XCTAssertEqual(.https, networkConfig.protocol)
+        XCTAssertEqual(.get, networkConfig.method)
+
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "appid"
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNotNil(tracker)
+    }
+
+    func testNetworkConfiguration_EndpointWithHttpProtocol_SuccessWithHttps() {
+        let networkConfig = NetworkConfiguration(endpoint: "http://fake-url.com", method: .get)
+        XCTAssertEqual("http://fake-url.com", networkConfig.endpoint)
+        XCTAssertEqual(.http, networkConfig.protocol)
+        XCTAssertEqual(.get, networkConfig.method)
+
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "appid"
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNotNil(tracker)
+    }
+
+    func testNetworkConfiguration_EndpointWithWrongProtocol_UseItAsEndpoint() {
+        let networkConfig = NetworkConfiguration(endpoint: "wrong://fake-url.com", method: .get)
+        XCTAssertEqual("https://wrong://fake-url.com", networkConfig.endpoint)
+        XCTAssertEqual(.https, networkConfig.protocol)
+        XCTAssertEqual(.get, networkConfig.method)
+
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "appid"
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNotNil(tracker)
+    }
+
+    func testNetworkConfiguration_EndpointWithOnlyProtocol_UseItAsEndpoint() {
+        let networkConfig = NetworkConfiguration(endpoint: "http://", method: .get)
+        XCTAssertEqual("http://", networkConfig.endpoint)
+        XCTAssertEqual(.http, networkConfig.protocol)
+        XCTAssertEqual(.get, networkConfig.method)
+
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "appid"
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNotNil(tracker)
+    }
+
+    func testBasicInitialization() {
+        let networkConfig = NetworkConfiguration(endpoint: "https://fake-url", method: .post)
+        let trackerConfig = TrackerConfiguration(appId: "appid")
+        trackerConfig.platformContext = true
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+
+        XCTAssertNotNil(tracker)
+        XCTAssertNotNil(tracker?.emitter)
+        let url = URL(string: tracker?.network?.endpoint ?? "")
+        XCTAssertNotNil(url)
+        let host = url?.host
+        let scheme = url?.scheme
+        let derivedEndpoint = "\(scheme ?? "")://\(host ?? "")"
+
+        let `protocol`: String? = networkConfig.protocol == .http ? "http" : networkConfig.protocol == .https ? "https" : nil
+
+        XCTAssertEqual(networkConfig.endpoint, derivedEndpoint)
+        XCTAssertEqual(`protocol`, scheme)
+
+        XCTAssertEqual(trackerConfig.appId, tracker?.appId)
+        XCTAssertEqual("namespace", tracker?.namespace)
+    }
+
+    func testSessionInitialization() {
+        let expectedForeground = 42
+        let expectedBackground = 24
+        let networkConfig = NetworkConfiguration(endpoint: "https://fake-url", method: .post)
+        let trackerConfig = TrackerConfiguration(appId: "appid")
+        let sessionConfig = SessionConfiguration(
+            foregroundTimeoutInSeconds: expectedForeground,
+            backgroundTimeoutInSeconds: expectedBackground)
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig, sessionConfig])
+
+        let foreground = tracker?.session?.foregroundTimeoutInSeconds ?? 0
+        let background = tracker?.session?.backgroundTimeoutInSeconds ?? 0
+        XCTAssertEqual(expectedForeground, foreground)
+        XCTAssertEqual(expectedBackground, background)
+
+        let foregroundMeasure = (tracker?.session)?.foregroundTimeout
+        let backgroundMeasure = (tracker?.session)?.backgroundTimeout
+        XCTAssertEqual(Measurement(value: Double(expectedForeground), unit: UnitDuration.seconds), foregroundMeasure)
+        XCTAssertEqual(Measurement(value: Double(expectedBackground), unit: UnitDuration.seconds), backgroundMeasure)
+    }
+
+    func testSessionControllerUnavailableWhenContextTurnedOff() {
+        let networkConfig = NetworkConfiguration(endpoint: "https://fake-url", method: .post)
+        let trackerConfig = TrackerConfiguration(appId: "appid")
+        trackerConfig.sessionContext = true
+        var tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNotNil(tracker?.session)
+
+        trackerConfig.sessionContext = false
+        tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig])
+        XCTAssertNil(tracker?.session)
+    }
+
+    func testSessionConfigurationCallback() {
+        _ = DataPersistence.remove(withNamespace: "namespace")
+        let expectation = XCTestExpectation()
+
+        let networkConfig = NetworkConfiguration(endpoint: "https://fake-url", method: .post)
+        let trackerConfig = TrackerConfiguration(appId: "appid")
+        let sessionConfig = SessionConfiguration(foregroundTimeoutInSeconds: 100, backgroundTimeoutInSeconds: 100)
+
+        sessionConfig.onSessionStateUpdate = { sessionState in
+            XCTAssertEqual(1, sessionState.sessionIndex)
+            XCTAssertNil(sessionState.previousSessionId)
+            expectation.fulfill()
+        }
+
+        guard let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig, sessionConfig]) else { return XCTFail() }
+
+        _ = tracker.track(Timing(category: "cat", variable: "var", timing: 123))
+
+        wait(for: [expectation], timeout: 10)
+    }
+
+    func testSessionConfigurationCallbackAfterNewSession() {
+        _ = DataPersistence.remove(withNamespace: "namespace")
+        let expectation = XCTestExpectation()
+
+        let networkConfig = NetworkConfiguration(endpoint: "https://fake-url", method: .post)
+        let trackerConfig = TrackerConfiguration(appId: "appid")
+        let sessionConfig = SessionConfiguration(foregroundTimeoutInSeconds: 100, backgroundTimeoutInSeconds: 100)
+        var sessionId: String?
+        sessionConfig.onSessionStateUpdate = { sessionState in
+            if sessionState.sessionIndex == 1 {
+                XCTAssertNil(sessionState.previousSessionId)
+                sessionId = sessionState.sessionId
+            } else {
+                XCTAssertEqual(2, sessionState.sessionIndex)
+                XCTAssertEqual(sessionId, sessionState.previousSessionId)
+                expectation.fulfill()
+            }
+        }
+
+        let tracker = Snowplow.createTracker(namespace: "namespace", network: networkConfig, configurations: [trackerConfig, sessionConfig])
+
+        _ = tracker?.track(Timing(category: "cat", variable: "var", timing: 123))
+        tracker?.session?.startNewSession()
+        _ = tracker?.track(Timing(category: "cat", variable: "var", timing: 123))
+
+        wait(for: [expectation], timeout: 10)
+    }
+
+    func testTrackerVersionSuffix() {
+        let trackerConfiguration = TrackerConfiguration()
+        trackerConfiguration.trackerVersionSuffix = "test With Space 1-2-3"
+
+        // Setup tracker
+        trackerConfiguration.base64Encoding = false
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, emitterConfiguration])
+
+        // Track fake event
+        let event = Structured(category: "category", action: "action")
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        let events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        let payload = events.first?.payload
+
+        // Check v_tracker field
+        let versionTracker = payload?.dictionary?["tv"] as? String
+        let expected = "\(kSPVersion) testWithSpace1-2-3"
+        XCTAssertEqual(expected, versionTracker)
+    }
+
+    func testGDPRConfiguration() {
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let trackerConfiguration = TrackerConfiguration(appId: "appid")
+        trackerConfiguration.base64Encoding = false
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let gdprConfiguration = GDPRConfiguration(basis: .consent, documentId: "id", documentVersion: "ver", documentDescription: "desc")
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, gdprConfiguration, emitterConfiguration])
+        let gdprController = trackerController?.gdpr
+
+        // Check gdpr settings
+        XCTAssertEqual(.consent, gdprController?.basisForProcessing)
+        XCTAssertEqual("id", gdprController?.documentId)
+
+        // Check gdpr settings reset
+        gdprController?.reset(basis: .contract, documentId: "id1", documentVersion: "ver1", documentDescription: "desc1")
+        XCTAssertEqual(.contract, gdprController?.basisForProcessing)
+        XCTAssertEqual("id1", gdprController?.documentId)
+        XCTAssertTrue(gdprController?.isEnabled ?? false)
+
+        // Check gdpr context added
+        var event = Structured(category: "category", action: "action")
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        var events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        var payload = events.first?.payload
+        var contexts = payload?.dictionary?["co"] as? String
+        XCTAssertTrue(contexts?.contains("\"basisForProcessing\":\"contract\"") ?? false)
+        XCTAssertTrue(contexts?.contains("\"documentId\":\"id1\"") ?? false)
+
+        // Check gdpr disabled
+        gdprController?.disable()
+        XCTAssertFalse(gdprController?.isEnabled ?? false)
+        XCTAssertEqual(.contract, gdprController?.basisForProcessing)
+        XCTAssertEqual("id1", gdprController?.documentId)
+
+        // Check gdpr context not added
+        event = Structured(category: "category", action: "action")
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        payload = events.first?.payload
+        contexts = payload?.dictionary?["co"] as? String
+        XCTAssertFalse(contexts?.contains("\"basisForProcessing\":\"contract\"") ?? false)
+        XCTAssertFalse(contexts?.contains("\"documentId\":\"id1\"") ?? false)
+
+        // Check gdpr enabled again
+        _ = gdprController?.enable()
+        XCTAssertTrue(gdprController?.isEnabled ?? false)
+    }
+
+    func testWithoutGDPRConfiguration() {
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let trackerConfiguration = TrackerConfiguration(appId: "appid")
+        trackerConfiguration.base64Encoding = false
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, emitterConfiguration])
+        let gdprController = trackerController?.gdpr
+
+        // Check gdpr settings
+        XCTAssertFalse(gdprController?.isEnabled ?? false)
+
+        // Check gdpr context not added
+        var event = Structured(category: "category", action: "action")
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        var events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        var payload = events.first?.payload
+        var contexts = payload?.dictionary?["co"] as? String
+        XCTAssertFalse(contexts?.contains("\"basisForProcessing\"") ?? true)
+
+        // Check gdpr can be enabled again
+        gdprController?.reset(basis: .contract, documentId: "id1", documentVersion: "ver1", documentDescription: "desc1")
+        XCTAssertEqual(.contract, gdprController?.basisForProcessing)
+        XCTAssertEqual("id1", gdprController?.documentId)
+        XCTAssertTrue(gdprController?.isEnabled ?? false)
+
+        // Check gdpr context added
+        event = Structured(category: "category", action: "action")
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        payload = events.first?.payload
+        contexts = payload?.dictionary?["co"] as? String
+        XCTAssertTrue(contexts?.contains("\"basisForProcessing\":\"contract\"") ?? false)
+        XCTAssertTrue(contexts?.contains("\"documentId\":\"id1\"") ?? false)
+    }
+
+    func testAnonymisesUserIdentifiersIfAnonymousUserTracking() {
+        // Initialize a tracker with anonymous user tracking
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let trackerConfiguration = TrackerConfiguration(appId: "appid")
+        trackerConfiguration.base64Encoding = false
+        trackerConfiguration.userAnonymisation = true
+        trackerConfiguration.sessionContext = true
+        trackerConfiguration.platformContext = true
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, emitterConfiguration])
+
+        // Track an event and retrieve tracked context JSON from event store
+        let event = Structured(category: "category", action: "action")
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        let events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        guard let payload = events.first?.payload,
+              let contexts = payload.dictionary?["co"] as? String else { return XCTFail() }
+
+        // Check empty userId in session context
+        XCTAssertTrue(contexts.contains("\"userId\":\"00000000-0000-0000-0000-000000000000\""))
+        // Check no user identifiers in platform context
+        XCTAssertFalse(contexts.contains("\"appleIdfa\":\""))
+        XCTAssertFalse(contexts.contains("\"appleIdfv\":\""))
+    }
+
+    func testTrackerReturnsTrackedEventId() {
+        // Setup tracker
+        let trackerConfiguration = TrackerConfiguration()
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, emitterConfiguration])
+
+        // Track fake event
+        let event = Structured(category: "category", action: "action")
+        let eventId = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        let events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        let payload = events.first?.payload
+
+        // Check eid field
+        let trackedEventId = payload?.dictionary?["eid"] as? String
+        XCTAssertTrue((eventId?.uuidString == trackedEventId))
+    }
+}
diff --git a/Tests/Configurations/TestTrackerController.swift b/Tests/Configurations/TestTrackerController.swift
new file mode 100644
index 000000000..203715c18
--- /dev/null
+++ b/Tests/Configurations/TestTrackerController.swift
@@ -0,0 +1,75 @@
+//
+//  TestTrackerController.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestTrackerController: XCTestCase {
+    func testSessionAccessibilityWhenEnabledAndDisabled() {
+        let tracker = Snowplow.createTracker(namespace: "namespace", endpoint: "https://fake-url", method: .post)
+        XCTAssertNotNil(tracker?.session)
+
+        tracker?.sessionContext = false
+        XCTAssertNil(tracker?.session)
+    }
+
+    func testSubjectUserIdCanBeUpdated() {
+        let tracker = Snowplow.createTracker(namespace: "namespace", endpoint: "https://fake-url", method: .post)
+        XCTAssertNotNil(tracker?.subject)
+        XCTAssertNil(tracker?.subject?.userId)
+        tracker?.subject?.userId = "fakeUserId"
+        XCTAssertEqual("fakeUserId", tracker?.subject?.userId)
+        tracker?.subject?.userId = nil
+        XCTAssertNil(tracker?.subject?.userId)
+    }
+
+    func testSubjectGeoLocationCanBeUpdated() {
+        let tracker = Snowplow.createTracker(namespace: "namespace", endpoint: "https://fake-url", method: .post)
+        XCTAssertNotNil(tracker?.subject)
+        XCTAssertNil(tracker?.subject?.geoLatitude)
+        tracker?.subject?.geoLatitude = NSNumber(value: 12.3456)
+        XCTAssertEqual(NSNumber(value: 12.3456), tracker?.subject?.geoLatitude)
+        tracker?.subject?.geoLatitude = nil
+        // TODO: On version 3 setting to nil should get back nil.
+        // Here it should be nil rather than 0 but it's the way the beneith SPSubject works.
+        XCTAssertNil(tracker?.subject?.geoLatitude)
+    }
+
+    func testStartsNewSessionWhenChangingAnonymousTracking() {
+        let tracker = Snowplow.createTracker(namespace: "n2", endpoint: "https://fake-url", method: .post)
+        tracker?.emitter?.pause()
+
+        _ = tracker?.track(Structured(category: "c", action: "a"))
+        let sessionIdBefore = tracker?.session?.sessionId
+
+        tracker?.userAnonymisation = true
+        _ = tracker?.track(Structured(category: "c", action: "a"))
+        let sessionIdAnonymous = tracker?.session?.sessionId
+
+        XCTAssertFalse((sessionIdBefore == sessionIdAnonymous))
+
+        tracker?.userAnonymisation = false
+        _ = tracker?.track(Structured(category: "c", action: "a"))
+        let sessionIdNotAnonymous = tracker?.session?.sessionId
+
+        XCTAssertFalse((sessionIdAnonymous == sessionIdNotAnonymous))
+    }
+}
diff --git a/Tests/Global Contexts/TestGlobalContexts.swift b/Tests/Global Contexts/TestGlobalContexts.swift
new file mode 100644
index 000000000..cb46fdfbe
--- /dev/null
+++ b/Tests/Global Contexts/TestGlobalContexts.swift	
@@ -0,0 +1,279 @@
+//
+//  TestGlobalContexts.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  Copyright: Copyright © 2020 Snowplow Analytics.
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+// MARK: - GlobalContextGenerator
+
+class GlobalContextGenerator: NSObject, ContextGenerator {
+    func filter(from event: InspectableEvent) -> Bool {
+        return "StringToMatch" == event.payload[kSPStuctCategory] as? String
+    }
+
+    func generator(from event: InspectableEvent) -> [SelfDescribingJson]? {
+        return [
+            SelfDescribingJson(schema: "schema", andDictionary: [
+                "key": "value" as NSObject
+            ])
+        ]
+    }
+}
+
+// MARK: - TestGlobalContexts
+
+class TestGlobalContexts: XCTestCase {
+    func testGlobalContexts() {
+        let staticGC = GlobalContext(staticContexts: [
+            SelfDescribingJson(schema: "schema", andDictionary: [
+                "key": "value" as NSObject
+            ])
+        ])
+        let generatorGC = GlobalContext(contextGenerator: GlobalContextGenerator())
+        let blockGC = GlobalContext(generator: { event in
+            return [
+                SelfDescribingJson(schema: "schemaBlock", andDictionary: [
+                    "key": "value" as NSObject
+                ])
+            ]
+        })
+        
+        var generators = [
+            "static": staticGC,
+            "generator": generatorGC,
+            "block": blockGC
+        ]
+        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+
+        var result = Set<String>(tracker.globalContextTags)
+        var expected = Set<String>(["static", "generator", "block"])
+        XCTAssertEqual(result, expected)
+
+        // Can't remove a not existing tag
+        var removedGC = tracker.removeGlobalContext("notExistingTag")
+        XCTAssertNil(removedGC)
+        result = Set<String>(tracker.globalContextTags)
+        expected = Set<String>(["static", "generator", "block"])
+        XCTAssertTrue(result == expected)
+
+        // Remove an existing tag
+        removedGC = tracker.removeGlobalContext("static")
+        XCTAssertNotNil(removedGC)
+        result = Set<String>(tracker.globalContextTags)
+        expected = Set<String>(["generator", "block"])
+        XCTAssertTrue(result == expected)
+
+        // Add a not existing tag
+        XCTAssertTrue(tracker.add(staticGC, tag: "static"))
+        result = Set<String>(tracker.globalContextTags)
+        expected = Set<String>(["generator", "block", "static"])
+        XCTAssertTrue(result == expected)
+
+        // Can't add an existing tag
+        XCTAssertFalse(tracker.add(staticGC, tag: "static"))
+        result = Set<String>(tracker.globalContextTags)
+        expected = Set<String>(["generator", "block", "static"])
+        XCTAssertTrue(result == expected)
+    }
+
+    func testAddRemove() {
+        let staticGC = GlobalContext(staticContexts: [
+            SelfDescribingJson(schema: "schema", andDictionary: [
+                "key": "value" as NSObject
+            ])
+        ])
+        var generators: [String : GlobalContext] = [:]
+        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+
+        var result = Set<String>(tracker.globalContextTags)
+        var expected = Set<String>([])
+        XCTAssertTrue(result == expected)
+
+        // Can't remove a not existing tag
+        var removedGC = tracker.removeGlobalContext("notExistingTag")
+        XCTAssertNil(removedGC)
+
+        // Add a not existing tag
+        XCTAssertTrue(tracker.add(staticGC, tag: "static"))
+        result = Set<String>(tracker.globalContextTags)
+        expected = Set<String>(["static"])
+        XCTAssertTrue(result == expected)
+
+        // Remove an existing tag
+        removedGC = tracker.removeGlobalContext("static")
+        XCTAssertNotNil(removedGC)
+        result = Set<String>(tracker.globalContextTags)
+        expected = Set<String>([])
+        XCTAssertTrue(result == expected)
+    }
+
+    func testStaticGenerator() {
+        let staticGC = GlobalContext(staticContexts: [
+            SelfDescribingJson(schema: "schema", andDictionary: [
+                "key": "value" as NSObject
+            ])
+        ])
+        var globalContexts = [
+            "static": staticGC
+        ]
+        let tracker = getTrackerWithGlobalContextGenerators(&globalContexts)
+
+        let event = Structured(category: "Category", action: "Action")
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+
+        var contexts: [SelfDescribingJson] = []
+        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
+        XCTAssertTrue(contexts.count == 1)
+        XCTAssertEqual(contexts[0].schema, "schema")
+    }
+
+    func testStaticGeneratortWithFilter() {
+        let stringToMatch = "StringToMatch"
+        let filterMatchingGC = GlobalContext(
+            staticContexts: [
+                SelfDescribingJson(schema: "schema", andDictionary: [
+                    "key": "value" as NSObject
+                ])
+            ],
+            filter: { event in
+                return stringToMatch == event.payload[kSPStuctCategory] as? String
+            })
+        let filterNotMatchingGC = GlobalContext(staticContexts: [
+            SelfDescribingJson(schema: "schemaNotMatching", andDictionary: [
+                "key": "value"
+            ])
+        ], filter: { event in
+            return false
+        })
+        var globalContexts = [
+            "matching": filterMatchingGC,
+            "notMatching": filterNotMatchingGC
+        ]
+        let tracker = getTrackerWithGlobalContextGenerators(&globalContexts)
+
+        let event = Structured(category: stringToMatch, action: "Action")
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+
+        var contexts: [SelfDescribingJson] = []
+        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
+        XCTAssertTrue(contexts.count == 1)
+        XCTAssertEqual(contexts[0].schema, "schema")
+    }
+
+    func testStaticGeneratorWithRuleset() {
+        let allowed = "iglu:com.snowplowanalytics.*/*/jsonschema/*-*-*"
+        let denied = "iglu:com.snowplowanalytics.mobile/*/jsonschema/*-*-*"
+        let ruleset = SchemaRuleset(allowedList: [allowed], andDeniedList: [denied])
+
+        let rulesetGC = GlobalContext(staticContexts: [
+            SelfDescribingJson(schema: "schema", andDictionary: [
+                "key": "value" as NSObject
+            ])
+        ], ruleset: ruleset)
+        var globalContexts = [
+            "ruleset": rulesetGC
+        ]
+        let tracker = getTrackerWithGlobalContextGenerators(&globalContexts)
+
+        var contexts: [SelfDescribingJson] = []
+
+        // Not matching primitive event
+        let primitiveEvent = Structured(category: "Category", action: "Action")
+        var trackerEvent = TrackerEvent(event: primitiveEvent, state: nil)
+        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
+        XCTAssertTrue(contexts.count == 0)
+
+        // Not matching self-describing event with mobile schema
+        let screenView = ScreenView(name: "Name", screenId: nil)
+        screenView.type = "Type"
+        trackerEvent = TrackerEvent(event: screenView, state: nil)
+        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
+        XCTAssertTrue(contexts.count == 0)
+
+        // Matching self-describing event with general schema
+        let timing = Timing(category: "Category", variable: "Variable", timing: 123)
+        timing.label = "Label"
+        trackerEvent = TrackerEvent(event: timing, state: nil)
+        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
+        XCTAssertTrue(contexts.count == 1)
+        XCTAssertEqual(contexts[0].schema, "schema")
+    }
+
+    func testBlockGenerator() {
+        var generators = [
+            "generator": GlobalContext(generator: { event in
+                return [
+                    SelfDescribingJson(schema: "schema", andDictionary: [
+                        "key": "value"
+                    ])
+                ]
+            })
+        ]
+        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+
+        let event = Structured(category: "Category", action: "Action")
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+
+        var contexts: [SelfDescribingJson] = []
+        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
+        XCTAssertTrue(contexts.count == 1)
+        XCTAssertEqual(contexts[0].schema, "schema")
+    }
+
+    func testContextGenerator() {
+        let contextGeneratorGC = GlobalContext(contextGenerator: GlobalContextGenerator())
+        var generators = [
+            "contextGenerator": contextGeneratorGC
+        ]
+        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+
+        let event = Structured(category: "StringToMatch", action: "Action")
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+
+        var contexts: [SelfDescribingJson] = []
+        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
+        XCTAssertTrue(contexts.count == 1)
+        XCTAssertEqual(contexts[0].schema, "schema")
+    }
+
+    // MARK: - Utility function
+
+
+    func getTrackerWithGlobalContextGenerators(_ generators: inout [String : GlobalContext]) -> Tracker {
+        let networkConfig = NetworkConfiguration(
+            endpoint: "https://com.acme.fake",
+            method: .post)
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "anAppId"
+        trackerConfig.platformContext = true
+        trackerConfig.geoLocationContext = false
+        trackerConfig.base64Encoding = false
+        trackerConfig.sessionContext = true
+        let gcConfig = GlobalContextsConfiguration()
+        gcConfig.contextGenerators = generators
+        let serviceProvider = ServiceProvider(
+            namespace: "aNamespace",
+            network: networkConfig,
+            configurations: [gcConfig])
+        return serviceProvider.tracker
+    }
+}
diff --git a/Tests/Global Contexts/TestSchemaRuleset.swift b/Tests/Global Contexts/TestSchemaRuleset.swift
new file mode 100644
index 000000000..9cb0813e6
--- /dev/null
+++ b/Tests/Global Contexts/TestSchemaRuleset.swift	
@@ -0,0 +1,100 @@
+//
+//  TestSchemaRuleset.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  Copyright: Copyright © 2020 Snowplow Analytics.
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestSchemaRuleset: XCTestCase {
+    func testSchemaRules() {
+        guard let twoPartVendor = SchemaRule(rule: "iglu:com.acme/*/jsonschema/*-*-*") else { return XCTFail() }
+
+        // version and event wildcard
+        XCTAssertTrue(twoPartVendor.match(withUri: "iglu:com.acme/event/jsonschema/1-0-0"))
+        XCTAssertFalse(twoPartVendor.match(withUri: "iglu:com.snowplow/event/jsonschema/1-0-0"))
+        let equalRule = SchemaRule(rule: "iglu:com.acme/*/jsonschema/*-*-*")
+        XCTAssertEqual(twoPartVendor, equalRule)
+
+        let threePartVendor = SchemaRule(rule: "iglu:com.acme.marketing/*/jsonschema/*-*-*")
+        XCTAssertNotNil(threePartVendor)
+
+        guard let validVendorWildcard = SchemaRule(rule: "iglu:com.acme.*/*/jsonschema/*-*-*") else { return XCTFail() }
+        XCTAssertNotNil(validVendorWildcard)
+
+        let invalidVendorWildcard = SchemaRule(rule: "iglu:com.acme.*.whoops/*/jsonschema/*-*-*")
+        XCTAssertNil(invalidVendorWildcard)
+
+        // vendor matching
+        XCTAssertTrue(validVendorWildcard.match(withUri: "iglu:com.acme.marketing/event/jsonschema/1-0-0"))
+        XCTAssertFalse(validVendorWildcard.match(withUri: "iglu:com.snowplow/event/jsonschema/1-0-0"))
+
+        // vendor parts need to match in length, i.e. com.acme.* will not match com.acme.marketing.foo, only vendors of the form com.acme.x
+        XCTAssertFalse(validVendorWildcard.match(withUri: "iglu:com.acme.marketing.foo/event/jsonschema/1-0-0"))
+    }
+
+    func testSchemaRuleset() {
+        let acme = "iglu:com.acme.*/*/jsonschema/*-*-*"
+        let snowplow = "iglu:com.snowplow.*/*/jsonschema/*-*-*"
+        let snowplowTest = "iglu:com.snowplow.test/*/jsonschema/*-*-*"
+        let ruleset = SchemaRuleset(allowedList: [acme, snowplow], andDeniedList: [snowplowTest])
+        let allowed = [acme, snowplow]
+        XCTAssertEqual(ruleset.allowed, allowed)
+        let denied = [snowplowTest]
+        XCTAssertEqual(ruleset.denied, denied)
+
+        // matching
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.acme.marketing/event/jsonschema/1-0-0"))
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.snowplow.marketing/event/jsonschema/1-0-0"))
+        XCTAssertFalse(ruleset.match(withUri: "iglu:com.snowplow.test/event/jsonschema/1-0-0"))
+        XCTAssertFalse(ruleset.match(withUri: "iglu:com.brand/event/jsonschema/1-0-0"))
+    }
+
+    func testSchemaRulesetOnlyDenied() {
+        let snowplowTest = "iglu:com.snowplow.test/*/jsonschema/*-*-*"
+        let ruleset = SchemaRuleset(deniedList: [snowplowTest])
+        let allowed: [String]? = []
+        XCTAssertEqual(ruleset.allowed, allowed)
+        let denied = [snowplowTest]
+        XCTAssertEqual(ruleset.denied, denied)
+
+        // matching
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.acme.marketing/event/jsonschema/1-0-0"))
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.snowplow.marketing/event/jsonschema/1-0-0"))
+        XCTAssertFalse(ruleset.match(withUri: "iglu:com.snowplow.test/event/jsonschema/1-0-0"))
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.brand/event/jsonschema/1-0-0"))
+    }
+
+    func testSchemaRulesetOnlyAllowed() {
+        let acme = "iglu:com.acme.*/*/jsonschema/*-*-*"
+        let snowplow = "iglu:com.snowplow.*/*/jsonschema/*-*-*"
+        let ruleset = SchemaRuleset(allowedList: [acme, snowplow])
+        let allowed = [acme, snowplow]
+        XCTAssertEqual(ruleset.allowed, allowed)
+        let denied: [String]? = []
+        XCTAssertEqual(ruleset.denied, denied)
+
+        // matching
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.acme.marketing/event/jsonschema/1-0-0"))
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.snowplow.marketing/event/jsonschema/1-0-0"))
+        XCTAssertTrue(ruleset.match(withUri: "iglu:com.snowplow.test/event/jsonschema/1-0-0"))
+        XCTAssertFalse(ruleset.match(withUri: "iglu:com.brand/event/jsonschema/1-0-0"))
+    }
+}
diff --git a/Snowplow iOSTests/Info.plist b/Tests/Info.plist
similarity index 100%
rename from Snowplow iOSTests/Info.plist
rename to Tests/Info.plist
diff --git a/Tests/Legacy Tests/LegacyTestEmitter.swift b/Tests/Legacy Tests/LegacyTestEmitter.swift
new file mode 100644
index 000000000..1c319e051
--- /dev/null
+++ b/Tests/Legacy Tests/LegacyTestEmitter.swift	
@@ -0,0 +1,377 @@
+//
+//  LegacyTestEmitter.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+//class BrokenNetworkConnection: NetworkConnection {
+//    func sendRequests(_ requests: [Request]) -> [RequestResult] {
+//        NSException.raise("BrokenNetworkConnection", format: "Fake exception on network connection.")
+//        return nil
+//    }
+//
+//    var urlEndpoint: URL? {
+//        NSException.raise("BrokenNetworkConnection", format: "Fake exception on network connection.")
+//        return nil
+//    }
+//
+//    var httpMethod: HttpMethodOptions {
+//        NSException.raise("BrokenNetworkConnection", format: "Fake exception on network connection.")
+//        return .get
+//    }
+//}
+
+//#pragma clang diagnostic push
+//#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+let TEST_SERVER_EMITTER = "www.notarealurl.com"
+
+class LegacyTestEmitter: XCTestCase {
+    override func setUp() {
+        super.setUp()
+        Logger.logLevel = .verbose
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testEmitterBuilderAndOptions() {
+        let `protocol` = "https"
+
+        let emitter = Emitter(urlEndpoint: TEST_SERVER_EMITTER) { emitter in
+            emitter.method = .post
+            emitter.protocol = .https
+            emitter.emitThreadPoolSize = 30
+            emitter.byteLimitGet = 30000
+            emitter.byteLimitPost = 35000
+            emitter.emitRange = 500
+        }
+
+        var url = "\(`protocol`)://\(TEST_SERVER_EMITTER)/com.snowplowanalytics.snowplow/tp2"
+
+        // Test builder setting properly
+
+        XCTAssertNil(emitter.callback)
+        XCTAssertTrue((emitter.urlEndpoint == url))
+        XCTAssertEqual(emitter.method, .post)
+        XCTAssertEqual(emitter.emitRange, 500)
+        XCTAssertEqual(emitter.emitThreadPoolSize, 30)
+        XCTAssertEqual(emitter.byteLimitGet, 30000)
+        XCTAssertEqual(emitter.byteLimitPost, 35000)
+        XCTAssertEqual(emitter.protocol, .https)
+
+        let customPathEmitter = Emitter(urlEndpoint: TEST_SERVER_EMITTER) { emitter in
+            emitter.method = .post
+            emitter.protocol = .https
+            emitter.customPostPath = "/com.acme.company/tpx"
+            emitter.emitThreadPoolSize = 30
+            emitter.byteLimitGet = 30000
+            emitter.byteLimitPost = 35000
+            emitter.emitRange = 500
+        }
+
+        let customUrl = "\(`protocol`)://\(TEST_SERVER_EMITTER)/com.acme.company/tpx"
+        XCTAssertTrue((customPathEmitter.urlEndpoint == customUrl))
+
+        // Test setting variables to new values
+
+        emitter.urlEndpoint = "www.test.com"
+        url = "\(`protocol`)://www.test.com/com.snowplowanalytics.snowplow/tp2"
+        XCTAssertTrue((emitter.urlEndpoint == url))
+        emitter.method = .get
+        XCTAssertEqual(emitter.method, .get)
+        url = "\(`protocol`)://www.test.com/i"
+        XCTAssertTrue((emitter.urlEndpoint == url))
+        emitter.emitRange = 1000
+        XCTAssertEqual(emitter.emitRange, 1000)
+        emitter.emitThreadPoolSize = 50
+        XCTAssertEqual(emitter.emitThreadPoolSize, 50)
+        emitter.byteLimitGet = 1000
+        XCTAssertEqual(emitter.byteLimitGet, 1000)
+        emitter.byteLimitPost = 50
+        XCTAssertEqual(emitter.byteLimitPost, 50)
+
+        // Test extra functions
+        XCTAssertFalse(emitter.isSending)
+        XCTAssertTrue(emitter.dbCount >= 0)
+
+        // Allow timer to be set
+        RunLoop.main.run(until: Date(timeIntervalSinceNow: 1))
+        emitter.resumeTimer()
+        
+        emitter.flush()
+    }
+
+    // MARK: - Emitting tests
+
+//    func testEmitEventWithBrokenNetworkConnectionDoesntFreezeStatus() {
+//        let networkConnection = SPBrokenNetworkConnection()
+//        let emitter = self.emitter(with: networkConnection, bufferOption: SPBufferOptionSingle)
+//        emitter?.addPayload(toBuffer: generatePayloads(1)?.first)
+//
+//        Thread.sleep(forTimeInterval: 1)
+//
+//        XCTAssertFalse(emitter?.getSendingStatus())
+//
+//        emitter?.flush()
+//    }
+
+    func testEmitSingleGetEventWithSuccess() {
+        let networkConnection = MockNetworkConnection(requestOption: .get, statusCode: 200)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .single)
+        emitter.addPayload(toBuffer: generatePayloads(1).first!)
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(1, networkConnection.previousResults.count)
+        XCTAssertEqual(1, networkConnection.previousResults.first!.count)
+        XCTAssertTrue(networkConnection.previousResults.first!.first!.isSuccessful)
+        XCTAssertEqual(0, emitter.dbCount)
+
+        emitter.flush()
+    }
+
+    func testEmitSingleGetEventWithNoSuccess() {
+        let networkConnection = MockNetworkConnection(requestOption: .get, statusCode: 500)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .single)
+        emitter.addPayload(toBuffer: generatePayloads(1).first!)
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(1, networkConnection.previousResults.count)
+        XCTAssertEqual(1, networkConnection.previousResults.first!.count)
+        XCTAssertFalse(networkConnection.previousResults.first!.first!.isSuccessful)
+        XCTAssertEqual(1, emitter.dbCount)
+
+        emitter.flush()
+    }
+
+    func testEmitTwoGetEventsWithSuccess() {
+        let networkConnection = MockNetworkConnection(requestOption: .get, statusCode: 200)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .single)
+
+        for payload in generatePayloads(2) {
+            emitter.addPayload(toBuffer: payload)
+        }
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(0, emitter.dbCount)
+        var totEvents = 0
+        for results in networkConnection.previousResults {
+            for result in results {
+                XCTAssertTrue(result.isSuccessful)
+                totEvents += result.storeIds?.count ?? 0
+            }
+        }
+        XCTAssertEqual(2, totEvents)
+
+        emitter.flush()
+    }
+
+    func testEmitTwoGetEventsWithNoSuccess() {
+        let networkConnection = MockNetworkConnection(requestOption: .get, statusCode: 500)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .single)
+
+        for payload in generatePayloads(2) {
+            emitter.addPayload(toBuffer: payload)
+        }
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(2, emitter.dbCount)
+        for results in networkConnection.previousResults {
+            for result in results {
+                XCTAssertFalse(result.isSuccessful)
+            }
+        }
+
+        emitter.flush()
+    }
+
+    func testEmitSinglePostEventWithSuccess() {
+        let networkConnection = MockNetworkConnection(requestOption: .post, statusCode: 200)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .single)
+
+        emitter.addPayload(toBuffer: generatePayloads(1).first!)
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(1, networkConnection.previousResults.count)
+        XCTAssertEqual(1, networkConnection.previousResults.first?.count)
+        XCTAssertTrue(networkConnection.previousResults.first!.first!.isSuccessful)
+        XCTAssertEqual(0, emitter.dbCount)
+
+        emitter.flush()
+    }
+
+    func testEmitEventsPostAsGroup() {
+        let networkConnection = MockNetworkConnection(requestOption: .post, statusCode: 500)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .defaultGroup)
+
+        let payloads = generatePayloads(15)
+        for i in 0..<14 {
+            emitter.addPayload(toBuffer: payloads[i])
+        }
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(14, emitter.dbCount)
+        networkConnection.statusCode = 200
+        let prevSendingCount = networkConnection.sendingCount
+        emitter.addPayload(toBuffer: payloads[14])
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(0, emitter.dbCount)
+        var totEvents = 0
+        var areGrouped = false
+        let prevResults = networkConnection.previousResults[prevSendingCount..<networkConnection.previousResults.count]
+        for results in prevResults {
+            for result in results {
+                XCTAssertTrue(result.isSuccessful)
+                let ids = result.storeIds?.count ?? 0
+                totEvents += ids
+                areGrouped = areGrouped || ids > 1
+            }
+        }
+        XCTAssertEqual(15, totEvents)
+        XCTAssertTrue(areGrouped)
+
+        emitter.flush()
+    }
+
+    func testEmitOversizeEventsPostAsGroup() {
+        let networkConnection = MockNetworkConnection(requestOption: .post, statusCode: 500)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .defaultGroup)
+        emitter.byteLimitPost = 5
+
+        let payloads = generatePayloads(15)
+        for i in 0..<14 {
+            emitter.addPayload(toBuffer: payloads[i])
+        }
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(0, emitter.dbCount)
+        networkConnection.statusCode = 200
+        _ = networkConnection.sendingCount
+        emitter.addPayload(toBuffer: payloads[14])
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+
+        XCTAssertEqual(0, emitter.dbCount)
+
+        emitter.flush()
+    }
+
+    func testRemovesEventsFromQueueOnNoRetryStatus() {
+        let networkConnection = MockNetworkConnection(requestOption: .get, statusCode: 403)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .single)
+
+        emitter.addPayload(toBuffer: generatePayloads(1).first!)
+
+        Thread.sleep(forTimeInterval: 1)
+
+        XCTAssertEqual(0, emitter.dbCount)
+        for results in networkConnection.previousResults {
+            for result in results {
+                XCTAssertFalse(result.isSuccessful)
+            }
+        }
+
+        emitter.flush()
+    }
+
+    func testFollowCustomRetryRules() {
+        let networkConnection = MockNetworkConnection(requestOption: .get, statusCode: 500)
+        let emitter = self.emitter(with: networkConnection, bufferOption: .single)
+
+        var customRules: [Int : Bool] = [:]
+        customRules[403] = true
+        customRules[500] = false
+        emitter.customRetryForStatusCodes = customRules
+
+        emitter.addPayload(toBuffer: generatePayloads(1).first!)
+
+        Thread.sleep(forTimeInterval: 1)
+
+        // no events in queue since they were dropped because retrying is disabled for 500
+        XCTAssertEqual(0, emitter.dbCount)
+
+        networkConnection.statusCode = 403
+
+        emitter.addPayload(toBuffer: generatePayloads(1).first!)
+
+        Thread.sleep(forTimeInterval: 1)
+
+        // event still in queue because retrying is enabled for 403
+        XCTAssertEqual(1, emitter.dbCount)
+
+        emitter.flush()
+    }
+
+    // MARK: - Emitter builder
+
+    func emitter(with networkConnection: NetworkConnection, bufferOption: BufferOption = .single) -> Emitter {
+        let emitter = Emitter(networkConnection: networkConnection) { emitter in
+            emitter.bufferOption = bufferOption
+            emitter.emitRange = 200
+            emitter.byteLimitGet = 20000
+            emitter.byteLimitPost = 25000
+            emitter.eventStore = MockEventStore()
+        }
+        return emitter
+    }
+
+    // MARK: - Service methods
+
+    func generatePayloads(_ count: Int) -> [Payload] {
+        var payloads: [Payload] = []
+        for i in 0..<count {
+            let payload = Payload()
+            payload.addValueToPayload(NSNumber(value: i).description, forKey: "a")
+            payloads.append(payload)
+        }
+        return payloads
+    }
+}
+//#pragma clang diagnostic pop
diff --git a/Tests/Legacy Tests/LegacyTestSubject.swift b/Tests/Legacy Tests/LegacyTestSubject.swift
new file mode 100644
index 000000000..88ed69b5c
--- /dev/null
+++ b/Tests/Legacy Tests/LegacyTestSubject.swift	
@@ -0,0 +1,121 @@
+//
+//  TestLifecycleState.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+//#pragma clang diagnostic push
+//#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+class LegacyTestSubject: XCTestCase {
+    override func setUp() {
+        super.setUp()
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testSubjectInit() {
+        let subject = Subject()
+        XCTAssertNotNil(subject.getStandardDict(withUserAnonymisation: false))
+    }
+
+    func testSubjectInitWithOptions() {
+        let subject = Subject(platformContext: true, andGeoContext: false)
+        XCTAssertNotNil(subject.getPlatformDict(withUserAnonymisation: false))
+        XCTAssertNotNil(subject.getStandardDict(withUserAnonymisation: false))
+    }
+
+    func testSubjectSetterFunctions() {
+        let subject = Subject(platformContext: false, andGeoContext: true)
+        subject.userId = "aUserId"
+        subject.screenResolution = SPSize(width: 1920, height: 1080)
+        subject.screenViewPort = SPSize(width: 1080, height: 1920)
+        subject.colorDepth = NSNumber(value: 20)
+        subject.timezone = "UTC"
+        subject.language = "EN"
+        subject.ipAddress = "127.0.0.1"
+        subject.useragent = "aUseragent"
+        subject.networkUserId = "aNuid"
+        subject.domainUserId = "aDuid"
+
+        guard var values = subject.getStandardDict(withUserAnonymisation: false)?.dictionary else {
+            return XCTFail()
+        }
+
+        XCTAssertEqual(values[kSPUid], "aUserId" as NSObject)
+        XCTAssertTrue((values[kSPResolution] == "1920x1080" as NSObject))
+        XCTAssertTrue((values[kSPViewPort] == "1080x1920" as NSObject))
+        XCTAssertTrue((values[kSPColorDepth] == "20" as NSObject))
+        XCTAssertEqual(values[kSPTimezone], "UTC" as NSObject)
+        XCTAssertEqual(values[kSPLanguage], "EN" as NSObject)
+        XCTAssertEqual(values[kSPIpAddress], "127.0.0.1" as NSObject)
+        XCTAssertEqual(values[kSPUseragent], "aUseragent" as NSObject)
+        XCTAssertEqual(values[kSPNetworkUid], "aNuid" as NSObject)
+        XCTAssertEqual(values[kSPDomainUid], "aDuid" as NSObject)
+
+        // Setup GeoLocation
+        subject.geoLongitude = NSNumber(value: 5)
+        subject.geoLatitude = NSNumber(value: 89.2)
+        subject.geoTimestamp = NSNumber(value: 5)
+        subject.geoLatitudeLongitudeAccuracy = NSNumber(value: 5.5)
+        subject.geoSpeed = NSNumber(value: 6.2)
+        subject.geoBearing = NSNumber(value: 82.3)
+        subject.geoAltitude = NSNumber(value: 62.3)
+        subject.geoAltitudeAccuracy = NSNumber(value: 16.3)
+
+        values = subject.getGeoLocationDict()!
+
+        XCTAssertTrue((NSNumber(value: 5) == values[kSPGeoLongitude]))
+        XCTAssertTrue((NSNumber(value: 89.2) == values[kSPGeoLatitude]))
+        XCTAssertTrue((NSNumber(value: 5.5) == values[kSPGeoLatLongAccuracy]))
+        XCTAssertTrue((NSNumber(value: 6.2) == values[kSPGeoSpeed]))
+        XCTAssertTrue((NSNumber(value: 82.3) == values[kSPGeoBearing]))
+        XCTAssertTrue((NSNumber(value: 62.3) == values[kSPGeoAltitude]))
+        XCTAssertTrue((NSNumber(value: 16.3) == values[kSPGeoAltitudeAccuracy]))
+        XCTAssertTrue((NSNumber(value: 5) == values[kSPGeoTimestamp]))
+    }
+
+    func testGeoLocationGetWithoutNeededKeys() {
+        let subject = Subject(platformContext: false, andGeoContext: true)
+        XCTAssertNil(subject.getGeoLocationDict())
+
+        subject.geoLongitude = NSNumber(value: 5)
+        subject.geoLatitude = NSNumber(value: 89.2)
+
+        XCTAssertNotNil(subject.getGeoLocationDict())
+    }
+
+    func testGeoLocationWithSubjectConfiguration() {
+        let config = SubjectConfiguration()
+        config.geoLatitude = NSNumber(value: 12.12)
+        config.geoLongitude = NSNumber(value: 24.24)
+        let subject = Subject(platformContext: false, geoLocationContext: true, subjectConfiguration: config)
+
+        let values = subject.getGeoLocationDict()
+
+        XCTAssertEqual(NSNumber(value: 12.12), values?[kSPGeoLatitude])
+        XCTAssertEqual(NSNumber(value: 24.24), values?[kSPGeoLongitude])
+        XCTAssertNil(values?[kSPGeoAltitude])
+    }
+}
+//#pragma clang diagnostic pop
diff --git a/Tests/Legacy Tests/LegacyTestTracker.swift b/Tests/Legacy Tests/LegacyTestTracker.swift
new file mode 100644
index 000000000..949963dce
--- /dev/null
+++ b/Tests/Legacy Tests/LegacyTestTracker.swift	
@@ -0,0 +1,156 @@
+//
+//  LegacyTestTracker.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+//#pragma clang diagnostic push
+//#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+import XCTest
+@testable import SnowplowTracker
+
+let TEST_SERVER_TRACKER = "http://www.notarealurl.com"
+
+class LegacyTestTracker: XCTestCase {
+    func testTrackerSetup() {
+        let emitter = Emitter(urlEndpoint: "not-real.com") { emitter in }
+
+        let subject = Subject(platformContext: true, andGeoContext: true)
+
+        _ = Tracker(trackerNamespace: "aNamespace", appId: "anAppId", emitter: emitter) { tracker in
+            tracker.subject = subject
+            tracker.base64Encoded = false
+            tracker.sessionContext = true
+        }
+    }
+
+    func testTrackerBuilderAndOptions() {
+        let emitter = Emitter(urlEndpoint: TEST_SERVER_TRACKER) { emitter in}
+
+        let subject = Subject(platformContext: true, andGeoContext: true)
+
+        let tracker = Tracker(trackerNamespace: "aNamespace", appId: "anAppId", emitter: emitter) { tracker in
+            tracker.subject = subject
+            tracker.base64Encoded = false
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 300
+            tracker.backgroundTimeout = 150
+        }
+
+        // Test builder setting properly
+
+        XCTAssertNotNil(tracker.emitter)
+        XCTAssertEqual(tracker.emitter, emitter)
+        XCTAssertNotNil(tracker.subject)
+        XCTAssertEqual(tracker.subject, subject)
+        XCTAssertEqual(tracker.devicePlatform, Utilities.platform)
+        XCTAssertEqual(tracker.appId, "anAppId")
+        XCTAssertEqual(tracker.trackerNamespace, "aNamespace")
+        XCTAssertEqual(tracker.base64Encoded, false)
+        XCTAssertEqual(tracker.inBackground, false)
+        XCTAssertEqual(tracker.isTracking, true)
+
+        // Test Pause/Resume logic
+
+        tracker.pauseEventTracking()
+        XCTAssertEqual(tracker.isTracking, false)
+        XCTAssertNil(tracker.track(Structured(category: "c", action: "a")))
+        tracker.resumeEventTracking()
+        XCTAssertEqual(tracker.isTracking, true)
+
+        // Test setting variables to new values
+
+        tracker.appId = "newAppId"
+        XCTAssertEqual(tracker.appId, "newAppId")
+        tracker.trackerNamespace = "newNamespace"
+        XCTAssertEqual(tracker.trackerNamespace, "newNamespace")
+        tracker.base64Encoded = true
+        XCTAssertEqual(tracker.base64Encoded, true)
+        tracker.devicePlatform = .general
+        XCTAssertEqual(tracker.devicePlatform, .general)
+
+        let subject2 = Subject(platformContext: true, andGeoContext: true)
+        tracker.subject = subject2
+        XCTAssertNotEqual(tracker.subject, subject)
+        XCTAssertEqual(tracker.subject, subject2)
+
+        let emitter2 = Emitter(urlEndpoint: TEST_SERVER_TRACKER) { emitter in}
+        tracker.emitter = emitter2
+        XCTAssertNotEqual(tracker.emitter, emitter)
+        XCTAssertEqual(tracker.emitter, emitter2)
+
+        // Test Session Switch on/off
+
+        let oldSessionManager = tracker.session
+        tracker.sessionContext = false
+        XCTAssertNil(tracker.session)
+
+        tracker.sessionContext = true
+        XCTAssertNotNil(tracker.session)
+        XCTAssertFalse(oldSessionManager === tracker.session)
+    }
+
+    func testTrackerPayload() {
+        let emitter = Emitter(urlEndpoint: TEST_SERVER_TRACKER) { emitter in}
+
+        let subject = Subject(platformContext: true, andGeoContext: true)
+
+        let tracker = Tracker(trackerNamespace: "aNamespace", appId: "anAppId", emitter: emitter) { tracker in
+            tracker.subject = subject
+            tracker.devicePlatform = .general
+            tracker.appId = "anAppId"
+            tracker.base64Encoded = false
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 300
+            tracker.backgroundTimeout = 150
+        }
+
+        let event = Structured(category: "Category", action: "Action")
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+        var payload = tracker.payload(with: trackerEvent)
+        var payloadDict = payload.dictionary
+
+        XCTAssertEqual(payloadDict?[kSPPlatform] as? String, devicePlatformToString(.general))
+        XCTAssertEqual(payloadDict?[kSPAppId] as? String, "anAppId")
+        XCTAssertEqual(payloadDict?[kSPNamespace] as? String, "aNamespace")
+
+        // Test setting variables to new values
+
+        tracker.devicePlatform = .desktop
+        tracker.appId = "newAppId"
+        tracker.trackerNamespace = "newNamespace"
+
+        payload = tracker.payload(with: trackerEvent)
+        payloadDict = payload.dictionary
+
+        XCTAssertEqual(payloadDict?[kSPPlatform] as? String, "pc")
+        XCTAssertEqual(payloadDict?[kSPAppId] as? String, "newAppId")
+        XCTAssertEqual(payloadDict?[kSPNamespace] as? String, "newNamespace")
+    }
+
+    func testEventIdNotDuplicated() {
+        let event = Structured(category: "Category", action: "Action")
+        let eventId = TrackerEvent(event: event, state: nil).eventId
+        XCTAssertNotNil(eventId)
+        let newEventId = TrackerEvent(event: event, state: nil).eventId
+        XCTAssertNotNil(newEventId)
+        XCTAssertNotEqual(eventId, newEventId)
+    }
+}
+//#pragma clang diagnostic pop
diff --git a/Snowplow iOSTests/Products/iglu_resolver.json b/Tests/Products/iglu_resolver.json
similarity index 100%
rename from Snowplow iOSTests/Products/iglu_resolver.json
rename to Tests/Products/iglu_resolver.json
diff --git a/Snowplow iOSTests/SnowplowTests-Info.plist b/Tests/SnowplowTests-Info.plist
similarity index 100%
rename from Snowplow iOSTests/SnowplowTests-Info.plist
rename to Tests/SnowplowTests-Info.plist
diff --git a/Tests/TestDataPersistence.swift b/Tests/TestDataPersistence.swift
new file mode 100644
index 000000000..1344cc770
--- /dev/null
+++ b/Tests/TestDataPersistence.swift
@@ -0,0 +1,102 @@
+//
+//  TestLifecycleState.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestDataPersistence: XCTestCase {
+    override func setUp() {
+        _ = DataPersistence.remove(withNamespace: "namespace")
+        _ = DataPersistence.remove(withNamespace: "namespace1")
+        _ = DataPersistence.remove(withNamespace: "namespace2")
+    }
+
+    func testStringFromNamespace() {
+        XCTAssertEqual("abc-1_2_3", DataPersistence.string(fromNamespace: "abc 1_2_3"))
+    }
+
+    func testDataPersistenceForNamespaceWithDifferentNamespaces() {
+        let dp1 = DataPersistence.getFor(namespace: "namespace1")
+        let dp2 = DataPersistence.getFor(namespace: "namespace2")
+        XCTAssertNotEqual(dp1, dp2)
+    }
+
+    func testDataPersistenceForNamespaceWithSameNamespaces() {
+        let dp1 = DataPersistence.getFor(namespace: "namespace")
+        let dp2 = DataPersistence.getFor(namespace: "namespace")
+        XCTAssertEqual(dp1, dp2)
+    }
+
+    func testRemoveForNamespace() {
+        let dp1 = DataPersistence.getFor(namespace: "namespace")
+        _ = DataPersistence.remove(withNamespace: "namespace")
+        let dp2 = DataPersistence.getFor(namespace: "namespace")
+        XCTAssertNotEqual(dp1, dp2)
+    }
+
+    func testDataIsCorrectlyStored() {
+        commonTestDataIsCorrectlyStored(onFile: true)
+    }
+
+    func testDataIsCorrectlyStoredWhenNotStoredOnFile() {
+        commonTestDataIsCorrectlyStored(onFile: false)
+    }
+
+    func commonTestDataIsCorrectlyStored(onFile isStoredOnFile: Bool) {
+        let dp = DataPersistence.getFor(namespace: "namespace", storedOnFile: isStoredOnFile)
+        var session = [
+            "key": "value" as NSObject
+        ]
+        dp?.session = session
+        XCTAssertEqual(session, dp?.session)
+        XCTAssertEqual(session, dp?.data["session"])
+        // Override session
+        session = [
+            "key2": "value2" as NSObject
+        ]
+        dp?.session = session
+        XCTAssertEqual(session, dp?.session)
+        XCTAssertEqual(session, dp?.data["session"])
+    }
+
+    func testDataIsStoredWithoutInterference() {
+        commonTestDataIsStoredWithoutInterferenceStored(onFile: true)
+    }
+
+    func testDataIsStoredWithoutInterferenceWhenNotStoredOnFile() {
+        commonTestDataIsStoredWithoutInterferenceStored(onFile: false)
+    }
+
+    func commonTestDataIsStoredWithoutInterferenceStored(onFile isStoredOnFile: Bool) {
+        let dp1 = DataPersistence.getFor(namespace: "namespace1", storedOnFile: isStoredOnFile)
+        let dp2 = DataPersistence.getFor(namespace: "namespace2", storedOnFile: isStoredOnFile)
+        let session = [
+            "key": "value" as NSObject
+        ]
+        dp1?.session = session
+        // Check dp1
+        XCTAssertEqual(session, dp1?.session)
+        XCTAssertEqual(session, dp1?.data["session"])
+        // Check dp2
+        XCTAssertNotEqual(session, dp2?.session)
+        XCTAssertNotEqual(session, dp2?.data["session"])
+    }
+}
diff --git a/Tests/TestEvents.swift b/Tests/TestEvents.swift
new file mode 100644
index 000000000..e32839823
--- /dev/null
+++ b/Tests/TestEvents.swift
@@ -0,0 +1,371 @@
+
+//
+//  TestEvents.swift
+//  SnowplowTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestEvents: XCTestCase {
+    func testTrueTimestamp() {
+        let event = PageView(pageUrl: "DemoPageUrl")
+        XCTAssertNil(event.trueTimestamp)
+
+        // Set trueTimestamp
+        let testDate = Date()
+        event.trueTimestamp = testDate
+        XCTAssertEqual(event.trueTimestamp, testDate)
+    }
+
+    func testApplicationInstall() {
+        // Prepare ApplicationInstall event
+        let installEvent = SelfDescribingJson(schema: kSPApplicationInstallSchema, andDictionary: [String:NSObject]())
+        let event = SelfDescribing(eventData: installEvent)
+        let currentTimestamp = Date(timeIntervalSince1970: 12345)
+        event.trueTimestamp = currentTimestamp
+
+        // Setup tracker
+        let trackerConfiguration = TrackerConfiguration()
+        trackerConfiguration.base64Encoding = false
+        trackerConfiguration.installAutotracking = false
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, emitterConfiguration])
+
+        // Track event
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        let events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        let payload = events.first?.payload
+
+        // Check v_tracker field
+        let deviceTimestamp = payload?.dictionary?["dtm"] as? String
+        let expected = String(format: "%lld", Int64(currentTimestamp.timeIntervalSince1970 * 1000))
+        XCTAssertEqual(expected, deviceTimestamp)
+    }
+
+    func testWorkaroundForCampaignAttributionEnrichment() {
+        // Prepare DeepLinkReceived event
+        let event = DeepLinkReceived(url: "url")
+        event.referrer = "referrer"
+
+        // Setup tracker
+        let trackerConfiguration = TrackerConfiguration()
+        trackerConfiguration.base64Encoding = false
+        trackerConfiguration.installAutotracking = false
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, emitterConfiguration])
+
+        // Track event
+        _ = trackerController?.track(event)
+        for _ in 0..<1 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        let events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(1, events.count)
+        let payload = events.first?.payload
+
+        // Check url and referrer fields
+        let url = payload?.dictionary?[kSPPageUrl] as? String
+        let referrer = payload?.dictionary?[kSPPageRefr] as? String
+        XCTAssertEqual(url, "url")
+        XCTAssertEqual(referrer, "referrer")
+    }
+
+    func testDeepLinkContextAndAtomicPropertiesAddedToScreenView() {
+        // Prepare DeepLinkReceived event
+        let deepLink = DeepLinkReceived(url: "the_url")
+        deepLink.referrer = "the_referrer"
+
+        // Prepare ScreenView event
+        let screenView = ScreenView(name: "SV", screenId: UUID())
+
+        // Setup tracker
+        let trackerConfiguration = TrackerConfiguration()
+        trackerConfiguration.base64Encoding = false
+        trackerConfiguration.installAutotracking = false
+        let eventStore = MockEventStore()
+        let networkConfiguration = NetworkConfiguration(endpoint: "fake-url", method: .post)
+        let emitterConfiguration = EmitterConfiguration()
+        emitterConfiguration.eventStore = eventStore
+        emitterConfiguration.threadPoolSize = 10
+        let trackerController = Snowplow.createTracker(namespace: "namespace", network: networkConfiguration, configurations: [trackerConfiguration, emitterConfiguration])
+
+        // Track event
+        _ = trackerController?.track(deepLink)
+        let screenViewId = trackerController?.track(screenView)
+        for _ in 0..<2 {
+            Thread.sleep(forTimeInterval: 1)
+        }
+        let events = eventStore.emittableEvents(withQueryLimit: 10)
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(2, events.count)
+
+        var screenViewPayload: Payload? = nil
+        for event in events {
+            if (event.payload.dictionary?["eid"] as? String) == screenViewId?.uuidString {
+                screenViewPayload = event.payload
+            }
+        }
+        XCTAssertNotNil(screenViewPayload)
+
+        // Check the DeepLink context entity properties
+        let screenViewContext = screenViewPayload?.dictionary?["co"] as? String
+        XCTAssertTrue(screenViewContext?.contains("\"referrer\":\"the_referrer\"") ?? false)
+        XCTAssertTrue(screenViewContext?.contains("\"url\":\"the_url\"") ?? false)
+
+        // Check url and referrer fields for atomic table
+        let url = screenViewPayload?.dictionary?[kSPPageUrl] as? String
+        let referrer = screenViewPayload?.dictionary?[kSPPageRefr] as? String
+        XCTAssertEqual(url, "the_url")
+        XCTAssertEqual(referrer, "the_referrer")
+    }
+
+    func testPageView() {
+        let event = PageView(pageUrl: "DemoPageUrl")
+        XCTAssertEqual("DemoPageUrl", event.payload["url"] as? String)
+    }
+
+    func testStructured() {
+        let event = Structured(category: "category", action: "action")
+        XCTAssertEqual("category", event.payload["se_ca"] as? String)
+        XCTAssertEqual("action", event.payload["se_ac"] as? String)
+    }
+
+    func testUnstructured() {
+        var data: [String : NSObject] = [:]
+        data["level"] = NSNumber(value: 23)
+        data["score"] = NSNumber(value: 56473)
+        let sdj = SelfDescribingJson(
+            schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
+            andDictionary: data)
+        let event = SelfDescribing(eventData: sdj)
+        XCTAssertEqual("iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0", event.schema)
+        XCTAssertEqual(NSNumber(value: 23), event.payload["level"])
+    }
+
+    func testConsentWithdrawn() {
+        let event = ConsentWithdrawn()
+        event.name = "name"
+        event.all = false
+        event.version = "3"
+        event.documentId = "1000"
+        event.documentDescription = "description"
+        XCTAssertEqual(NSNumber(value: false), event.payload["all"])
+        XCTAssertEqual(1, event.allDocuments.count)
+    }
+
+    func testConsentGranted() {
+        let event = ConsentGranted(expiry: "expiry", documentId: "1000", version: "3")
+        event.name = "name"
+        event.documentDescription = "description"
+        XCTAssertEqual("expiry", event.payload["expiry"] as? String)
+    }
+
+    func testScreenView() {
+        let screenId = UUID()
+
+        let event = ScreenView(name: "name", screenId: screenId)
+        XCTAssertEqual("name", event.payload["name"] as? String)
+    }
+
+    func testTiming() {
+        let event = Timing(category: "cat", variable: "var", timing: 5)
+        XCTAssertEqual("cat", event.payload["category"] as? String)
+    }
+
+    func testEcommerce() {
+        let event = Ecommerce(orderId: "id", totalValue: 5, items: [])
+        XCTAssertEqual("id", event.payload["tr_id"] as? String)
+    }
+
+    func testEcommerceItem() {
+        let event = EcommerceItem(sku: "sku", price: 5.3, quantity: 5)
+        XCTAssertEqual("sku", event.payload["ti_sk"] as? String)
+    }
+
+    func testPushNotificationContent() {
+        let attachments = [
+            [
+                "identifier": "id",
+                "url": "www.test.com",
+                "type": "test"
+            ],
+            [
+                "identifier": "id2",
+                "url": "www.test2.com",
+                "type": "test2"
+            ]
+        ]
+        
+        let userInfo = [
+            "aps": [
+                "alert": "test",
+                "sound": "sound",
+                "category": "category"
+            ]
+        ]
+        
+        let event = NotificationContent(title: "title", body: "body", badge: NSNumber(value: 5))
+        event.subtitle = "subtitle"
+        event.sound = "sound"
+        event.launchImageName = "image"
+        event.userInfo = userInfo as [String : NSObject]
+        event.attachments = attachments as [NSObject]
+        XCTAssertEqual("sound", event.payload["sound"] as? String)
+    }
+
+    func testPushNotification() {
+        let attachments = [
+            [
+                "identifier": "id",
+                "url": "www.test.com",
+                "type": "test"
+            ],
+            [
+                "identifier": "id2",
+                "url": "www.test2.com",
+                "type": "test2"
+            ]
+        ]
+        
+        let userInfo = [
+            "aps": [
+                "alert": [
+                    "title": "test-title",
+                    "body": "test-body"
+                ]
+            ]
+        ]
+        
+        let content = NotificationContent(title: "title", body: "body", badge: NSNumber(value: 5))
+        content.subtitle = "subtitle"
+        content.sound = "sound"
+        content.launchImageName = "image"
+        content.userInfo = userInfo as [String : NSObject]
+        content.attachments = attachments as [NSObject]
+
+        let event = PushNotification(
+            date: "date",
+            action: "action",
+            trigger: "PUSH",
+            category: "category",
+            thread: "thread",
+            notification: content)
+        XCTAssertEqual("action", event.payload["action"] as? String)
+    }
+
+    func testMessageNotification() {
+        let event = MessageNotification(title: "title", body: "body", trigger: .push)
+        event.notificationTimestamp = "2020-12-31T15:59:60-08:00"
+        event.action = "action"
+        event.bodyLocKey = "loc key"
+        event.bodyLocArgs = ["loc arg1", "loc arg2"]
+        event.sound = "chime.mp3"
+        // TODO: commented out because Obj-C does not support the property
+        //    event.notificationCount = @9;
+        event.category = "category1"
+        event.attachments = [
+            MessageNotificationAttachment(identifier: "id", type: "type", url: "url")
+        ]
+
+        let payload = event.payload
+        XCTAssertEqual("title", payload["title"] as? String)
+        XCTAssertEqual("body", payload["body"] as? String)
+        XCTAssertEqual("2020-12-31T15:59:60-08:00", payload["notificationTimestamp"] as? String)
+        XCTAssertEqual("push", payload["trigger"] as? String)
+        XCTAssertEqual("action", payload["action"] as? String)
+        XCTAssertEqual("loc key", payload["bodyLocKey"] as? String)
+        let locArgs = (payload["bodyLocArgs"]) as? [String]
+        XCTAssertNotNil(locArgs)
+        XCTAssertEqual(2, (locArgs?.count ?? 0))
+        XCTAssertEqual("loc arg1", locArgs?[0])
+        XCTAssertEqual("loc arg2", locArgs?[1])
+        XCTAssertEqual("chime.mp3", payload["sound"] as? String)
+        //    XCTAssertEqualObjects(@9, payload["notificationCount"]);
+        XCTAssertEqual("category1", payload["category"] as? String)
+        let attachments = (payload["attachments"]) as? [[String : NSObject]]
+        XCTAssertNotNil(attachments)
+        XCTAssertEqual(1, (attachments?.count ?? 0))
+        let attachment = attachments?[0] as? [String : NSObject]
+        XCTAssertEqual("id", attachment?["identifier"] as? String)
+        XCTAssertEqual("type", attachment?["type"] as? String)
+        XCTAssertEqual("url", attachment?["url"] as? String)
+    }
+
+    func testMessageNotificationWithUserInfo() {
+        let userInfo: [String : Any] = [
+            "aps": [
+                "alert": [
+                    "title": "test-title",
+                    "body": "test-body",
+                    "loc-key": "loc key",
+                    "loc-args": ["loc arg1", "loc arg2"]
+                ],
+                "sound": "chime.aiff",
+                "badge": NSNumber(value: 9),
+                "category": "category1",
+                "content-available": NSNumber(value: 1)
+            ],
+            "custom-element": NSNumber(value: 1)
+        ]
+        let event = MessageNotification.messageNotification(userInfo: userInfo as! [String : NSObject], defaultTitle: nil, defaultBody: nil)!
+        let payload = event.payload
+        XCTAssertEqual("test-title", payload["title"] as? String)
+        XCTAssertEqual("test-body", payload["body"] as? String)
+        XCTAssertEqual("loc key", payload["bodyLocKey"] as? String)
+        let locArgs = payload["bodyLocArgs"] as? [AnyHashable]
+        XCTAssertEqual(2, (locArgs?.count ?? 0))
+        XCTAssertEqual("loc arg1", locArgs?[0] as? String)
+        XCTAssertEqual("loc arg2", locArgs?[1] as? String)
+        XCTAssertEqual(NSNumber(value: 9), payload["notificationCount"])
+        XCTAssertEqual("chime.aiff", payload["sound"] as? String)
+        XCTAssertEqual("category1", payload["category"] as? String)
+        XCTAssertEqual(NSNumber(value: true), payload["contentAvailable"])
+    }
+
+    func testError() {
+        // Valid construction
+        let error = SNOWError(message: "message")
+        error.name = "name"
+        error.stackTrace = "stacktrace"
+        XCTAssertEqual("name", error.payload["exceptionName"] as? String)
+    }
+
+    func testTrackerError() {
+        let trackerError = TrackerError(source: "classname", message: "message", error: nil, exception: NSException(name: NSExceptionName("CustomException"), reason: "reason", userInfo: nil))
+        let payload = trackerError.payload
+        XCTAssertEqual(payload["message"] as? String, "message")
+        XCTAssertEqual(payload["className"] as? String, "classname")
+        XCTAssertEqual(payload["exceptionName"] as? String, "CustomException")
+    }
+}
diff --git a/Tests/TestGeneratedJsons.swift b/Tests/TestGeneratedJsons.swift
new file mode 100644
index 000000000..850cd932a
--- /dev/null
+++ b/Tests/TestGeneratedJsons.swift
@@ -0,0 +1,390 @@
+//
+//  TestGeneratedJsons.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+let IGLU_PATH = "http://raw.githubusercontent.com/snowplow/iglu-central/master/schemas/"
+
+class TestGeneratedJsons: XCTestCase {
+    private var validator: IGLUClient?
+
+    override func setUp() {
+        super.setUp()
+        validator = IGLUClient(jsonString: getJSONAsString(withFilePath: "iglu_resolver.json"), andBundles: [Bundle(for: TestGeneratedJsons.self)])
+    }
+
+    override func tearDown() {
+        validator = nil
+        super.tearDown()
+    }
+
+    func testScreenContextJson() {
+        let stateMachine = ScreenStateMachine()
+        let fakeEvent = TrackerEvent(event: Structured(category: "fake", action: "fake"), state: nil)
+        let screenState = ScreenState(name: "name", type: "type", screenId: nil, transitionType: "transition", topViewControllerClassName: "topVCname", viewControllerClassName: "VCname")
+        let entities = stateMachine.entities(from: fakeEvent, state: screenState)
+        let screenContext = entities?.first
+        XCTAssertNotNil(screenContext)
+        XCTAssertTrue(validator!.validateJson(screenContext?.dictionary))
+    }
+
+    // TODO: this test fails for reasons I don't understand
+//    func testClientSessionContextJson() {
+//        let session = Session(foregroundTimeout: 1800, andBackgroundTimeout: 1800)
+//        let data = session.getDictWithEventId(Utilities.getUUIDString(), eventTimestamp: 1654496481346, userAnonymisation: false)
+//        let json = SelfDescribingJson(schema: kSPSessionContextSchema, andDictionary: data!).getAsDictionary()
+//        XCTAssertTrue(validator!.validateJson(json))
+//    }
+
+    //#pragma clang diagnostic push
+    //#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+    func testPlatformContextJson() {
+        let subject = Subject(platformContext: true, andGeoContext: true)
+        let data = subject.getPlatformDict(withUserAnonymisation: false)?.dictionary
+        var json: [String : NSObject]?
+        #if os(iOS)
+        json = SelfDescribingJson(schema: kSPMobileContextSchema, andDictionary: data!).dictionary
+        #else
+        json = SelfDescribingJson(schema: kSPDesktopContextSchema, andDictionary: data!).dictionary
+        #endif
+        XCTAssertTrue(validator!.validateJson(json))
+    }
+
+    func testGeoContextJson() {
+        let subject = Subject(platformContext: false, andGeoContext: true)
+        subject.geoLongitude = NSNumber(value: 5)
+        subject.geoLatitude = NSNumber(value: 89.2)
+        subject.geoTimestamp = NSNumber(value: 5)
+        subject.geoLatitudeLongitudeAccuracy = NSNumber(value: 5.5)
+        subject.geoSpeed = NSNumber(value: 6.2)
+        subject.geoBearing = NSNumber(value: 82.3)
+        subject.geoAltitude = NSNumber(value: 62.3)
+        subject.geoAltitudeAccuracy = NSNumber(value: 16.3)
+        let data = subject.getGeoLocationDict()
+        let json = SelfDescribingJson(schema: kSPGeoContextSchema, andDictionary: data!).dictionary
+        XCTAssertTrue(validator!.validateJson(json))
+    }
+
+    //#pragma clang diagnostic pop
+
+    func testGdprContextJson() {
+        let gdpr = GDPRContext(
+            basis: .consent,
+            documentId: "id",
+            documentVersion: "version",
+            documentDescription: "description")
+        XCTAssertTrue(validator!.validateJson(gdpr.context.dictionary))
+    }
+
+    func testStructuredEventPayloadJson() {
+        let tracker = getTracker("acme.fake.url")
+        tracker.base64Encoded = false
+        let event = Structured(category: "DemoCategory", action: "DemoAction")
+        event.label = "DemoLabel"
+        event.property = "DemoProperty"
+        event.value = NSNumber(value: 5)
+
+        // Check that the final payload passes validation
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+        let data = tracker.payload(with: trackerEvent).dictionary
+
+        let dataArray = [data] as NSObject
+        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
+
+        XCTAssertTrue(validator!.validateJson(json))
+    }
+
+    func testUnstructuredEventPayloadJson() {
+        let tracker = getTracker("acme.fake.url")
+        tracker.base64Encoded = false
+        var input: [String : NSObject] = [:]
+        input["level"] = NSNumber(value: 23)
+        input["score"] = NSNumber(value: 56473)
+        let sdj = SelfDescribingJson(
+            schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
+            andDictionary: input)
+        let event = SelfDescribing(eventData: sdj)
+
+        // Check that the final payload passes validation
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+        let data = tracker.payload(with: trackerEvent).dictionary
+
+        let dataArray = [data] as NSObject
+        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
+
+        XCTAssertTrue(validator!.validateJson(json))
+
+        // Check that the nested unstructured event passes validation
+        let ue_pr = data?["ue_pr"] as? String
+        var unstructDictionary: [AnyHashable : Any]? = nil
+        do {
+            if let aData = ue_pr?.data(using: .utf8) {
+                unstructDictionary = try JSONSerialization.jsonObject(with: aData, options: []) as? [AnyHashable : Any]
+            }
+        } catch {
+        }
+
+        XCTAssertTrue(validator!.validateJson(unstructDictionary))
+    }
+
+    func testSelfDescribingEventPayloadJson() {
+        let tracker = getTracker("acme.fake.url")
+        tracker.base64Encoded = false
+        var input: [String : NSObject] = [:]
+        input["level"] = NSNumber(value: 23)
+        input["score"] = NSNumber(value: 56473)
+        let sdj = SelfDescribingJson(
+            schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
+            andDictionary: input)
+        let event = SelfDescribing(eventData: sdj)
+
+        // Check that the final payload passes validation
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+        let data = tracker.payload(with: trackerEvent).dictionary
+
+        let dataArray = [data] as NSObject
+        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
+
+        XCTAssertTrue(validator!.validateJson(json))
+
+        // Check that the nested unstructured event passes validation
+        let ue_pr = data?["ue_pr"] as? String
+        var unstructDictionary: [AnyHashable : Any]? = nil
+        do {
+            if let aData = ue_pr?.data(using: .utf8) {
+                unstructDictionary = try JSONSerialization.jsonObject(with: aData, options: []) as? [AnyHashable : Any]
+            }
+        } catch {
+        }
+
+        XCTAssertTrue(validator!.validateJson(unstructDictionary))
+    }
+
+    func testConsentWithdrawnEventPayloadJson() {
+        let event = ConsentWithdrawn()
+        event.documentDescription = "Description"
+        event.documentId = "1234"
+        event.version = "10"
+        event.all = false
+        event.name = "Name"
+
+        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testConsentDocumentEventPayloadJson() {
+        let event = ConsentDocument(documentId: "1234", version: "10")
+        event.documentDescription = "Description"
+        event.name = "Name"
+
+        let sdj = event.payload.dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testConsentGrantedEventPayloadJson() {
+        let event = ConsentGranted(expiry: "2012-04-23T18:25:43.511Z", documentId: "1234", version: "10")
+        event.documentDescription = "Description"
+        event.name = "Name"
+
+        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testPageViewEventPayloadJson() {
+        let tracker = getTracker("acme.fake.url")
+        let event = PageView(pageUrl: "DemoPageUrl")
+        event.pageTitle = "DemoPageTitle"
+        event.referrer = "DemoPageReferrer"
+
+        // Check that the final payload passes validation
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+        let data = tracker.payload(with: trackerEvent).dictionary
+
+        let dataArray = [data] as NSObject
+        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
+
+        XCTAssertTrue(validator!.validateJson(json))
+    }
+
+    func testEcommerceEventPayloadJson() {
+        let tracker = getTracker("acme.fake.url")
+
+        let transactionID = "6a8078be"
+        var itemArray: [EcommerceItem] = []
+        let item = EcommerceItem(sku: "DemoItemSku", price: 0.75, quantity: 1)
+        item.name = "DemoItemName"
+        item.category = "DemoItemCategory"
+        item.currency = "USD"
+
+        itemArray.append(item)
+        let event = Ecommerce(orderId: transactionID, totalValue: 350, items: itemArray)
+        event.affiliation = "DemoTranAffiliation"
+        // TODO: incompatible properties with ObjC
+        //    event.taxValue = 10;
+        //    event.shipping = 15;
+        event.city = "Boston"
+        event.state = "Massachusetts"
+        event.country = "USA"
+        event.currency = "USD"
+
+        // Check that the main payload passes validation
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+        var data = tracker.payload(with: trackerEvent).dictionary
+
+        var dataArray = [data] as NSObject
+        var json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
+
+        XCTAssertTrue(validator!.validateJson(json))
+
+        // Check that the item payload passes validation
+        data = tracker.payload(with: trackerEvent).dictionary
+
+        dataArray = [data] as NSObject
+        json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
+
+        XCTAssertTrue(validator!.validateJson(json))
+    }
+
+    func testTimingEventJson() {
+        let event = Timing(category: "DemoTimingCategory", variable: "DemoTimingVariable", timing: 5)
+        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testScreenViewEventJson() {
+        let event = ScreenView(name: "DemoScreenName", screenId: UUID())
+        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testPushNotificationEventJson() {
+        var attachments: [AnyHashable] = []
+        attachments.append(
+            [
+                kSPPnAttachmentId: "identifier",
+                kSPPnAttachmentUrl: "url",
+                kSPPnAttachmentType: "type"
+            ])
+        
+        let userInfo = [
+            "aps": [
+                "alert": [
+                    "title": "test title",
+                    "body": "test",
+                    "loc-key": "test key"
+                ],
+                "content-available": NSNumber(value: 0)
+            ] as NSObject
+        ]
+        
+        let content = NotificationContent(title: "title", body: "body", badge: NSNumber(value: 5))
+        content.subtitle = "subtitle"
+        content.sound = "sound"
+        content.launchImageName = "launchImageName"
+        content.userInfo = userInfo
+
+        let event = PushNotification(date: "date", action: "action", trigger: "PUSH", category: "category", thread: "thread", notification: content)
+
+        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testMessageNotificationEventJson() {
+        let userInfo = [
+            "aps": [
+                "alert": [
+                    "title": "test title",
+                    "body": "test",
+                    "loc-key": "test key"
+                ],
+                "content-available": NSNumber(value: 0)
+            ] as NSObject
+        ]
+        let event = MessageNotification.messageNotification(userInfo: userInfo, defaultTitle: nil, defaultBody: nil)
+        let sdj = SelfDescribingJson(schema: event!.schema, andDictionary: event!.payload).dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testApplicationInstallJson() {
+        let installEvent = SelfDescribingJson(schema: kSPApplicationInstallSchema, andDictionary: [String : NSObject]())
+        let json = installEvent.dictionary
+        XCTAssertTrue(validator!.validateJson(json))
+    }
+
+    func testApplicationContextJson() {
+        let json = Utilities.getApplicationContext(withVersion: "testversion", andBuild: "testbuild")
+        XCTAssertTrue(validator!.validateJson(json.dictionary))
+    }
+
+    func testErrorEventJson() {
+        let event = SNOWError(message: "some error message")
+        event.name = "some exception name"
+        event.stackTrace = "some stack trace"
+        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
+        XCTAssertTrue(validator!.validateJson(sdj))
+    }
+
+    func testFinalEventPayloadJson() {
+        let tracker = getTracker("acme.fake.url")
+        let event = PageView(pageUrl: "DemoPageUrl")
+        event.pageTitle = "DemoPageTitle"
+        event.referrer = "DemoPageReferrer"
+
+        // Check that the final payload passes validation
+        let trackerEvent = TrackerEvent(event: event, state: nil)
+        let data = tracker.payload(with: trackerEvent).dictionary
+
+        let dataArray = [data] as NSObject
+        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
+        XCTAssertTrue(validator!.validateJson(json))
+
+        // Check that the nested context json passes validation
+        let contextsJson = data?["co"] as? String
+        var contextDictionary: [AnyHashable : Any]? = nil
+        if let aData = contextsJson?.data(using: .utf8) {
+            contextDictionary = try? JSONSerialization.jsonObject(with: aData, options: []) as? [AnyHashable : Any]
+        }
+        XCTAssertTrue(validator!.validateJson(contextDictionary))
+    }
+
+    func getJSONAsString(withFilePath filePath: String?) -> String? {
+        let path = Bundle(for: TestGeneratedJsons.self).path(forResource: filePath, ofType: nil, inDirectory: "Products")
+        if let data = NSData(contentsOfFile: path ?? "") as Data? {
+            return String(data: data, encoding: .utf8)
+        }
+        return nil
+    }
+
+    func getTracker(_ url: String) -> Tracker {
+        let endpoint = "https://\(url)"
+        let networkConfig = NetworkConfiguration(endpoint: endpoint, method: .post)
+        let trackerConfig = TrackerConfiguration(appId: "anAppId")
+        trackerConfig.platformContext = true
+        trackerConfig.geoLocationContext = true
+        trackerConfig.base64Encoding = false
+        trackerConfig.sessionContext = true
+        let serviceProvider = ServiceProvider(namespace: "aNamespace", network: networkConfig, configurations: [trackerConfig])
+        return serviceProvider.tracker
+    }
+}
diff --git a/Tests/TestLifecycleState.swift b/Tests/TestLifecycleState.swift
new file mode 100644
index 000000000..eeee4db40
--- /dev/null
+++ b/Tests/TestLifecycleState.swift
@@ -0,0 +1,100 @@
+//
+//  TestLifecycleState.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestLifecycleState: XCTestCase {
+    override func setUp() {
+        super.setUp()
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testLifecycleStateMachine() {
+        let eventStore = MockEventStore()
+        let emitter = Emitter(urlEndpoint: "http://snowplow-fake-url.com") { emitter in
+            emitter.eventStore = eventStore
+        }
+        let tracker = Tracker(trackerNamespace: "namespace", appId: nil, emitter: emitter) { tracker in
+            tracker.base64Encoded = false
+            tracker.lifecycleEvents = true
+        }
+
+        // Send events
+        _ = tracker.track(Timing(category: "category", variable: "variable", timing: 123))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        var payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        var entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains("\"isVisible\":true"))
+
+        _ = tracker.track(Background(index: 1))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains("\"isVisible\":false"))
+
+        _ = tracker.track(Timing(category: "category", variable: "variable", timing: 123))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertTrue(entities!.contains("\"isVisible\":false"))
+
+        _ = tracker.track(Foreground(index: 1))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains("\"isVisible\":true"))
+
+        let uuid = UUID()
+        _ = tracker.track(ScreenView(name: "screen1", screenId: uuid))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains("\"isVisible\":true"))
+    }
+}
diff --git a/Tests/TestLogger.swift b/Tests/TestLogger.swift
new file mode 100644
index 000000000..997d31836
--- /dev/null
+++ b/Tests/TestLogger.swift
@@ -0,0 +1,101 @@
+//
+//  TestLogger.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class MockDiagnosticLogger: NSObject {
+    var callback: ((_ tag: String?, _ message: String?, _ error: Error?, _ exception: NSException?) -> Void)?
+
+    override init() {
+        super.init()
+        NotificationCenter.default.addObserver(self, selector: #selector(logDiagnosticError(_:)), name: NSNotification.Name("SPTrackerDiagnostic"), object: nil)
+    }
+
+    @objc func logDiagnosticError(_ notification: Notification?) {
+        let userInfo = notification?.userInfo
+        let tag = userInfo?["tag"] as? String
+        let message = userInfo?["message"] as? String
+        let error = userInfo?["error"] as? Error
+        let exception = userInfo?["exception"] as? NSException
+        callback?(tag, message, error, exception)
+    }
+
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+}
+
+class TestLogger: XCTestCase {
+    override func setUp() {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+
+    override func tearDown() {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+    }
+
+    func testDiagnosticTracking() {
+        let expectation = XCTestExpectation()
+        let diagnostic = MockDiagnosticLogger()
+        diagnostic.callback = { tag, message, error, exception in
+            XCTAssertEqual(tag, String(describing: TestLogger.self))
+            let expectedMessage = "Error test \(1) \(NSNumber(value: 12.3))"
+            XCTAssertEqual(message, expectedMessage)
+            expectation.fulfill()
+        }
+
+        Logger.diagnostic(String(describing: TestLogger.self), message: String(format: "Error test %d %@", 1, NSNumber(value: 12.3)), errorOrException: nil)
+        wait(for: [expectation], timeout: 10)
+    }
+
+    func testDiagnosticTrackingWithError() {
+        let expectation = XCTestExpectation()
+        let raisedError = NSError(domain: NSURLErrorDomain, code: 400, userInfo: nil)
+
+        let diagnostic = MockDiagnosticLogger()
+        diagnostic.callback = { tag, message, error, exception in
+            XCTAssertEqual(tag, String(describing: TestLogger.self))
+            XCTAssertEqual(message, "Error test")
+            XCTAssert(error as? NSError == raisedError)
+            expectation.fulfill()
+        }
+
+        Logger.diagnostic(String(describing: TestLogger.self), message: "Error test", errorOrException: raisedError)
+        wait(for: [expectation], timeout: 10)
+    }
+
+    func testDiagnosticTrackingWithException() {
+        let expectation = XCTestExpectation()
+        let raisedException = NSException(name: .invalidArgumentException, reason: nil, userInfo: nil)
+
+        let diagnostic = MockDiagnosticLogger()
+        diagnostic.callback = { tag, message, error, exception in
+            XCTAssertEqual(tag, String(describing: TestLogger.self))
+            XCTAssertEqual(message, "Exception test")
+            XCTAssertEqual(exception, raisedException)
+            expectation.fulfill()
+        }
+
+        Logger.diagnostic(String(describing: TestLogger.self), message: "Exception test", errorOrException: raisedException)
+        wait(for: [expectation], timeout: 10)
+    }
+}
diff --git a/Tests/TestMemoryEventStore.swift b/Tests/TestMemoryEventStore.swift
new file mode 100644
index 000000000..95613fec2
--- /dev/null
+++ b/Tests/TestMemoryEventStore.swift
@@ -0,0 +1,75 @@
+//
+//  TestMemoryEventStore.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestMemoryEventStore: XCTestCase {
+    func testInit() {
+        let eventStore = MemoryEventStore()
+        XCTAssertNotNil(eventStore)
+    }
+
+    func testInsertPayload() {
+        let eventStore = MemoryEventStore()
+        _ = eventStore.removeAllEvents()
+
+        // Build an event
+        let payload = Payload()
+        payload.addValueToPayload("pv", forKey: "e")
+        payload.addValueToPayload("www.foobar.com", forKey: "url")
+        payload.addValueToPayload("Welcome to foobar!", forKey: "page")
+        payload.addValueToPayload("MEEEE", forKey: "refr")
+
+        // Insert an event
+        eventStore.addEvent(payload)
+
+        XCTAssertEqual(eventStore.count(), 1)
+        let events = eventStore.emittableEvents(withQueryLimit: 1)
+        XCTAssertEqual(events[0].payload.dictionary, payload.dictionary)
+        _ = eventStore.removeEvent(withId: 0)
+
+        XCTAssertEqual(eventStore.count(), 0)
+    }
+
+    func testInsertManyPayloads() {
+        let eventStore = MemoryEventStore()
+        _ = eventStore.removeAllEvents()
+
+        // Build an event
+        let payload = Payload()
+        payload.addValueToPayload("pv", forKey: "e")
+        payload.addValueToPayload("www.foobar.com", forKey: "url")
+        payload.addValueToPayload("Welcome to foobar!", forKey: "page")
+        payload.addValueToPayload("MEEEE", forKey: "refr")
+
+        for _ in 0..<250 {
+            eventStore.addEvent(payload)
+        }
+
+        XCTAssertEqual(eventStore.count(), 250)
+        XCTAssertEqual(eventStore.emittableEvents(withQueryLimit: 600).count, 250)
+        XCTAssertEqual(eventStore.emittableEvents(withQueryLimit: 150).count, 150)
+
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(eventStore.count(), 0)
+    }
+}
diff --git a/Tests/TestNetworkConnection.swift b/Tests/TestNetworkConnection.swift
new file mode 100644
index 000000000..68de1de60
--- /dev/null
+++ b/Tests/TestNetworkConnection.swift
@@ -0,0 +1,195 @@
+//
+//  TestNetworkConnection.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Nocilla
+import XCTest
+@testable import SnowplowTracker
+
+let TEST_URL_ENDPOINT = "acme.test.url.com"
+
+class TestNetworkConnection: XCTestCase {
+    override func setUp() {
+        super.setUp()
+        if LSNocilla.sharedInstance().isStarted {
+            LSNocilla.sharedInstance().stop()
+        }
+        LSNocilla.sharedInstance().start()
+    }
+
+    override func tearDown() {
+        super.tearDown()
+        LSNocilla.sharedInstance().clearStubs()
+        LSNocilla.sharedInstance().stop()
+    }
+
+    func testGetRequestWithSuccess() {
+        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
+        _ = stubRequest("GET", regex).andReturn(200)
+        
+        let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .get)
+
+        let payload = Payload()
+        payload.addValueToPayload("value", forKey: "key")
+        let request = Request(payload: payload, emitterEventId: 1)
+        let results = connection.sendRequests([request])
+
+        // Check successful result
+        let result = results[0]
+        XCTAssertTrue(result.isSuccessful)
+        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+    }
+
+    func testGetRequestWithNoSuccess() {
+        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
+        _ = stubRequest("GET", regex).andReturn(404)
+
+        let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .get)
+        
+        let payload = Payload()
+        payload.addValueToPayload("value", forKey: "key")
+        let request = Request(payload: payload, emitterEventId: 1)
+        let results = connection.sendRequests([request])
+
+        // Check unsuccessful result
+        let result = results[0]
+        XCTAssertFalse(result.isSuccessful)
+        XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
+    }
+
+    func testPostRequestWithSuccess() {
+        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
+        _ = stubRequest("POST", regex).andReturn(200)
+
+        let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
+        
+        let payload = Payload()
+        payload.addValueToPayload("value", forKey: "key")
+        let request = Request(payload: payload, emitterEventId: 1)
+        let results = connection.sendRequests([request])
+
+        // Check successful result
+        let result = results[0]
+        XCTAssertTrue(result.isSuccessful)
+        XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
+    }
+
+    func testPostRequestWithNoSuccess() {
+        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
+        _ = stubRequest("POST", regex).andReturn(404)
+
+        let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
+
+        let payload = Payload()
+        payload.addValueToPayload("value", forKey: "key")
+        let request = Request(payload: payload, emitterEventId: 1)
+        let results = connection.sendRequests([request])
+
+        // Check unsuccessful result
+        let result = results[0]
+        XCTAssertFalse(result.isSuccessful)
+        XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
+    }
+
+    func testFreeEndpoint_GetHttpsUrl() {
+        let connection = DefaultNetworkConnection(urlString: "acme.test.url.com", httpMethod: .post)
+        XCTAssertTrue(connection.urlEndpoint!.absoluteString.hasPrefix("https://acme.test.url.com"))
+    }
+
+    func testHttpsEndpoint_GetHttpsUrl() {
+        let connection = DefaultNetworkConnection(urlString: "https://acme.test.url.com", httpMethod: .post)
+        XCTAssertTrue(connection.urlEndpoint!.absoluteString.hasPrefix("https://acme.test.url.com"))
+    }
+
+    func testHttpEndpoint_GetHttpUrl() {
+        let connection = DefaultNetworkConnection(urlString: "http://acme.test.url.com", httpMethod: .post)
+        XCTAssertTrue(connection.urlEndpoint!.absoluteString.hasPrefix("http://acme.test.url.com"))
+    }
+
+    func testStripsTrailingSlashInEndpoint() {
+        let connection = DefaultNetworkConnection(urlString: "http://acme.test.url.com/", httpMethod: .get)
+        XCTAssertTrue((connection.urlEndpoint?.absoluteString == "http://acme.test.url.com/i"))
+    }
+
+    func testDoesntAddHeaderWithoutServerAnonymisation() {
+        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
+        _ = stubRequest("POST", regex).withHeader(
+            "SP-Anonymous",
+            "*")?.andReturn(
+            500)
+        _ = stubRequest("POST", regex).andReturn(
+            200)
+
+        let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
+        connection.serverAnonymisation = false
+
+        let payload = Payload()
+        payload.addValueToPayload("value", forKey: "key")
+        let request = Request(payload: payload, emitterEventId: 1)
+        let results = connection.sendRequests([request])
+
+        // Check successful result
+        let result = results[0]
+        XCTAssertTrue(result.isSuccessful)
+        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+    }
+
+    func testAddsHeaderForServerAnonymisationForPostRequest() {
+        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
+        _ = stubRequest("POST", regex).withHeader(
+            "SP-Anonymous",
+            "*")?.andReturn(
+            200)
+
+        let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
+        connection.serverAnonymisation = true
+
+        let payload = Payload()
+        payload.addValueToPayload("value", forKey: "key")
+        let request = Request(payload: payload, emitterEventId: 1)
+        let results = connection.sendRequests([request])
+
+        // Check successful result
+        let result = results[0]
+        XCTAssertTrue(result.isSuccessful)
+        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+    }
+
+    func testAddsHeaderForServerAnonymisationForGetRequest() {
+        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
+       _ = stubRequest("GET", regex).withHeader(
+            "SP-Anonymous",
+            "*")?.andReturn(
+            200)
+
+        let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .get)
+        connection.serverAnonymisation = true
+
+        let payload = Payload()
+        payload.addValueToPayload("value", forKey: "key")
+        let request = Request(payload: payload, emitterEventId: 1)
+        let results = connection.sendRequests([request])
+
+        // Check successful result
+        let result = results[0]
+        XCTAssertTrue(result.isSuccessful)
+        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+    }
+}
diff --git a/Tests/TestPayload.swift b/Tests/TestPayload.swift
new file mode 100644
index 000000000..f868a3a37
--- /dev/null
+++ b/Tests/TestPayload.swift
@@ -0,0 +1,326 @@
+//
+//  TestPayload.swift
+//  SnowplowTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestPayload: XCTestCase {
+    override func setUp() {
+        super.setUp()
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testInit() {
+        let sample_payload = Payload()
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            [String : NSObject]())
+
+    }
+
+    func testInitWithNSDictionary() {
+        let sample_dict: [String : NSObject] = [
+            "Key1": "Value1" as NSObject,
+            "Key2": "Value2" as NSObject
+        ]
+        let sample_payload = Payload(dictionary: sample_dict)
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dict)
+        XCTAssertTrue(sample_payload.description.contains("\"Key1\": Value1"))
+        XCTAssertTrue(sample_payload.description.contains("\"Key2\": Value2"))
+    }
+
+    func testInitWithWrongDictionary() {
+        let sample_dict: [String : NSObject] = [
+            "Key1": "Value1" as NSObject,
+            "Key2": "Value2" as NSObject
+        ]
+        let sample_dict2: [String : NSObject] = [
+            "Key2": "Value1" as NSObject,
+            "Key1": "Value2" as NSObject
+        ]
+        let sample_payload = Payload(dictionary: sample_dict)
+
+        XCTAssertNotEqual(
+            sample_payload.dictionary,
+            sample_dict2,
+            "Payload is not initialized with the correct JSON or NSDictionary")
+    }
+
+    func testAddValueToPayload() {
+        let sample_dict: [String : NSObject] = [
+            "Key1": "Value1" as NSObject
+        ]
+        let sample_payload = Payload()
+        sample_payload.addValueToPayload("Value1", forKey: "Key1")
+
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dict)
+    }
+
+    func testAddValueToPayload2() {
+        let sample_dict: [String : NSObject] = [
+            "Key2": "Value2" as NSObject
+        ]
+        let sample_payload = Payload()
+        sample_payload.addValueToPayload("Value1", forKey: "Key1")
+
+
+        XCTAssertNotEqual(
+            sample_payload.dictionary,
+            sample_dict,
+            "Payload should not be the same as sample_dict")
+    }
+
+    func testAddValueToPayload3() {
+        let sample_dict_init: [String : NSObject] = [
+            "Key1": "Value1" as NSObject
+        ]
+        let sample_dict_final: [String : NSObject] = [
+            "Key1": "Value1" as NSObject,
+            "Key2": "Value2" as NSObject
+        ]
+        let sample_payload = Payload(dictionary: sample_dict_init)
+        sample_payload.addValueToPayload("Value2", forKey: "Key2")
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dict_final)
+    }
+
+    func testAddNilValueToPayload() {
+        let payload = Payload()
+        payload.addValueToPayload(nil, forKey: "foo")
+        XCTAssertEqual(payload.dictionary, [String : NSObject]())
+    }
+
+    func testAddNilValueToPayloadUnsetsKey() {
+        let payload = Payload(dictionary: [
+            "foo": "bar" as NSObject
+        ])
+        payload.addValueToPayload(nil, forKey: "foo")
+        XCTAssertEqual(payload.dictionary, [String : NSObject]())
+    }
+
+    func testAddNumericValueToPayload() {
+        let sample_dict = [
+            "Key1": NSNumber(value: 100)
+        ]
+        let sample_payload = Payload()
+        sample_payload.addNumericValueToPayload(NSNumber(value: 100), forKey: "Key1")
+
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dict)
+    }
+
+    func testAddNilNumericValueToPayload() {
+        let sample_payload = Payload()
+        sample_payload.addNumericValueToPayload(nil, forKey: "Key1")
+
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            [String : NSObject]())
+    }
+
+    func testAddNilNumericValueToPayloadUnsetsKey() {
+        let sample_payload = Payload(dictionary: [
+            "Key1": NSNumber(value: 100)
+        ])
+        sample_payload.addNumericValueToPayload(nil, forKey: "Key1")
+
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            [String : NSObject]())
+    }
+
+    func testAddDictToPayload() {
+        let sample_dic = [
+            "Key1": "Value1" as NSObject
+        ]
+        let sample_payload = Payload()
+        sample_payload.addDictionaryToPayload(sample_dic)
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dic)
+    }
+
+    func testAddDictToPayload2() {
+        let sample_dic = [
+            "Key1": "Value1" as NSObject
+        ]
+        let sample_dic2 = [
+            "Key2": "Value2" as NSObject
+        ]
+        let sample_dict_final = [
+            "Key1": "Value1" as NSObject,
+            "Key2": "Value2" as NSObject
+        ]
+        let sample_payload = Payload(dictionary: sample_dic)
+        sample_payload.addDictionaryToPayload(sample_dic2)
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dict_final)
+    }
+
+    func testAddDictToPayload3() {
+        let sample_dic = [
+            "Key1": "Value1" as NSObject
+        ]
+        let sample_dic2 = [
+            "Key2": NSNumber(value: 2)
+        ]
+        let sample_dict_final = [
+            "Key1": "Value1" as NSObject
+        ]
+
+        let sample_payload = Payload(dictionary: sample_dic)
+        sample_payload.addDictionaryToPayload(sample_dic2)
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dict_final)
+    }
+
+    func testJsonToPayload() {
+        // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
+
+        let sample_dic = [
+            "Key1": "Value1"
+        ]
+        let sample_enc = [
+            "type_enc": "eyJLZXkxIjoiVmFsdWUxIn0" as NSObject
+        ]
+
+        // NSDictionary conversion to JSON string
+        let somedata = try? JSONSerialization.data(withJSONObject: sample_dic, options: [])
+
+        let sample_payload = Payload()
+        sample_payload.addJsonToPayload(
+            somedata!,
+            base64Encoded: true,
+            typeWhenEncoded: "type_enc",
+            typeWhenNotEncoded: "type_notenc")
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_enc)
+    }
+
+    func testJsonToPayload2() {
+        // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
+
+        let sample_dic = [
+            "Key1": "Value1"
+        ]
+        let sample_enc = [
+            "type_notenc": "{\"Key1\":\"Value1\"}" as NSObject
+        ]
+
+        // NSDictionary conversion to JSON string
+        let somedata = try! JSONSerialization.data(withJSONObject: sample_dic, options: [])
+
+        let sample_payload = Payload()
+        sample_payload.addJsonToPayload(
+            somedata,
+            base64Encoded: false,
+            typeWhenEncoded: "type_enc",
+            typeWhenNotEncoded: "type_notenc")
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_enc)
+    }
+
+    func testJsonStringToPayload() {
+        // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
+
+        let sample_enc = [
+            "type_notenc": "{\"Key1\":\"Value1\"}" as NSObject
+        ]
+        let json_str = "{\"Key1\":\"Value1\"}"
+
+        let sample_payload = Payload()
+        sample_payload.addJsonStringToPayload(
+            json_str,
+            base64Encoded: false,
+            typeWhenEncoded: "type_enc",
+            typeWhenNotEncoded: "type_notenc")
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_enc)
+    }
+
+    func testJsonStringToPayload2() {
+        // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
+
+        let sample_enc = [
+            "type_enc": "eyJLZXkxIjoiVmFsdWUxIn0" as NSObject
+        ]
+        let json_str = "{\"Key1\":\"Value1\"}"
+
+        let sample_payload = Payload()
+        sample_payload.addJsonStringToPayload(
+            json_str,
+            base64Encoded: true,
+            typeWhenEncoded: "type_enc",
+            typeWhenNotEncoded: "type_notenc")
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_enc)
+    }
+
+    func testgetPayloadAsDictionary() {
+        let sample_payload = Payload()
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            [String : NSObject]())
+    }
+
+    func testgetPayloadAsDictionary2() {
+        let sample_dict = [
+            "Key1": "Value1" as NSObject
+        ]
+        let sample_payload = Payload(dictionary: [
+            "Key1": "Value1" as NSObject
+        ])
+
+        XCTAssertEqual(
+            sample_payload.dictionary,
+            sample_dict)
+    }
+}
diff --git a/Tests/TestPlatformContext.swift b/Tests/TestPlatformContext.swift
new file mode 100644
index 000000000..598f98c37
--- /dev/null
+++ b/Tests/TestPlatformContext.swift
@@ -0,0 +1,197 @@
+//
+//  TestPlatformContext.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestPlatformContext: XCTestCase {
+    func testContainsPlatformInfo() {
+        let context = PlatformContext()
+        let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary
+        XCTAssertNotNil(platformDict)
+        XCTAssertNotNil(platformDict)
+    }
+
+    func testContainsMobileInfo() {
+        #if os(iOS)
+        let context = PlatformContext()
+        let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary
+        XCTAssertNotNil(platformDict)
+        XCTAssertNotNil(platformDict)
+        #endif
+    }
+
+    func testAddsAllMockedInfo() {
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        guard let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary else {
+            return XCTFail()
+        }
+        XCTAssertEqual("appleIdfa" as NSObject, platformDict[kSPMobileAppleIdfa])
+        XCTAssertEqual("appleIdfv" as NSObject, platformDict[kSPMobileAppleIdfv])
+        XCTAssertEqual("Apple Inc." as NSObject, platformDict[kSPPlatformDeviceManu])
+        XCTAssertEqual("deviceModel" as NSObject, platformDict[kSPPlatformDeviceModel])
+        XCTAssertEqual("13.0.0" as NSObject, platformDict[kSPPlatformOsVersion])
+        XCTAssertEqual("ios" as NSObject, platformDict[kSPPlatformOsType])
+        XCTAssertEqual("att" as NSObject, platformDict[kSPMobileCarrier])
+        XCTAssertEqual("3g" as NSObject, platformDict[kSPMobileNetworkTech])
+        XCTAssertEqual("wifi" as NSObject, platformDict[kSPMobileNetworkType])
+        XCTAssertEqual(NSNumber(value: 20), platformDict[kSPMobileBatteryLevel])
+        XCTAssertEqual("charging" as NSObject, platformDict[kSPMobileBatteryState])
+        XCTAssertEqual(NSNumber(value: false), platformDict[kSPMobileLowPowerMode])
+        XCTAssertEqual(NSNumber(value: 100000), platformDict[kSPMobilePhysicalMemory])
+        XCTAssertEqual(NSNumber(value: 1000), platformDict[kSPMobileAppAvailableMemory])
+        XCTAssertEqual(NSNumber(value: 9000), platformDict[kSPMobileAvailableStorage])
+        XCTAssertEqual(NSNumber(value: 900000), platformDict[kSPMobileTotalStorage])
+    }
+
+    func testUpdatesMobileInfo() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(2, deviceInfoMonitor.accessCount("batteryLevel"))
+        XCTAssertEqual(2, deviceInfoMonitor.accessCount("appAvailableMemory"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(3, deviceInfoMonitor.accessCount("batteryLevel"))
+        XCTAssertEqual(3, deviceInfoMonitor.accessCount("appAvailableMemory"))
+        #endif
+    }
+
+    func testDoesntUpdateMobileInfoWithinUpdateWindow() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 1000, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
+        #endif
+    }
+
+    func testUpdatesNetworkInfo() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 1, networkDictUpdateFrequency: 0, deviceInfoMonitor: deviceInfoMonitor)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(2, deviceInfoMonitor.accessCount("networkTechnology"))
+        XCTAssertEqual(2, deviceInfoMonitor.accessCount("networkType"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(3, deviceInfoMonitor.accessCount("networkTechnology"))
+        XCTAssertEqual(3, deviceInfoMonitor.accessCount("networkType"))
+        #endif
+    }
+
+    func testDoesntUpdateNetworkInfoWithinUpdateWindow() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1000, deviceInfoMonitor: deviceInfoMonitor)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
+        #endif
+    }
+
+    func testDoesntUpdateNonEphemeralInfo() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 0, deviceInfoMonitor: deviceInfoMonitor)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("totalStorage"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("totalStorage"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("totalStorage"))
+        #endif
+    }
+
+    func testDoesntUpdateIdfaAndIdfvIfNotNil() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfv"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfv"))
+        #endif
+    }
+
+    func testUpdatesIdfaAndIdfvIfNil() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        deviceInfoMonitor.customAppleIdfa = nil
+        deviceInfoMonitor.customAppleIdfv = nil
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
+        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfv"))
+        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        XCTAssertEqual(2, deviceInfoMonitor.accessCount("appleIdfa"))
+        XCTAssertEqual(2, deviceInfoMonitor.accessCount("appleIdfv"))
+        #endif
+    }
+
+    func testAnonymisesUserIdentifiers() {
+        #if os(iOS)
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        guard let platformDict = context.fetchPlatformDict(withUserAnonymisation: true).dictionary else {
+            return XCTFail()
+        }
+        XCTAssertNil(platformDict[kSPMobileAppleIdfa])
+        XCTAssertNil(platformDict[kSPMobileAppleIdfv])
+        #endif
+    }
+
+//    func testPerformanceOfFetchingNetworkDict() {
+//        let context = PlatformContext(mobileDictUpdateFrequency: 1000, networkDictUpdateFrequency: 0)
+//        measure({
+//            for _ in 0..<100 {
+//                _ = context.fetchPlatformDict(withUserAnonymisation: false)
+//            }
+//        })
+//    }
+//
+//    func testPerformanceOfFetchingMobileDict() {
+//        _ = XCTSkip()
+//        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1000)
+//        measure({
+//            for _ in 0..<10000 {
+//                _ = context.fetchPlatformDict(withUserAnonymisation: false)
+//            }
+//        })
+//    }
+}
diff --git a/Tests/TestRequest.swift b/Tests/TestRequest.swift
new file mode 100644
index 000000000..6b9993f4f
--- /dev/null
+++ b/Tests/TestRequest.swift
@@ -0,0 +1,236 @@
+//
+//  TestRequest.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida, Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+// MARK: - Mocks
+
+// MARK: - Tests
+
+class TestRequest: XCTestCase, RequestCallback {
+    private var successCount = 0
+    private var failureCount = 0
+
+    override func setUp() {
+        super.setUp()
+        successCount = 0
+        failureCount = 0
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    // Tests
+
+    func testRequestSendWithPost() {
+        let mockStore = MockEventStore()
+        let tracker = getTrackerWithRequestType(.post, resultCode: 200, eventStore: mockStore)
+        let sentEventsCount = sendAll(tracker)
+        forceFlushes(5, emitter: tracker.emitter)
+
+        XCTAssertEqual(successCount, sentEventsCount)
+        XCTAssertEqual(tracker.emitter.dbCount, 0, String(format: "Error on mockStore db: %@", [mockStore.db]))
+    }
+
+    func testRequestSendWithGet() {
+        let mockStore = MockEventStore()
+        let tracker = getTrackerWithRequestType(.get, resultCode: 200, eventStore: mockStore)
+        let sentEventsCount = sendAll(tracker)
+        forceFlushes(5, emitter: tracker.emitter)
+        XCTAssertEqual(successCount, sentEventsCount)
+        XCTAssertEqual(tracker.emitter.dbCount, 0, String(format: "Error on mockStore db: %@", [mockStore.db]))
+    }
+
+    func testRequestSendWithBadUrl() {
+        let mockConnection = MockNetworkConnection(requestOption: .post, statusCode: 404)
+        let mockStore = MockEventStore()
+
+        // Send all events with a bad URL
+        let tracker = getTrackerWith(mockConnection, eventStore: mockStore)
+        let sentEventsCount = sendAll(tracker)
+        forceFlushes(5, emitter: tracker.emitter)
+        XCTAssertGreaterThan(failureCount, 0)
+        XCTAssertEqual(successCount, 0)
+        XCTAssertEqual(tracker.emitter.dbCount, sentEventsCount, String(format: "Error on mockStore db: %@", [mockStore.db]))
+
+        // Update the URL and flush
+        tracker.pauseEventTracking()
+        Thread.sleep(forTimeInterval: 5)
+        mockConnection.statusCode = 200
+        tracker.resumeEventTracking()
+
+        forceFlushes(5, emitter: tracker.emitter)
+        XCTAssertEqual(successCount, 7)
+        XCTAssertEqual(tracker.emitter.dbCount, 0, String(format: "Error on mockStore db: %@", [mockStore.db]))
+    }
+
+    func testRequestSendWithoutSubject() {
+        let mockStore = MockEventStore()
+        let tracker = getTrackerWithRequestType(.get, resultCode: 200, eventStore: mockStore)
+        tracker.subject = nil
+        let sentEventsCount = sendAll(tracker)
+        forceFlushes(5, emitter: tracker.emitter)
+        XCTAssertEqual(successCount, sentEventsCount)
+        XCTAssertEqual(tracker.emitter.dbCount, 0, String(format: "Error on mockStore db: %@", [mockStore.db]))
+    }
+
+    func testRequestSendWithCollectionOff() {
+        let mockStore = MockEventStore()
+        let tracker = getTrackerWithRequestType(.post, resultCode: 200, eventStore: mockStore)
+        tracker.pauseEventTracking()
+        _ = sendAll(tracker)
+        forceFlushes(5, emitter: tracker.emitter)
+        XCTAssertEqual(failureCount, 0)
+        XCTAssertEqual(successCount, 0)
+        XCTAssertEqual(tracker.emitter.dbCount, 0, String(format: "Error on mockStore db: %@", mockStore.db))
+    }
+
+    // Helpers
+
+    func getTrackerWith(_ mockNetworkConnection: NetworkConnection, eventStore mockEventStore: EventStore) -> Tracker {
+        let networkConfig = NetworkConfiguration(networkConnection: mockNetworkConnection)
+        let trackerConfig = TrackerConfiguration(appId: "anAppId")
+        trackerConfig.platformContext = true
+        trackerConfig.geoLocationContext = true
+        trackerConfig.base64Encoding = false
+        trackerConfig.sessionContext = true
+        let emitterConfig = EmitterConfiguration()
+        emitterConfig.requestCallback = self
+        emitterConfig.eventStore = mockEventStore
+        let serviceProvider = ServiceProvider(namespace: "aNamespace", network: networkConfig, configurations: [trackerConfig, emitterConfig])
+        return serviceProvider.tracker
+    }
+
+    func getTrackerWithRequestType(_ type: HttpMethodOptions, resultCode: Int, eventStore mockEventStore: EventStore) -> Tracker {
+        let mockConnection = MockNetworkConnection(requestOption: type, statusCode: resultCode)
+        return getTrackerWith(mockConnection, eventStore: mockEventStore)
+    }
+
+    func forceFlushes(_ count: Int, emitter: Emitter?) {
+        Thread.sleep(forTimeInterval: 3)
+        for _ in 0..<count {
+            if emitter?.dbCount == 0 {
+                break
+            }
+            emitter?.flush()
+            Thread.sleep(forTimeInterval: 5)
+        }
+        Thread.sleep(forTimeInterval: 3)
+    }
+
+    // Callback
+
+    func onSuccess(withCount successCount: Int) {
+        self.successCount += successCount
+    }
+
+    func onFailure(withCount failureCount: Int, successCount: Int) {
+        self.successCount += successCount
+        self.failureCount += failureCount
+    }
+
+    // Pre-Built Events for sending!
+
+    func sendAll(_ tracker: Tracker) -> Int {
+        return trackStructuredEvent(with: tracker) + trackUnstructuredEvent(with: tracker) + trackPageView(with: tracker) + trackScreenView(with: tracker) + trackTimingWithCategory(with: tracker) + trackEcommerceTransaction(with: tracker)
+    }
+
+    func trackStructuredEvent(with tracker_: Tracker) -> Int {
+        let event = Structured(category: "DemoCategory", action: "DemoAction")
+        event.label = "DemoLabel"
+        event.property = "DemoProperty"
+        event.value = NSNumber(value: 5)
+        event.contexts = customContext()
+        _ = tracker_.track(event)
+        return 1
+    }
+
+    func trackUnstructuredEvent(with tracker_: Tracker) -> Int {
+        var data: [String : NSObject] = [:]
+        data["level"] = NSNumber(value: 23)
+        data["score"] = NSNumber(value: 56473)
+        let sdj = SelfDescribingJson(
+            schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
+            andDictionary: data)
+        let event = SelfDescribing(eventData: sdj)
+        event.contexts = customContext()
+        _ = tracker_.track(event)
+        return 1
+    }
+
+    func trackPageView(with tracker_: Tracker) -> Int {
+        let event = PageView(pageUrl: "DemoPageUrl")
+        event.pageTitle = "DemoPageTitle"
+        event.referrer = "DemoPageReferrer"
+        event.contexts = customContext()
+        _ = tracker_.track(event)
+        return 1
+    }
+
+    func trackScreenView(with tracker_: Tracker) -> Int {
+        let event = ScreenView(name: "DemoScreenName", screenId: nil)
+        event.contexts = customContext()
+        _ = tracker_.track(event)
+        return 1
+    }
+
+    func trackTimingWithCategory(with tracker_: Tracker) -> Int {
+        let event = Timing(category: "DemoTimingCategory", variable: "DemoTimingVariable", timing: 5)
+        event.label = "DemoTimingLabel"
+        event.contexts = customContext()
+        _ = tracker_.track(event)
+        return 1
+    }
+
+    func trackEcommerceTransaction(with tracker_: Tracker) -> Int {
+        let transactionID = "6a8078be"
+
+        let item = EcommerceItem(sku: "DemoItemSku", price: 0.75, quantity: 1)
+        item.name = "DemoItemName"
+        item.category = "DemoItemCategory"
+        item.currency = "USD"
+        item.contexts = customContext()
+
+        let event = Ecommerce(orderId: transactionID, totalValue: 350, items: [item])
+        event.affiliation = "DemoTranAffiliation"
+        event.taxValue = 10;
+        event.shipping = 15;
+        event.city = "Boston"
+        event.state = "Massachusetts"
+        event.country = "USA"
+        event.currency = "USD"
+        event.contexts = customContext()
+        _ = tracker_.track(event)
+        return 2
+    }
+
+    func customContext() -> [SelfDescribingJson] {
+        let data = [
+            "snowplow": "demo-tracker" as NSObject
+        ]
+        let context = SelfDescribingJson(
+            schema: "iglu:com.acme_company/demo_ios/jsonschema/1-0-0",
+            andDictionary: data)
+        return [context]
+    }
+}
diff --git a/Tests/TestRequestResult.swift b/Tests/TestRequestResult.swift
new file mode 100644
index 000000000..82735d6cb
--- /dev/null
+++ b/Tests/TestRequestResult.swift
@@ -0,0 +1,85 @@
+//
+//  TestRequestResult.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestRequestResult: XCTestCase {
+    override func setUp() {
+        super.setUp()
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testSuccessfulRequest() {
+        var emitterEventIds: [NSNumber]? = []
+        emitterEventIds?.append(NSNumber(value: 1))
+        let result = RequestResult(statusCode: 200, oversize: false, storeIds: emitterEventIds)
+
+        XCTAssertNotNil(result)
+        XCTAssertEqual(result.isSuccessful, true)
+        XCTAssertEqual(result.shouldRetry([:]), false)
+        XCTAssertEqual(result.storeIds, emitterEventIds)
+    }
+
+    func testFailedRequest() {
+        var emitterEventIds: [NSNumber]? = []
+        emitterEventIds?.append(NSNumber(value: 1))
+        let result = RequestResult(statusCode: 500, oversize: false, storeIds: emitterEventIds)
+        XCTAssertEqual(result.isSuccessful, false)
+        XCTAssertEqual(result.shouldRetry([:]), true)
+    }
+
+    func testDefaultResult() {
+        let result = RequestResult()
+
+        XCTAssertNotNil(result)
+        XCTAssertEqual(result.isSuccessful, false)
+        XCTAssertEqual(result.storeIds?.count, 0)
+    }
+
+    func testOversizedFailedRequest() {
+        let result = RequestResult(statusCode: 500, oversize: true, storeIds: [])
+        XCTAssertEqual(result.isSuccessful, false)
+        XCTAssertEqual(result.shouldRetry([:]), false)
+    }
+
+    func testFailedRequestWithNoRetryStatus() {
+        let result = RequestResult(statusCode: 403, oversize: false, storeIds: [])
+        XCTAssertEqual(result.isSuccessful, false)
+        XCTAssertEqual(result.shouldRetry([:]), false)
+    }
+
+    func testFailedRequestWithCustomNoRetryStatus() {
+        var customRetryRules: [Int : Bool] = [:]
+        customRetryRules[403] = true
+        customRetryRules[500] = false
+
+        var result = RequestResult(statusCode: 403, oversize: false, storeIds: [])
+        XCTAssertEqual(result.shouldRetry(customRetryRules), true)
+
+        result = RequestResult(statusCode: 500, oversize: false, storeIds: [])
+        XCTAssertEqual(result.shouldRetry(customRetryRules), false)
+    }
+}
diff --git a/Tests/TestSQLiteEventStore.swift b/Tests/TestSQLiteEventStore.swift
new file mode 100644
index 000000000..869ba1dce
--- /dev/null
+++ b/Tests/TestSQLiteEventStore.swift
@@ -0,0 +1,154 @@
+//
+//  TestSQLiteEventStore.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestSQLiteEventStore: XCTestCase {
+    override func setUp() {
+        _ = SQLiteEventStore.removeUnsentEventsExcept(forNamespaces: [])
+    }
+
+    func testInit() {
+        let eventStore = SQLiteEventStore(namespace: "aNamespace")
+        XCTAssertNotNil(eventStore)
+    }
+
+    func testInsertPayload() {
+        let eventStore = SQLiteEventStore(namespace: "aNamespace")
+        _ = eventStore.removeAllEvents()
+
+        // Build an event
+        let payload = Payload()
+        payload.addValueToPayload("pv", forKey: "e")
+        payload.addValueToPayload("www.foobar.com", forKey: "url")
+        payload.addValueToPayload("Welcome to foobar!", forKey: "page")
+        payload.addValueToPayload("MEEEE", forKey: "refr")
+
+        // Insert an event
+        _ = eventStore.insertEvent(payload)
+
+        XCTAssertEqual(eventStore.count(), 1)
+        XCTAssertEqual(eventStore.getEventWithId(1)?.payload.dictionary, payload.dictionary)
+        XCTAssertEqual(eventStore.getLastInsertedRowId(), 1)
+        _ = eventStore.removeEvent(withId: 1)
+
+        XCTAssertEqual(eventStore.count(), 0)
+    }
+
+    func testInsertManyPayloads() {
+        let eventStore = SQLiteEventStore(namespace: "aNamespace")
+        _ = eventStore.removeAllEvents()
+
+        // Build an event
+        let payload = Payload()
+        payload.addValueToPayload("pv", forKey: "e")
+        payload.addValueToPayload("www.foobar.com", forKey: "url")
+        payload.addValueToPayload("Welcome to foobar!", forKey: "page")
+        payload.addValueToPayload("MEEEE", forKey: "refr")
+
+        for _ in 0..<250 {
+            _ = eventStore.insertEvent(payload)
+        }
+
+        XCTAssertEqual(eventStore.count(), 250)
+        XCTAssertEqual(eventStore.getAllEventsLimited(600)?.count, 250)
+        XCTAssertEqual(eventStore.getAllEventsLimited(150)?.count, 150)
+        XCTAssertEqual(eventStore.getAllEvents()?.count, 250)
+
+        _ = eventStore.removeAllEvents()
+        XCTAssertEqual(eventStore.count(), 0)
+    }
+
+    func testSQLiteEventStoreCreateSQLiteFile() {
+        _ = SQLiteEventStore(namespace: "aNamespace")
+        let libraryPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).map(\.path)[0]
+        let snowplowDirPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplow").path
+        let dbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent("snowplowEvents-aNamespace.sqlite").path
+        XCTAssertTrue(FileManager.default.fileExists(atPath: dbPath))
+    }
+
+    func testSQLiteEventStoreRemoveFiles() {
+        _ = SQLiteEventStore(namespace: "aNamespace1")
+        _ = SQLiteEventStore(namespace: "aNamespace2")
+        _ = SQLiteEventStore(namespace: "aNamespace3")
+        let libraryPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).map(\.path)[0]
+        let snowplowDirPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplow").path
+        _ = SQLiteEventStore.removeUnsentEventsExcept(forNamespaces: ["aNamespace2"])
+        var dbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent("snowplowEvents-aNamespace1.sqlite").path
+        XCTAssertFalse(FileManager.default.fileExists(atPath: dbPath))
+        dbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent("snowplowEvents-aNamespace2.sqlite").path
+        XCTAssertTrue(FileManager.default.fileExists(atPath: dbPath))
+        dbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent("snowplowEvents-aNamespace3.sqlite").path
+        XCTAssertFalse(FileManager.default.fileExists(atPath: dbPath))
+    }
+
+    func testSQLiteEventStoreInvalidNamespaceConversion() {
+        _ = SQLiteEventStore(namespace: "namespace*.^?1ò2@")
+        let libraryPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).map(\.path)[0]
+        let snowplowDirPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplow").path
+        let dbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent("snowplowEvents-namespace-1-2-.sqlite").path
+        XCTAssertTrue(FileManager.default.fileExists(atPath: dbPath))
+    }
+
+    func testMigrationFromLegacyToNamespacedEventStore() {
+        var eventStore = SQLiteEventStore(namespace: "aNamespace")
+        eventStore.addEvent(Payload(dictionary: [
+            "key": "value" as NSObject
+        ]))
+        XCTAssertEqual(1, eventStore.count())
+
+        // Create fake legacy database
+        let libraryPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).map(\.path)[0]
+        let snowplowDirPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplow").path
+        var newDbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent("snowplowEvents-aNamespace.sqlite").path
+        let oldDbPath = URL(fileURLWithPath: libraryPath).appendingPathComponent("snowplowEvents.sqlite").path
+        XCTAssertFalse(FileManager.default.fileExists(atPath: oldDbPath))
+        XCTAssertTrue(FileManager.default.fileExists(atPath: newDbPath))
+        try? FileManager.default.moveItem(atPath: newDbPath, toPath: oldDbPath)
+        XCTAssertTrue(FileManager.default.fileExists(atPath: oldDbPath))
+        XCTAssertFalse(FileManager.default.fileExists(atPath: newDbPath))
+
+        // Migrate database when SQLiteEventStore is launched the first time
+        eventStore = SQLiteEventStore(namespace: "aNewNamespace")
+        newDbPath = URL(fileURLWithPath: snowplowDirPath).appendingPathComponent("snowplowEvents-aNewNamespace.sqlite").path
+        XCTAssertFalse(FileManager.default.fileExists(atPath: oldDbPath))
+        XCTAssertTrue(FileManager.default.fileExists(atPath: newDbPath))
+        XCTAssertEqual(1, eventStore.count())
+        for event in eventStore.getAllEvents() ?? [] {
+            XCTAssertEqual("value", event.payload.dictionary?["key"] as? String)
+        }
+    }
+
+    func testMultipleAccessToSameSQLiteFile() {
+        let eventStore1 = SQLiteEventStore(namespace: "aNamespace")
+        eventStore1.addEvent(Payload(dictionary: [
+            "key1": "value1" as NSObject
+        ]))
+        XCTAssertEqual(1, eventStore1.count())
+
+        let eventStore2 = SQLiteEventStore(namespace: "aNamespace")
+        eventStore2.addEvent(Payload(dictionary: [
+            "key2": "value2" as NSObject
+        ]))
+        XCTAssertEqual(2, eventStore2.count())
+    }
+}
diff --git a/Tests/TestScreenState.swift b/Tests/TestScreenState.swift
new file mode 100644
index 000000000..e6c0468bd
--- /dev/null
+++ b/Tests/TestScreenState.swift
@@ -0,0 +1,141 @@
+//
+//  TestScreenState.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+import Foundation
+class TestScreenState: XCTestCase {
+    override func setUp() {
+        super.setUp()
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testInvalidScreenState() {
+        var screenState = ScreenState(name: "name", screenId: "some id")
+
+        // Test builder setting properly
+        XCTAssertEqual(screenState.isValid, false)
+
+        // ID and name required (screen id generated)
+        screenState = ScreenState(name: "some name", type: "some type", screenId: nil)
+
+        // Test builder setting properly
+        XCTAssertEqual(screenState.isValid, true)
+    }
+
+    func testValidScreenState() {
+        let uuid = UUID().uuidString
+        var screenState = ScreenState(name: "some name", type: "some type", screenId: uuid)
+
+        // Test builder
+        XCTAssertEqual(screenState.isValid, true)
+        XCTAssertNotNil(screenState.payload)
+
+        // ID and name required
+        screenState = ScreenState(name: "some name", screenId: uuid)
+
+        // Test builder setting properly
+        XCTAssertEqual(screenState.isValid, true)
+        XCTAssertNotNil(screenState.payload)
+        let payload = screenState.payload
+        let dictionary = payload?.dictionary
+        XCTAssertEqual(dictionary?[kSPScreenName] as? String, "some name")
+        XCTAssertEqual(dictionary?[kSPScreenId] as? String, uuid)
+    }
+
+    func testScreenStateMachine() {
+        let eventStore = MockEventStore()
+        let emitter = Emitter(urlEndpoint: "http://snowplow-fake-url.com") { emitter in
+            emitter.eventStore = eventStore
+        }
+        let tracker = Tracker(trackerNamespace: "namespace", appId: nil, emitter: emitter) { tracker in
+            tracker.base64Encoded = false
+            tracker.screenContext = true
+            tracker.applicationContext = false
+        }
+        emitter.pauseEmit()
+
+        // Send events
+        _ = tracker.track(Timing(category: "category", variable: "variable", timing: 123))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        var payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        var entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNil(entities)
+
+        let uuid = UUID()
+        _ = tracker.track(ScreenView(name: "screen1", screenId: uuid))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains(uuid.uuidString))
+
+        _ = tracker.track(Timing(category: "category", variable: "variable", timing: 123))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains(uuid.uuidString))
+
+        let uuid2 = UUID()
+        _ = tracker.track(ScreenView(name: "screen2", screenId: uuid2))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains(uuid2.uuidString))
+        let eventPayload = (payload?.dictionary?["ue_pr"]) as? String
+        XCTAssertNotNil(eventPayload)
+        XCTAssertTrue(eventPayload!.contains(uuid.uuidString))
+        XCTAssertTrue(eventPayload!.contains(uuid2.uuidString))
+
+        _ = tracker.track(Timing(category: "category", variable: "variable", timing: 123))
+        Thread.sleep(forTimeInterval: 1)
+        if eventStore.lastInsertedRow == -1 {
+            XCTFail()
+        }
+        payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
+        _ = eventStore.removeAllEvents()
+        entities = (payload?.dictionary?["co"]) as? String
+        XCTAssertNotNil(entities)
+        XCTAssertTrue(entities!.contains(uuid2.uuidString))
+    }
+}
diff --git a/Tests/TestSelfDescribingJson.swift b/Tests/TestSelfDescribingJson.swift
new file mode 100644
index 000000000..63103dd4b
--- /dev/null
+++ b/Tests/TestSelfDescribingJson.swift
@@ -0,0 +1,165 @@
+//
+//  TestSelfDescribingJson.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestSelfDescribingJson: XCTestCase {
+    override func setUp() {
+        super.setUp()
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testInitWithObject() {
+        let expected = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+            "data": [
+                "hello": "world"
+            ] as NSObject
+        ]
+        let data = [
+            "hello": "world" as NSObject
+        ]
+        let sdj = SelfDescribingJson(
+            schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
+            andDictionary: data)
+        XCTAssertEqual(expected, sdj.dictionary)
+    }
+
+    func testInitWithSPPayload() {
+        let expected = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+            "data": [
+                "hello": "world"
+            ] as NSObject
+        ]
+        let data = Payload()
+        data.addValueToPayload("world", forKey: "hello")
+        let sdj = SelfDescribingJson(
+            schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
+            andPayload: data)
+        XCTAssertEqual(expected, sdj.dictionary)
+    }
+
+    func testInitWithSPSelfDescribingJson() {
+        let expected = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+            "data": [
+                "schema": "iglu:acme.com/nested_event/jsonschema/1-0-0",
+                "data": [
+                    "hello": "world"
+                ]
+            ] as NSObject
+        ]
+        let nestedData = [
+            "hello": "world"
+        ]
+        let data = SelfDescribingJson(
+            schema: "iglu:acme.com/nested_event/jsonschema/1-0-0",
+            andDictionary: nestedData)
+        let sdj = SelfDescribingJson(
+            schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
+            andSelfDescribingJson: data)
+        XCTAssertEqual(expected, sdj.dictionary)
+    }
+
+    func testUpdateSchema() {
+        let expected = [
+            "schema": "iglu:acme.com/test_event_2/jsonschema/1-0-0" as NSObject,
+            "data": [
+                "hello": "world"
+            ] as NSObject
+        ]
+        let data = [
+            "hello": "world"
+        ]
+        let sdj = SelfDescribingJson(
+            schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
+            andDictionary: data)
+        sdj.schema = "iglu:acme.com/test_event_2/jsonschema/1-0-0"
+        XCTAssertEqual(expected, sdj.dictionary)
+    }
+    
+    func testUpdateDataWithObject() {
+        let expected = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+            "data": [
+                "world": "hello"
+            ] as NSObject
+        ]
+        let sdj = SelfDescribingJson(
+            schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
+            andDictionary: [
+                "hello": "world"
+            ])
+        sdj.setData(withObject: [
+            "world": "hello"
+        ] as NSObject)
+        XCTAssertEqual(expected, sdj.dictionary)
+    }
+    
+    func testUpdateDataWithSPPayload() {
+        let expected = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+            "data": [
+                "world": "hello"
+            ] as NSObject
+        ]
+        let data = Payload()
+        data.addValueToPayload("hello", forKey: "world")
+        let sdj = SelfDescribingJson(
+            schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
+            andDictionary: [
+                "hello": "world"
+            ])
+        sdj.setData(withPayload: data)
+        XCTAssertEqual(expected, sdj.dictionary)
+    }
+
+    func testUpdateDataWithSPSelfDescribingJson() {
+        let expected = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+            "data": [
+                "schema": "iglu:acme.com/nested_event/jsonschema/1-0-0",
+                "data": [
+                    "hello": "world"
+                ]
+            ] as NSObject
+        ]
+        let nestedData = [
+            "hello": "world"
+        ]
+        let data = SelfDescribingJson(
+            schema: "iglu:acme.com/nested_event/jsonschema/1-0-0",
+            andDictionary: nestedData)
+        let sdj = SelfDescribingJson(
+            schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
+            andDictionary: [
+                "hello": "world"
+            ])
+        sdj.setData(withSelfDescribingJson: data)
+        XCTAssertEqual(expected, sdj.dictionary)
+    }
+}
+
diff --git a/Tests/TestServiceProvider.swift b/Tests/TestServiceProvider.swift
new file mode 100644
index 000000000..e344a7b4e
--- /dev/null
+++ b/Tests/TestServiceProvider.swift
@@ -0,0 +1,90 @@
+//
+//  TestServiceProvider.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestServiceProvider: XCTestCase {
+    override func setUp() {
+        super.setUp()
+        Logger.logLevel = .verbose
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testUpdatingConfigurationRetainsPausedEmitter() {
+        let networkConnection = MockNetworkConnection(requestOption: .post, statusCode: 200)
+        let emitterConfig = EmitterConfiguration()
+        emitterConfig.eventStore = MockEventStore()
+        emitterConfig.bufferOption = .single
+        let networkConfig = NetworkConfiguration(endpoint: "", method: .post)
+        networkConfig.networkConnection = networkConnection
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.appId = "appid"
+        trackerConfig.installAutotracking = false
+        trackerConfig.screenViewAutotracking = false
+        trackerConfig.lifecycleAutotracking = false
+        let serviceProvider = ServiceProvider(namespace: "ns", network: networkConfig, configurations: [emitterConfig, trackerConfig])
+        XCTAssertNotNil(serviceProvider)
+
+        // pause emitter
+        serviceProvider.emitterController.pause()
+
+        // refresh configuration
+        serviceProvider.reset(withConfigurations: [EmitterConfigurationUpdate()])
+
+        // track event and check that emitter is paused
+        _ = serviceProvider.trackerController.track(Structured(category: "cat", action: "act"))
+        Thread.sleep(forTimeInterval: 3)
+        XCTAssertEqual(1, serviceProvider.emitter.dbCount)
+        XCTAssertEqual(0, networkConnection.sendingCount)
+
+        // resume emitting
+        serviceProvider.emitterController.resume()
+        Thread.sleep(forTimeInterval: 3)
+        XCTAssertEqual(1, networkConnection.sendingCount)
+        XCTAssertEqual(0, serviceProvider.emitter.dbCount)
+    }
+    // TODO: fix logging and handle the case
+    //- (void)testLogsErrorWhenAccessingShutDownTracker {
+    //    SPMockNetworkConnection *networkConnection = [[SPMockNetworkConnection alloc] initWithRequestOption:SPHttpMethodPost statusCode:200];
+    //    SPEmitterConfiguration *emitterConfig = [[SPEmitterConfiguration alloc] init];
+    //    emitterConfig.eventStore = [SPMockEventStore new];
+    //    emitterConfig.bufferOption = SPBufferOptionSingle;
+    //    SPNetworkConfiguration *networkConfig = [[SPNetworkConfiguration alloc] initWithEndpoint:@"" method:SPHttpMethodPost];
+    //    networkConfig.networkConnection = networkConnection;
+    //    SPServiceProvider *serviceProvider = [[SPServiceProvider alloc] initWithNamespace:@"ns" network:networkConfig configurations:@[emitterConfig]];
+    //    XCTAssertNotNil(serviceProvider);
+    //
+    //    // listen for the error log
+    //    id<SPTrackerController> trackerController = [serviceProvider trackerController];
+    //    SPMockLoggerDelegate *logger = [SPMockLoggerDelegate new];
+    //    [trackerController setLoggerDelegate:logger];
+    //
+    //    // shutting down and accessing the tracker should log the error
+    //    [serviceProvider shutdown];
+    //    [trackerController namespace];
+    //    XCTAssertEqual(1, [[logger errorLogs] count]);
+    //    XCTAssertTrue([[[logger errorLogs] objectAtIndex:0] containsString:@"Recreating tracker instance"]);
+    //}
+}
diff --git a/Tests/TestSession.swift b/Tests/TestSession.swift
new file mode 100644
index 000000000..4dff1f99d
--- /dev/null
+++ b/Tests/TestSession.swift
@@ -0,0 +1,489 @@
+//
+//  TestSession.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestSession: XCTestCase {
+    override func setUp() {
+        super.setUp()
+        cleanFile(withNamespace: "tracker")
+        UserDefaults.standard.removeObject(forKey: kSPInstallationUserId)
+    }
+
+    override func tearDown() {
+        super.tearDown()
+    }
+
+    func testInit() {
+        let session = Session(foregroundTimeout: 600, andBackgroundTimeout: 300)
+        XCTAssertNil(session.tracker)
+        XCTAssertTrue(!session.inBackground)
+        XCTAssertNotNil(session.getDictWithEventId("eventid-1", eventTimestamp: 1654496481346, userAnonymisation: false))
+        XCTAssertTrue(session.state!.sessionIndex >= 1)
+        XCTAssertEqual(session.foregroundTimeout, 600000)
+        XCTAssertEqual(session.backgroundTimeout, 300000)
+    }
+
+    func testInitWithOptions() {
+        let session = Session(foregroundTimeout: 5, andBackgroundTimeout: 300, andTracker: nil)
+        XCTAssertEqual(session.foregroundTimeout, 5000)
+        XCTAssertEqual(session.backgroundTimeout, 300000)
+
+        session.backgroundTimeout = 5
+        session.foregroundTimeout = 10
+
+        XCTAssertEqual(session.foregroundTimeout, 10)
+        XCTAssertEqual(session.backgroundTimeout, 5)
+    }
+
+    func testInitInBgThread() {
+        var session: Session? = nil
+        DispatchQueue.global(qos: .default).async(execute: {
+            session = Session(foregroundTimeout: 1, andBackgroundTimeout: 1, andTracker: nil)
+        })
+        RunLoop.main.run(until: Date(timeIntervalSinceNow: 1))
+        Thread.sleep(forTimeInterval: 1)
+        XCTAssertNotNil(session)
+    }
+
+    func testFirstSession() {
+        let session = Session(foregroundTimeout: 3, andBackgroundTimeout: 3, andTracker: nil)
+
+        let sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
+        let sessionIndex = session.state!.sessionIndex
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+    }
+
+    func testForegroundEventsOnSameSession() {
+        let session = Session(foregroundTimeout: 3, andBackgroundTimeout: 3, andTracker: nil)
+
+        var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
+        var sessionIndex = session.state?.sessionIndex
+        let sessionId = sessionContext?[kSPSessionId]
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+
+        Thread.sleep(forTimeInterval: 1)
+
+        sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481347, userAnonymisation: false)
+        sessionIndex = session.state?.sessionIndex
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+
+        Thread.sleep(forTimeInterval: 1)
+
+        sessionContext = session.getDictWithEventId("event_3", eventTimestamp: 1654496481348, userAnonymisation: false)
+        sessionIndex = session.state?.sessionIndex
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+
+        Thread.sleep(forTimeInterval: 3.1)
+
+        sessionContext = session.getDictWithEventId("event_4", eventTimestamp: 1654496481349, userAnonymisation: false)
+        sessionIndex = session.state?.sessionIndex
+        XCTAssertEqual(2, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual("event_4" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.349Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertNotEqual(sessionId, sessionContext?[kSPSessionId])
+    }
+
+    func testBackgroundEventsOnWhenLifecycleEventsDisabled() {
+        cleanFile(withNamespace: "tracker")
+
+        let emitter = Emitter(urlEndpoint: "") { emitter in}
+        let tracker = Tracker(trackerNamespace: "tracker", appId: nil, emitter: emitter) { tracker in
+            tracker.lifecycleEvents = false
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 3
+            tracker.backgroundTimeout = 2
+        }
+        
+        let session = tracker.session
+        session?.updateInBackground()
+
+        let sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
+        let sessionIndex = session?.state?.sessionIndex ?? 0
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
+        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertFalse(session!.inBackground)
+        XCTAssertEqual(0, session?.backgroundIndex)
+    }
+
+    func testBackgroundEventsOnSameSession() {
+        cleanFile(withNamespace: "t1")
+        
+        let emitter = Emitter(urlEndpoint: "") { emitter in}
+        let tracker = Tracker(trackerNamespace: "t1", appId: nil, emitter: emitter) { tracker in
+            tracker.installEvent = false
+            tracker.lifecycleEvents = true
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 3
+            tracker.backgroundTimeout = 2
+        }
+        let session = tracker.session
+
+        session?.updateInBackground() // It sends a background event
+
+        let sessionId = session?.state?.sessionId as? NSString
+
+        var sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
+        var sessionIndex = session?.state?.sessionIndex ?? 0
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertTrue(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+
+        Thread.sleep(forTimeInterval: 1)
+
+        sessionContext = session?.getDictWithEventId("event_2", eventTimestamp: 1654496481347, userAnonymisation: false)
+        sessionIndex = session?.state?.sessionIndex ?? 0
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertTrue(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+
+        Thread.sleep(forTimeInterval: 1)
+
+        sessionContext = session?.getDictWithEventId("event_3", eventTimestamp: 1654496481348, userAnonymisation: false)
+        sessionIndex = session?.state?.sessionIndex ?? 0
+        XCTAssertEqual(1, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertTrue(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+
+        Thread.sleep(forTimeInterval: 2.1)
+
+        sessionContext = session?.getDictWithEventId("event_4", eventTimestamp: 1654496481349, userAnonymisation: false)
+        sessionIndex = session?.state?.sessionIndex ?? 0
+        XCTAssertEqual(2, sessionIndex)
+        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
+        XCTAssertEqual("event_4" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.349Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertNotEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertTrue(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+    }
+
+    func testMixedEventsOnManySessions() {
+        cleanFile(withNamespace: "t2")
+        
+        let emitter = Emitter(urlEndpoint: "") { emitter in}
+        let tracker = Tracker(trackerNamespace: "t2", appId: nil, emitter: emitter) { tracker in
+            tracker.lifecycleEvents = true
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 1
+            tracker.backgroundTimeout = 1
+        }
+        let session = tracker.session
+
+        var sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481351, userAnonymisation: false)
+        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.351Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertFalse(session!.inBackground)
+        XCTAssertEqual(0, session?.backgroundIndex)
+        XCTAssertEqual(0, session?.foregroundIndex)
+        var oldSessionId = sessionContext?[kSPSessionId]
+
+        session?.updateInBackground()
+        Thread.sleep(forTimeInterval: 1.1)
+
+        sessionContext = session?.getDictWithEventId("event_2", eventTimestamp: 1654496481352, userAnonymisation: false)
+        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId])
+        XCTAssertEqual("event_2" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.352Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertTrue(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+        XCTAssertEqual(0, session?.foregroundIndex)
+        oldSessionId = sessionContext?[kSPSessionId]
+
+        session?.updateInForeground()
+        Thread.sleep(forTimeInterval: 1.1)
+
+        sessionContext = session?.getDictWithEventId("event_3", eventTimestamp: 1654496481353, userAnonymisation: false)
+        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId])
+        XCTAssertEqual("event_3" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.353Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertFalse(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+        XCTAssertEqual(1, session?.foregroundIndex)
+        oldSessionId = sessionContext?[kSPSessionId]
+
+        session?.updateInBackground()
+        Thread.sleep(forTimeInterval: 1.1)
+
+        sessionContext = session?.getDictWithEventId("event_4", eventTimestamp: 1654496481354, userAnonymisation: false)
+        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId])
+        XCTAssertEqual("event_4" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.354Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertTrue(session!.inBackground)
+        XCTAssertEqual(2, session?.backgroundIndex)
+        XCTAssertEqual(1, session?.foregroundIndex)
+    }
+
+    func testTimeoutSessionWhenPauseAndResume() {
+        let session = Session(foregroundTimeout: 1, andBackgroundTimeout: 1, andTracker: nil)
+
+        var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481355, userAnonymisation: false)
+        var prevSessionId = sessionContext?[kSPSessionId]
+        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.355Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+
+        session.stopChecker()
+        Thread.sleep(forTimeInterval: 2)
+
+        sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481356, userAnonymisation: false)
+        XCTAssertEqual(1, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual(prevSessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.355Z" as NSObject, sessionContext?[kSPSessionFirstEventTimestamp])
+        prevSessionId = sessionContext?[kSPSessionId]
+
+        session.startChecker()
+
+        sessionContext = session.getDictWithEventId("event_3", eventTimestamp: 1654496481357, userAnonymisation: false)
+        XCTAssertEqual(2, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual(prevSessionId, sessionContext?[kSPSessionPreviousId])
+        XCTAssertEqual("event_3" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("2022-06-06T06:21:21.357Z" as NSObject, sessionContext?[kSPSessionFirstEventTimestamp])
+    }
+
+    func testBackgroundTimeBiggerThanBackgroundTimeoutCausesNewSession() {
+        cleanFile(withNamespace: "tracker")
+        
+        let emitter = Emitter(urlEndpoint: "") { emitter in}
+        let tracker = Tracker(trackerNamespace: "tracker", appId: nil, emitter: emitter) { tracker in
+            tracker.lifecycleEvents = true
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 100
+            tracker.backgroundTimeout = 2
+        }
+        let session = tracker.session
+
+        let sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481361, userAnonymisation: false)
+        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertFalse(session!.inBackground)
+        XCTAssertEqual(0, session?.backgroundIndex)
+        XCTAssertEqual(0, session?.foregroundIndex)
+        let oldSessionId = sessionContext?[kSPSessionId] as? String
+
+        Thread.sleep(forTimeInterval: 1) // Smaller than background timeout
+        session?.updateInBackground() // Sends a background event
+        Thread.sleep(forTimeInterval: 3) // Bigger than background timeout
+        session?.updateInForeground() // Sends a foreground event
+
+        XCTAssertEqual(oldSessionId, session?.state?.previousSessionId)
+        XCTAssertEqual(2, session?.state?.sessionIndex)
+        XCTAssertFalse(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+        XCTAssertEqual(1, session?.foregroundIndex)
+    }
+
+    func testBackgroundTimeSmallerThanBackgroundTimeoutDoesntCauseNewSession() {
+        cleanFile(withNamespace: "tracker")
+        
+        let emitter = Emitter(urlEndpoint: "") { emitter in}
+        let tracker = Tracker(trackerNamespace: "tracker", appId: nil, emitter: emitter) { tracker in
+            tracker.lifecycleEvents = true
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 100
+            tracker.backgroundTimeout = 2
+        }
+        let session = tracker.session
+
+        let sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481358, userAnonymisation: false)
+        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertFalse(session!.inBackground)
+        XCTAssertEqual(0, session?.backgroundIndex)
+        XCTAssertEqual(0, session?.foregroundIndex)
+        let oldSessionId = sessionContext?[kSPSessionId] as? String
+
+        Thread.sleep(forTimeInterval: 3) // Bigger than background timeout
+        session?.updateInBackground() // Sends a background event
+        Thread.sleep(forTimeInterval: 1) // Smaller than background timeout
+        session?.updateInForeground() // Sends a foreground event
+
+        XCTAssertEqual(oldSessionId, session?.state?.sessionId)
+        XCTAssertEqual(1, session?.state?.sessionIndex)
+        XCTAssertFalse(session!.inBackground)
+        XCTAssertEqual(1, session?.backgroundIndex)
+        XCTAssertEqual(1, session?.foregroundIndex)
+    }
+
+    func testNoEventsForLongTimeDontIncreaseIndexMultipleTimes() {
+        let session = Session(foregroundTimeout: 1, andBackgroundTimeout: 1, andTracker: nil)
+
+        var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481359, userAnonymisation: false)
+        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
+
+        Thread.sleep(forTimeInterval: 4)
+
+        sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481360, userAnonymisation: false)
+        XCTAssertEqual(2, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
+        XCTAssertEqual("event_2" as NSObject, sessionContext?[kSPSessionFirstEventId])
+    }
+
+    func testMultipleTrackersUpdateDifferentSessions() {
+        cleanFile(withNamespace: "tracker1")
+        cleanFile(withNamespace: "tracker2")
+
+        let emitter = Emitter(urlEndpoint: "") { emitter in}
+        let tracker1 = Tracker(trackerNamespace: "tracker1", appId: nil, emitter: emitter) { tracker in
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 10
+            tracker.backgroundTimeout = 10
+        }
+        let tracker2 = Tracker(trackerNamespace: "tracker2", appId: nil, emitter: emitter) { tracker in
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 10
+            tracker.backgroundTimeout = 10
+        }
+        let event = Structured(category: "c", action: "a")
+        _ = tracker1.track(event)
+        _ = tracker2.track(event)
+
+        guard let initialValue1 = tracker1.session?.state?.sessionIndex else { return XCTFail() }
+        guard let id1 = tracker1.session?.state?.sessionId else { return XCTFail() }
+        guard let initialValue2 = tracker2.session?.state?.sessionIndex else { return XCTFail() }
+        guard var id2 = tracker2.session?.state?.sessionId else { return XCTFail() }
+
+        // Retrigger session in tracker1
+        Thread.sleep(forTimeInterval: 7)
+        _ = tracker1.track(event)
+        Thread.sleep(forTimeInterval: 5)
+
+        // Send event to force update of session on tracker2
+        _ = tracker2.track(event)
+        id2 = tracker2.session!.state!.sessionId
+
+        // Check sessions have the correct state
+        XCTAssertEqual(0, tracker1.session!.state!.sessionIndex - initialValue1) // retriggered
+        XCTAssertEqual(1, tracker2.session!.state!.sessionIndex - initialValue2) // timed out
+
+        //Recreate tracker2
+        let tracker2b = Tracker(trackerNamespace: "tracker2", appId: nil, emitter: emitter) { tracker in
+            tracker.sessionContext = true
+            tracker.foregroundTimeout = 5
+            tracker.backgroundTimeout = 5
+        }
+        _ = tracker2b.track(event)
+        guard let initialValue2b = tracker2b.session?.state?.sessionIndex else { return XCTFail() }
+        guard let previousId2b = tracker2b.session?.state?.previousSessionId else { return XCTFail() }
+
+        // Check the new tracker session gets the data from the old tracker2 session
+        XCTAssertEqual(initialValue2 + 2, initialValue2b)
+        XCTAssertEqual(id2, previousId2b)
+        XCTAssertNotEqual(id1, previousId2b)
+    }
+
+    func testMigrateSessionFromV3_0() {
+        cleanFile(withNamespace: "tracker")
+        storeAsV3_0(withNamespace: "tracker", eventId: "eventId", sessionId: "sessionId", sessionIndex: 123, userId: "userId")
+
+        let emitter = Emitter(urlEndpoint: "") { emitter in}
+        let tracker = Tracker(trackerNamespace: "tracker", appId: nil, emitter: emitter) { tracker in
+            tracker.sessionContext = true
+        }
+        let event = Structured(category: "c", action: "a")
+        _ = tracker.track(event)
+
+        guard let sessionState = tracker.session?.state else { return XCTFail() }
+        XCTAssertEqual("sessionId", sessionState.previousSessionId)
+        XCTAssertEqual(124, sessionState.sessionIndex)
+        XCTAssertEqual("userId", sessionState.userId)
+        XCTAssertNotEqual("eventId", sessionState.firstEventId)
+    }
+
+    func testIncrementsEventIndex() {
+        let session = Session(foregroundTimeout: 3, andBackgroundTimeout: 3, andTracker: nil)
+
+        var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
+        XCTAssertEqual(NSNumber(value: 1), sessionContext?[kSPSessionEventIndex])
+
+        Thread.sleep(forTimeInterval: 1)
+
+        sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481347, userAnonymisation: false)
+        XCTAssertEqual(NSNumber(value: 2), sessionContext?[kSPSessionEventIndex])
+
+        Thread.sleep(forTimeInterval: 1)
+
+        sessionContext = session.getDictWithEventId("event_3", eventTimestamp: 1654496481348, userAnonymisation: false)
+        XCTAssertEqual(NSNumber(value: 3), sessionContext?[kSPSessionEventIndex])
+
+        Thread.sleep(forTimeInterval: 3.1)
+
+        sessionContext = session.getDictWithEventId("event_4", eventTimestamp: 1654496481349, userAnonymisation: false)
+        XCTAssertEqual(NSNumber(value: 1), sessionContext?[kSPSessionEventIndex])
+    }
+
+    func testAnonymisesUserIdentifiers() {
+        let session = Session(foregroundTimeout: 3, andBackgroundTimeout: 3, andTracker: nil)
+        _ = session.getDictWithEventId("event_1", eventTimestamp: 1654496481345, userAnonymisation: false)
+        session.startNewSession() // create previous session ID reference
+
+        let withoutAnonymisation = session.getDictWithEventId("event_2", eventTimestamp: 1654496481346, userAnonymisation: false)
+        XCTAssertNotEqual("00000000-0000-0000-0000-000000000000" as NSObject, withoutAnonymisation?[kSPSessionUserId])
+        XCTAssertNotNil(withoutAnonymisation?[kSPSessionPreviousId])
+
+        let withAnonymisation = session.getDictWithEventId("event_3", eventTimestamp: 1654496481347, userAnonymisation: true)
+        XCTAssertEqual("00000000-0000-0000-0000-000000000000" as NSObject, withAnonymisation?[kSPSessionUserId])
+        XCTAssertNil(withAnonymisation?[kSPSessionPreviousId])
+    }
+
+    // Service methods
+
+    func cleanFile(withNamespace namespace: String) {
+        _ = DataPersistence.remove(withNamespace: namespace)
+    }
+
+    // Migration methods
+
+    func storeAsV3_0(withNamespace namespace: String, eventId: String?, sessionId: String?, sessionIndex: Int, userId: String?) {
+        let dataPersistence = DataPersistence.getFor(namespace: namespace)
+        var newSessionDict: [String : NSObject] = [:]
+        newSessionDict[kSPSessionFirstEventId] = eventId as? NSObject
+        newSessionDict[kSPSessionId] = sessionId as? NSObject
+        newSessionDict[kSPSessionIndex] = NSNumber(value: sessionIndex)
+        dataPersistence?.session = newSessionDict
+
+        //Store userId
+        let userDefaults = UserDefaults.standard
+        userDefaults.set(userId, forKey: kSPInstallationUserId)
+    }
+}
diff --git a/Tests/TestStateManager.swift b/Tests/TestStateManager.swift
new file mode 100644
index 000000000..33769cc3f
--- /dev/null
+++ b/Tests/TestStateManager.swift
@@ -0,0 +1,222 @@
+//
+//  TestStateManager.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+
+import XCTest
+@testable import SnowplowTracker
+
+// MARK: - MockState
+
+class MockState: NSObject, State {
+    var value = 0
+
+    required init(value: Int) {
+        super.init()
+        self.value = value
+    }
+}
+
+// MARK: - MockStateMachine
+
+class MockStateMachine: StateMachineProtocol {
+    var identifier: String
+    
+    init(_ identifier: String = "MockStateMachine") {
+        self.identifier = identifier
+    }
+    
+    var subscribedEventSchemasForTransitions: [String] {
+        return ["inc", "dec"]
+    }
+
+    func transition(from event: Event, state currentState: State?) -> State? {
+        let e = event as? SelfDescribingAbstract
+        let state = currentState as? MockState ?? MockState(value: 0)
+        if e?.schema == "inc" {
+            return MockState(value: state.value + 1)
+        } else if e?.schema == "dec" {
+            return MockState(value: state.value - 1)
+        } else {
+            return MockState(value: 0)
+        }
+    }
+
+    var subscribedEventSchemasForEntitiesGeneration: [String] {
+        return ["*"]
+    }
+
+    func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]? {
+        let mockState = state as? MockState
+        let sdj = SelfDescribingJson(schema: "entity", andDictionary: [
+            "value": NSNumber(value: mockState?.value ?? 0)
+        ])
+        return [sdj]
+    }
+
+    var subscribedEventSchemasForPayloadUpdating: [String] {
+        return ["event"]
+    }
+
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+        return [
+            "newParam": "value" as NSObject
+        ]
+    }
+}
+
+class MockStateMachine1: MockStateMachine {
+    override init(_ identifier: String = "MockStateMachine1") {
+        super.init(identifier)
+    }
+}
+
+class MockStateMachine2: MockStateMachine {
+    override init(_ identifier: String = "MockStateMachine2") {
+        super.init(identifier)
+    }
+}
+
+// MARK: - Test
+
+class TestStateManager: XCTestCase {
+    func testStateManager() {
+        let stateManager = StateManager()
+        let stateMachine = MockStateMachine()
+        stateManager.addOrReplaceStateMachine(MockStateMachine())
+
+        let eventInc = SelfDescribing(schema: "inc", payload: [
+            "value": NSNumber(value: 1)
+        ])
+        let eventDec = SelfDescribing(schema: "dec", payload: [
+            "value": NSNumber(value: 2)
+        ])
+        let event = SelfDescribing(schema: "event", payload: [
+            "value": NSNumber(value: 3)
+        ])
+
+        var trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
+        let mockState = trackerState?.state(withStateMachine: stateMachine) as? MockState
+        XCTAssertEqual(1, mockState?.value)
+        var e = TrackerEvent(event: eventInc, state: trackerState)
+        var entities = stateManager.entities(forProcessedEvent: e)
+        XCTAssertEqual(NSNumber(value: 1), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertTrue(stateManager.addPayloadValues(to: e))
+        XCTAssertNil((e.payload)["newParam"])
+
+        trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
+        XCTAssertEqual(2, (trackerState?.state(withStateMachine: stateMachine) as? MockState)?.value)
+        e = TrackerEvent(event: eventInc, state: trackerState)
+        entities = stateManager.entities(forProcessedEvent: e)
+        XCTAssertEqual(NSNumber(value: 2), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertTrue(stateManager.addPayloadValues(to: e))
+        XCTAssertNil((e.payload)["newParam"])
+
+        trackerState = stateManager.trackerState(forProcessedEvent: eventDec)
+        XCTAssertEqual(1, (trackerState?.state(withStateMachine: stateMachine) as? MockState)?.value)
+        e = TrackerEvent(event: eventDec, state: trackerState)
+        entities = stateManager.entities(forProcessedEvent: e)
+        XCTAssertEqual(NSNumber(value: 1), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertTrue(stateManager.addPayloadValues(to: e))
+        XCTAssertNil((e.payload)["newParam"])
+
+        trackerState = stateManager.trackerState(forProcessedEvent: event)
+        XCTAssertEqual(1, (trackerState?.state(withStateMachine: stateMachine) as? MockState)?.value)
+        e = TrackerEvent(event: event, state: trackerState)
+        entities = stateManager.entities(forProcessedEvent: e)
+        XCTAssertEqual(NSNumber(value: 1), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertTrue(stateManager.addPayloadValues(to: e))
+        XCTAssertEqual("value" as NSObject, (e.payload)["newParam"])
+    }
+
+    func testAddRemoveStateMachine() {
+        let stateManager = StateManager()
+        let stateMachine = MockStateMachine("identifier")
+        stateManager.addOrReplaceStateMachine(stateMachine)
+        _ = stateManager.removeStateMachine("identifier")
+
+        let eventInc = SelfDescribing(schema: "inc", payload: [
+            "value": NSNumber(value: 1)
+        ])
+
+        let trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
+        let mockState = trackerState?.state(withIdentifier: "identifier") as? MockState
+        XCTAssertNil(mockState)
+        let e = TrackerEvent(event: eventInc, state: trackerState)
+        let entities = stateManager.entities(forProcessedEvent: e)
+        XCTAssertEqual(0, entities.count)
+    }
+
+    func testAllowsMultipleStateMachines() {
+        let stateManager = StateManager()
+        stateManager.addOrReplaceStateMachine(MockStateMachine1())
+        stateManager.addOrReplaceStateMachine(MockStateMachine2())
+
+        let eventInc = SelfDescribing(schema: "inc", payload: [
+            "value": NSNumber(value: 1)
+        ])
+
+        let trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
+        let e = TrackerEvent(event: eventInc, state: trackerState)
+        let entities = stateManager.entities(forProcessedEvent: e)
+        XCTAssertEqual(2, entities.count)
+    }
+
+    func testDoesntDuplicateStateFromStateMachinesWithSameId() {
+        let stateManager = StateManager()
+        stateManager.addOrReplaceStateMachine(MockStateMachine())
+        stateManager.addOrReplaceStateMachine(MockStateMachine())
+
+        let eventInc = SelfDescribing(schema: "inc", payload: [
+            "value": NSNumber(value: 1)
+        ])
+
+        let trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
+        let e = TrackerEvent(event: eventInc, state: trackerState)
+        let entities = stateManager.entities(forProcessedEvent: e)
+        XCTAssertEqual(1, entities.count)
+    }
+
+    func testReplacingStateMachineDoesntResetTrackerState() {
+        let stateManager = StateManager()
+        stateManager.addOrReplaceStateMachine(MockStateMachine("identifier"))
+        let trackerState1 = stateManager.trackerState(forProcessedEvent: SelfDescribing(schema: "inc", payload: [
+            "value": NSNumber(value: 1)
+        ]))
+        XCTAssertEqual(1, (trackerState1?.state(withIdentifier: "identifier") as? MockState)?.value)
+
+        stateManager.addOrReplaceStateMachine(MockStateMachine())
+        let trackerState2 = stateManager.trackerState(forProcessedEvent: Structured(category: "category", action: "action"))
+        XCTAssertEqual(1, (trackerState2?.state(withIdentifier: "identifier") as? MockState)?.value)
+    }
+
+    func testReplacingStateMachineWithDifferentOneResetsTrackerState() {
+        let stateManager = StateManager()
+        stateManager.addOrReplaceStateMachine(MockStateMachine1("identifier"))
+        let trackerState1 = stateManager.trackerState(forProcessedEvent: SelfDescribing(schema: "inc", payload: [
+            "value": NSNumber(value: 1)
+        ]))
+        XCTAssertEqual(1, (trackerState1?.state(withIdentifier: "identifier") as? MockState)?.value)
+
+        stateManager.addOrReplaceStateMachine(MockStateMachine2("identifier"))
+        let trackerState2 = stateManager.trackerState(forProcessedEvent: Structured(category: "category", action: "action"))
+        XCTAssertNil(trackerState2?.state(withIdentifier: "identifier"))
+    }
+}
diff --git a/Tests/TestSubject.swift b/Tests/TestSubject.swift
new file mode 100644
index 000000000..6c469e250
--- /dev/null
+++ b/Tests/TestSubject.swift
@@ -0,0 +1,73 @@
+//
+//  TestSubject.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestSubject: XCTestCase {
+    func testReturnsPlatformContextIfEnabled() {
+        let subject = Subject(platformContext: true, andGeoContext: false)
+        let platformDict = subject.getPlatformDict(withUserAnonymisation: false)
+        XCTAssertNotNil(platformDict)
+        XCTAssertNotNil(platformDict?.dictionary?[kSPPlatformOsType])
+    }
+
+    func testDoesntReturnPlatformContextIfDisabled() {
+        let subject = Subject(platformContext: false, andGeoContext: false)
+        let platformDict = subject.getPlatformDict(withUserAnonymisation: false)
+        XCTAssertNil(platformDict)
+    }
+
+    func testReturnsGeolocationContextIfEnabled() {
+        let subject = Subject(platformContext: false, andGeoContext: true)
+        subject.geoLatitude = NSNumber(value: 10.0)
+        subject.geoLongitude = NSNumber(value: 10.0)
+        let geoLocationDict = subject.getGeoLocationDict()
+        XCTAssertNotNil(geoLocationDict)
+        XCTAssertNotNil(geoLocationDict)
+    }
+
+    func testDoesntReturnGeolocationContextIfDisabled() {
+        let subject = Subject(platformContext: false, andGeoContext: false)
+        subject.geoLatitude = NSNumber(value: 10.0)
+        subject.geoLongitude = NSNumber(value: 10.0)
+        let geoLocationDict = subject.getGeoLocationDict()
+        XCTAssertNil(geoLocationDict)
+    }
+
+    func testAnonymisesUserIdentifiers() {
+        let subject = Subject(platformContext: false, andGeoContext: false)
+        subject.userId = "aUserId"
+        subject.ipAddress = "127.0.0.1"
+        subject.networkUserId = "aNuid"
+        subject.domainUserId = "aDuid"
+        subject.language = "EN"
+
+        guard let values = subject.getStandardDict(withUserAnonymisation: true)?.dictionary else {
+            return XCTFail()
+        }
+        XCTAssertNil(values[kSPUid])
+        XCTAssertNil(values[kSPIpAddress])
+        XCTAssertNil(values[kSPNetworkUid])
+        XCTAssertNil(values[kSPDomainUid])
+        XCTAssertEqual(values[kSPLanguage], "EN" as NSObject)
+    }
+}
diff --git a/Tests/TestUtils.swift b/Tests/TestUtils.swift
new file mode 100644
index 000000000..e27242837
--- /dev/null
+++ b/Tests/TestUtils.swift
@@ -0,0 +1,174 @@
+//
+//  TestUtils.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Jonathan Almeida
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestUtils: XCTestCase {
+    override func setUp() {
+        super.setUp()
+    }
+    
+    override func tearDown() {
+        super.tearDown()
+    }
+    
+    func testGetTimeZone() {
+        XCTAssertEqual(
+            Utilities.timezone,
+            NSTimeZone.system.identifier)
+    }
+    
+    func testGetLanguage() {
+        XCTAssertNotNil(Utilities.language)
+    }
+    
+    func testGetPlatform() {
+#if os(iOS)
+        XCTAssertEqual(Utilities.platform, .mobile)
+#else
+        XCTAssertEqual(Utilities.platform, .desktop)
+#endif
+    }
+    
+    func testGetResolution() {
+        let actualResolution = Utilities.resolution
+        XCTAssertTrue(actualResolution != nil)
+    }
+    
+    func testGetEventId() {
+        let sample_uuid = Utilities.getUUIDString()
+        
+        // For regex pattern matching to verify if it's of UUID type 4
+        let pattern = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}"
+        let searchRange = NSRange(location: 0, length: sample_uuid.count)
+        var regex: NSRegularExpression? = nil
+        do {
+            regex = try NSRegularExpression(pattern: pattern, options: [])
+        } catch {
+        }
+        let matches = regex?.matches(in: sample_uuid, options: [], range: searchRange)
+        
+        XCTAssertEqual(
+            (matches?.count ?? 0),
+            Int(1),
+            "UUID generated doesn't match the type 4 UUID RFC")
+    }
+    
+    func testGetTimestamp() {
+        let sample_rand = String(format: "%lld", Utilities.getTimestamp().int64Value)
+        
+        // For regex pattern matching to verify if it's of UUID type 4
+        let pattern = "[0-9]+"
+        let searchRange = NSRange(location: 0, length: sample_rand.count)
+        var regex: NSRegularExpression? = nil
+        do {
+            regex = try NSRegularExpression(pattern: pattern, options: [])
+        } catch {
+        }
+        let matches = regex?.matches(in: sample_rand, options: [], range: searchRange)
+        
+        XCTAssertEqual(
+            (matches?.count ?? 0),
+            Int(1),
+            "Timestamp generated doesn't match the correct format 1234.567")
+        
+    }
+    
+    func testTimestampToISOString() {
+        XCTAssertEqual(Utilities.timestamp(toISOString: 1654496481347), "2022-06-06T06:21:21.347Z")
+        XCTAssertEqual(Utilities.timestamp(toISOString: 1654498990916), "2022-06-06T07:03:10.916Z")
+    }
+    
+    func testAppId() {
+        // This is always NULL in a test environment
+        print("appId: \(Utilities.appId!)")
+    }
+    
+    func testUrlEncodingString() {
+        XCTAssertEqual(Utilities.urlEncode(""), "")
+        XCTAssertEqual(Utilities.urlEncode("a"), "a")
+        XCTAssertEqual(Utilities.urlEncode("a b"), "a%20b")
+        XCTAssertEqual(Utilities.urlEncode("a=&"), "a%3D%26")
+    }
+    
+    func testUrlEncodingDictionary() {
+        XCTAssertEqual(Utilities.urlEncode([
+            "a": "b" as NSObject
+        ]), "a=b")
+        
+        let twoKeys = [
+            "a": "b" as NSObject,
+            "c": "d" as NSObject
+        ]
+        assertEqualUrlEncode(Utilities.urlEncode(twoKeys), "a=b&c=d")
+        
+        let intValues = [
+            "a": NSNumber(value: -5),
+            "c": NSNumber(value: 3)
+        ]
+        assertEqualUrlEncode(Utilities.urlEncode(intValues), "a=-5&c=3")
+        
+        let boolValues = [
+            "a": NSNumber(value: false),
+            "c": NSNumber(value: true)
+        ]
+        assertEqualUrlEncode(Utilities.urlEncode(boolValues), "a=0&c=1")
+        
+        let encodedValues = [
+            "a": " " as NSObject,
+            "c": "=" as NSObject
+        ]
+        assertEqualUrlEncode(Utilities.urlEncode(encodedValues), "a=%20&c=%3D")
+    }
+    
+    func testEscapedQueryString() {
+        let testValues = [
+            "null_value_key" : NSNull(),
+            "string_value_key" : "Not null" as NSObject,
+            "characters_key" : "|!\" £$%&/()=?^짰ç*éùàò+{}◊∞DZ¿≈ ⁄›‰¢’”»ıè¶#@][ˆ¡≠`´÷‹~~¥‘“«`" as NSObject
+        ]
+        let now = Utilities.urlEncode(testValues)
+        let then = "string_value_key=Not%20null&characters_key=%7C%21%22%20%C2%A3%24%25%26%2F%28%29%3D%3F%5E%C3%AC%C2%A7%C2%B0%C3%A7%2A%C3%A9%C3%B9%C3%A0%C3%B2%2B%7B%7D%E2%97%8A%E2%88%9E%C3%87%C2%B1%C2%BF%E2%89%88%20%EF%A3%BF%E2%81%84%E2%80%BA%E2%80%B0%C2%A2%E2%80%99%E2%80%9D%C2%BB%C4%B1%C3%A8%C2%B6%23%40%5D%5B%CB%86%C2%A1%E2%89%A0%60%C2%B4%C3%B7%E2%80%B9~~%C2%A5%E2%80%98%E2%80%9C%C2%AB%60&null_value_key=%3Cnull%3E"
+        assertEqualUrlEncode(now, then)
+        let urlString = "\("http://www.snowplow.com")?\(now)"
+        let url = URL(string: urlString)
+        XCTAssertNotNil(url)
+    }
+    
+    
+    func testDictionaryNullRemover() {
+        let dict = [
+            "a_null_value" : NSNull(),
+            "a_string_value" : "Not null!" as NSObject
+        ]
+        XCTAssertEqual(dict.count, 2)
+        let result = Utilities.removeNullValuesFromDict(withDict: dict)
+        XCTAssertEqual(result.count, 1)
+    }
+    
+    private func assertEqualUrlEncode(_ now: String, _ then: String) {
+        XCTAssertEqual(
+            Set<String>(now.split(separator: "&").map { String($0) }),
+            Set<String>(then.split(separator: "&").map { String($0) })
+        )
+    }
+}
diff --git a/Tests/TestWebViewMessageHandler.swift b/Tests/TestWebViewMessageHandler.swift
new file mode 100644
index 000000000..823b2379e
--- /dev/null
+++ b/Tests/TestWebViewMessageHandler.swift
@@ -0,0 +1,138 @@
+//
+//  TestWebViewMessageHandler.h
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+#if os(iOS) || os(macOS)
+import XCTest
+@testable import SnowplowTracker
+
+class TestWebViewMessageHandler: XCTestCase {
+    var webViewMessageHandler: WebViewMessageHandler?
+    var networkConnection: MockNetworkConnection?
+
+    override func setUp() {
+        webViewMessageHandler = WebViewMessageHandler()
+        networkConnection = MockNetworkConnection(requestOption: .post, statusCode: 200)
+
+        let networkConfig = NetworkConfiguration(networkConnection: networkConnection)
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.base64Encoding = false
+        trackerConfig.sessionContext = false
+        trackerConfig.platformContext = false
+
+        Snowplow.removeAllTrackers()
+        _ = Snowplow.createTracker(namespace: UUID().uuidString, network: networkConfig, configurations: [trackerConfig])
+    }
+
+    override func tearDown() {
+        Snowplow.removeAllTrackers()
+    }
+
+    func testTracksStructuredEventWithAllProperties() {
+        let message = MockWKScriptMessage(
+            body: [
+                "command": "trackStructEvent",
+                "event": [
+                "category": "cat",
+                "action": "act",
+                "label": "lbl",
+                "property": "prop",
+                "value": NSNumber(value: 10.0)
+            ]
+            ])
+        webViewMessageHandler?.receivedMesssage(message)
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
+
+        XCTAssertEqual(1, networkConnection?.sendingCount)
+        XCTAssertEqual(1, (networkConnection?.previousRequests)?[0].count)
+        let request = (networkConnection?.previousRequests)?[0][0]
+        let payload = (request?.payload?.dictionary?["data"] as? [[String: Any]])?[0]
+        XCTAssert((payload?["se_ca"] as? String == "cat"))
+        XCTAssert((payload?["se_ac"] as? String == "act"))
+        XCTAssert((payload?["se_pr"] as? String == "prop"))
+        XCTAssert((payload?["se_la"] as? String == "lbl"))
+        XCTAssert((payload?["se_va"] as? String == "10"))
+    }
+
+    func testTracksEventWithCorrectTracker() {
+        // create the second tracker
+        let networkConnection2 = MockNetworkConnection(requestOption: .post, statusCode: 200)
+        let networkConfig = NetworkConfiguration(networkConnection: networkConnection2)
+        _ = Snowplow.createTracker(namespace: "ns2", network: networkConfig, configurations: [])
+
+        // track an event using the second tracker
+        let message = MockWKScriptMessage(
+            body: [
+                "command": "trackPageView",
+                "event": [
+                    "url": "http://localhost"
+                ],
+                "trackers": ["ns2"]
+            ])
+        webViewMessageHandler?.receivedMesssage(message)
+
+        // wait and check for the event
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
+
+        XCTAssertEqual(0, networkConnection?.sendingCount)
+        XCTAssertEqual(1, networkConnection2.sendingCount)
+        XCTAssertEqual(1, networkConnection2.previousRequests[0].count)
+    }
+
+    func testTracksEventWithContext() {
+        let message = MockWKScriptMessage(
+            body: [
+                "command": "trackSelfDescribingEvent",
+                "event": [
+                    "schema": "http://schema.com",
+                    "data": [
+                        "key": "val"
+                    ]
+                ],
+                "context": [
+                    [
+                        "schema": "http://context-schema.com",
+                        "data": [
+                            "a": "b"
+                        ]
+                    ]
+                ]
+            ])
+        webViewMessageHandler?.receivedMesssage(message)
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
+
+        XCTAssertEqual(1, networkConnection?.sendingCount)
+        XCTAssertEqual(1, (networkConnection?.previousRequests)?[0].count)
+        let request = (networkConnection?.previousRequests)?[0][0]
+        let payload = (request?.payload?.dictionary?["data"] as? [[String : Any]])?[0]
+
+        let context = payload?["co"] as? String
+        XCTAssert(context?.contains("{\"a\":\"b\"}") ?? false)
+    }
+}
+#endif
diff --git a/Snowplow iOSTests/Utils/SPMockWKScriptMessage.h b/Tests/Tests-bridging-header.h
similarity index 81%
rename from Snowplow iOSTests/Utils/SPMockWKScriptMessage.h
rename to Tests/Tests-bridging-header.h
index ec53c6385..04eecfe6d 100644
--- a/Snowplow iOSTests/Utils/SPMockWKScriptMessage.h	
+++ b/Tests/Tests-bridging-header.h
@@ -1,5 +1,5 @@
 //
-//  SPMockWKScriptMessage.h
+//  Tests-bridging-header.h
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,14 +19,9 @@
 //  License: Apache License Version 2.0
 //
 
-#import <WebKit/WebKit.h>
+#ifndef Tests_bridging_header_h
+#define Tests_bridging_header_h
 
-NS_ASSUME_NONNULL_BEGIN
+#import <SnowplowIgluClient/IGLUClient.h>
 
-@interface SPMockWKScriptMessage : WKScriptMessage
-
-- (id) initWithBody:(id)body;
-
-@end
-
-NS_ASSUME_NONNULL_END
+#endif /* Tests_bridging_header_h */
diff --git a/Tests/Utils/MockDeviceInfoMonitor.swift b/Tests/Utils/MockDeviceInfoMonitor.swift
new file mode 100644
index 000000000..b4f4b6fd0
--- /dev/null
+++ b/Tests/Utils/MockDeviceInfoMonitor.swift
@@ -0,0 +1,122 @@
+//
+//  MockDeviceInfoMonitor.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+@testable import SnowplowTracker
+
+class MockDeviceInfoMonitor: DeviceInfoMonitor {
+    var methodAccessCounts: [String : Int] = [:]
+    var customAppleIdfa: String? = "appleIdfa"
+    var customAppleIdfv: String? = "appleIdfv"
+
+    override var appleIdfa: String? {
+        increaseMethodAccessCount("appleIdfa")
+        return customAppleIdfa
+    }
+
+    override var appleIdfv: String? {
+        increaseMethodAccessCount("appleIdfv")
+        return customAppleIdfv
+    }
+
+    override var deviceVendor: String? {
+        increaseMethodAccessCount("deviceVendor")
+        return "Apple Inc."
+    }
+
+    override var deviceModel: String? {
+        increaseMethodAccessCount("deviceModel")
+        return "deviceModel"
+    }
+
+    override var osVersion: String? {
+        increaseMethodAccessCount("osVersion")
+        return "13.0.0"
+    }
+
+    override var osType: String? {
+        increaseMethodAccessCount("osType")
+        return "ios"
+    }
+
+    override var carrierName: String? {
+        increaseMethodAccessCount("carrierName")
+        return "att"
+    }
+
+    override var networkTechnology: String {
+        increaseMethodAccessCount("networkTechnology")
+        return "3g"
+    }
+
+    override var carrierKey: String {
+        return ""
+    }
+
+    override var networkType: String {
+        increaseMethodAccessCount("networkType")
+        return "wifi"
+    }
+
+    override var batteryLevel: Int? {
+        increaseMethodAccessCount("batteryLevel")
+        return 20
+    }
+
+    override var batteryState: String {
+        increaseMethodAccessCount("batteryState")
+        return "charging"
+    }
+
+    override var isLowPowerModeEnabled: Bool? {
+        increaseMethodAccessCount("isLowPowerModeEnabled")
+        return false
+    }
+
+    override var physicalMemory: UInt64 {
+        increaseMethodAccessCount("physicalMemory")
+        return 100000
+    }
+
+    override var appAvailableMemory: Int? {
+        increaseMethodAccessCount("appAvailableMemory")
+        return 1000
+    }
+
+    override var availableStorage: Int64? {
+        increaseMethodAccessCount("availableStorage")
+        return 9000
+    }
+
+    override var totalStorage: Int? {
+        increaseMethodAccessCount("totalStorage")
+        return 900000
+    }
+
+    func accessCount(_ method: String) -> Int {
+        let count = methodAccessCounts[method] ?? 0
+        return count
+    }
+
+    func increaseMethodAccessCount(_ method: String) {
+        methodAccessCounts[method] = accessCount(method) + 1
+    }
+}
diff --git a/Tests/Utils/MockEventStore.swift b/Tests/Utils/MockEventStore.swift
new file mode 100644
index 000000000..6e8a27528
--- /dev/null
+++ b/Tests/Utils/MockEventStore.swift
@@ -0,0 +1,90 @@
+//
+//  MockEventStore.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+@testable import SnowplowTracker
+
+class MockEventStore: NSObject, EventStore {
+    
+    var db: [Int64 : Payload] = [:]
+    var lastInsertedRow = 0
+
+    override init() {
+        super.init()
+        db = [:]
+        lastInsertedRow = -1
+    }
+
+    func addEvent(_ payload: Payload) {
+        objc_sync_enter(self)
+        lastInsertedRow += 1
+        logVerbose(message: "Add \(payload)")
+        db[Int64(lastInsertedRow)] = payload
+        objc_sync_exit(self)
+    }
+
+    func removeEvent(withId storeId: Int64) -> Bool {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        logVerbose(message: "Remove \(storeId)")
+        return db.removeValue(forKey: storeId) != nil
+    }
+
+    func removeEvents(withIds storeIds: [NSNumber]) -> Bool {
+        let result = true
+        for storeId in storeIds {
+            db.removeValue(forKey: storeId.int64Value)
+        }
+        return result
+    }
+
+    func removeAllEvents() -> Bool {
+        objc_sync_enter(self)
+        db.removeAll()
+        lastInsertedRow = -1
+        objc_sync_exit(self)
+        return true
+    }
+
+    func count() -> UInt {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        return UInt(db.count)
+    }
+
+    func emittableEvents(withQueryLimit queryLimit: UInt) -> [EmitterEvent] {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        var eventIds: [Int64] = []
+        var events: [EmitterEvent] = []
+        for (key, obj) in db {
+            let payloadCopy = Payload(dictionary: obj.dictionary ?? [:])
+            let event = EmitterEvent(payload: payloadCopy, storeId: key)
+            events.append(event)
+            eventIds.append(event.storeId)
+        }
+        if queryLimit < events.count {
+            events = Array(events.prefix(Int(queryLimit)))
+        }
+        logVerbose(message: "emittableEventsWithQueryLimit: \(eventIds)")
+        return events
+    }
+}
diff --git a/Snowplow iOSTests/Utils/SPMockLoggerDelegate.h b/Tests/Utils/MockLoggerDelegate.swift
similarity index 61%
rename from Snowplow iOSTests/Utils/SPMockLoggerDelegate.h
rename to Tests/Utils/MockLoggerDelegate.swift
index 5fc97b5fd..7a93ec720 100644
--- a/Snowplow iOSTests/Utils/SPMockLoggerDelegate.h	
+++ b/Tests/Utils/MockLoggerDelegate.swift
@@ -1,5 +1,5 @@
 //
-//  SPMockLoggerDelegate.h
+//  SPMockLoggerDelegate.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -19,17 +19,23 @@
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
-#import "SPLoggerDelegate.h"
+import Foundation
+@testable import SnowplowTracker
 
-NS_ASSUME_NONNULL_BEGIN
+class MockLoggerDelegate: NSObject, LoggerDelegate {
+    var errorLogs: [String] = []
+    var debugLogs: [String] = []
+    var verboseLogs: [String] = []
 
-@interface SPMockLoggerDelegate : NSObject <SPLoggerDelegate>
+    func debug(_ tag: String, message: String) {
+        debugLogs.append(message)
+    }
 
-@property (nonatomic) NSMutableArray<NSString *> *errorLogs;
-@property (nonatomic) NSMutableArray<NSString *> *debugLogs;
-@property (nonatomic) NSMutableArray<NSString *> *verboseLogs;
+    func error(_ tag: String, message: String) {
+        errorLogs.append(message)
+    }
 
-@end
-
-NS_ASSUME_NONNULL_END
+    func verbose(_ tag: String, message: String) {
+        verboseLogs.append(message)
+    }
+}
diff --git a/Tests/Utils/MockNetworkConnection.swift b/Tests/Utils/MockNetworkConnection.swift
new file mode 100644
index 000000000..9e8c5ec66
--- /dev/null
+++ b/Tests/Utils/MockNetworkConnection.swift
@@ -0,0 +1,59 @@
+//
+//  SPMockNetworkConnection.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini, Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+@testable import SnowplowTracker
+
+class MockNetworkConnection: NSObject, NetworkConnection {
+    var statusCode = 0
+
+    private var _httpMethod: HttpMethodOptions?
+    var httpMethod: HttpMethodOptions {
+        return _httpMethod!
+    }
+    var previousResults: [[RequestResult]] = []
+    var previousRequests: [[Request]] = []
+    var urlEndpoint: URL? {
+        return (URL(string: "http://fake-url.com"))!
+    }
+
+    var sendingCount: Int {
+        return previousResults.count
+    }
+
+    init(requestOption httpMethod: HttpMethodOptions, statusCode: Int) {
+        super.init()
+        self._httpMethod = httpMethod
+        self.statusCode = statusCode
+    }
+
+    func sendRequests(_ requests: [Request]) -> [RequestResult] {
+        var requestResults: [RequestResult] = []
+        for request in requests {
+            let result = RequestResult(statusCode: NSNumber(value: statusCode), oversize: request.oversize, storeIds: request.emitterEventIds)
+            logVerbose(message: "Sent \(String(describing: request.emitterEventIds)) with success \(result.isSuccessful ? "YES" : "NO")")
+            requestResults.append(result)
+        }
+        previousRequests.append(requests)
+        previousResults.append(requestResults)
+        return requestResults
+    }
+}
diff --git a/Snowplow/Internal/Emitter/SPEmitterEventProcessing.h b/Tests/Utils/MockWKScriptMessage.swift
similarity index 70%
rename from Snowplow/Internal/Emitter/SPEmitterEventProcessing.h
rename to Tests/Utils/MockWKScriptMessage.swift
index dab5db0f5..71bd85831 100644
--- a/Snowplow/Internal/Emitter/SPEmitterEventProcessing.h
+++ b/Tests/Utils/MockWKScriptMessage.swift
@@ -1,5 +1,5 @@
 //
-//  SPEmitterEventProcessing.h
+//  SPMockWKScriptMessage.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -15,22 +15,24 @@
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
 //
-//  Authors: Alex Benini
+//  Authors: Matus Tomlein
 //  License: Apache License Version 2.0
 //
 
-#import <Foundation/Foundation.h>
+#if os(iOS) || os(macOS)
+import WebKit
 
-NS_ASSUME_NONNULL_BEGIN
+class MockWKScriptMessage: WKScriptMessage {
+    private var messageBody: Any?
 
-NS_SWIFT_NAME(EmitterEventProcessing)
-@protocol SPEmitterEventProcessing
+    init(body: Any?) {
+        super.init()
 
-- (void)addPayloadToBuffer:(SPPayload *)eventPayload;
-- (void)pauseTimer;
-- (void)resumeTimer;
-- (void)flush;
+        messageBody = body
+    }
 
-@end
-
-NS_ASSUME_NONNULL_END
+    override var body: Any {
+        return messageBody!
+    }
+}
+#endif
diff --git a/Snowplow iOSTests/en.lproj/InfoPlist.strings b/Tests/en.lproj/InfoPlist.strings
similarity index 100%
rename from Snowplow iOSTests/en.lproj/InfoPlist.strings
rename to Tests/en.lproj/InfoPlist.strings

From 16202dc5f1fbfc1660a404a293317aaf23e95efd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matu=CC=81s=CC=8C=20Tomlein?= <matus.tomlein@gmail.com>
Date: Wed, 30 Nov 2022 07:30:02 +0100
Subject: [PATCH 02/19] Prepare for 5.0.0-alpha.1 release

---
 CHANGELOG                           | 4 ++++
 SnowplowTracker.podspec             | 2 +-
 Sources/Core/TrackerConstants.swift | 8 ++++----
 VERSION                             | 2 +-
 4 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index f44c12165..65315fcd1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+Version 5.0.0-alpha.1 (2022-11-30)
+--------------------------
+Migrate to Swift (#732)
+
 Version 4.1.0 (2022-11-16)
 --------------------------
 Anonymise previous session ID and user identifiers in subject when user anonymisation is enabled (#720)
diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec
index 87c0c40a3..8fa8d93ac 100644
--- a/SnowplowTracker.podspec
+++ b/SnowplowTracker.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = "SnowplowTracker"
-    s.version          = "4.1.0"
+    s.version          = "5.0.0-alpha.1"
     s.summary          = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
     s.description      = <<-DESC
     Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
diff --git a/Sources/Core/TrackerConstants.swift b/Sources/Core/TrackerConstants.swift
index be678473d..da8a9555f 100644
--- a/Sources/Core/TrackerConstants.swift
+++ b/Sources/Core/TrackerConstants.swift
@@ -23,13 +23,13 @@ import Foundation
 
 // --- Version
 #if os(iOS)
-let kSPVersion = "ios-4.1.0"
+let kSPVersion = "ios-5.0.0-alpha.1"
 #elseif os(tvOS)
-let kSPVersion = "tvos-4.1.0"
+let kSPVersion = "tvos-5.0.0-alpha.1"
 #elseif os(watchOS)
-let kSPVersion = "watchos-4.1.0"
+let kSPVersion = "watchos-5.0.0-alpha.1"
 #else
-let kSPVersion = "osx-4.1.0"
+let kSPVersion = "osx-5.0.0-alpha.1"
 #endif
 
 // --- Session Dictionary keys
diff --git a/VERSION b/VERSION
index ee74734aa..5d200d543 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.1.0
+5.0.0-alpha.1

From 0cb138b6606dc1acdafe458a37e8aa7b9ff9e50a Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Wed, 7 Dec 2022 11:22:35 +0100
Subject: [PATCH 03/19] Drop Carthage and build using Swift Package Manager
 (close #735)

PR #737
---
 .github/workflows/build.yml                   |   98 +-
 .github/workflows/snyk.yml                    |    2 +-
 .scripts/carthage-workaround.sh               |   24 -
 .scripts/demo_apps_branch_name                |    2 +-
 .scripts/setup.sh                             |   27 -
 .scripts/test_framework.sh                    |   18 -
 Cartfile                                      |    2 -
 Cartfile.private                              |    5 -
 Cartfile.resolved                             |    4 -
 Package.resolved                              |    9 +
 Package.swift                                 |   12 +-
 README.md                                     |    2 +-
 Snowplow iOS/Info.plist                       |   22 -
 Snowplow macOS/Info.plist                     |   22 -
 Snowplow watchOS/Info.plist                   |   24 -
 Snowplow watchOS/Snowplow_watchOS.h           |   29 -
 Snowplow.xcodeproj/project.pbxproj            | 2905 -----------------
 .../contents.xcworkspacedata                  |    7 -
 .../xcshareddata/IDEWorkspaceChecks.plist     |    8 -
 .../xcshareddata/Snowplow.xccheckout          |   41 -
 .../xcshareddata/WorkspaceSettings.xcsettings |    5 -
 ...DF6688A4-3B86-4BD9-9AA9-38554F93D718.plist |   32 -
 .../Info.plist                                |   40 -
 .../xcschemes/Snowplow-iOS-Static.xcscheme    |   67 -
 .../xcschemes/Snowplow-iOS.xcscheme           |   96 -
 .../xcschemes/Snowplow-macOS.xcscheme         |   95 -
 .../xcschemes/Snowplow-watchOS.xcscheme       |   76 -
 SnowplowTracker.podspec                       |    4 +-
 Sources/Core/Subject/PlatformContext.swift    |   23 +-
 .../Snowplow/Payload/SelfDescribingJson.swift |    8 +-
 Sources/Snowplow/Tracker/SessionState.swift   |    6 +-
 .../TestRemoteConfiguration.swift             |  110 +-
 Tests/TestGeneratedJsons.swift                |  390 ---
 Tests/TestNetworkConnection.swift             |   87 +-
 Tests/TestPlatformContext.swift               |   29 +-
 Tests/TestSQLiteEventStore.swift              |    4 +
 Tests/Tests-bridging-header.h                 |   27 -
 37 files changed, 198 insertions(+), 4164 deletions(-)
 delete mode 100755 .scripts/carthage-workaround.sh
 delete mode 100755 .scripts/setup.sh
 delete mode 100755 .scripts/test_framework.sh
 delete mode 100644 Cartfile
 delete mode 100644 Cartfile.private
 delete mode 100644 Cartfile.resolved
 delete mode 100644 Snowplow iOS/Info.plist
 delete mode 100644 Snowplow macOS/Info.plist
 delete mode 100644 Snowplow watchOS/Info.plist
 delete mode 100644 Snowplow watchOS/Snowplow_watchOS.h
 delete mode 100644 Snowplow.xcodeproj/project.pbxproj
 delete mode 100644 Snowplow.xcodeproj/project.xcworkspace/contents.xcworkspacedata
 delete mode 100644 Snowplow.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
 delete mode 100644 Snowplow.xcodeproj/project.xcworkspace/xcshareddata/Snowplow.xccheckout
 delete mode 100644 Snowplow.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
 delete mode 100644 Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/DF6688A4-3B86-4BD9-9AA9-38554F93D718.plist
 delete mode 100644 Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/Info.plist
 delete mode 100644 Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme
 delete mode 100644 Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme
 delete mode 100644 Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme
 delete mode 100644 Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme
 delete mode 100644 Tests/TestGeneratedJsons.swift
 delete mode 100644 Tests/Tests-bridging-header.h

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 46d5def7a..efda17635 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,32 +3,72 @@ name: Build
 on: [push]
 
 jobs:
-  test_framework:
-    name: "Framework (iOS ${{ matrix.version.ios }})"
-    runs-on: macos-${{ matrix.version.macos }}
-    env:
-      DEVELOPER_DIR: /Applications/Xcode_${{ matrix.version.xcode }}.app/Contents/Developer
-      
+  podspec:
+    name: Lint Podspec for ${{ matrix.platform }}
+    runs-on: macos-latest
     strategy:
-      fail-fast: false
       matrix:
-        version:
-          - {ios: 15.5, iphone: iPhone 12 Pro, watchos: 8.5, watch: Apple Watch Series 5 - 44mm, macos: '12', xcode: 13.4}
-          - {ios: 14.4, iphone: iPhone 8, watchos: 7.2, watch: Apple Watch Series 4 - 40mm, macos: '11', xcode: 12.4}
+        platform: [ios, osx, tvos, watchos]
+    steps:
+      - uses: actions/checkout@v3
+      - name: Lint Podspec
+        run: pod lib lint --platforms=${{ matrix.platform }}
 
+  test_framework:
+    name: ${{ matrix.name }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        include:
+          - name: "xcodebuild (iOS 14.0, Xcode 12.0.1)"
+            os: macos-10.15
+            xcode-version: "12"
+            sdk: iphonesimulator14.0
+            destination: "platform=iOS Simulator,OS=14.0,name=iPhone 11"
+          - name: "xcodebuild (iOS 15.2, Xcode 13.2.1)"
+            os: macos-11
+            xcode-version: "13.2.1"
+            sdk: iphonesimulator15.2
+            destination: "platform=iOS Simulator,OS=15.2,name=iPhone 13"
+          - name: "xcodebuild (macOS 10.15, Xcode 12.0.1)"
+            os: macos-10.15
+            xcode-version: "12"
+            sdk: macosx10.15
+            destination: "platform=OS X"
+          - name: "xcodebuild (macOS 12.1, Xcode 13.2.1)"
+            os: macos-11
+            xcode-version: "13.2.1"
+            sdk: macosx12.1
+            destination: "platform=OS X"
+          - name: "xcodebuild (watchOS 8.3, Xcode 13.2.1)"
+            os: macos-11
+            xcode-version: "13.2.1"
+            sdk: watchos8.3
+            destination: "platform=watchOS Simulator,OS=8.3,name=Apple Watch Series 7 - 45mm"
+          - name: "xcodebuild (tvOS 14.0, Xcode 12.0.1)"
+            os: macos-10.15
+            xcode-version: "12"
+            sdk: appletvsimulator14.0
+            destination: "platform=tvOS Simulator,OS=14.0,name=Apple TV"
+          - name: "xcodebuild (tvOS 15.2, Xcode 13.2.1)"
+            os: macos-11
+            xcode-version: "13.2.1"
+            sdk: appletvsimulator15.2
+            destination: "platform=tvOS Simulator,OS=15.2,name=Apple TV"
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
-      - name: Test
-        env:
-          IOS: ${{ matrix.version.ios }}
-          WATCHOS: ${{ matrix.version.watchos }}
-          IPHONE: ${{ matrix.version.iphone }}
-          WATCH: ${{ matrix.version.watch }}
+      - name: Select Xcode Version
+        run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer
+
+      - name: Build & Test
         run: |
-          . .scripts/setup.sh
-          .scripts/test_framework.sh "${BUILD_PROJECT_LIB}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_LIB_IOS}" "${BUILD_DEST_WATCH}" "${BUILD_SCHEME_LIB_WATCH}"  
+          xcodebuild \
+            -scheme SnowplowTracker \
+            -sdk "${{ matrix.sdk }}" \
+            -destination "${{ matrix.destination }}" \
+            clean test | xcpretty
 
   build_objc_demo_app:
     name: "ObjC demo (iOS ${{ matrix.version.ios }})"
@@ -46,17 +86,18 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Get example branch name
         id: example_branch
         run: |
           DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
           echo ::set-output name=name::${DEMO_BRANCH}
+
       - name: Checkout demo app
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
-          repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
+          repository: snowplow-incubator/snowplow-objc-tracker-examples
           ref: ${{ steps.example_branch.outputs.name }}
           path: examples
 
@@ -86,17 +127,18 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Get example branch name
         id: example_branch
         run: |
           DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
           echo ::set-output name=name::${DEMO_BRANCH}
+
       - name: Checkout demo app
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
-          repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
+          repository: snowplow-incubator/snowplow-objc-tracker-examples
           ref: ${{ steps.example_branch.outputs.name }}
           path: examples
 
@@ -126,7 +168,7 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Get example branch name
         id: example_branch
@@ -150,9 +192,9 @@ jobs:
           echo ::set-output name=name::${GIT_BRANCH}
 
       - name: Checkout demo app
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
-          repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
+          repository: snowplow-incubator/snowplow-objc-tracker-examples
           ref: ${{ steps.example_branch.outputs.name }}
           path: examples
 
diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml
index fdbf7f45b..b4da86a17 100644
--- a/.github/workflows/snyk.yml
+++ b/.github/workflows/snyk.yml
@@ -16,7 +16,7 @@ jobs:
     - name: Checkout demo app
       uses: actions/checkout@v2
       with:
-        repository: snowplow-incubator/snowplow-swift-ios-tracker-examples
+        repository: snowplow-incubator/snowplow-objc-tracker-examples
         path: examples
       
     - name: Run Snyk to check for vulnerabilities in tracker
diff --git a/.scripts/carthage-workaround.sh b/.scripts/carthage-workaround.sh
deleted file mode 100755
index 6cb644584..000000000
--- a/.scripts/carthage-workaround.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-# carthage-build.sh
-# Usage example: ./carthage-build.sh --platform iOS
-
-set -euo pipefail
-
-xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
-trap 'rm -f "$xcconfig"' INT TERM HUP EXIT
-
-# For Xcode 12+ make sure EXCLUDED_ARCHS is set to arm architectures otherwise
-# the build will fail on lipo due to duplicate architectures.
-
-CURRENT_XCODE_VERSION="$(xcodebuild -version | grep "Xcode" | cut -d' ' -f2 | cut -d'.' -f1)00"
-CURRENT_XCODE_BUILD=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3)
-echo $CURRENT_XCODE_VERSION
-echo $CURRENT_XCODE_BUILD
-
-echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_${CURRENT_XCODE_VERSION}__BUILD_${CURRENT_XCODE_BUILD} = arm64 arm64e armv7 armv7s armv6 armv8" >> $xcconfig
-
-echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_'${CURRENT_XCODE_VERSION}' = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_$(XCODE_VERSION_MAJOR)__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' >> $xcconfig
-echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig
-
-export XCODE_XCCONFIG_FILE="$xcconfig"
diff --git a/.scripts/demo_apps_branch_name b/.scripts/demo_apps_branch_name
index ba2906d06..f23b39592 100644
--- a/.scripts/demo_apps_branch_name
+++ b/.scripts/demo_apps_branch_name
@@ -1 +1 @@
-main
+swift
diff --git a/.scripts/setup.sh b/.scripts/setup.sh
deleted file mode 100755
index cb09a7cba..000000000
--- a/.scripts/setup.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-
-# Set testing environment variables
-BUILD_DEST_IOS="platform=iOS Simulator,OS=${IOS:-14.0},name=${IPHONE:-iPhone 11 Pro}"
-BUILD_DEST_PAIRED="platform=iOS Simulator,OS=${IOS:-14.0},name=${IPHONE:-iPhone 11 Pro}"
-BUILD_DEST_WATCH="platform=watchOS Simulator,OS=${WATCHOS:-7.0},name=${WATCH:-Apple Watch Series 5 - 44mm}"
-
-BUILD_PROJECT_LIB="-project Snowplow.xcodeproj"
-BUILD_SCHEME_LIB_IOS="-scheme Snowplow-iOS"
-BUILD_SCHEME_LIB_WATCH="-scheme Snowplow-watchOS"
-
-BUILD_WORKSPACE_OBJC_DEMO="-workspace SnowplowDemo.xcworkspace"
-BUILD_SCHEME_OBJC_DEMO="-scheme SnowplowDemo"
-
-BUILD_WORKSPACE_SWIFT_DEMO="-workspace SnowplowSwiftDemo.xcworkspace"
-BUILD_PROJECT_SWIFT_DEMO="-project SnowplowSwiftDemo.xcodeproj"
-BUILD_SCHEME_SWIFT_DEMO_IOS="-scheme SnowplowSwiftDemo"
-BUILD_SCHEME_SWIFT_DEMO_WATCH="-scheme SnowplowSwiftWatch"
-
-BUILD_PROJECT_SWIFT_SPM_DEMO="-project SnowplowSwiftSPMDemo.xcodeproj"
-BUILD_SCHEME_SWIFT_SPM_DEMO_IOS="-scheme SnowplowSwiftSPMDemo"
-
-#if [ "$CI" = true ]; then
-#	printf "\n\n Install Slather - Code coverage tool \n"
-#	gem install slather
-#fi
-# TODO: Once Slather is ready for GH Actions, add a call to `slather` as last command to upload code coverage data
diff --git a/.scripts/test_framework.sh b/.scripts/test_framework.sh
deleted file mode 100755
index 701aecdf9..000000000
--- a/.scripts/test_framework.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-PROJECT=$1
-DEST_IOS=$2
-SCHEME_IOS=$3
-DEST_WATCH=$4
-SCHEME_WATCH=$5
-
-printf "\n\n Carthage bootstrap \n"
-. .scripts/carthage-workaround.sh # to remove when fixed https://github.com/Carthage/Carthage/issues/3019#issuecomment-665136323
-carthage bootstrap
-
-printf "\n\n Test framework on iOS \n"
-set -o pipefail && xcodebuild -destination "${DEST_IOS}" ${PROJECT} ${SCHEME_IOS} clean test | xcpretty
-
-printf "\n\n Build framework on watchOS \n"
-set -o pipefail && xcodebuild -destination "${DEST_WATCH}" ${PROJECT} ${SCHEME_WATCH} clean build | xcpretty
diff --git a/Cartfile b/Cartfile
deleted file mode 100644
index 0191571c9..000000000
--- a/Cartfile
+++ /dev/null
@@ -1,2 +0,0 @@
-# FMDB
-github "ccgus/fmdb" ~> 2.7
diff --git a/Cartfile.private b/Cartfile.private
deleted file mode 100644
index dbc7b33ca..000000000
--- a/Cartfile.private
+++ /dev/null
@@ -1,5 +0,0 @@
-# Stub library for testing
-github "luisobo/Nocilla" == 0.11.0
-
-# objc-iglu-client
-github "snowplow/iglu-objc-client" "feature/carthage"
diff --git a/Cartfile.resolved b/Cartfile.resolved
deleted file mode 100644
index e491aee93..000000000
--- a/Cartfile.resolved
+++ /dev/null
@@ -1,4 +0,0 @@
-github "ccgus/fmdb" "2.7.7"
-github "luisobo/Nocilla" "0.11.0"
-github "snowplow/iglu-objc-client" "10b9758ef35c21d4bfee9c11d8f4813347a50097"
-github "vlas-voloshin/JSONSchemaValidation" "1.6.0"
diff --git a/Package.resolved b/Package.resolved
index 129a9849b..0d5690d38 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -9,6 +9,15 @@
           "revision": "61e51fde7f7aab6554f30ab061cc588b28a97d04",
           "version": "2.7.7"
         }
+      },
+      {
+        "package": "Mocker",
+        "repositoryURL": "https://github.com/WeTransfer/Mocker.git",
+        "state": {
+          "branch": null,
+          "revision": "5d86f27a8f80d4ba388bc1a379a3c2289a1f3d18",
+          "version": "2.6.0"
+        }
       }
     ]
   },
diff --git a/Package.swift b/Package.swift
index 91ac3299d..17509b0a7 100644
--- a/Package.swift
+++ b/Package.swift
@@ -7,8 +7,8 @@ let package = Package(
     platforms: [
         .macOS("10.13"),
         .iOS("11.0"),
-        .tvOS("11.0"),
-        .watchOS("4.0")
+        .tvOS("12.0"),
+        .watchOS("6.0")
     ],
     products: [
         .library(
@@ -16,7 +16,8 @@ let package = Package(
             targets: ["SnowplowTracker"]),
     ],
     dependencies: [
-        .package(name: "FMDB", url: "https://github.com/ccgus/fmdb", from: "2.7.6")
+        .package(name: "FMDB", url: "https://github.com/ccgus/fmdb", from: "2.7.6"),
+        .package(name: "Mocker", url: "https://github.com/WeTransfer/Mocker.git", from: "2.5.4"),
     ],
     targets: [
         .target(
@@ -25,7 +26,10 @@ let package = Package(
             path: "./Sources"),
         .testTarget(
             name: "Tests",
-            dependencies: ["SnowplowTracker"],
+            dependencies: [
+                "SnowplowTracker",
+                "Mocker"
+            ],
             path: "Tests")
     ]
 )
diff --git a/README.md b/README.md
index 637e3ad30..702fcbdf8 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ With this tracker you can collect event data from your applications, games or fr
 
 ### Demo apps using the Snowplow iOS Tracker
 
-Some examples of demo apps instrumented with our iOS Tracker can be found in the [snowplow-swift-ios-tracker-examples](https://github.com/snowplow-incubator/snowplow-swift-ios-tracker-examples) repository.
+Some examples of demo apps instrumented with our iOS Tracker can be found in the [snowplow-objc-tracker-examples](https://github.com/snowplow-incubator/snowplow-objc-tracker-examples) repository.
 
 ### Instrument the iOS Tracker
 
diff --git a/Snowplow iOS/Info.plist b/Snowplow iOS/Info.plist
deleted file mode 100644
index e1fe4cfb7..000000000
--- a/Snowplow iOS/Info.plist	
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>FMWK</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleVersion</key>
-	<string>$(CURRENT_PROJECT_VERSION)</string>
-</dict>
-</plist>
diff --git a/Snowplow macOS/Info.plist b/Snowplow macOS/Info.plist
deleted file mode 100644
index e1fe4cfb7..000000000
--- a/Snowplow macOS/Info.plist	
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>FMWK</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleVersion</key>
-	<string>$(CURRENT_PROJECT_VERSION)</string>
-</dict>
-</plist>
diff --git a/Snowplow watchOS/Info.plist b/Snowplow watchOS/Info.plist
deleted file mode 100644
index be8f9894b..000000000
--- a/Snowplow watchOS/Info.plist	
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleDisplayName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>FMWK</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleVersion</key>
-	<string>$(CURRENT_PROJECT_VERSION)</string>
-</dict>
-</plist>
diff --git a/Snowplow watchOS/Snowplow_watchOS.h b/Snowplow watchOS/Snowplow_watchOS.h
deleted file mode 100644
index 1c27343d2..000000000
--- a/Snowplow watchOS/Snowplow_watchOS.h	
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-//  Snowplow_watchOS.h
-//  Snowplow watchOS
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-
-#import <WatchKit/WatchKit.h>
-
-//! Project version number for Snowplow_watchOS.
-FOUNDATION_EXPORT double Snowplow_watchOSVersionNumber;
-
-//! Project version string for Snowplow_watchOS.
-FOUNDATION_EXPORT const unsigned char Snowplow_watchOSVersionString[];
-
-// In this header, you should import all the public headers of your framework using statements like #import <Snowplow_watchOS/PublicHeader.h>
-
-
diff --git a/Snowplow.xcodeproj/project.pbxproj b/Snowplow.xcodeproj/project.pbxproj
deleted file mode 100644
index 30a259d4e..000000000
--- a/Snowplow.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,2905 +0,0 @@
-// !$*UTF8*$!
-{
-	archiveVersion = 1;
-	classes = {
-	};
-	objectVersion = 50;
-	objects = {
-
-/* Begin PBXBuildFile section */
-		23DFEC7D2362FAA500BD19C4 /* FMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DAC1021CC3F010065F874 /* FMDB.framework */; };
-		23DFEC7E2362FAB100BD19C4 /* FMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DAC1321CC3F140065F874 /* FMDB.framework */; };
-		23DFEC7F2362FAC700BD19C4 /* Nocilla.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 750E7F1121F2735C0050A993 /* Nocilla.framework */; };
-		23DFEC802362FAD500BD19C4 /* FMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DAC0C21CC3EEA0065F874 /* FMDB.framework */; };
-		23DFEC822362FAED00BD19C4 /* SnowplowIgluClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75CAC3B921F28BD600271FB3 /* SnowplowIgluClient.framework */; };
-		23DFEC832362FAF800BD19C4 /* VVJSONSchemaValidation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75CAC3C021F2930100271FB3 /* VVJSONSchemaValidation.framework */; };
-		6B0CCFB5292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
-		6B0CCFB6292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
-		6B0CCFB7292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
-		6B0CCFB8292262650054954B /* GDPRControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */; };
-		6B0CCFB9292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
-		6B0CCFBA292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
-		6B0CCFBB292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
-		6B0CCFBC292262650054954B /* GDPRController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFB4292262640054954B /* GDPRController.swift */; };
-		6B0CCFBE292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
-		6B0CCFBF292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
-		6B0CCFC0292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
-		6B0CCFC1292275580054954B /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFBD292275580054954B /* Controller.swift */; };
-		6B0CCFC9292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
-		6B0CCFCA292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
-		6B0CCFCB292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
-		6B0CCFCC292284390054954B /* GDPRConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */; };
-		6B0CCFCE29236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
-		6B0CCFCF29236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
-		6B0CCFD029236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
-		6B0CCFD129236B0B0054954B /* GDPRContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFCD29236B0A0054954B /* GDPRContext.swift */; };
-		6B0CCFD429236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
-		6B0CCFD529236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
-		6B0CCFD629236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
-		6B0CCFD729236D4A0054954B /* SubjectControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */; };
-		6B0CCFD829236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
-		6B0CCFD929236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
-		6B0CCFDA29236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
-		6B0CCFDB29236D4A0054954B /* SubjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0CCFD329236D4A0054954B /* SubjectController.swift */; };
-		6B4B3087287730D200B4C16B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B4B3086287730D200B4C16B /* WebKit.framework */; };
-		6B4B3089287730E000B4C16B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B4B3088287730E000B4C16B /* WebKit.framework */; };
-		6B4B308A287730F000B4C16B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B4B3086287730D200B4C16B /* WebKit.framework */; };
-		6B8586F6292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
-		6B8586F7292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
-		6B8586F8292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
-		6B8586F9292388C9006E4A5F /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F4292388C9006E4A5F /* SessionController.swift */; };
-		6B8586FA292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
-		6B8586FB292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
-		6B8586FC292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
-		6B8586FD292388C9006E4A5F /* SessionControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */; };
-		6B85870129239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
-		6B85870229239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
-		6B85870329239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
-		6B85870429239A9D006E4A5F /* SQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */; };
-		6B85870929239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
-		6B85870A29239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
-		6B85870B29239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
-		6B85870C29239A9D006E4A5F /* MemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870029239A9D006E4A5F /* MemoryEventStore.swift */; };
-		6B85870E2923C18D006E4A5F /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
-		6B85872D2923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
-		6B85872E2923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
-		6B85872F2923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
-		6B8587302923C5F0006E4A5F /* TrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872B2923C5F0006E4A5F /* TrackerController.swift */; };
-		6B8587312923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
-		6B8587322923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
-		6B8587332923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
-		6B8587342923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */; };
-		6B8587362923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
-		6B8587372923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
-		6B8587382923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
-		6B8587392923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */; };
-		6B85873C2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
-		6B85873D2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
-		6B85873E2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
-		6B85873F2923E025006E4A5F /* EmitterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873A2923E024006E4A5F /* EmitterController.swift */; };
-		6B8587402923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
-		6B8587412923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
-		6B8587422923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
-		6B8587432923E025006E4A5F /* EmitterControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */; };
-		6B8587462923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
-		6B8587472923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
-		6B8587482923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
-		6B8587492923E5A2006E4A5F /* GlobalContextsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */; };
-		6B85874A2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
-		6B85874B2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
-		6B85874C2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
-		6B85874D2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */; };
-		6B8587502923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
-		6B8587512923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
-		6B8587522923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
-		6B8587532923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */; };
-		6B8587542923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
-		6B8587552923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
-		6B8587562923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
-		6B8587572923E7D0006E4A5F /* NetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85874F2923E7CF006E4A5F /* NetworkController.swift */; };
-		6B8587592924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
-		6B85875A2924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
-		6B85875B2924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
-		6B85875C2924CDB0006E4A5F /* Snowplow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587582924CDB0006E4A5F /* Snowplow.swift */; };
-		6B85875E2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
-		6B85875F2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
-		6B8587602924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
-		6B8587612924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */; };
-		6B8587632924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
-		6B8587642924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
-		6B8587652924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
-		6B8587662924F76F006E4A5F /* ConfigurationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */; };
-		6B858769292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
-		6B85876A292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
-		6B85876B292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
-		6B85876C292509AD006E4A5F /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858767292509AC006E4A5F /* ConfigurationCache.swift */; };
-		6B85876D292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
-		6B85876E292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
-		6B85876F292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
-		6B858770292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */; };
-		6B85877229251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
-		6B85877329251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
-		6B85877429251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
-		6B85877529251779006E4A5F /* ConfigurationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877129251779006E4A5F /* ConfigurationState.swift */; };
-		6B85877829251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
-		6B85877929251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
-		6B85877A29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
-		6B85877B29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */; };
-		6B85877C29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
-		6B85877D29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
-		6B85877E29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
-		6B85877F29251CBD006E4A5F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85877729251CBD006E4A5F /* NetworkConnection.swift */; };
-		6B85878229251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
-		6B85878329251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
-		6B85878429251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
-		6B85878529251DF4006E4A5F /* SelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */; };
-		6B85878629251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
-		6B85878729251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
-		6B85878829251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
-		6B85878929251DF4006E4A5F /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85878129251DF4006E4A5F /* Payload.swift */; };
-		6B8F5605291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
-		6B8F5606291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
-		6B8F5607291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
-		6B8F5608291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */; };
-		6B9DA53F29253951006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
-		6B9DA54029253952006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
-		6B9DA54129253952006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
-		6B9DA54229253952006D721A /* LifecycleEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53D29253951006D721A /* LifecycleEntity.swift */; };
-		6B9DA54329253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
-		6B9DA54429253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
-		6B9DA54529253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
-		6B9DA54629253952006D721A /* DeepLinkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA53E29253951006D721A /* DeepLinkEntity.swift */; };
-		6B9DA55329261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
-		6B9DA55429261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
-		6B9DA55529261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
-		6B9DA55629261E5B006D721A /* ConsentWithdrawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */; };
-		6B9DA55729261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
-		6B9DA55829261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
-		6B9DA55929261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
-		6B9DA55A29261E5B006D721A /* ConsentGranted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54829261E5A006D721A /* ConsentGranted.swift */; };
-		6B9DA55B29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
-		6B9DA55C29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
-		6B9DA55D29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
-		6B9DA55E29261E5B006D721A /* DeepLinkReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */; };
-		6B9DA55F29261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
-		6B9DA56029261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
-		6B9DA56129261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
-		6B9DA56229261E5B006D721A /* ConsentDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54A29261E5A006D721A /* ConsentDocument.swift */; };
-		6B9DA56329261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
-		6B9DA56429261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
-		6B9DA56529261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
-		6B9DA56629261E5B006D721A /* EventBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54B29261E5A006D721A /* EventBase.swift */; };
-		6B9DA56B29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
-		6B9DA56C29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
-		6B9DA56D29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
-		6B9DA56E29261E5C006D721A /* Foreground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54D29261E5A006D721A /* Foreground.swift */; };
-		6B9DA56F29261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
-		6B9DA57029261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
-		6B9DA57129261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
-		6B9DA57229261E5C006D721A /* SelfDescribing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54E29261E5B006D721A /* SelfDescribing.swift */; };
-		6B9DA57329261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
-		6B9DA57429261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
-		6B9DA57529261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
-		6B9DA57629261E5C006D721A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA54F29261E5B006D721A /* Background.swift */; };
-		6B9DA57729261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
-		6B9DA57829261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
-		6B9DA57929261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
-		6B9DA57A29261E5C006D721A /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55029261E5B006D721A /* Timing.swift */; };
-		6B9DA57B29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
-		6B9DA57C29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
-		6B9DA57D29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
-		6B9DA57E29261E5C006D721A /* Structured.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55129261E5B006D721A /* Structured.swift */; };
-		6B9DA57F29261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
-		6B9DA58029261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
-		6B9DA58129261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
-		6B9DA58229261E5C006D721A /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA55229261E5B006D721A /* ScreenView.swift */; };
-		6B9DA5842926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
-		6B9DA5852926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
-		6B9DA5862926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
-		6B9DA5872926437F006D721A /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5832926437F006D721A /* PushNotification.swift */; };
-		6B9DA58929264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
-		6B9DA58A29264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
-		6B9DA58B29264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
-		6B9DA58C29264EC2006D721A /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA58829264EC2006D721A /* MessageNotification.swift */; };
-		6B9DA598292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
-		6B9DA599292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
-		6B9DA59A292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
-		6B9DA59B292677E0006D721A /* MessageNotificationAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */; };
-		6B9DA59D29267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
-		6B9DA59E29267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
-		6B9DA59F29267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
-		6B9DA5A029267B66006D721A /* Ecommerce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA59C29267B66006D721A /* Ecommerce.swift */; };
-		6B9DA5A229268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
-		6B9DA5A329268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
-		6B9DA5A429268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
-		6B9DA5A529268956006D721A /* EcommerceItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A129268956006D721A /* EcommerceItem.swift */; };
-		6B9DA5A729268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
-		6B9DA5A829268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
-		6B9DA5A929268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
-		6B9DA5AA29268B0E006D721A /* TrackerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5A629268B0E006D721A /* TrackerError.swift */; };
-		6B9DA5AC29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
-		6B9DA5AD29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
-		6B9DA5AE29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
-		6B9DA5AF29268E4E006D721A /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5AB29268E4E006D721A /* PageView.swift */; };
-		6B9DA5B129269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
-		6B9DA5B229269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
-		6B9DA5B329269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
-		6B9DA5B429269036006D721A /* SNOWError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B029269036006D721A /* SNOWError.swift */; };
-		6B9DA5B629269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
-		6B9DA5B729269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
-		6B9DA5B829269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
-		6B9DA5B929269755006D721A /* TrackerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5B529269755006D721A /* TrackerEvent.swift */; };
-		6B9DA5C42927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
-		6B9DA5C52927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
-		6B9DA5C62927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
-		6B9DA5C72927BCD6006D721A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5BF2927BCD5006D721A /* Request.swift */; };
-		6B9DA5C82927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
-		6B9DA5C92927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
-		6B9DA5CA2927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
-		6B9DA5CB2927BCD6006D721A /* RequestCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C02927BCD5006D721A /* RequestCallback.swift */; };
-		6B9DA5CC2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
-		6B9DA5CD2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
-		6B9DA5CE2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
-		6B9DA5CF2927BCD6006D721A /* RequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C12927BCD6006D721A /* RequestResult.swift */; };
-		6B9DA5D02927BCD6006D721A /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
-		6B9DA5D42927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
-		6B9DA5D52927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
-		6B9DA5D62927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
-		6B9DA5D72927BCD6006D721A /* Emitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C32927BCD6006D721A /* Emitter.swift */; };
-		6B9DA5E32927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
-		6B9DA5E42927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
-		6B9DA5E52927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
-		6B9DA5E62927D1F7006D721A /* EmitterEventProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */; };
-		6B9DA5EC2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
-		6B9DA5ED2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
-		6B9DA5EE2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
-		6B9DA5EF2927E635006D721A /* LifecycleStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */; };
-		6B9DA5F02927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
-		6B9DA5F12927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
-		6B9DA5F22927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
-		6B9DA5F32927E635006D721A /* InstallTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E82927E635006D721A /* InstallTracker.swift */; };
-		6B9DA5F42927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
-		6B9DA5F52927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
-		6B9DA5F62927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
-		6B9DA5F72927E635006D721A /* DeepLinkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5E92927E635006D721A /* DeepLinkState.swift */; };
-		6B9DA5F82927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
-		6B9DA5F92927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
-		6B9DA5FA2927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
-		6B9DA5FB2927E635006D721A /* DeepLinkStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */; };
-		6B9DA5FC2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
-		6B9DA5FD2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
-		6B9DA5FE2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
-		6B9DA5FF2927E635006D721A /* LifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5EB2927E635006D721A /* LifecycleState.swift */; };
-		6B9DA6012927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
-		6B9DA6022927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
-		6B9DA6032927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
-		6B9DA6042927E669006D721A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6002927E669006D721A /* State.swift */; };
-		6B9DA6062927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
-		6B9DA6072927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
-		6B9DA6082927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
-		6B9DA6092927E6F2006D721A /* StateMachineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */; };
-		6B9DA60F2927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
-		6B9DA6102927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
-		6B9DA6112927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
-		6B9DA6122927E99C006D721A /* WebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */; };
-		6B9DA6132927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
-		6B9DA6142927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
-		6B9DA6152927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
-		6B9DA6162927E99C006D721A /* StateFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60B2927E99B006D721A /* StateFuture.swift */; };
-		6B9DA6172927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
-		6B9DA6182927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
-		6B9DA6192927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
-		6B9DA61A2927E99C006D721A /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60C2927E99B006D721A /* StateManager.swift */; };
-		6B9DA61B2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
-		6B9DA61C2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
-		6B9DA61D2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
-		6B9DA61E2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */; };
-		6B9DA61F2927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
-		6B9DA6202927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
-		6B9DA6212927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
-		6B9DA6222927E99C006D721A /* TrackerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA60E2927E99C006D721A /* TrackerState.swift */; };
-		6B9DA6252928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
-		6B9DA6262928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
-		6B9DA6272928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
-		6B9DA6282928B2FC006D721A /* SessionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6232928B2FB006D721A /* SessionState.swift */; };
-		6B9DA6292928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
-		6B9DA62A2928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
-		6B9DA62B2928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
-		6B9DA62C2928B2FC006D721A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6242928B2FB006D721A /* Session.swift */; };
-		6B9DA62E2928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
-		6B9DA62F2928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
-		6B9DA6302928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
-		6B9DA6312928C08D006D721A /* DataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA62D2928C08D006D721A /* DataPersistence.swift */; };
-		6B9DA6332928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
-		6B9DA6342928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
-		6B9DA6352928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
-		6B9DA6362928CAA3006D721A /* Tracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6322928CAA3006D721A /* Tracker.swift */; };
-		6B9DA63E292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
-		6B9DA63F292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
-		6B9DA640292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
-		6B9DA641292B5197006D721A /* ScreenStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */; };
-		6B9DA642292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
-		6B9DA643292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
-		6B9DA644292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
-		6B9DA645292B5197006D721A /* ScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA63D292B5196006D721A /* ScreenState.swift */; };
-		6B9DA648292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
-		6B9DA649292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
-		6B9DA64A292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
-		6B9DA64B292B5487006D721A /* DevicePlatform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA646292B5487006D721A /* DevicePlatform.swift */; };
-		6B9DA64C292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
-		6B9DA64D292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
-		6B9DA64E292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
-		6B9DA64F292B5487006D721A /* PlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA647292B5487006D721A /* PlatformContext.swift */; };
-		6B9DA651292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
-		6B9DA652292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
-		6B9DA653292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
-		6B9DA654292B562D006D721A /* DeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */; };
-		6B9DA656292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
-		6B9DA657292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
-		6B9DA658292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
-		6B9DA659292B60E2006D721A /* Subject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA655292B60E1006D721A /* Subject.swift */; };
-		6B9DA65B292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
-		6B9DA65C292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
-		6B9DA65D292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
-		6B9DA65E292B6E68006D721A /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65A292B6E68006D721A /* Utilities.swift */; };
-		6B9DA661292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
-		6B9DA662292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
-		6B9DA663292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
-		6B9DA664292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */; };
-		6B9DA665292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
-		6B9DA666292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
-		6B9DA667292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
-		6B9DA668292BAE5F006D721A /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA660292BAE5F006D721A /* ServiceProvider.swift */; };
-		6B9DA66A292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
-		6B9DA66B292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
-		6B9DA66C292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
-		6B9DA66D292BBC83006D721A /* SPSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA669292BBC83006D721A /* SPSize.swift */; };
-		6B9DA66F292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
-		6B9DA670292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
-		6B9DA671292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
-		6B9DA672292BBCDE006D721A /* SNOWReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */; };
-		6B9DA676292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
-		6B9DA677292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
-		6B9DA678292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
-		6B9DA679292BD2D7006D721A /* SchemaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA673292BD2D6006D721A /* SchemaRule.swift */; };
-		6B9DA67A292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
-		6B9DA67B292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
-		6B9DA67C292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
-		6B9DA67D292BD2D7006D721A /* SchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */; };
-		6B9DA67E292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
-		6B9DA67F292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
-		6B9DA680292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
-		6B9DA681292BD2D7006D721A /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA675292BD2D7006D721A /* GlobalContext.swift */; };
-		6B9DA684292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
-		6B9DA685292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
-		6B9DA686292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
-		6B9DA687292BDD3A006D721A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA682292BDD39006D721A /* Logger.swift */; };
-		6B9DA688292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
-		6B9DA689292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
-		6B9DA68A292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
-		6B9DA68B292BDD3A006D721A /* LoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */; };
-		6B9DA68D292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
-		6B9DA68E292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
-		6B9DA68F292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
-		6B9DA690292BF03C006D721A /* TrackerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA68C292BF03B006D721A /* TrackerConstants.swift */; };
-		6B9DA692292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
-		6B9DA693292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
-		6B9DA694292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
-		6B9DA695292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */; };
-		6B9DA69B292CB9A3006D721A /* MockNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA696292CB9A3006D721A /* MockNetworkConnection.swift */; };
-		6B9DA69C292CB9A3006D721A /* MockLoggerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA697292CB9A3006D721A /* MockLoggerDelegate.swift */; };
-		6B9DA69D292CB9A3006D721A /* MockEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA698292CB9A3006D721A /* MockEventStore.swift */; };
-		6B9DA69E292CB9A3006D721A /* MockDeviceInfoMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA699292CB9A3006D721A /* MockDeviceInfoMonitor.swift */; };
-		6B9DA69F292CB9A3006D721A /* MockWKScriptMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA69A292CB9A3006D721A /* MockWKScriptMessage.swift */; };
-		6B9DA6A1292CBFEB006D721A /* TestEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A0292CBFEA006D721A /* TestEvents.swift */; };
-		6B9DA6A3292CFF47006D721A /* TestPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A2292CFF47006D721A /* TestPayload.swift */; };
-		6B9DA6A5292D051A006D721A /* TestRequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A4292D051A006D721A /* TestRequestResult.swift */; };
-		6B9DA6AB292D060F006D721A /* TestTrackerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A6292D060E006D721A /* TestTrackerConfiguration.swift */; };
-		6B9DA6AC292D060F006D721A /* TestEmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A7292D060E006D721A /* TestEmitterConfiguration.swift */; };
-		6B9DA6AD292D060F006D721A /* TestMultipleInstances.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A8292D060E006D721A /* TestMultipleInstances.swift */; };
-		6B9DA6AE292D060F006D721A /* TestRemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6A9292D060F006D721A /* TestRemoteConfiguration.swift */; };
-		6B9DA6AF292D060F006D721A /* TestTrackerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6AA292D060F006D721A /* TestTrackerController.swift */; };
-		6B9DA6B2292D203E006D721A /* TestStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B0292D203E006D721A /* TestStateManager.swift */; };
-		6B9DA6B3292D203E006D721A /* TestSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B1292D203E006D721A /* TestSession.swift */; };
-		6B9DA6B5292E1181006D721A /* TestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B4292E1181006D721A /* TestRequest.swift */; };
-		6B9DA6B8292E15FF006D721A /* TestSchemaRuleset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B6292E15FE006D721A /* TestSchemaRuleset.swift */; };
-		6B9DA6B9292E15FF006D721A /* TestGlobalContexts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6B7292E15FF006D721A /* TestGlobalContexts.swift */; };
-		6B9DA6BB292E258A006D721A /* TestNetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6BA292E258A006D721A /* TestNetworkConnection.swift */; };
-		6B9DA6BD292E2923006D721A /* TestGeneratedJsons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6BC292E2923006D721A /* TestGeneratedJsons.swift */; };
-		6B9DA6C1292E4D75006D721A /* LegacyTestEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6BF292E4D74006D721A /* LegacyTestEmitter.swift */; };
-		6B9DA6C2292E4D75006D721A /* LegacyTestTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C0292E4D74006D721A /* LegacyTestTracker.swift */; };
-		6B9DA6C4292E5761006D721A /* TestSQLiteEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C3292E5760006D721A /* TestSQLiteEventStore.swift */; };
-		6B9DA6C6292E589F006D721A /* TestWebViewMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C5292E589E006D721A /* TestWebViewMessageHandler.swift */; };
-		6B9DA6C8292E5B2B006D721A /* TestPlatformContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C7292E5B2A006D721A /* TestPlatformContext.swift */; };
-		6B9DA6CA292E5DD9006D721A /* TestMemoryEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6C9292E5DD9006D721A /* TestMemoryEventStore.swift */; };
-		6B9DA6CC292E5DF9006D721A /* TestSelfDescribingJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6CB292E5DF9006D721A /* TestSelfDescribingJson.swift */; };
-		6B9DA6CE292E5E39006D721A /* TestScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6CD292E5E39006D721A /* TestScreenState.swift */; };
-		6B9DA6D0292E5E7F006D721A /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6CF292E5E7F006D721A /* TestUtils.swift */; };
-		6B9DA6D2292E5F5E006D721A /* TestLifecycleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D1292E5F5E006D721A /* TestLifecycleState.swift */; };
-		6B9DA6D4292E5F71006D721A /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D3292E5F71006D721A /* TestLogger.swift */; };
-		6B9DA6D6292E5FB6006D721A /* TestDataPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D5292E5FB6006D721A /* TestDataPersistence.swift */; };
-		6B9DA6D8292E5FE6006D721A /* TestSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D7292E5FE6006D721A /* TestSubject.swift */; };
-		6B9DA6DA292E6010006D721A /* TestServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6D9292E6010006D721A /* TestServiceProvider.swift */; };
-		6B9DA6DC292E6034006D721A /* LegacyTestSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DB292E6034006D721A /* LegacyTestSubject.swift */; };
-		6B9DA6DF292F4A0C006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
-		6B9DA6E0292F4A0D006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
-		6B9DA6E1292F4A0D006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
-		6B9DA6E2292F4A0E006D721A /* EmitterDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */; };
-		6B9DA6E4292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
-		6B9DA6E5292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
-		6B9DA6E6292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
-		6B9DA6E7292F4ED9006D721A /* TrackerDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */; };
-		6BABE8BD291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
-		6BABE8BE291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
-		6BABE8BF291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
-		6BABE8C0291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */; };
-		6BABEB6C291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
-		6BABEB6D291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
-		6BABEB6E291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
-		6BABEB6F291CE94900F6798A /* NetworkConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */; };
-		6BABEB71291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
-		6BABEB72291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
-		6BABEB73291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
-		6BABEB74291CF1B900F6798A /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */; };
-		6BABEB7C291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
-		6BABEB7D291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
-		6BABEB7E291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
-		6BABEB7F291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */; };
-		6BABED21291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
-		6BABED22291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
-		6BABED23291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
-		6BABED24291E3DB200F6798A /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED20291E3DB200F6798A /* Configuration.swift */; };
-		6BABED26291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
-		6BABED27291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
-		6BABED28291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
-		6BABED29291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */; };
-		6BABED35291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
-		6BABED36291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
-		6BABED37291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
-		6BABED38291E596100F6798A /* EmitterConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED34291E596100F6798A /* EmitterConfiguration.swift */; };
-		6BABED3A291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
-		6BABED3B291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
-		6BABED3C291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
-		6BABED3D291E60F000F6798A /* GDPRConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED39291E60F000F6798A /* GDPRConfiguration.swift */; };
-		6BABED3F291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
-		6BABED40291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
-		6BABED41291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
-		6BABED42291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */; };
-		6BABED44291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
-		6BABED45291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
-		6BABED46291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
-		6BABED47291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */; };
-		6BABED49291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
-		6BABED4A291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
-		6BABED4B291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
-		6BABED4C291E884900F6798A /* ConfigurationBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED48291E884900F6798A /* ConfigurationBundle.swift */; };
-		6BABED4E291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
-		6BABED4F291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
-		6BABED50291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
-		6BABED51291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */; };
-		6BABED53291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
-		6BABED54291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
-		6BABED55291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
-		6BABED56291E91E000F6798A /* RemoteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BABED52291E91E000F6798A /* RemoteConfiguration.swift */; };
-		6BE7084A292F64A700911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
-		6BE7084D292F654D00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
-		6BE7084E292F656500911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
-		6BE7084F292F656500911E55 /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
-		6BE70850292F656500911E55 /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
-		6BE70851292F656500911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
-		6BE70852292F656500911E55 /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
-		6BE70853292F656500911E55 /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
-		6BE70854292F656700911E55 /* BufferOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70849292F64A700911E55 /* BufferOption.swift */; };
-		6BE70855292F656700911E55 /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B85870D2923C18C006E4A5F /* EventStore.swift */; };
-		6BE70856292F656700911E55 /* EmitterEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */; };
-		6BE70857292F657E00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
-		6BE70858292F657F00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
-		6BE70859292F657F00911E55 /* ContextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7084C292F654D00911E55 /* ContextGenerator.swift */; };
-		6BE7085C292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
-		6BE7085D292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
-		6BE7085E292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
-		6BE7085F292F66B300911E55 /* HttpMethodOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */; };
-		6BE70861292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
-		6BE70862292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
-		6BE70863292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
-		6BE70864292F66E800911E55 /* ProtocolOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70860292F66E800911E55 /* ProtocolOptions.swift */; };
-		6BE70868292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
-		6BE70869292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
-		6BE7086A292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
-		6BE7086B292F67C000911E55 /* InspectableEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70867292F67C000911E55 /* InspectableEvent.swift */; };
-		6BE7086D292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
-		6BE7086E292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
-		6BE7086F292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
-		6BE70870292F683600911E55 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7086C292F683600911E55 /* LogLevel.swift */; };
-		6BE70873292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
-		6BE70874292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
-		6BE70875292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
-		6BE70876292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */; };
-		752DABD521CC38560065F874 /* SnowplowTracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DABCC21CC38550065F874 /* SnowplowTracker.framework */; };
-		752DABFE21CC3B380065F874 /* Snowplow_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DABF521CC3B380065F874 /* Snowplow_macOS.framework */; };
-		752DAC3F21CC4A7B0065F874 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
-		752DAC4021CC4A830065F874 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABB67D8C192D9552009A1ECE /* UIKit.framework */; };
-		753DDA6D21F803B10007C3AE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 753DDA6C21F803B10007C3AE /* Cocoa.framework */; };
-		75CAC41A21F2959800271FB3 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
-		75CAC42221F2962E00271FB3 /* CoreTelephony.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
-		75CAC42A21F29E6C00271FB3 /* iglu_resolver.json in Copy Files */ = {isa = PBXBuildFile; fileRef = 75CAC3FF21F2955100271FB3 /* iglu_resolver.json */; };
-		75CAC45621F2A1CC00271FB3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0C27C0191B408200018557 /* Foundation.framework */; };
-		75CAC46921F2A25B00271FB3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0C27C0191B408200018557 /* Foundation.framework */; };
-		75F9C5D121FA2E8B00A5B8FC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABB67D8C192D9552009A1ECE /* UIKit.framework */; };
-		75F9C5D221FA2E9F00A5B8FC /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; };
-		75F9C5D321FA352800A5B8FC /* FMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DAC0C21CC3EEA0065F874 /* FMDB.framework */; };
-		ED26E6BA23842AB00096AF7C /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED26E6B923842AAF0096AF7C /* AdSupport.framework */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXContainerItemProxy section */
-		752DABFF21CC3B380065F874 /* PBXContainerItemProxy */ = {
-			isa = PBXContainerItemProxy;
-			containerPortal = AB0C27B5191B408200018557 /* Project object */;
-			proxyType = 1;
-			remoteGlobalIDString = 752DABF421CC3B380065F874;
-			remoteInfo = "Snowplow macOS";
-		};
-		EDB27D2425E0111200DF8CE7 /* PBXContainerItemProxy */ = {
-			isa = PBXContainerItemProxy;
-			containerPortal = AB0C27B5191B408200018557 /* Project object */;
-			proxyType = 1;
-			remoteGlobalIDString = 752DABCB21CC38550065F874;
-			remoteInfo = "Snowplow-iOS";
-		};
-/* End PBXContainerItemProxy section */
-
-/* Begin PBXCopyFilesBuildPhase section */
-		75CAC41C21F2960F00271FB3 /* CopyFiles */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = "";
-			dstSubfolderSpec = 16;
-			files = (
-				75CAC42221F2962E00271FB3 /* CoreTelephony.framework in CopyFiles */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		75CAC42921F29E5900271FB3 /* Copy Files */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = Products;
-			dstSubfolderSpec = 7;
-			files = (
-				75CAC42A21F29E6C00271FB3 /* iglu_resolver.json in Copy Files */,
-			);
-			name = "Copy Files";
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		75F9C5C621FA2E0F00A5B8FC /* CopyFiles */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = "include/$(PRODUCT_NAME)";
-			dstSubfolderSpec = 16;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXCopyFilesBuildPhase section */
-
-/* Begin PBXFileReference section */
-		6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRControllerImpl.swift; sourceTree = "<group>"; };
-		6B0CCFB4292262640054954B /* GDPRController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRController.swift; sourceTree = "<group>"; };
-		6B0CCFBD292275580054954B /* Controller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Controller.swift; sourceTree = "<group>"; };
-		6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRConfigurationUpdate.swift; sourceTree = "<group>"; };
-		6B0CCFCD29236B0A0054954B /* GDPRContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRContext.swift; sourceTree = "<group>"; };
-		6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectControllerImpl.swift; sourceTree = "<group>"; };
-		6B0CCFD329236D4A0054954B /* SubjectController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectController.swift; sourceTree = "<group>"; };
-		6B4B3086287730D200B4C16B /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/iOSSupport/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
-		6B4B3088287730E000B4C16B /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
-		6B8586F4292388C9006E4A5F /* SessionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionController.swift; sourceTree = "<group>"; };
-		6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionControllerImpl.swift; sourceTree = "<group>"; };
-		6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteEventStore.swift; sourceTree = "<group>"; };
-		6B85870029239A9D006E4A5F /* MemoryEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryEventStore.swift; sourceTree = "<group>"; };
-		6B85870D2923C18C006E4A5F /* EventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventStore.swift; sourceTree = "<group>"; };
-		6B85872B2923C5F0006E4A5F /* TrackerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerController.swift; sourceTree = "<group>"; };
-		6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerControllerImpl.swift; sourceTree = "<group>"; };
-		6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkConfigurationUpdate.swift; sourceTree = "<group>"; };
-		6B85873A2923E024006E4A5F /* EmitterController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterController.swift; sourceTree = "<group>"; };
-		6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterControllerImpl.swift; sourceTree = "<group>"; };
-		6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContextsController.swift; sourceTree = "<group>"; };
-		6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContextsControllerImpl.swift; sourceTree = "<group>"; };
-		6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkControllerImpl.swift; sourceTree = "<group>"; };
-		6B85874F2923E7CF006E4A5F /* NetworkController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkController.swift; sourceTree = "<group>"; };
-		6B8587582924CDB0006E4A5F /* Snowplow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Snowplow.swift; sourceTree = "<group>"; };
-		6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchedConfigurationBundle.swift; sourceTree = "<group>"; };
-		6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationProvider.swift; sourceTree = "<group>"; };
-		6B858767292509AC006E4A5F /* ConfigurationCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationCache.swift; sourceTree = "<group>"; };
-		6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationFetcher.swift; sourceTree = "<group>"; };
-		6B85877129251779006E4A5F /* ConfigurationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationState.swift; sourceTree = "<group>"; };
-		6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkConnection.swift; sourceTree = "<group>"; };
-		6B85877729251CBD006E4A5F /* NetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = "<group>"; };
-		6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfDescribingJson.swift; sourceTree = "<group>"; };
-		6B85878129251DF4006E4A5F /* Payload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Payload.swift; sourceTree = "<group>"; };
-		6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerConfiguration.swift; sourceTree = "<group>"; };
-		6B9DA53D29253951006D721A /* LifecycleEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifecycleEntity.swift; sourceTree = "<group>"; };
-		6B9DA53E29253951006D721A /* DeepLinkEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkEntity.swift; sourceTree = "<group>"; };
-		6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentWithdrawn.swift; sourceTree = "<group>"; };
-		6B9DA54829261E5A006D721A /* ConsentGranted.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentGranted.swift; sourceTree = "<group>"; };
-		6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkReceived.swift; sourceTree = "<group>"; };
-		6B9DA54A29261E5A006D721A /* ConsentDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentDocument.swift; sourceTree = "<group>"; };
-		6B9DA54B29261E5A006D721A /* EventBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventBase.swift; sourceTree = "<group>"; };
-		6B9DA54D29261E5A006D721A /* Foreground.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Foreground.swift; sourceTree = "<group>"; };
-		6B9DA54E29261E5B006D721A /* SelfDescribing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfDescribing.swift; sourceTree = "<group>"; };
-		6B9DA54F29261E5B006D721A /* Background.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Background.swift; sourceTree = "<group>"; };
-		6B9DA55029261E5B006D721A /* Timing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timing.swift; sourceTree = "<group>"; };
-		6B9DA55129261E5B006D721A /* Structured.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Structured.swift; sourceTree = "<group>"; };
-		6B9DA55229261E5B006D721A /* ScreenView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenView.swift; sourceTree = "<group>"; };
-		6B9DA5832926437F006D721A /* PushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotification.swift; sourceTree = "<group>"; };
-		6B9DA58829264EC2006D721A /* MessageNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNotification.swift; sourceTree = "<group>"; };
-		6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNotificationAttachment.swift; sourceTree = "<group>"; };
-		6B9DA59C29267B66006D721A /* Ecommerce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ecommerce.swift; sourceTree = "<group>"; };
-		6B9DA5A129268956006D721A /* EcommerceItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcommerceItem.swift; sourceTree = "<group>"; };
-		6B9DA5A629268B0E006D721A /* TrackerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerError.swift; sourceTree = "<group>"; };
-		6B9DA5AB29268E4E006D721A /* PageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageView.swift; sourceTree = "<group>"; };
-		6B9DA5B029269036006D721A /* SNOWError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SNOWError.swift; sourceTree = "<group>"; };
-		6B9DA5B529269755006D721A /* TrackerEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerEvent.swift; sourceTree = "<group>"; };
-		6B9DA5BF2927BCD5006D721A /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
-		6B9DA5C02927BCD5006D721A /* RequestCallback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestCallback.swift; sourceTree = "<group>"; };
-		6B9DA5C12927BCD6006D721A /* RequestResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestResult.swift; sourceTree = "<group>"; };
-		6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterEvent.swift; sourceTree = "<group>"; };
-		6B9DA5C32927BCD6006D721A /* Emitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Emitter.swift; sourceTree = "<group>"; };
-		6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmitterEventProcessing.swift; sourceTree = "<group>"; };
-		6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifecycleStateMachine.swift; sourceTree = "<group>"; };
-		6B9DA5E82927E635006D721A /* InstallTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstallTracker.swift; sourceTree = "<group>"; };
-		6B9DA5E92927E635006D721A /* DeepLinkState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkState.swift; sourceTree = "<group>"; };
-		6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkStateMachine.swift; sourceTree = "<group>"; };
-		6B9DA5EB2927E635006D721A /* LifecycleState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifecycleState.swift; sourceTree = "<group>"; };
-		6B9DA6002927E669006D721A /* State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = "<group>"; };
-		6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachineProtocol.swift; sourceTree = "<group>"; };
-		6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewMessageHandler.swift; sourceTree = "<group>"; };
-		6B9DA60B2927E99B006D721A /* StateFuture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateFuture.swift; sourceTree = "<group>"; };
-		6B9DA60C2927E99B006D721A /* StateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateManager.swift; sourceTree = "<group>"; };
-		6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerStateSnapshot.swift; sourceTree = "<group>"; };
-		6B9DA60E2927E99C006D721A /* TrackerState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerState.swift; sourceTree = "<group>"; };
-		6B9DA6232928B2FB006D721A /* SessionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionState.swift; sourceTree = "<group>"; };
-		6B9DA6242928B2FB006D721A /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; };
-		6B9DA62D2928C08D006D721A /* DataPersistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataPersistence.swift; sourceTree = "<group>"; };
-		6B9DA6322928CAA3006D721A /* Tracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tracker.swift; sourceTree = "<group>"; };
-		6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenStateMachine.swift; sourceTree = "<group>"; };
-		6B9DA63D292B5196006D721A /* ScreenState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenState.swift; sourceTree = "<group>"; };
-		6B9DA646292B5487006D721A /* DevicePlatform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DevicePlatform.swift; sourceTree = "<group>"; };
-		6B9DA647292B5487006D721A /* PlatformContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformContext.swift; sourceTree = "<group>"; };
-		6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceInfoMonitor.swift; sourceTree = "<group>"; };
-		6B9DA655292B60E1006D721A /* Subject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subject.swift; sourceTree = "<group>"; };
-		6B9DA65A292B6E68006D721A /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
-		6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceProviderProtocol.swift; sourceTree = "<group>"; };
-		6B9DA660292BAE5F006D721A /* ServiceProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceProvider.swift; sourceTree = "<group>"; };
-		6B9DA669292BBC83006D721A /* SPSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPSize.swift; sourceTree = "<group>"; };
-		6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SNOWReachability.swift; sourceTree = "<group>"; };
-		6B9DA673292BD2D6006D721A /* SchemaRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaRule.swift; sourceTree = "<group>"; };
-		6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaRuleset.swift; sourceTree = "<group>"; };
-		6B9DA675292BD2D7006D721A /* GlobalContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContext.swift; sourceTree = "<group>"; };
-		6B9DA682292BDD39006D721A /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
-		6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggerDelegate.swift; sourceTree = "<group>"; };
-		6B9DA68C292BF03B006D721A /* TrackerConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerConstants.swift; sourceTree = "<group>"; };
-		6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitScreenViewTracking.swift; sourceTree = "<group>"; };
-		6B9DA696292CB9A3006D721A /* MockNetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockNetworkConnection.swift; sourceTree = "<group>"; };
-		6B9DA697292CB9A3006D721A /* MockLoggerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLoggerDelegate.swift; sourceTree = "<group>"; };
-		6B9DA698292CB9A3006D721A /* MockEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockEventStore.swift; sourceTree = "<group>"; };
-		6B9DA699292CB9A3006D721A /* MockDeviceInfoMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDeviceInfoMonitor.swift; sourceTree = "<group>"; };
-		6B9DA69A292CB9A3006D721A /* MockWKScriptMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockWKScriptMessage.swift; sourceTree = "<group>"; };
-		6B9DA6A0292CBFEA006D721A /* TestEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEvents.swift; sourceTree = "<group>"; };
-		6B9DA6A2292CFF47006D721A /* TestPayload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestPayload.swift; sourceTree = "<group>"; };
-		6B9DA6A4292D051A006D721A /* TestRequestResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRequestResult.swift; sourceTree = "<group>"; };
-		6B9DA6A6292D060E006D721A /* TestTrackerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestTrackerConfiguration.swift; sourceTree = "<group>"; };
-		6B9DA6A7292D060E006D721A /* TestEmitterConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEmitterConfiguration.swift; sourceTree = "<group>"; };
-		6B9DA6A8292D060E006D721A /* TestMultipleInstances.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestMultipleInstances.swift; sourceTree = "<group>"; };
-		6B9DA6A9292D060F006D721A /* TestRemoteConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRemoteConfiguration.swift; sourceTree = "<group>"; };
-		6B9DA6AA292D060F006D721A /* TestTrackerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestTrackerController.swift; sourceTree = "<group>"; };
-		6B9DA6B0292D203E006D721A /* TestStateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestStateManager.swift; sourceTree = "<group>"; };
-		6B9DA6B1292D203E006D721A /* TestSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSession.swift; sourceTree = "<group>"; };
-		6B9DA6B4292E1181006D721A /* TestRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRequest.swift; sourceTree = "<group>"; };
-		6B9DA6B6292E15FE006D721A /* TestSchemaRuleset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSchemaRuleset.swift; sourceTree = "<group>"; };
-		6B9DA6B7292E15FF006D721A /* TestGlobalContexts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestGlobalContexts.swift; sourceTree = "<group>"; };
-		6B9DA6BA292E258A006D721A /* TestNetworkConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNetworkConnection.swift; sourceTree = "<group>"; };
-		6B9DA6BC292E2923006D721A /* TestGeneratedJsons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestGeneratedJsons.swift; sourceTree = "<group>"; };
-		6B9DA6BE292E2B16006D721A /* Tests-bridging-header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-bridging-header.h"; sourceTree = "<group>"; };
-		6B9DA6BF292E4D74006D721A /* LegacyTestEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyTestEmitter.swift; sourceTree = "<group>"; };
-		6B9DA6C0292E4D74006D721A /* LegacyTestTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyTestTracker.swift; sourceTree = "<group>"; };
-		6B9DA6C3292E5760006D721A /* TestSQLiteEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSQLiteEventStore.swift; sourceTree = "<group>"; };
-		6B9DA6C5292E589E006D721A /* TestWebViewMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestWebViewMessageHandler.swift; sourceTree = "<group>"; };
-		6B9DA6C7292E5B2A006D721A /* TestPlatformContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestPlatformContext.swift; sourceTree = "<group>"; };
-		6B9DA6C9292E5DD9006D721A /* TestMemoryEventStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestMemoryEventStore.swift; sourceTree = "<group>"; };
-		6B9DA6CB292E5DF9006D721A /* TestSelfDescribingJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSelfDescribingJson.swift; sourceTree = "<group>"; };
-		6B9DA6CD292E5E39006D721A /* TestScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestScreenState.swift; sourceTree = "<group>"; };
-		6B9DA6CF292E5E7F006D721A /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = "<group>"; };
-		6B9DA6D1292E5F5E006D721A /* TestLifecycleState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestLifecycleState.swift; sourceTree = "<group>"; };
-		6B9DA6D3292E5F71006D721A /* TestLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestLogger.swift; sourceTree = "<group>"; };
-		6B9DA6D5292E5FB6006D721A /* TestDataPersistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDataPersistence.swift; sourceTree = "<group>"; };
-		6B9DA6D7292E5FE6006D721A /* TestSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSubject.swift; sourceTree = "<group>"; };
-		6B9DA6D9292E6010006D721A /* TestServiceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestServiceProvider.swift; sourceTree = "<group>"; };
-		6B9DA6DB292E6034006D721A /* LegacyTestSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTestSubject.swift; sourceTree = "<group>"; };
-		6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmitterDefaults.swift; sourceTree = "<group>"; };
-		6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerDefaults.swift; sourceTree = "<group>"; };
-		6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerConfigurationUpdate.swift; sourceTree = "<group>"; };
-		6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkConfiguration.swift; sourceTree = "<group>"; };
-		6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionConfiguration.swift; sourceTree = "<group>"; };
-		6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionConfigurationUpdate.swift; sourceTree = "<group>"; };
-		6BABED20291E3DB200F6798A /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
-		6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectConfiguration.swift; sourceTree = "<group>"; };
-		6BABED34291E596100F6798A /* EmitterConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterConfiguration.swift; sourceTree = "<group>"; };
-		6BABED39291E60F000F6798A /* GDPRConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GDPRConfiguration.swift; sourceTree = "<group>"; };
-		6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmitterConfigurationUpdate.swift; sourceTree = "<group>"; };
-		6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectConfigurationUpdate.swift; sourceTree = "<group>"; };
-		6BABED48291E884900F6798A /* ConfigurationBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationBundle.swift; sourceTree = "<group>"; };
-		6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalContextsConfiguration.swift; sourceTree = "<group>"; };
-		6BABED52291E91E000F6798A /* RemoteConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteConfiguration.swift; sourceTree = "<group>"; };
-		6BE70849292F64A700911E55 /* BufferOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BufferOption.swift; sourceTree = "<group>"; };
-		6BE7084C292F654D00911E55 /* ContextGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextGenerator.swift; sourceTree = "<group>"; };
-		6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpMethodOptions.swift; sourceTree = "<group>"; };
-		6BE70860292F66E800911E55 /* ProtocolOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolOptions.swift; sourceTree = "<group>"; };
-		6BE70867292F67C000911E55 /* InspectableEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectableEvent.swift; sourceTree = "<group>"; };
-		6BE7086C292F683600911E55 /* LogLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogLevel.swift; sourceTree = "<group>"; };
-		6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRProcessingBasis.swift; sourceTree = "<group>"; };
-		750E7F1121F2735C0050A993 /* Nocilla.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nocilla.framework; path = Carthage/Build/iOS/Nocilla.framework; sourceTree = "<group>"; };
-		752DABCC21CC38550065F874 /* SnowplowTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnowplowTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		752DABD421CC38560065F874 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
-		752DABE821CC3A090065F874 /* SnowplowTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnowplowTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		752DABF521CC3B380065F874 /* Snowplow_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Snowplow_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		752DABFD21CC3B380065F874 /* Snowplow-macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Snowplow-macOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
-		752DAC0C21CC3EEA0065F874 /* FMDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FMDB.framework; path = Carthage/Build/iOS/FMDB.framework; sourceTree = "<group>"; };
-		752DAC1021CC3F010065F874 /* FMDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FMDB.framework; path = Carthage/Build/watchOS/FMDB.framework; sourceTree = "<group>"; };
-		752DAC1221CC3F140065F874 /* Reachability.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reachability.framework; path = Carthage/Build/Mac/Reachability.framework; sourceTree = "<group>"; };
-		752DAC1321CC3F140065F874 /* FMDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FMDB.framework; path = Carthage/Build/Mac/FMDB.framework; sourceTree = "<group>"; };
-		753DDA6C21F803B10007C3AE /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
-		75CAC3B921F28BD600271FB3 /* SnowplowIgluClient.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SnowplowIgluClient.framework; path = Carthage/Build/iOS/SnowplowIgluClient.framework; sourceTree = "<group>"; };
-		75CAC3C021F2930100271FB3 /* VVJSONSchemaValidation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VVJSONSchemaValidation.framework; path = Carthage/Build/iOS/VVJSONSchemaValidation.framework; sourceTree = "<group>"; };
-		75CAC3F921F2955000271FB3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = InfoPlist.strings; sourceTree = "<group>"; };
-		75CAC3FF21F2955100271FB3 /* iglu_resolver.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iglu_resolver.json; sourceTree = "<group>"; };
-		75CAC40321F2955100271FB3 /* SnowplowTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SnowplowTests-Info.plist"; sourceTree = "<group>"; };
-		75CAC40421F2955100271FB3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		75F9C5C821FA2E0F00A5B8FC /* libSnowplow-iOS-Static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSnowplow-iOS-Static.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		AB0C27C0191B408200018557 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
-		AB0C27C4191B408200018557 /* Snowplow-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Snowplow-Prefix.pch"; sourceTree = "<group>"; };
-		AB9E8213192DEC38006744C9 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
-		ABB67D8C192D9552009A1ECE /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
-		B3D9BE0F237ACE0D009B310A /* watchos.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = watchos.modulemap; sourceTree = "<group>"; };
-		ED26E6B923842AAF0096AF7C /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AdSupport.framework; sourceTree = DEVELOPER_DIR; };
-		ED6AC5152369D42800A8F8A3 /* ios.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = ios.modulemap; sourceTree = "<group>"; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
-		752DABC921CC38550065F874 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6B4B3087287730D200B4C16B /* WebKit.framework in Frameworks */,
-				752DAC4021CC4A830065F874 /* UIKit.framework in Frameworks */,
-				752DAC3F21CC4A7B0065F874 /* CoreTelephony.framework in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABD121CC38560065F874 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6B4B308A287730F000B4C16B /* WebKit.framework in Frameworks */,
-				ED26E6BA23842AB00096AF7C /* AdSupport.framework in Frameworks */,
-				23DFEC832362FAF800BD19C4 /* VVJSONSchemaValidation.framework in Frameworks */,
-				23DFEC822362FAED00BD19C4 /* SnowplowIgluClient.framework in Frameworks */,
-				23DFEC802362FAD500BD19C4 /* FMDB.framework in Frameworks */,
-				23DFEC7F2362FAC700BD19C4 /* Nocilla.framework in Frameworks */,
-				75CAC41A21F2959800271FB3 /* CoreTelephony.framework in Frameworks */,
-				752DABD521CC38560065F874 /* SnowplowTracker.framework in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABE521CC3A090065F874 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				23DFEC7D2362FAA500BD19C4 /* FMDB.framework in Frameworks */,
-				75CAC46921F2A25B00271FB3 /* Foundation.framework in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABF221CC3B380065F874 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6B4B3089287730E000B4C16B /* WebKit.framework in Frameworks */,
-				23DFEC7E2362FAB100BD19C4 /* FMDB.framework in Frameworks */,
-				753DDA6D21F803B10007C3AE /* Cocoa.framework in Frameworks */,
-				75CAC45621F2A1CC00271FB3 /* Foundation.framework in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABFA21CC3B380065F874 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				752DABFE21CC3B380065F874 /* Snowplow_macOS.framework in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		75F9C5C521FA2E0F00A5B8FC /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				75F9C5D321FA352800A5B8FC /* FMDB.framework in Frameworks */,
-				75F9C5D221FA2E9F00A5B8FC /* CoreTelephony.framework in Frameworks */,
-				75F9C5D121FA2E8B00A5B8FC /* UIKit.framework in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
-		6BE70847292F63D000911E55 /* Controllers */ = {
-			isa = PBXGroup;
-			children = (
-				6B0CCFBD292275580054954B /* Controller.swift */,
-				6B85873A2923E024006E4A5F /* EmitterController.swift */,
-				6B0CCFD329236D4A0054954B /* SubjectController.swift */,
-				6B0CCFB4292262640054954B /* GDPRController.swift */,
-				6B8586F4292388C9006E4A5F /* SessionController.swift */,
-				6B85874F2923E7CF006E4A5F /* NetworkController.swift */,
-				6B85872B2923C5F0006E4A5F /* TrackerController.swift */,
-				6B8587442923E5A2006E4A5F /* GlobalContextsController.swift */,
-			);
-			path = Controllers;
-			sourceTree = "<group>";
-		};
-		6BE70848292F648000911E55 /* Emitter */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA6DD292F49B2006D721A /* EmitterDefaults.swift */,
-				6BE70849292F64A700911E55 /* BufferOption.swift */,
-				6B9DA5C22927BCD6006D721A /* EmitterEvent.swift */,
-				6B85870D2923C18C006E4A5F /* EventStore.swift */,
-			);
-			path = Emitter;
-			sourceTree = "<group>";
-		};
-		6BE7084B292F653100911E55 /* GlobalContexts */ = {
-			isa = PBXGroup;
-			children = (
-				6BE7084C292F654D00911E55 /* ContextGenerator.swift */,
-				6B9DA675292BD2D7006D721A /* GlobalContext.swift */,
-				6B9DA674292BD2D6006D721A /* SchemaRuleset.swift */,
-			);
-			path = GlobalContexts;
-			sourceTree = "<group>";
-		};
-		6BE7085A292F668F00911E55 /* Network */ = {
-			isa = PBXGroup;
-			children = (
-				6BE7085B292F66B300911E55 /* HttpMethodOptions.swift */,
-				6BE70860292F66E800911E55 /* ProtocolOptions.swift */,
-				6B9DA5BF2927BCD5006D721A /* Request.swift */,
-				6B9DA5C02927BCD5006D721A /* RequestCallback.swift */,
-				6B9DA5C12927BCD6006D721A /* RequestResult.swift */,
-				6B85877629251CBD006E4A5F /* DefaultNetworkConnection.swift */,
-				6B85877729251CBD006E4A5F /* NetworkConnection.swift */,
-			);
-			path = Network;
-			sourceTree = "<group>";
-		};
-		6BE70866292F678000911E55 /* Tracker */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA646292B5487006D721A /* DevicePlatform.swift */,
-				6BE70867292F67C000911E55 /* InspectableEvent.swift */,
-				6B9DA683292BDD3A006D721A /* LoggerDelegate.swift */,
-				6BE7086C292F683600911E55 /* LogLevel.swift */,
-				6B9DA6232928B2FB006D721A /* SessionState.swift */,
-				6B9DA6002927E669006D721A /* State.swift */,
-				6B9DA6052927E6F2006D721A /* StateMachineProtocol.swift */,
-				6B9DA60D2927E99B006D721A /* TrackerStateSnapshot.swift */,
-			);
-			path = Tracker;
-			sourceTree = "<group>";
-		};
-		6BE70871292F686A00911E55 /* Utils */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA669292BBC83006D721A /* SPSize.swift */,
-				6BE70872292F68A700911E55 /* GDPRProcessingBasis.swift */,
-			);
-			path = Utils;
-			sourceTree = "<group>";
-		};
-		6BEBA0EF29195AF700B5737F /* Sources */ = {
-			isa = PBXGroup;
-			children = (
-				6BEBA0F029195B1700B5737F /* Core */,
-				AB0C27C2191B408200018557 /* Snowplow */,
-			);
-			path = Sources;
-			sourceTree = "<group>";
-		};
-		6BEBA0F029195B1700B5737F /* Core */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA68C292BF03B006D721A /* TrackerConstants.swift */,
-				ED7F080426190AF2005D377E /* RemoteConfiguration */,
-				EDC9B37B255C3DC600F4136D /* Subject */,
-				ED88B5F9257953EE0048FAD1 /* GDPR */,
-				EDC9B37A255C3D3700F4136D /* ScreenViewTracking */,
-				EDC9B36D255C3C8A00F4136D /* Session */,
-				EDC9B35A255C3B9700F4136D /* Utils */,
-				EDC9B353255B02AB00F4136D /* Storage */,
-				EDC9B352255B027E00F4136D /* NetworkConnection */,
-				EDC9B351255AEEBC00F4136D /* Tracker */,
-				EDC9B34F255AEC7E00F4136D /* Logger */,
-				EDC9B34E255AE66D00F4136D /* GlobalContexts */,
-				EDC9B34D255AE46300F4136D /* Emitter */,
-			);
-			path = Core;
-			sourceTree = "<group>";
-		};
-		752DABD821CC38560065F874 /* Tests */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA6D1292E5F5E006D721A /* TestLifecycleState.swift */,
-				6B9DA6C9292E5DD9006D721A /* TestMemoryEventStore.swift */,
-				6B9DA6C7292E5B2A006D721A /* TestPlatformContext.swift */,
-				6B9DA6C5292E589E006D721A /* TestWebViewMessageHandler.swift */,
-				6B9DA6C3292E5760006D721A /* TestSQLiteEventStore.swift */,
-				6B9DA6BC292E2923006D721A /* TestGeneratedJsons.swift */,
-				6B9DA6BA292E258A006D721A /* TestNetworkConnection.swift */,
-				6B9DA6B4292E1181006D721A /* TestRequest.swift */,
-				6B9DA6B1292D203E006D721A /* TestSession.swift */,
-				6B9DA6B0292D203E006D721A /* TestStateManager.swift */,
-				6B9DA6A4292D051A006D721A /* TestRequestResult.swift */,
-				6B9DA6A2292CFF47006D721A /* TestPayload.swift */,
-				6B9DA6A0292CBFEA006D721A /* TestEvents.swift */,
-				EDA06FB32664CD02007FA773 /* Utils */,
-				ED88B6C62583A3770048FAD1 /* Legacy Tests */,
-				ED8BF8EB2570FAFB001DFDD9 /* Configurations */,
-				ED914EB02430D03A0068DA0A /* Global Contexts */,
-				75CAC3F721F2955000271FB3 /* en.lproj */,
-				75CAC40421F2955100271FB3 /* Info.plist */,
-				75CAC3FE21F2955100271FB3 /* Products */,
-				75CAC40321F2955100271FB3 /* SnowplowTests-Info.plist */,
-				6B9DA6BE292E2B16006D721A /* Tests-bridging-header.h */,
-				6B9DA6CB292E5DF9006D721A /* TestSelfDescribingJson.swift */,
-				6B9DA6CD292E5E39006D721A /* TestScreenState.swift */,
-				6B9DA6CF292E5E7F006D721A /* TestUtils.swift */,
-				6B9DA6D3292E5F71006D721A /* TestLogger.swift */,
-				6B9DA6D5292E5FB6006D721A /* TestDataPersistence.swift */,
-				6B9DA6D7292E5FE6006D721A /* TestSubject.swift */,
-				6B9DA6D9292E6010006D721A /* TestServiceProvider.swift */,
-			);
-			path = Tests;
-			sourceTree = "<group>";
-		};
-		75CAC3F721F2955000271FB3 /* en.lproj */ = {
-			isa = PBXGroup;
-			children = (
-				75CAC3F821F2955000271FB3 /* InfoPlist.strings */,
-			);
-			path = en.lproj;
-			sourceTree = "<group>";
-		};
-		75CAC3FE21F2955100271FB3 /* Products */ = {
-			isa = PBXGroup;
-			children = (
-				75CAC3FF21F2955100271FB3 /* iglu_resolver.json */,
-			);
-			path = Products;
-			sourceTree = "<group>";
-		};
-		AB0C27B4191B408200018557 = {
-			isa = PBXGroup;
-			children = (
-				6BEBA0EF29195AF700B5737F /* Sources */,
-				752DABD821CC38560065F874 /* Tests */,
-				AB0C27BF191B408200018557 /* Frameworks */,
-				AB0C27BE191B408200018557 /* Products */,
-			);
-			sourceTree = "<group>";
-		};
-		AB0C27BE191B408200018557 /* Products */ = {
-			isa = PBXGroup;
-			children = (
-				752DABCC21CC38550065F874 /* SnowplowTracker.framework */,
-				752DABD421CC38560065F874 /* Tests.xctest */,
-				752DABE821CC3A090065F874 /* SnowplowTracker.framework */,
-				752DABF521CC3B380065F874 /* Snowplow_macOS.framework */,
-				752DABFD21CC3B380065F874 /* Snowplow-macOSTests.xctest */,
-				75F9C5C821FA2E0F00A5B8FC /* libSnowplow-iOS-Static.a */,
-			);
-			name = Products;
-			sourceTree = "<group>";
-		};
-		AB0C27BF191B408200018557 /* Frameworks */ = {
-			isa = PBXGroup;
-			children = (
-				6B4B3086287730D200B4C16B /* WebKit.framework */,
-				6B4B3088287730E000B4C16B /* WebKit.framework */,
-				ED26E6B923842AAF0096AF7C /* AdSupport.framework */,
-				753DDA6C21F803B10007C3AE /* Cocoa.framework */,
-				75CAC3C021F2930100271FB3 /* VVJSONSchemaValidation.framework */,
-				75CAC3B921F28BD600271FB3 /* SnowplowIgluClient.framework */,
-				750E7F1121F2735C0050A993 /* Nocilla.framework */,
-				752DAC1021CC3F010065F874 /* FMDB.framework */,
-				752DAC1321CC3F140065F874 /* FMDB.framework */,
-				752DAC0C21CC3EEA0065F874 /* FMDB.framework */,
-				752DAC1221CC3F140065F874 /* Reachability.framework */,
-				AB9E8213192DEC38006744C9 /* CoreTelephony.framework */,
-				ABB67D8C192D9552009A1ECE /* UIKit.framework */,
-				AB0C27C0191B408200018557 /* Foundation.framework */,
-			);
-			name = Frameworks;
-			sourceTree = "<group>";
-		};
-		AB0C27C2191B408200018557 /* Snowplow */ = {
-			isa = PBXGroup;
-			children = (
-				6B8587582924CDB0006E4A5F /* Snowplow.swift */,
-				ED8BF8B0256FFEA4001DFDD9 /* Configurations */,
-				6BE70847292F63D000911E55 /* Controllers */,
-				6BE70848292F648000911E55 /* Emitter */,
-				EDAB65D626CD2F970067755F /* Entities */,
-				EDC9B346255ACF5100F4136D /* Events */,
-				6BE7084B292F653100911E55 /* GlobalContexts */,
-				6BE7085A292F668F00911E55 /* Network */,
-				EDC9B350255AEDF000F4136D /* Payload */,
-				AB0C27C3191B408200018557 /* Supporting Files */,
-				6BE70866292F678000911E55 /* Tracker */,
-				6BE70871292F686A00911E55 /* Utils */,
-			);
-			path = Snowplow;
-			sourceTree = "<group>";
-		};
-		AB0C27C3191B408200018557 /* Supporting Files */ = {
-			isa = PBXGroup;
-			children = (
-				ED6AC5152369D42800A8F8A3 /* ios.modulemap */,
-				B3D9BE0F237ACE0D009B310A /* watchos.modulemap */,
-				AB0C27C4191B408200018557 /* Snowplow-Prefix.pch */,
-			);
-			name = "Supporting Files";
-			sourceTree = "<group>";
-		};
-		ED7F080426190AF2005D377E /* RemoteConfiguration */ = {
-			isa = PBXGroup;
-			children = (
-				6B858767292509AC006E4A5F /* ConfigurationCache.swift */,
-				6B858768292509AC006E4A5F /* ConfigurationFetcher.swift */,
-				6B8587622924F76F006E4A5F /* ConfigurationProvider.swift */,
-				6B85875D2924DA67006E4A5F /* FetchedConfigurationBundle.swift */,
-			);
-			path = RemoteConfiguration;
-			sourceTree = "<group>";
-		};
-		ED88B5F9257953EE0048FAD1 /* GDPR */ = {
-			isa = PBXGroup;
-			children = (
-				6B0CCFCD29236B0A0054954B /* GDPRContext.swift */,
-				6B0CCFC8292284380054954B /* GDPRConfigurationUpdate.swift */,
-				6B0CCFB3292262640054954B /* GDPRControllerImpl.swift */,
-			);
-			path = GDPR;
-			sourceTree = "<group>";
-		};
-		ED88B6C62583A3770048FAD1 /* Legacy Tests */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA6BF292E4D74006D721A /* LegacyTestEmitter.swift */,
-				6B9DA6C0292E4D74006D721A /* LegacyTestTracker.swift */,
-				6B9DA6DB292E6034006D721A /* LegacyTestSubject.swift */,
-			);
-			path = "Legacy Tests";
-			sourceTree = "<group>";
-		};
-		ED8BF8B0256FFEA4001DFDD9 /* Configurations */ = {
-			isa = PBXGroup;
-			children = (
-				6B85877129251779006E4A5F /* ConfigurationState.swift */,
-				6BABED52291E91E000F6798A /* RemoteConfiguration.swift */,
-				6BABED4D291E8B6E00F6798A /* GlobalContextsConfiguration.swift */,
-				6BABED48291E884900F6798A /* ConfigurationBundle.swift */,
-				6BABED39291E60F000F6798A /* GDPRConfiguration.swift */,
-				6BABED34291E596100F6798A /* EmitterConfiguration.swift */,
-				6BABED25291E4FEF00F6798A /* SubjectConfiguration.swift */,
-				6BABED20291E3DB200F6798A /* Configuration.swift */,
-				6BABEB70291CF1B900F6798A /* SessionConfiguration.swift */,
-				6BABEB6B291CE94900F6798A /* NetworkConfiguration.swift */,
-				6B8F5604291BDAF900CD3F1B /* TrackerConfiguration.swift */,
-			);
-			path = Configurations;
-			sourceTree = "<group>";
-		};
-		ED8BF8EB2570FAFB001DFDD9 /* Configurations */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA6A7292D060E006D721A /* TestEmitterConfiguration.swift */,
-				6B9DA6A8292D060E006D721A /* TestMultipleInstances.swift */,
-				6B9DA6A9292D060F006D721A /* TestRemoteConfiguration.swift */,
-				6B9DA6A6292D060E006D721A /* TestTrackerConfiguration.swift */,
-				6B9DA6AA292D060F006D721A /* TestTrackerController.swift */,
-			);
-			path = Configurations;
-			sourceTree = "<group>";
-		};
-		ED914EB02430D03A0068DA0A /* Global Contexts */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA6B7292E15FF006D721A /* TestGlobalContexts.swift */,
-				6B9DA6B6292E15FE006D721A /* TestSchemaRuleset.swift */,
-			);
-			path = "Global Contexts";
-			sourceTree = "<group>";
-		};
-		EDA06FB32664CD02007FA773 /* Utils */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA699292CB9A3006D721A /* MockDeviceInfoMonitor.swift */,
-				6B9DA698292CB9A3006D721A /* MockEventStore.swift */,
-				6B9DA697292CB9A3006D721A /* MockLoggerDelegate.swift */,
-				6B9DA696292CB9A3006D721A /* MockNetworkConnection.swift */,
-				6B9DA69A292CB9A3006D721A /* MockWKScriptMessage.swift */,
-			);
-			path = Utils;
-			sourceTree = "<group>";
-		};
-		EDAB65D626CD2F970067755F /* Entities */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA53E29253951006D721A /* DeepLinkEntity.swift */,
-				6B9DA53D29253951006D721A /* LifecycleEntity.swift */,
-			);
-			path = Entities;
-			sourceTree = "<group>";
-		};
-		EDC9B346255ACF5100F4136D /* Events */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA54F29261E5B006D721A /* Background.swift */,
-				6B9DA54A29261E5A006D721A /* ConsentDocument.swift */,
-				6B9DA54829261E5A006D721A /* ConsentGranted.swift */,
-				6B9DA54729261E5A006D721A /* ConsentWithdrawn.swift */,
-				6B9DA54929261E5A006D721A /* DeepLinkReceived.swift */,
-				6B9DA54B29261E5A006D721A /* EventBase.swift */,
-				6B9DA54D29261E5A006D721A /* Foreground.swift */,
-				6B9DA55229261E5B006D721A /* ScreenView.swift */,
-				6B9DA54E29261E5B006D721A /* SelfDescribing.swift */,
-				6B9DA55129261E5B006D721A /* Structured.swift */,
-				6B9DA55029261E5B006D721A /* Timing.swift */,
-				6B9DA5832926437F006D721A /* PushNotification.swift */,
-				6B9DA58829264EC2006D721A /* MessageNotification.swift */,
-				6B9DA597292677E0006D721A /* MessageNotificationAttachment.swift */,
-				6B9DA59C29267B66006D721A /* Ecommerce.swift */,
-				6B9DA5A129268956006D721A /* EcommerceItem.swift */,
-				6B9DA5A629268B0E006D721A /* TrackerError.swift */,
-				6B9DA5AB29268E4E006D721A /* PageView.swift */,
-				6B9DA5B029269036006D721A /* SNOWError.swift */,
-			);
-			path = Events;
-			sourceTree = "<group>";
-		};
-		EDC9B34D255AE46300F4136D /* Emitter */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA5C32927BCD6006D721A /* Emitter.swift */,
-				6B85873B2923E024006E4A5F /* EmitterControllerImpl.swift */,
-				6BABED3E291E833D00F6798A /* EmitterConfigurationUpdate.swift */,
-				6B9DA5E22927D1F7006D721A /* EmitterEventProcessing.swift */,
-			);
-			path = Emitter;
-			sourceTree = "<group>";
-		};
-		EDC9B34E255AE66D00F4136D /* GlobalContexts */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA673292BD2D6006D721A /* SchemaRule.swift */,
-				6B8587452923E5A2006E4A5F /* GlobalContextsControllerImpl.swift */,
-			);
-			path = GlobalContexts;
-			sourceTree = "<group>";
-		};
-		EDC9B34F255AEC7E00F4136D /* Logger */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA682292BDD39006D721A /* Logger.swift */,
-			);
-			path = Logger;
-			sourceTree = "<group>";
-		};
-		EDC9B350255AEDF000F4136D /* Payload */ = {
-			isa = PBXGroup;
-			children = (
-				6B85878129251DF4006E4A5F /* Payload.swift */,
-				6B85878029251DF4006E4A5F /* SelfDescribingJson.swift */,
-			);
-			path = Payload;
-			sourceTree = "<group>";
-		};
-		EDC9B351255AEEBC00F4136D /* Tracker */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA5E92927E635006D721A /* DeepLinkState.swift */,
-				6B9DA5EA2927E635006D721A /* DeepLinkStateMachine.swift */,
-				6B9DA5E82927E635006D721A /* InstallTracker.swift */,
-				6B9DA5EB2927E635006D721A /* LifecycleState.swift */,
-				6B9DA5E72927E634006D721A /* LifecycleStateMachine.swift */,
-				6B9DA660292BAE5F006D721A /* ServiceProvider.swift */,
-				6B9DA65F292BAE5F006D721A /* ServiceProviderProtocol.swift */,
-				6B9DA60B2927E99B006D721A /* StateFuture.swift */,
-				6B9DA60C2927E99B006D721A /* StateManager.swift */,
-				6B9DA6322928CAA3006D721A /* Tracker.swift */,
-				6BABE8BC291BFCAD00F6798A /* TrackerConfigurationUpdate.swift */,
-				6B85872C2923C5F0006E4A5F /* TrackerControllerImpl.swift */,
-				6B9DA6E3292F4ED9006D721A /* TrackerDefaults.swift */,
-				6B9DA5B529269755006D721A /* TrackerEvent.swift */,
-				6B9DA60E2927E99C006D721A /* TrackerState.swift */,
-				6B9DA60A2927E99A006D721A /* WebViewMessageHandler.swift */,
-			);
-			path = Tracker;
-			sourceTree = "<group>";
-		};
-		EDC9B352255B027E00F4136D /* NetworkConnection */ = {
-			isa = PBXGroup;
-			children = (
-				6B8587352923DCA2006E4A5F /* NetworkConfigurationUpdate.swift */,
-				6B85874E2923E7CF006E4A5F /* NetworkControllerImpl.swift */,
-			);
-			path = NetworkConnection;
-			sourceTree = "<group>";
-		};
-		EDC9B353255B02AB00F4136D /* Storage */ = {
-			isa = PBXGroup;
-			children = (
-				6B85870029239A9D006E4A5F /* MemoryEventStore.swift */,
-				6B8586FE29239A9D006E4A5F /* SQLiteEventStore.swift */,
-			);
-			path = Storage;
-			sourceTree = "<group>";
-		};
-		EDC9B35A255C3B9700F4136D /* Utils */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA66E292BBCDE006D721A /* SNOWReachability.swift */,
-				6B9DA65A292B6E68006D721A /* Utilities.swift */,
-				6B9DA650292B562D006D721A /* DeviceInfoMonitor.swift */,
-				6B9DA62D2928C08D006D721A /* DataPersistence.swift */,
-			);
-			path = Utils;
-			sourceTree = "<group>";
-		};
-		EDC9B36D255C3C8A00F4136D /* Session */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA6242928B2FB006D721A /* Session.swift */,
-				6B8586F5292388C9006E4A5F /* SessionControllerImpl.swift */,
-				6BABEB7B291D1C7C00F6798A /* SessionConfigurationUpdate.swift */,
-			);
-			path = Session;
-			sourceTree = "<group>";
-		};
-		EDC9B37A255C3D3700F4136D /* ScreenViewTracking */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA691292CA13B006D721A /* UIKitScreenViewTracking.swift */,
-				6B9DA63D292B5196006D721A /* ScreenState.swift */,
-				6B9DA63C292B5196006D721A /* ScreenStateMachine.swift */,
-			);
-			path = ScreenViewTracking;
-			sourceTree = "<group>";
-		};
-		EDC9B37B255C3DC600F4136D /* Subject */ = {
-			isa = PBXGroup;
-			children = (
-				6B9DA655292B60E1006D721A /* Subject.swift */,
-				6B9DA647292B5487006D721A /* PlatformContext.swift */,
-				6B0CCFD229236D4A0054954B /* SubjectControllerImpl.swift */,
-				6BABED43291E85F200F6798A /* SubjectConfigurationUpdate.swift */,
-			);
-			path = Subject;
-			sourceTree = "<group>";
-		};
-/* End PBXGroup section */
-
-/* Begin PBXHeadersBuildPhase section */
-		752DABC721CC38550065F874 /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABE321CC3A090065F874 /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABF021CC3B380065F874 /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		75F9C5E321FA359400A5B8FC /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXHeadersBuildPhase section */
-
-/* Begin PBXNativeTarget section */
-		752DABCB21CC38550065F874 /* Snowplow-iOS */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 752DABDD21CC38560065F874 /* Build configuration list for PBXNativeTarget "Snowplow-iOS" */;
-			buildPhases = (
-				752DABC721CC38550065F874 /* Headers */,
-				752DABC821CC38550065F874 /* Sources */,
-				752DABC921CC38550065F874 /* Frameworks */,
-				752DABCA21CC38550065F874 /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = "Snowplow-iOS";
-			productName = "Snowplow iOS";
-			productReference = 752DABCC21CC38550065F874 /* SnowplowTracker.framework */;
-			productType = "com.apple.product-type.framework";
-		};
-		752DABD321CC38560065F874 /* Tests */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 752DABE021CC38560065F874 /* Build configuration list for PBXNativeTarget "Tests" */;
-			buildPhases = (
-				752DABD021CC38560065F874 /* Sources */,
-				752DABD121CC38560065F874 /* Frameworks */,
-				75CAC41C21F2960F00271FB3 /* CopyFiles */,
-				75CAC42921F29E5900271FB3 /* Copy Files */,
-				ED8EA918258A7118008E292C /* Run Script - Copy Frameworks (Carthage) */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-				EDB27D2525E0111200DF8CE7 /* PBXTargetDependency */,
-			);
-			name = Tests;
-			productName = "Snowplow iOSTests";
-			productReference = 752DABD421CC38560065F874 /* Tests.xctest */;
-			productType = "com.apple.product-type.bundle.unit-test";
-		};
-		752DABE721CC3A090065F874 /* Snowplow-watchOS */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 752DABED21CC3A090065F874 /* Build configuration list for PBXNativeTarget "Snowplow-watchOS" */;
-			buildPhases = (
-				752DABE321CC3A090065F874 /* Headers */,
-				752DABE421CC3A090065F874 /* Sources */,
-				752DABE521CC3A090065F874 /* Frameworks */,
-				752DABE621CC3A090065F874 /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = "Snowplow-watchOS";
-			productName = "Snowplow watchOS";
-			productReference = 752DABE821CC3A090065F874 /* SnowplowTracker.framework */;
-			productType = "com.apple.product-type.framework";
-		};
-		752DABF421CC3B380065F874 /* Snowplow-macOS */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 752DAC0621CC3B380065F874 /* Build configuration list for PBXNativeTarget "Snowplow-macOS" */;
-			buildPhases = (
-				752DABF021CC3B380065F874 /* Headers */,
-				752DABF121CC3B380065F874 /* Sources */,
-				752DABF221CC3B380065F874 /* Frameworks */,
-				752DABF321CC3B380065F874 /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = "Snowplow-macOS";
-			productName = "Snowplow macOS";
-			productReference = 752DABF521CC3B380065F874 /* Snowplow_macOS.framework */;
-			productType = "com.apple.product-type.framework";
-		};
-		752DABFC21CC3B380065F874 /* Snowplow-macOSTests */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 752DAC0921CC3B380065F874 /* Build configuration list for PBXNativeTarget "Snowplow-macOSTests" */;
-			buildPhases = (
-				752DABF921CC3B380065F874 /* Sources */,
-				752DABFA21CC3B380065F874 /* Frameworks */,
-				752DABFB21CC3B380065F874 /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-				752DAC0021CC3B380065F874 /* PBXTargetDependency */,
-			);
-			name = "Snowplow-macOSTests";
-			productName = "Snowplow macOSTests";
-			productReference = 752DABFD21CC3B380065F874 /* Snowplow-macOSTests.xctest */;
-			productType = "com.apple.product-type.bundle.unit-test";
-		};
-		75F9C5C721FA2E0F00A5B8FC /* Snowplow-iOS-Static */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 75F9C5CE21FA2E0F00A5B8FC /* Build configuration list for PBXNativeTarget "Snowplow-iOS-Static" */;
-			buildPhases = (
-				75F9C5E321FA359400A5B8FC /* Headers */,
-				75F9C5C421FA2E0F00A5B8FC /* Sources */,
-				75F9C5C521FA2E0F00A5B8FC /* Frameworks */,
-				75F9C5C621FA2E0F00A5B8FC /* CopyFiles */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = "Snowplow-iOS-Static";
-			productName = "Snowplow-iOS-Static";
-			productReference = 75F9C5C821FA2E0F00A5B8FC /* libSnowplow-iOS-Static.a */;
-			productType = "com.apple.product-type.library.static";
-		};
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
-		AB0C27B5191B408200018557 /* Project object */ = {
-			isa = PBXProject;
-			attributes = {
-				LastUpgradeCheck = 1410;
-				ORGANIZATIONNAME = "Snowplow Analytics";
-				TargetAttributes = {
-					752DABCB21CC38550065F874 = {
-						CreatedOnToolsVersion = 10.1;
-						LastSwiftMigration = 1410;
-					};
-					752DABD321CC38560065F874 = {
-						CreatedOnToolsVersion = 10.1;
-						LastSwiftMigration = 1140;
-					};
-					752DABE721CC3A090065F874 = {
-						CreatedOnToolsVersion = 10.1;
-						LastSwiftMigration = 1410;
-					};
-					752DABF421CC3B380065F874 = {
-						CreatedOnToolsVersion = 10.1;
-						LastSwiftMigration = 1410;
-					};
-					752DABFC21CC3B380065F874 = {
-						CreatedOnToolsVersion = 10.1;
-					};
-					75F9C5C721FA2E0F00A5B8FC = {
-						CreatedOnToolsVersion = 10.1;
-						LastSwiftMigration = 1410;
-					};
-				};
-			};
-			buildConfigurationList = AB0C27B8191B408200018557 /* Build configuration list for PBXProject "Snowplow" */;
-			compatibilityVersion = "Xcode 9.3";
-			developmentRegion = en;
-			hasScannedForEncodings = 0;
-			knownRegions = (
-				en,
-				Base,
-			);
-			mainGroup = AB0C27B4191B408200018557;
-			productRefGroup = AB0C27BE191B408200018557 /* Products */;
-			projectDirPath = "";
-			projectRoot = "";
-			targets = (
-				752DABCB21CC38550065F874 /* Snowplow-iOS */,
-				752DABD321CC38560065F874 /* Tests */,
-				752DABE721CC3A090065F874 /* Snowplow-watchOS */,
-				752DABF421CC3B380065F874 /* Snowplow-macOS */,
-				752DABFC21CC3B380065F874 /* Snowplow-macOSTests */,
-				75F9C5C721FA2E0F00A5B8FC /* Snowplow-iOS-Static */,
-			);
-		};
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
-		752DABCA21CC38550065F874 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABE621CC3A090065F874 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABF321CC3B380065F874 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABFB21CC3B380065F874 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXShellScriptBuildPhase section */
-		ED8EA918258A7118008E292C /* Run Script - Copy Frameworks (Carthage) */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"$(SRCROOT)/Carthage/Build/iOS/VVJSONSchemaValidation.framework",
-				"$(SRCROOT)/Carthage/Build/iOS/SnowplowIgluClient.framework",
-				"$(SRCROOT)/Carthage/Build/iOS/Nocilla.framework",
-				"$(SRCROOT)/Carthage/Build/iOS/FMDB.framework",
-			);
-			name = "Run Script - Copy Frameworks (Carthage)";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/VVJSONSchemaValidation.framework",
-				"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SnowplowIgluClient.framework",
-				"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Nocilla.framework",
-				"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/FMDB.framework",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "/usr/local/bin/carthage copy-frameworks\n";
-		};
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
-		752DABC821CC38550065F874 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6BE70873292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
-				6B9DA6332928CAA3006D721A /* Tracker.swift in Sources */,
-				6B9DA6E4292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
-				6B9DA62E2928C08D006D721A /* DataPersistence.swift in Sources */,
-				6B9DA6292928B2FC006D721A /* Session.swift in Sources */,
-				6B85876D292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
-				6B85873C2923E025006E4A5F /* EmitterController.swift in Sources */,
-				6B8F5605291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
-				6B8587462923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
-				6B9DA5F82927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
-				6B85877229251779006E4A5F /* ConfigurationState.swift in Sources */,
-				6B9DA676292BD2D7006D721A /* SchemaRule.swift in Sources */,
-				6B9DA6012927E669006D721A /* State.swift in Sources */,
-				6B9DA5AC29268E4E006D721A /* PageView.swift in Sources */,
-				6BABED49291E884900F6798A /* ConfigurationBundle.swift in Sources */,
-				6BABED35291E596100F6798A /* EmitterConfiguration.swift in Sources */,
-				6B9DA642292B5197006D721A /* ScreenState.swift in Sources */,
-				6B9DA5A729268B0E006D721A /* TrackerError.swift in Sources */,
-				6B9DA5CC2927BCD6006D721A /* RequestResult.swift in Sources */,
-				6B9DA61B2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
-				6B85870129239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
-				6B9DA61F2927E99C006D721A /* TrackerState.swift in Sources */,
-				6B85875E2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
-				6B0CCFCE29236B0B0054954B /* GDPRContext.swift in Sources */,
-				6B8587502923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
-				6BABED4E291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
-				6B9DA67E292BD2D7006D721A /* GlobalContext.swift in Sources */,
-				6B9DA60F2927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
-				6B9DA651292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
-				6B8587592924CDB0006E4A5F /* Snowplow.swift in Sources */,
-				6B9DA5842926437F006D721A /* PushNotification.swift in Sources */,
-				6BABED26291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
-				6B85874A2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
-				6B9DA64C292B5487006D721A /* PlatformContext.swift in Sources */,
-				6B0CCFD429236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
-				6B9DA55329261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
-				6B9DA648292B5487006D721A /* DevicePlatform.swift in Sources */,
-				6B9DA656292B60E2006D721A /* Subject.swift in Sources */,
-				6B8587542923E7D0006E4A5F /* NetworkController.swift in Sources */,
-				6BE7084A292F64A700911E55 /* BufferOption.swift in Sources */,
-				6B9DA55F29261E5B006D721A /* ConsentDocument.swift in Sources */,
-				6B8587632924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
-				6BABED44291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
-				6B9DA56B29261E5C006D721A /* Foreground.swift in Sources */,
-				6B85870E2923C18D006E4A5F /* EventStore.swift in Sources */,
-				6B85872D2923C5F0006E4A5F /* TrackerController.swift in Sources */,
-				6B9DA55B29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
-				6B858769292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
-				6B9DA56F29261E5C006D721A /* SelfDescribing.swift in Sources */,
-				6B9DA67A292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
-				6B9DA692292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
-				6B9DA5D02927BCD6006D721A /* EmitterEvent.swift in Sources */,
-				6B9DA665292BAE5F006D721A /* ServiceProvider.swift in Sources */,
-				6B9DA57B29261E5C006D721A /* Structured.swift in Sources */,
-				6BABED3F291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
-				6B9DA5F02927E635006D721A /* InstallTracker.swift in Sources */,
-				6B9DA56329261E5B006D721A /* EventBase.swift in Sources */,
-				6B9DA57729261E5C006D721A /* Timing.swift in Sources */,
-				6BE70868292F67C000911E55 /* InspectableEvent.swift in Sources */,
-				6B9DA5B629269755006D721A /* TrackerEvent.swift in Sources */,
-				6B9DA684292BDD3A006D721A /* Logger.swift in Sources */,
-				6B9DA688292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
-				6B9DA5C82927BCD6006D721A /* RequestCallback.swift in Sources */,
-				6BABEB6C291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
-				6BE70861292F66E800911E55 /* ProtocolOptions.swift in Sources */,
-				6B9DA5B129269036006D721A /* SNOWError.swift in Sources */,
-				6B9DA6062927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
-				6B8587362923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
-				6B9DA5D42927BCD6006D721A /* Emitter.swift in Sources */,
-				6B9DA5A229268956006D721A /* EcommerceItem.swift in Sources */,
-				6B9DA5EC2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
-				6B9DA53F29253951006D721A /* LifecycleEntity.swift in Sources */,
-				6B85878229251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
-				6BE7084D292F654D00911E55 /* ContextGenerator.swift in Sources */,
-				6BABED3A291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
-				6BABEB7C291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
-				6B8586FA292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
-				6B9DA5C42927BCD6006D721A /* Request.swift in Sources */,
-				6B8587402923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
-				6B9DA6172927E99C006D721A /* StateManager.swift in Sources */,
-				6B9DA6252928B2FC006D721A /* SessionState.swift in Sources */,
-				6B85870929239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
-				6BE7085C292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
-				6BABE8BD291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
-				6B9DA598292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
-				6BABEB71291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
-				6B85877829251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
-				6B9DA661292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
-				6B9DA5F42927E635006D721A /* DeepLinkState.swift in Sources */,
-				6B9DA59D29267B66006D721A /* Ecommerce.swift in Sources */,
-				6B8586F6292388C9006E4A5F /* SessionController.swift in Sources */,
-				6B9DA5E32927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
-				6B9DA55729261E5B006D721A /* ConsentGranted.swift in Sources */,
-				6B9DA6DF292F4A0C006D721A /* EmitterDefaults.swift in Sources */,
-				6B9DA66F292BBCDE006D721A /* SNOWReachability.swift in Sources */,
-				6B85877C29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
-				6B9DA68D292BF03C006D721A /* TrackerConstants.swift in Sources */,
-				6B0CCFD829236D4A0054954B /* SubjectController.swift in Sources */,
-				6B9DA63E292B5197006D721A /* ScreenStateMachine.swift in Sources */,
-				6BE7086D292F683600911E55 /* LogLevel.swift in Sources */,
-				6B8587312923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
-				6B9DA54329253952006D721A /* DeepLinkEntity.swift in Sources */,
-				6B9DA58929264EC2006D721A /* MessageNotification.swift in Sources */,
-				6B0CCFBE292275580054954B /* Controller.swift in Sources */,
-				6B85878629251DF4006E4A5F /* Payload.swift in Sources */,
-				6B0CCFB9292262650054954B /* GDPRController.swift in Sources */,
-				6B9DA66A292BBC83006D721A /* SPSize.swift in Sources */,
-				6B9DA5FC2927E635006D721A /* LifecycleState.swift in Sources */,
-				6BABED21291E3DB200F6798A /* Configuration.swift in Sources */,
-				6B9DA57329261E5C006D721A /* Background.swift in Sources */,
-				6B0CCFB5292262650054954B /* GDPRControllerImpl.swift in Sources */,
-				6BABED53291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
-				6B9DA57F29261E5C006D721A /* ScreenView.swift in Sources */,
-				6B9DA6132927E99C006D721A /* StateFuture.swift in Sources */,
-				6B0CCFC9292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
-				6B9DA65B292B6E68006D721A /* Utilities.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABD021CC38560065F874 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6B9DA6C2292E4D75006D721A /* LegacyTestTracker.swift in Sources */,
-				6B9DA6AC292D060F006D721A /* TestEmitterConfiguration.swift in Sources */,
-				6B9DA6B9292E15FF006D721A /* TestGlobalContexts.swift in Sources */,
-				6B9DA69C292CB9A3006D721A /* MockLoggerDelegate.swift in Sources */,
-				6B9DA69D292CB9A3006D721A /* MockEventStore.swift in Sources */,
-				6B9DA6D8292E5FE6006D721A /* TestSubject.swift in Sources */,
-				6B9DA6B5292E1181006D721A /* TestRequest.swift in Sources */,
-				6B9DA69F292CB9A3006D721A /* MockWKScriptMessage.swift in Sources */,
-				6B9DA6A3292CFF47006D721A /* TestPayload.swift in Sources */,
-				6B9DA6AD292D060F006D721A /* TestMultipleInstances.swift in Sources */,
-				6B9DA6AF292D060F006D721A /* TestTrackerController.swift in Sources */,
-				6B9DA6B8292E15FF006D721A /* TestSchemaRuleset.swift in Sources */,
-				6B9DA6B3292D203E006D721A /* TestSession.swift in Sources */,
-				6B9DA6AB292D060F006D721A /* TestTrackerConfiguration.swift in Sources */,
-				6B9DA6DA292E6010006D721A /* TestServiceProvider.swift in Sources */,
-				6B9DA69E292CB9A3006D721A /* MockDeviceInfoMonitor.swift in Sources */,
-				6B9DA6BB292E258A006D721A /* TestNetworkConnection.swift in Sources */,
-				6B9DA6C1292E4D75006D721A /* LegacyTestEmitter.swift in Sources */,
-				6B9DA6AE292D060F006D721A /* TestRemoteConfiguration.swift in Sources */,
-				6B9DA6DC292E6034006D721A /* LegacyTestSubject.swift in Sources */,
-				6B9DA6CC292E5DF9006D721A /* TestSelfDescribingJson.swift in Sources */,
-				6B9DA6CA292E5DD9006D721A /* TestMemoryEventStore.swift in Sources */,
-				6B9DA69B292CB9A3006D721A /* MockNetworkConnection.swift in Sources */,
-				6B9DA6D0292E5E7F006D721A /* TestUtils.swift in Sources */,
-				6B9DA6BD292E2923006D721A /* TestGeneratedJsons.swift in Sources */,
-				6B9DA6C6292E589F006D721A /* TestWebViewMessageHandler.swift in Sources */,
-				6B9DA6C8292E5B2B006D721A /* TestPlatformContext.swift in Sources */,
-				6B9DA6C4292E5761006D721A /* TestSQLiteEventStore.swift in Sources */,
-				6B9DA6A1292CBFEB006D721A /* TestEvents.swift in Sources */,
-				6B9DA6B2292D203E006D721A /* TestStateManager.swift in Sources */,
-				6B9DA6D2292E5F5E006D721A /* TestLifecycleState.swift in Sources */,
-				6B9DA6CE292E5E39006D721A /* TestScreenState.swift in Sources */,
-				6B9DA6D6292E5FB6006D721A /* TestDataPersistence.swift in Sources */,
-				6B9DA6A5292D051A006D721A /* TestRequestResult.swift in Sources */,
-				6B9DA6D4292E5F71006D721A /* TestLogger.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABE421CC3A090065F874 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6BE70874292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
-				6B9DA649292B5487006D721A /* DevicePlatform.swift in Sources */,
-				6B9DA6E5292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
-				6B85874B2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
-				6B0CCFCF29236B0B0054954B /* GDPRContext.swift in Sources */,
-				6B0CCFCA292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
-				6B9DA5852926437F006D721A /* PushNotification.swift in Sources */,
-				6BABED45291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
-				6B85872E2923C5F0006E4A5F /* TrackerController.swift in Sources */,
-				6B85877329251779006E4A5F /* ConfigurationState.swift in Sources */,
-				6B9DA5B729269755006D721A /* TrackerEvent.swift in Sources */,
-				6BABED3B291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
-				6B9DA5ED2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
-				6B9DA5E42927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
-				6B9DA63F292B5197006D721A /* ScreenStateMachine.swift in Sources */,
-				6B85875A2924CDB0006E4A5F /* Snowplow.swift in Sources */,
-				6B9DA599292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
-				6B85878329251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
-				6B9DA66B292BBC83006D721A /* SPSize.swift in Sources */,
-				6B9DA59E29267B66006D721A /* Ecommerce.swift in Sources */,
-				6B8587552923E7D0006E4A5F /* NetworkController.swift in Sources */,
-				6B8F5606291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
-				6B9DA670292BBCDE006D721A /* SNOWReachability.swift in Sources */,
-				6B9DA5F12927E635006D721A /* InstallTracker.swift in Sources */,
-				6B9DA57829261E5C006D721A /* Timing.swift in Sources */,
-				6B9DA5CD2927BCD6006D721A /* RequestResult.swift in Sources */,
-				6B9DA657292B60E2006D721A /* Subject.swift in Sources */,
-				6B9DA5FD2927E635006D721A /* LifecycleState.swift in Sources */,
-				6B9DA693292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
-				6B9DA6142927E99C006D721A /* StateFuture.swift in Sources */,
-				6B8587372923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
-				6BABE8BE291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
-				6B9DA643292B5197006D721A /* ScreenState.swift in Sources */,
-				6B9DA55829261E5B006D721A /* ConsentGranted.swift in Sources */,
-				6B85877929251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
-				6B9DA5D52927BCD6006D721A /* Emitter.swift in Sources */,
-				6B9DA5A829268B0E006D721A /* TrackerError.swift in Sources */,
-				6B0CCFD529236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
-				6BABED22291E3DB200F6798A /* Configuration.swift in Sources */,
-				6B8587472923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
-				6B9DA67B292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
-				6BE7084E292F656500911E55 /* BufferOption.swift in Sources */,
-				6B9DA685292BDD3A006D721A /* Logger.swift in Sources */,
-				6B9DA65C292B6E68006D721A /* Utilities.swift in Sources */,
-				6BABED27291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
-				6B9DA54429253952006D721A /* DeepLinkEntity.swift in Sources */,
-				6B9DA5F92927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
-				6BABEB72291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
-				6BABED40291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
-				6B9DA666292BAE5F006D721A /* ServiceProvider.swift in Sources */,
-				6B9DA57029261E5C006D721A /* SelfDescribing.swift in Sources */,
-				6B8587412923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
-				6B85877D29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
-				6B0CCFBF292275580054954B /* Controller.swift in Sources */,
-				6B9DA6072927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
-				6B9DA58029261E5C006D721A /* ScreenView.swift in Sources */,
-				6B9DA6102927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
-				6B0CCFD929236D4A0054954B /* SubjectController.swift in Sources */,
-				6BE70869292F67C000911E55 /* InspectableEvent.swift in Sources */,
-				6B9DA67F292BD2D7006D721A /* GlobalContext.swift in Sources */,
-				6B8586F7292388C9006E4A5F /* SessionController.swift in Sources */,
-				6B9DA54029253952006D721A /* LifecycleEntity.swift in Sources */,
-				6B9DA689292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
-				6B9DA5C52927BCD6006D721A /* Request.swift in Sources */,
-				6BE70862292F66E800911E55 /* ProtocolOptions.swift in Sources */,
-				6BABED36291E596100F6798A /* EmitterConfiguration.swift in Sources */,
-				6B8586FB292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
-				6B8587512923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
-				6B85878729251DF4006E4A5F /* Payload.swift in Sources */,
-				6B9DA57429261E5C006D721A /* Background.swift in Sources */,
-				6B9DA56C29261E5C006D721A /* Foreground.swift in Sources */,
-				6B9DA677292BD2D7006D721A /* SchemaRule.swift in Sources */,
-				6BABED54291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
-				6BE70857292F657E00911E55 /* ContextGenerator.swift in Sources */,
-				6B9DA6342928CAA3006D721A /* Tracker.swift in Sources */,
-				6B85870229239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
-				6B9DA652292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
-				6B9DA62A2928B2FC006D721A /* Session.swift in Sources */,
-				6B9DA62F2928C08D006D721A /* DataPersistence.swift in Sources */,
-				6B9DA6202927E99C006D721A /* TrackerState.swift in Sources */,
-				6B9DA64D292B5487006D721A /* PlatformContext.swift in Sources */,
-				6B9DA6262928B2FC006D721A /* SessionState.swift in Sources */,
-				6BE7085D292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
-				6B9DA6022927E669006D721A /* State.swift in Sources */,
-				6BABED4F291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
-				6B9DA5F52927E635006D721A /* DeepLinkState.swift in Sources */,
-				6B9DA57C29261E5C006D721A /* Structured.swift in Sources */,
-				6B9DA5A329268956006D721A /* EcommerceItem.swift in Sources */,
-				6B85875F2924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
-				6BABED4A291E884900F6798A /* ConfigurationBundle.swift in Sources */,
-				6B85876A292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
-				6B9DA6E0292F4A0D006D721A /* EmitterDefaults.swift in Sources */,
-				6B9DA68E292BF03C006D721A /* TrackerConstants.swift in Sources */,
-				6BE7084F292F656500911E55 /* EventStore.swift in Sources */,
-				6B85870A29239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
-				6B9DA662292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
-				6B85873D2923E025006E4A5F /* EmitterController.swift in Sources */,
-				6B8587322923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
-				6BABEB7D291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
-				6BE7086E292F683600911E55 /* LogLevel.swift in Sources */,
-				6B9DA55C29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
-				6B9DA55429261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
-				6B9DA61C2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
-				6BE70850292F656500911E55 /* EmitterEvent.swift in Sources */,
-				6B9DA5B229269036006D721A /* SNOWError.swift in Sources */,
-				6B9DA5C92927BCD6006D721A /* RequestCallback.swift in Sources */,
-				6B9DA6182927E99C006D721A /* StateManager.swift in Sources */,
-				6B9DA58A29264EC2006D721A /* MessageNotification.swift in Sources */,
-				6B9DA5AD29268E4E006D721A /* PageView.swift in Sources */,
-				6B9DA56429261E5B006D721A /* EventBase.swift in Sources */,
-				6B0CCFBA292262650054954B /* GDPRController.swift in Sources */,
-				6B0CCFB6292262650054954B /* GDPRControllerImpl.swift in Sources */,
-				6B9DA56029261E5B006D721A /* ConsentDocument.swift in Sources */,
-				6B8587642924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
-				6B85876E292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
-				6BABEB6D291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABF121CC3B380065F874 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6BE70875292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
-				6B9DA64A292B5487006D721A /* DevicePlatform.swift in Sources */,
-				6B9DA6E6292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
-				6B85874C2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
-				6B0CCFD029236B0B0054954B /* GDPRContext.swift in Sources */,
-				6B0CCFCB292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
-				6B9DA680292BD2D7006D721A /* GlobalContext.swift in Sources */,
-				6B9DA67C292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
-				6B9DA5862926437F006D721A /* PushNotification.swift in Sources */,
-				6BABED46291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
-				6B85872F2923C5F0006E4A5F /* TrackerController.swift in Sources */,
-				6B85877429251779006E4A5F /* ConfigurationState.swift in Sources */,
-				6B9DA5B829269755006D721A /* TrackerEvent.swift in Sources */,
-				6BABED3C291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
-				6B9DA5EE2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
-				6B9DA5E52927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
-				6B9DA640292B5197006D721A /* ScreenStateMachine.swift in Sources */,
-				6B85875B2924CDB0006E4A5F /* Snowplow.swift in Sources */,
-				6B9DA59A292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
-				6B85878429251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
-				6B9DA68F292BF03C006D721A /* TrackerConstants.swift in Sources */,
-				6B9DA66C292BBC83006D721A /* SPSize.swift in Sources */,
-				6B9DA59F29267B66006D721A /* Ecommerce.swift in Sources */,
-				6B8587562923E7D0006E4A5F /* NetworkController.swift in Sources */,
-				6B8F5607291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
-				6B9DA671292BBCDE006D721A /* SNOWReachability.swift in Sources */,
-				6B9DA5F22927E635006D721A /* InstallTracker.swift in Sources */,
-				6B9DA57929261E5C006D721A /* Timing.swift in Sources */,
-				6B9DA694292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
-				6B9DA5CE2927BCD6006D721A /* RequestResult.swift in Sources */,
-				6B9DA5FE2927E635006D721A /* LifecycleState.swift in Sources */,
-				6B9DA658292B60E2006D721A /* Subject.swift in Sources */,
-				6B8587382923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
-				6B9DA6152927E99C006D721A /* StateFuture.swift in Sources */,
-				6BABE8BF291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
-				6B9DA55929261E5B006D721A /* ConsentGranted.swift in Sources */,
-				6B9DA644292B5197006D721A /* ScreenState.swift in Sources */,
-				6B85877A29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
-				6B9DA5D62927BCD6006D721A /* Emitter.swift in Sources */,
-				6B9DA5A929268B0E006D721A /* TrackerError.swift in Sources */,
-				6B0CCFD629236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
-				6BE70851292F656500911E55 /* BufferOption.swift in Sources */,
-				6BABED23291E3DB200F6798A /* Configuration.swift in Sources */,
-				6B8587482923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
-				6B9DA68A292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
-				6B9DA65D292B6E68006D721A /* Utilities.swift in Sources */,
-				6BABED28291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
-				6B9DA54529253952006D721A /* DeepLinkEntity.swift in Sources */,
-				6B9DA5FA2927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
-				6BABEB73291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
-				6BABED41291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
-				6B9DA686292BDD3A006D721A /* Logger.swift in Sources */,
-				6B9DA667292BAE5F006D721A /* ServiceProvider.swift in Sources */,
-				6B9DA57129261E5C006D721A /* SelfDescribing.swift in Sources */,
-				6B8587422923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
-				6B85877E29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
-				6B0CCFC0292275580054954B /* Controller.swift in Sources */,
-				6B9DA6082927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
-				6BE7086A292F67C000911E55 /* InspectableEvent.swift in Sources */,
-				6B9DA58129261E5C006D721A /* ScreenView.swift in Sources */,
-				6B9DA6112927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
-				6B0CCFDA29236D4A0054954B /* SubjectController.swift in Sources */,
-				6B8586F8292388C9006E4A5F /* SessionController.swift in Sources */,
-				6B9DA5C62927BCD6006D721A /* Request.swift in Sources */,
-				6BE70863292F66E800911E55 /* ProtocolOptions.swift in Sources */,
-				6B9DA54129253952006D721A /* LifecycleEntity.swift in Sources */,
-				6BABED37291E596100F6798A /* EmitterConfiguration.swift in Sources */,
-				6B8586FC292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
-				6B8587522923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
-				6B9DA57529261E5C006D721A /* Background.swift in Sources */,
-				6B9DA56D29261E5C006D721A /* Foreground.swift in Sources */,
-				6B85878829251DF4006E4A5F /* Payload.swift in Sources */,
-				6B9DA6352928CAA3006D721A /* Tracker.swift in Sources */,
-				6BE70858292F657F00911E55 /* ContextGenerator.swift in Sources */,
-				6BABED55291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
-				6B85870329239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
-				6B9DA62B2928B2FC006D721A /* Session.swift in Sources */,
-				6B9DA653292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
-				6B9DA6032927E669006D721A /* State.swift in Sources */,
-				6B9DA6302928C08D006D721A /* DataPersistence.swift in Sources */,
-				6B9DA6212927E99C006D721A /* TrackerState.swift in Sources */,
-				6B9DA64E292B5487006D721A /* PlatformContext.swift in Sources */,
-				6BE7085E292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
-				6B9DA6272928B2FC006D721A /* SessionState.swift in Sources */,
-				6BABED50291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
-				6B9DA5F62927E635006D721A /* DeepLinkState.swift in Sources */,
-				6B9DA57D29261E5C006D721A /* Structured.swift in Sources */,
-				6B9DA5A429268956006D721A /* EcommerceItem.swift in Sources */,
-				6B8587602924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
-				6BABED4B291E884900F6798A /* ConfigurationBundle.swift in Sources */,
-				6B85876B292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
-				6B9DA6E1292F4A0D006D721A /* EmitterDefaults.swift in Sources */,
-				6B85870B29239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
-				6BE70852292F656500911E55 /* EventStore.swift in Sources */,
-				6B9DA663292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
-				6B85873E2923E025006E4A5F /* EmitterController.swift in Sources */,
-				6B8587332923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
-				6BABEB7E291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
-				6B9DA55D29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
-				6BE7086F292F683600911E55 /* LogLevel.swift in Sources */,
-				6B9DA678292BD2D7006D721A /* SchemaRule.swift in Sources */,
-				6B9DA61D2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
-				6B9DA55529261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
-				6BE70853292F656500911E55 /* EmitterEvent.swift in Sources */,
-				6B9DA5B329269036006D721A /* SNOWError.swift in Sources */,
-				6B9DA6192927E99C006D721A /* StateManager.swift in Sources */,
-				6B9DA5CA2927BCD6006D721A /* RequestCallback.swift in Sources */,
-				6B9DA58B29264EC2006D721A /* MessageNotification.swift in Sources */,
-				6B9DA5AE29268E4E006D721A /* PageView.swift in Sources */,
-				6B9DA56529261E5B006D721A /* EventBase.swift in Sources */,
-				6B0CCFBB292262650054954B /* GDPRController.swift in Sources */,
-				6B0CCFB7292262650054954B /* GDPRControllerImpl.swift in Sources */,
-				6B9DA56129261E5B006D721A /* ConsentDocument.swift in Sources */,
-				6B8587652924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
-				6B85876F292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
-				6BABEB6E291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		752DABF921CC3B380065F874 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		75F9C5C421FA2E0F00A5B8FC /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				6BE70876292F68A700911E55 /* GDPRProcessingBasis.swift in Sources */,
-				6B9DA668292BAE5F006D721A /* ServiceProvider.swift in Sources */,
-				6B9DA6E7292F4ED9006D721A /* TrackerDefaults.swift in Sources */,
-				6B9DA645292B5197006D721A /* ScreenState.swift in Sources */,
-				6B85874D2923E5A2006E4A5F /* GlobalContextsControllerImpl.swift in Sources */,
-				6B9DA6362928CAA3006D721A /* Tracker.swift in Sources */,
-				6B0CCFD129236B0B0054954B /* GDPRContext.swift in Sources */,
-				6B9DA55629261E5B006D721A /* ConsentWithdrawn.swift in Sources */,
-				6B0CCFCC292284390054954B /* GDPRConfigurationUpdate.swift in Sources */,
-				6B9DA5D72927BCD6006D721A /* Emitter.swift in Sources */,
-				6B9DA5CB2927BCD6006D721A /* RequestCallback.swift in Sources */,
-				6B8587302923C5F0006E4A5F /* TrackerController.swift in Sources */,
-				6B9DA55E29261E5B006D721A /* DeepLinkReceived.swift in Sources */,
-				6BABED47291E85F200F6798A /* SubjectConfigurationUpdate.swift in Sources */,
-				6B9DA5F32927E635006D721A /* InstallTracker.swift in Sources */,
-				6BABED3D291E60F000F6798A /* GDPRConfiguration.swift in Sources */,
-				6B9DA54229253952006D721A /* LifecycleEntity.swift in Sources */,
-				6B85877B29251CBD006E4A5F /* DefaultNetworkConnection.swift in Sources */,
-				6B9DA659292B60E2006D721A /* Subject.swift in Sources */,
-				6B9DA58229261E5C006D721A /* ScreenView.swift in Sources */,
-				6B9DA65E292B6E68006D721A /* Utilities.swift in Sources */,
-				6B9DA54629253952006D721A /* DeepLinkEntity.swift in Sources */,
-				6B85875C2924CDB0006E4A5F /* Snowplow.swift in Sources */,
-				6B8587572923E7D0006E4A5F /* NetworkController.swift in Sources */,
-				6B9DA57229261E5C006D721A /* SelfDescribing.swift in Sources */,
-				6B9DA56229261E5B006D721A /* ConsentDocument.swift in Sources */,
-				6B85877F29251CBD006E4A5F /* NetworkConnection.swift in Sources */,
-				6B8F5608291BDAF900CD3F1B /* TrackerConfiguration.swift in Sources */,
-				6B9DA641292B5197006D721A /* ScreenStateMachine.swift in Sources */,
-				6B9DA695292CA13B006D721A /* UIKitScreenViewTracking.swift in Sources */,
-				6B9DA5B429269036006D721A /* SNOWError.swift in Sources */,
-				6B9DA56E29261E5C006D721A /* Foreground.swift in Sources */,
-				6B9DA57A29261E5C006D721A /* Timing.swift in Sources */,
-				6B9DA59B292677E0006D721A /* MessageNotificationAttachment.swift in Sources */,
-				6B8587392923DCA2006E4A5F /* NetworkConfigurationUpdate.swift in Sources */,
-				6B9DA5B929269755006D721A /* TrackerEvent.swift in Sources */,
-				6B9DA5EF2927E635006D721A /* LifecycleStateMachine.swift in Sources */,
-				6BABE8C0291BFCAD00F6798A /* TrackerConfigurationUpdate.swift in Sources */,
-				6B9DA64F292B5487006D721A /* PlatformContext.swift in Sources */,
-				6B9DA67D292BD2D7006D721A /* SchemaRuleset.swift in Sources */,
-				6B9DA64B292B5487006D721A /* DevicePlatform.swift in Sources */,
-				6BE70854292F656700911E55 /* BufferOption.swift in Sources */,
-				6B9DA687292BDD3A006D721A /* Logger.swift in Sources */,
-				6B858770292509AD006E4A5F /* ConfigurationFetcher.swift in Sources */,
-				6B0CCFD729236D4A0054954B /* SubjectControllerImpl.swift in Sources */,
-				6B9DA66D292BBC83006D721A /* SPSize.swift in Sources */,
-				6B9DA6162927E99C006D721A /* StateFuture.swift in Sources */,
-				6B9DA5A029267B66006D721A /* Ecommerce.swift in Sources */,
-				6BABED24291E3DB200F6798A /* Configuration.swift in Sources */,
-				6BABED29291E4FEF00F6798A /* SubjectConfiguration.swift in Sources */,
-				6B9DA6122927E99C006D721A /* WebViewMessageHandler.swift in Sources */,
-				6B8587492923E5A2006E4A5F /* GlobalContextsController.swift in Sources */,
-				6B9DA62C2928B2FC006D721A /* Session.swift in Sources */,
-				6B9DA6282928B2FC006D721A /* SessionState.swift in Sources */,
-				6B9DA5A529268956006D721A /* EcommerceItem.swift in Sources */,
-				6B85876C292509AD006E4A5F /* ConfigurationCache.swift in Sources */,
-				6B9DA664292BAE5F006D721A /* ServiceProviderProtocol.swift in Sources */,
-				6BABEB74291CF1B900F6798A /* SessionConfiguration.swift in Sources */,
-				6BE7086B292F67C000911E55 /* InspectableEvent.swift in Sources */,
-				6B9DA61E2927E99C006D721A /* TrackerStateSnapshot.swift in Sources */,
-				6B9DA681292BD2D7006D721A /* GlobalContext.swift in Sources */,
-				6BABED42291E833E00F6798A /* EmitterConfigurationUpdate.swift in Sources */,
-				6B9DA68B292BDD3A006D721A /* LoggerDelegate.swift in Sources */,
-				6B9DA57E29261E5C006D721A /* Structured.swift in Sources */,
-				6BE70864292F66E800911E55 /* ProtocolOptions.swift in Sources */,
-				6B8587432923E025006E4A5F /* EmitterControllerImpl.swift in Sources */,
-				6B9DA61A2927E99C006D721A /* StateManager.swift in Sources */,
-				6B0CCFC1292275580054954B /* Controller.swift in Sources */,
-				6B9DA6222927E99C006D721A /* TrackerState.swift in Sources */,
-				6B9DA5872926437F006D721A /* PushNotification.swift in Sources */,
-				6B9DA5AF29268E4E006D721A /* PageView.swift in Sources */,
-				6B9DA679292BD2D7006D721A /* SchemaRule.swift in Sources */,
-				6B9DA57629261E5C006D721A /* Background.swift in Sources */,
-				6BE70859292F657F00911E55 /* ContextGenerator.swift in Sources */,
-				6B85877529251779006E4A5F /* ConfigurationState.swift in Sources */,
-				6B9DA5E62927D1F7006D721A /* EmitterEventProcessing.swift in Sources */,
-				6B0CCFDB29236D4A0054954B /* SubjectController.swift in Sources */,
-				6B9DA58C29264EC2006D721A /* MessageNotification.swift in Sources */,
-				6B8586F9292388C9006E4A5F /* SessionController.swift in Sources */,
-				6B9DA6312928C08D006D721A /* DataPersistence.swift in Sources */,
-				6BABED38291E596100F6798A /* EmitterConfiguration.swift in Sources */,
-				6B8586FD292388C9006E4A5F /* SessionControllerImpl.swift in Sources */,
-				6BE7085F292F66B300911E55 /* HttpMethodOptions.swift in Sources */,
-				6B9DA5C72927BCD6006D721A /* Request.swift in Sources */,
-				6B9DA55A29261E5B006D721A /* ConsentGranted.swift in Sources */,
-				6B8587532923E7D0006E4A5F /* NetworkControllerImpl.swift in Sources */,
-				6BABED56291E91E000F6798A /* RemoteConfiguration.swift in Sources */,
-				6B85870429239A9D006E4A5F /* SQLiteEventStore.swift in Sources */,
-				6B9DA5AA29268B0E006D721A /* TrackerError.swift in Sources */,
-				6BABED51291E8B6F00F6798A /* GlobalContextsConfiguration.swift in Sources */,
-				6B9DA690292BF03C006D721A /* TrackerConstants.swift in Sources */,
-				6B9DA6E2292F4A0E006D721A /* EmitterDefaults.swift in Sources */,
-				6B9DA5FF2927E635006D721A /* LifecycleState.swift in Sources */,
-				6BE70855292F656700911E55 /* EventStore.swift in Sources */,
-				6B8587612924DA67006E4A5F /* FetchedConfigurationBundle.swift in Sources */,
-				6BABED4C291E884900F6798A /* ConfigurationBundle.swift in Sources */,
-				6B9DA56629261E5B006D721A /* EventBase.swift in Sources */,
-				6B85878929251DF4006E4A5F /* Payload.swift in Sources */,
-				6B85870C29239A9D006E4A5F /* MemoryEventStore.swift in Sources */,
-				6BE70870292F683600911E55 /* LogLevel.swift in Sources */,
-				6B9DA6092927E6F2006D721A /* StateMachineProtocol.swift in Sources */,
-				6B85873F2923E025006E4A5F /* EmitterController.swift in Sources */,
-				6B8587342923C5F0006E4A5F /* TrackerControllerImpl.swift in Sources */,
-				6BE70856292F656700911E55 /* EmitterEvent.swift in Sources */,
-				6B9DA672292BBCDE006D721A /* SNOWReachability.swift in Sources */,
-				6B9DA5F72927E635006D721A /* DeepLinkState.swift in Sources */,
-				6B85878529251DF4006E4A5F /* SelfDescribingJson.swift in Sources */,
-				6B9DA6042927E669006D721A /* State.swift in Sources */,
-				6B9DA5FB2927E635006D721A /* DeepLinkStateMachine.swift in Sources */,
-				6BABEB7F291D1C7C00F6798A /* SessionConfigurationUpdate.swift in Sources */,
-				6B9DA5CF2927BCD6006D721A /* RequestResult.swift in Sources */,
-				6B0CCFBC292262650054954B /* GDPRController.swift in Sources */,
-				6B0CCFB8292262650054954B /* GDPRControllerImpl.swift in Sources */,
-				6B9DA654292B562D006D721A /* DeviceInfoMonitor.swift in Sources */,
-				6B8587662924F76F006E4A5F /* ConfigurationProvider.swift in Sources */,
-				6BABEB6F291CE94900F6798A /* NetworkConfiguration.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXTargetDependency section */
-		752DAC0021CC3B380065F874 /* PBXTargetDependency */ = {
-			isa = PBXTargetDependency;
-			target = 752DABF421CC3B380065F874 /* Snowplow-macOS */;
-			targetProxy = 752DABFF21CC3B380065F874 /* PBXContainerItemProxy */;
-		};
-		EDB27D2525E0111200DF8CE7 /* PBXTargetDependency */ = {
-			isa = PBXTargetDependency;
-			target = 752DABCB21CC38550065F874 /* Snowplow-iOS */;
-			targetProxy = EDB27D2425E0111200DF8CE7 /* PBXContainerItemProxy */;
-		};
-/* End PBXTargetDependency section */
-
-/* Begin PBXVariantGroup section */
-		75CAC3F821F2955000271FB3 /* InfoPlist.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				75CAC3F921F2955000271FB3 /* en */,
-			);
-			name = InfoPlist.strings;
-			sourceTree = "<group>";
-		};
-/* End PBXVariantGroup section */
-
-/* Begin XCBuildConfiguration section */
-		752DABDE21CC38560065F874 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "";
-				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 1;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEFINES_MODULE = YES;
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/iOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-					"SNOWPLOW_APP_EXTENSIONS=0",
-				);
-				INFOPLIST_FILE = "Snowplow iOS/Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MODULEMAP_FILE = Sources/Snowplow/ios.modulemap;
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-iOS";
-				PRODUCT_NAME = SnowplowTracker;
-				SKIP_INSTALL = YES;
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Debug;
-		};
-		752DABDF21CC38560065F874 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "";
-				CODE_SIGN_STYLE = Manual;
-				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEFINES_MODULE = YES;
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/iOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"$(inherited)",
-					"SNOWPLOW_APP_EXTENSIONS=0",
-				);
-				INFOPLIST_FILE = "Snowplow iOS/Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MODULEMAP_FILE = Sources/Snowplow/ios.modulemap;
-				MTL_ENABLE_DEBUG_INFO = NO;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-iOS";
-				PRODUCT_NAME = SnowplowTracker;
-				SKIP_INSTALL = YES;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Release;
-		};
-		752DABE121CC38560065F874 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_CODE_COVERAGE = NO;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				CODE_SIGN_STYLE = Manual;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEVELOPMENT_TEAM = "";
-				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/iOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_GENERATE_TEST_COVERAGE_FILES = NO;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"SNOWPLOW_TEST=1",
-					"$(inherited)",
-				);
-				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Snowplow\"";
-				INFOPLIST_FILE = Tests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					/usr/lib/swift,
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
-					"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
-				);
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-iOSTests";
-				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Tests/Tests-bridging-header.h";
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				TARGETED_DEVICE_FAMILY = "1,2";
-				USER_HEADER_SEARCH_PATHS = "";
-			};
-			name = Debug;
-		};
-		752DABE221CC38560065F874 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_CODE_COVERAGE = NO;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				CODE_SIGN_STYLE = Manual;
-				COPY_PHASE_STRIP = NO;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = "";
-				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/iOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_GENERATE_TEST_COVERAGE_FILES = NO;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"SNOWPLOW_TEST=1",
-					"$(inherited)",
-				);
-				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Snowplow\"";
-				INFOPLIST_FILE = Tests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					/usr/lib/swift,
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				LIBRARY_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
-					"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
-				);
-				MTL_ENABLE_DEBUG_INFO = NO;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-iOSTests";
-				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Tests/Tests-bridging-header.h";
-				TARGETED_DEVICE_FAMILY = "1,2";
-				USER_HEADER_SEARCH_PATHS = "";
-			};
-			name = Release;
-		};
-		752DABEE21CC3A090065F874 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "";
-				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 1;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/watchOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-					"SNOWPLOW_APP_EXTENSIONS=1",
-				);
-				INFOPLIST_FILE = "Snowplow watchOS/Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MODULEMAP_FILE = Sources/Snowplow/watchos.modulemap;
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-watchOS";
-				PRODUCT_NAME = SnowplowTracker;
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SDKROOT = watchos;
-				SKIP_INSTALL = YES;
-				TARGETED_DEVICE_FAMILY = 4;
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 4.0;
-			};
-			name = Debug;
-		};
-		752DABEF21CC3A090065F874 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				APPLICATION_EXTENSION_API_ONLY = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "";
-				CODE_SIGN_STYLE = Manual;
-				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/watchOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = "SNOWPLOW_APP_EXTENSIONS=1";
-				INFOPLIST_FILE = "Snowplow watchOS/Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MODULEMAP_FILE = Sources/Snowplow/watchos.modulemap;
-				MTL_ENABLE_DEBUG_INFO = NO;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-watchOS";
-				PRODUCT_NAME = SnowplowTracker;
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SDKROOT = watchos;
-				SKIP_INSTALL = YES;
-				TARGETED_DEVICE_FAMILY = 4;
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 4.0;
-			};
-			name = Release;
-		};
-		752DAC0721CC3B380065F874 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "-";
-				CODE_SIGN_STYLE = Manual;
-				COMBINE_HIDPI_IMAGES = YES;
-				CURRENT_PROJECT_VERSION = 1;
-				DEAD_CODE_STRIPPING = YES;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/Mac",
-				);
-				FRAMEWORK_VERSION = A;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-					"SNOWPLOW_APP_EXTENSIONS=0",
-				);
-				INFOPLIST_FILE = "Snowplow macOS/Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/../Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MACOSX_DEPLOYMENT_TARGET = 10.13;
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-macOS";
-				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SDKROOT = macosx;
-				SKIP_INSTALL = YES;
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Debug;
-		};
-		752DAC0821CC3B380065F874 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "-";
-				CODE_SIGN_STYLE = Manual;
-				COMBINE_HIDPI_IMAGES = YES;
-				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1;
-				DEAD_CODE_STRIPPING = YES;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/Mac",
-				);
-				FRAMEWORK_VERSION = A;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"SNOWPLOW_APP_EXTENSIONS=0",
-					"$(inherited)",
-				);
-				INFOPLIST_FILE = "Snowplow macOS/Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/../Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MACOSX_DEPLOYMENT_TARGET = 10.13;
-				MTL_ENABLE_DEBUG_INFO = NO;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-macOS";
-				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SDKROOT = macosx;
-				SKIP_INSTALL = YES;
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Release;
-		};
-		752DAC0A21CC3B380065F874 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "-";
-				CODE_SIGN_STYLE = Manual;
-				COMBINE_HIDPI_IMAGES = YES;
-				DEAD_CODE_STRIPPING = YES;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEVELOPMENT_TEAM = "";
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				INFOPLIST_FILE = "Snowplow macOSTests/Info.plist";
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/../Frameworks",
-					"@loader_path/../Frameworks",
-				);
-				MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-macOSTests";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SDKROOT = macosx;
-			};
-			name = Debug;
-		};
-		752DAC0B21CC3B380065F874 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "-";
-				CODE_SIGN_STYLE = Manual;
-				COMBINE_HIDPI_IMAGES = YES;
-				COPY_PHASE_STRIP = NO;
-				DEAD_CODE_STRIPPING = YES;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = "";
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				INFOPLIST_FILE = "Snowplow macOSTests/Info.plist";
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/../Frameworks",
-					"@loader_path/../Frameworks",
-				);
-				MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
-				MTL_ENABLE_DEBUG_INFO = NO;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.snowplowanalytics.Snowplow-macOSTests";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SDKROOT = macosx;
-			};
-			name = Release;
-		};
-		75F9C5CF21FA2E0F00A5B8FC /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				CODE_SIGN_STYLE = Automatic;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/iOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"SNOWPLOW_IOS_STATIC=1",
-					"$(inherited)",
-				);
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				OTHER_CFLAGS = "";
-				OTHER_LDFLAGS = "-ObjC";
-				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 4.2;
-				TARGETED_DEVICE_FAMILY = "1,2";
-			};
-			name = Debug;
-		};
-		75F9C5D021FA2E0F00A5B8FC /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "iPhone Developer";
-				CODE_SIGN_STYLE = Automatic;
-				COPY_PHASE_STRIP = NO;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Carthage/Build/iOS",
-				);
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"SNOWPLOW_IOS_STATIC=1",
-					"$(inherited)",
-				);
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MTL_ENABLE_DEBUG_INFO = NO;
-				MTL_FAST_MATH = YES;
-				OTHER_CFLAGS = "";
-				OTHER_LDFLAGS = "-ObjC";
-				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				SKIP_INSTALL = YES;
-				SWIFT_VERSION = 4.2;
-				TARGETED_DEVICE_FAMILY = "1,2";
-			};
-			name = Release;
-		};
-		AB0C27DE191B408200018557 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				APPLICATION_EXTENSION_API_ONLY = NO;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
-				CLANG_CXX_LIBRARY = "libc++";
-				CLANG_ENABLE_CODE_COVERAGE = YES;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_ARC = YES;
-				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
-				CLANG_WARN_BOOL_CONVERSION = YES;
-				CLANG_WARN_COMMA = YES;
-				CLANG_WARN_CONSTANT_CONVERSION = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-				CLANG_WARN_EMPTY_BODY = YES;
-				CLANG_WARN_ENUM_CONVERSION = YES;
-				CLANG_WARN_INFINITE_RECURSION = YES;
-				CLANG_WARN_INT_CONVERSION = YES;
-				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
-				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
-				CLANG_WARN_STRICT_PROTOTYPES = YES;
-				CLANG_WARN_SUSPICIOUS_MOVE = YES;
-				CLANG_WARN_UNREACHABLE_CODE = YES;
-				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
-				COPY_PHASE_STRIP = NO;
-				DEFINES_MODULE = YES;
-				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				ENABLE_TESTABILITY = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
-				GCC_DYNAMIC_NO_PIC = NO;
-				GCC_GENERATE_TEST_COVERAGE_FILES = YES;
-				GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
-				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-				);
-				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
-				GCC_WARN_UNDECLARED_SELECTOR = YES;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
-				GCC_WARN_UNUSED_FUNCTION = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Sources\"";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				ONLY_ACTIVE_ARCH = YES;
-				SDKROOT = iphoneos;
-				SWIFT_VERSION = 5.0;
-				VALID_ARCHS = "$(ARCHS_STANDARD)";
-			};
-			name = Debug;
-		};
-		AB0C27DF191B408200018557 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				APPLICATION_EXTENSION_API_ONLY = NO;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
-				CLANG_CXX_LIBRARY = "libc++";
-				CLANG_ENABLE_CODE_COVERAGE = YES;
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_ARC = YES;
-				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
-				CLANG_WARN_BOOL_CONVERSION = YES;
-				CLANG_WARN_COMMA = YES;
-				CLANG_WARN_CONSTANT_CONVERSION = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-				CLANG_WARN_EMPTY_BODY = YES;
-				CLANG_WARN_ENUM_CONVERSION = YES;
-				CLANG_WARN_INFINITE_RECURSION = YES;
-				CLANG_WARN_INT_CONVERSION = YES;
-				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
-				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
-				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
-				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
-				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
-				CLANG_WARN_STRICT_PROTOTYPES = YES;
-				CLANG_WARN_SUSPICIOUS_MOVE = YES;
-				CLANG_WARN_UNREACHABLE_CODE = YES;
-				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
-				COPY_PHASE_STRIP = YES;
-				DEFINES_MODULE = YES;
-				ENABLE_NS_ASSERTIONS = NO;
-				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
-				GCC_GENERATE_TEST_COVERAGE_FILES = YES;
-				GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
-				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_PREPROCESSOR_DEFINITIONS = "";
-				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
-				GCC_WARN_UNDECLARED_SELECTOR = YES;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
-				GCC_WARN_UNUSED_FUNCTION = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Sources\"";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				SDKROOT = iphoneos;
-				SWIFT_COMPILATION_MODE = wholemodule;
-				SWIFT_VERSION = 5.0;
-				VALIDATE_PRODUCT = YES;
-				VALID_ARCHS = "$(ARCHS_STANDARD)";
-			};
-			name = Release;
-		};
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
-		752DABDD21CC38560065F874 /* Build configuration list for PBXNativeTarget "Snowplow-iOS" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				752DABDE21CC38560065F874 /* Debug */,
-				752DABDF21CC38560065F874 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		752DABE021CC38560065F874 /* Build configuration list for PBXNativeTarget "Tests" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				752DABE121CC38560065F874 /* Debug */,
-				752DABE221CC38560065F874 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		752DABED21CC3A090065F874 /* Build configuration list for PBXNativeTarget "Snowplow-watchOS" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				752DABEE21CC3A090065F874 /* Debug */,
-				752DABEF21CC3A090065F874 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		752DAC0621CC3B380065F874 /* Build configuration list for PBXNativeTarget "Snowplow-macOS" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				752DAC0721CC3B380065F874 /* Debug */,
-				752DAC0821CC3B380065F874 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		752DAC0921CC3B380065F874 /* Build configuration list for PBXNativeTarget "Snowplow-macOSTests" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				752DAC0A21CC3B380065F874 /* Debug */,
-				752DAC0B21CC3B380065F874 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		75F9C5CE21FA2E0F00A5B8FC /* Build configuration list for PBXNativeTarget "Snowplow-iOS-Static" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				75F9C5CF21FA2E0F00A5B8FC /* Debug */,
-				75F9C5D021FA2E0F00A5B8FC /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		AB0C27B8191B408200018557 /* Build configuration list for PBXProject "Snowplow" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				AB0C27DE191B408200018557 /* Debug */,
-				AB0C27DF191B408200018557 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-/* End XCConfigurationList section */
-	};
-	rootObject = AB0C27B5191B408200018557 /* Project object */;
-}
diff --git a/Snowplow.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Snowplow.xcodeproj/project.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 026790a22..000000000
--- a/Snowplow.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Workspace
-   version = "1.0">
-   <FileRef
-      location = "self:Snowplow.xcodeproj">
-   </FileRef>
-</Workspace>
diff --git a/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d981003..000000000
--- a/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>IDEDidComputeMac32BitWarning</key>
-	<true/>
-</dict>
-</plist>
diff --git a/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/Snowplow.xccheckout b/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/Snowplow.xccheckout
deleted file mode 100644
index 507239e73..000000000
--- a/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/Snowplow.xccheckout
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>IDESourceControlProjectFavoriteDictionaryKey</key>
-	<false/>
-	<key>IDESourceControlProjectIdentifier</key>
-	<string>2FE3A69A-C932-458D-9890-AAF1CB21BA96</string>
-	<key>IDESourceControlProjectName</key>
-	<string>Snowplow</string>
-	<key>IDESourceControlProjectOriginsDictionary</key>
-	<dict>
-		<key>6E34EC2B-E58D-4D8A-A9E4-FBC55BCAF1ED</key>
-		<string>ssh://github.com/snowplow/snowplow-ios-tracker.git</string>
-	</dict>
-	<key>IDESourceControlProjectPath</key>
-	<string>Snowplow.xcodeproj/project.xcworkspace</string>
-	<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
-	<dict>
-		<key>6E34EC2B-E58D-4D8A-A9E4-FBC55BCAF1ED</key>
-		<string>../..</string>
-	</dict>
-	<key>IDESourceControlProjectURL</key>
-	<string>ssh://github.com/snowplow/snowplow-ios-tracker.git</string>
-	<key>IDESourceControlProjectVersion</key>
-	<integer>110</integer>
-	<key>IDESourceControlProjectWCCIdentifier</key>
-	<string>6E34EC2B-E58D-4D8A-A9E4-FBC55BCAF1ED</string>
-	<key>IDESourceControlProjectWCConfigurations</key>
-	<array>
-		<dict>
-			<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
-			<string>public.vcs.git</string>
-			<key>IDESourceControlWCCIdentifierKey</key>
-			<string>6E34EC2B-E58D-4D8A-A9E4-FBC55BCAF1ED</string>
-			<key>IDESourceControlWCCName</key>
-			<string>Snowplow</string>
-		</dict>
-	</array>
-</dict>
-</plist>
diff --git a/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
deleted file mode 100644
index 0c67376eb..000000000
--- a/Snowplow.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict/>
-</plist>
diff --git a/Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/DF6688A4-3B86-4BD9-9AA9-38554F93D718.plist b/Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/DF6688A4-3B86-4BD9-9AA9-38554F93D718.plist
deleted file mode 100644
index 1c25354c3..000000000
--- a/Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/DF6688A4-3B86-4BD9-9AA9-38554F93D718.plist
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>classNames</key>
-	<dict>
-		<key>TestPlatformContext</key>
-		<dict>
-			<key>testPerformanceOfFetchingMobileDict</key>
-			<dict>
-				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
-				<dict>
-					<key>baselineAverage</key>
-					<real>0.089600</real>
-					<key>baselineIntegrationDisplayName</key>
-					<string>Local Baseline</string>
-				</dict>
-			</dict>
-			<key>testPerformanceOfFetchingPlatformDict</key>
-			<dict>
-				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
-				<dict>
-					<key>baselineAverage</key>
-					<real>0.323000</real>
-					<key>baselineIntegrationDisplayName</key>
-					<string>Local Baseline</string>
-				</dict>
-			</dict>
-		</dict>
-	</dict>
-</dict>
-</plist>
diff --git a/Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/Info.plist b/Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/Info.plist
deleted file mode 100644
index e00e9d745..000000000
--- a/Snowplow.xcodeproj/xcshareddata/xcbaselines/752DABD321CC38560065F874.xcbaseline/Info.plist
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>runDestinationsByUUID</key>
-	<dict>
-		<key>DF6688A4-3B86-4BD9-9AA9-38554F93D718</key>
-		<dict>
-			<key>localComputer</key>
-			<dict>
-				<key>busSpeedInMHz</key>
-				<integer>100</integer>
-				<key>cpuCount</key>
-				<integer>1</integer>
-				<key>cpuKind</key>
-				<string>Unknown</string>
-				<key>cpuSpeedInMHz</key>
-				<integer>2400</integer>
-				<key>logicalCPUCoresPerPackage</key>
-				<integer>8</integer>
-				<key>modelCode</key>
-				<string>MacBookPro17,1</string>
-				<key>physicalCPUCoresPerPackage</key>
-				<integer>8</integer>
-				<key>platformIdentifier</key>
-				<string>com.apple.platform.macosx</string>
-			</dict>
-			<key>targetArchitecture</key>
-			<string>x86_64</string>
-			<key>targetDevice</key>
-			<dict>
-				<key>modelCode</key>
-				<string>iPhone14,5</string>
-				<key>platformIdentifier</key>
-				<string>com.apple.platform.iphonesimulator</string>
-			</dict>
-		</dict>
-	</dict>
-</dict>
-</plist>
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme
deleted file mode 100644
index dc20d4299..000000000
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS-Static.xcscheme
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1410"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "75F9C5C721FA2E0F00A5B8FC"
-               BuildableName = "libSnowplow-iOS-Static.a"
-               BlueprintName = "Snowplow-iOS-Static"
-               ReferencedContainer = "container:Snowplow.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "75F9C5C721FA2E0F00A5B8FC"
-            BuildableName = "libSnowplow-iOS-Static.a"
-            BlueprintName = "Snowplow-iOS-Static"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme
deleted file mode 100644
index 5e78a95d4..000000000
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-iOS.xcscheme
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1410"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "752DABCB21CC38550065F874"
-               BuildableName = "SnowplowTracker.framework"
-               BlueprintName = "Snowplow-iOS"
-               ReferencedContainer = "container:Snowplow.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      codeCoverageEnabled = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABCB21CC38550065F874"
-            BuildableName = "SnowplowTracker.framework"
-            BlueprintName = "Snowplow-iOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "752DABD321CC38560065F874"
-               BuildableName = "Tests.xctest"
-               BlueprintName = "Tests"
-               ReferencedContainer = "container:Snowplow.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABCB21CC38550065F874"
-            BuildableName = "SnowplowTracker.framework"
-            BlueprintName = "Snowplow-iOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABCB21CC38550065F874"
-            BuildableName = "SnowplowTracker.framework"
-            BlueprintName = "Snowplow-iOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme
deleted file mode 100644
index 569f454a8..000000000
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-macOS.xcscheme
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1410"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "752DABF421CC3B380065F874"
-               BuildableName = "Snowplow_macOS.framework"
-               BlueprintName = "Snowplow-macOS"
-               ReferencedContainer = "container:Snowplow.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABF421CC3B380065F874"
-            BuildableName = "Snowplow_macOS.framework"
-            BlueprintName = "Snowplow-macOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "752DABFC21CC3B380065F874"
-               BuildableName = "Snowplow-macOSTests.xctest"
-               BlueprintName = "Snowplow-macOSTests"
-               ReferencedContainer = "container:Snowplow.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABF421CC3B380065F874"
-            BuildableName = "Snowplow_macOS.framework"
-            BlueprintName = "Snowplow-macOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABF421CC3B380065F874"
-            BuildableName = "Snowplow_macOS.framework"
-            BlueprintName = "Snowplow-macOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme b/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme
deleted file mode 100644
index 76ebf923d..000000000
--- a/Snowplow.xcodeproj/xcshareddata/xcschemes/Snowplow-watchOS.xcscheme
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1410"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "752DABE721CC3A090065F874"
-               BuildableName = "SnowplowTracker.framework"
-               BlueprintName = "Snowplow-watchOS"
-               ReferencedContainer = "container:Snowplow.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABE721CC3A090065F874"
-            BuildableName = "SnowplowTracker.framework"
-            BlueprintName = "Snowplow-watchOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "752DABE721CC3A090065F874"
-            BuildableName = "SnowplowTracker.framework"
-            BlueprintName = "Snowplow-watchOS"
-            ReferencedContainer = "container:Snowplow.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec
index 8fa8d93ac..1e550d69a 100644
--- a/SnowplowTracker.podspec
+++ b/SnowplowTracker.podspec
@@ -16,8 +16,8 @@ Pod::Spec.new do |s|
     s.swift_version = '5.0'
     s.ios.deployment_target = '11.0'
     s.osx.deployment_target = '10.13'
-    s.tvos.deployment_target = '11.0'
-    s.watchos.deployment_target = '4.0'
+    s.tvos.deployment_target = '12.0'
+    s.watchos.deployment_target = '6.0'
   
     s.source_files = 'Sources/**/*.swift'
   
diff --git a/Sources/Core/Subject/PlatformContext.swift b/Sources/Core/Subject/PlatformContext.swift
index 7e40816dd..29e9d3445 100644
--- a/Sources/Core/Subject/PlatformContext.swift
+++ b/Sources/Core/Subject/PlatformContext.swift
@@ -28,36 +28,19 @@ import UIKit
 /// Manages a dictionary (Payload) with platform context. Some properties for mobile platforms are updated on fetch in set intervals.
 class PlatformContext {
     private var platformDict: Payload = Payload()
-    private var mobileDictUpdateFrequency: TimeInterval = 0.0
-    private var networkDictUpdateFrequency: TimeInterval = 0.0
+    private var mobileDictUpdateFrequency: TimeInterval = 0.1
+    private var networkDictUpdateFrequency: TimeInterval = 10.0
     private var lastUpdatedEphemeralMobileDict: TimeInterval = 0.0
     private var lastUpdatedEphemeralNetworkDict: TimeInterval = 0.0
     private var deviceInfoMonitor: DeviceInfoMonitor
 
-    /// Initializes a newly allocated PlatformContext object with default update frequency
-    /// - Returns: a PlatformContext object
-    convenience init() {
-        self.init(mobileDictUpdateFrequency: 0.1, networkDictUpdateFrequency: 10.0)
-    }
-
-    /// Initializes a newly allocated PlatformContext object with custom update frequency for mobile and network properties
-    /// - Parameters:
-    ///   - mobileDictUpdateFrequency: Minimal gap between subsequent updates of mobile platform information
-    ///   - networkDictUpdateFrequency: Minimal gap between subsequent updates of network platform information
-    /// - Returns: a PlatformContext object
-    convenience init(mobileDictUpdateFrequency: TimeInterval, networkDictUpdateFrequency: TimeInterval) {
-        self.init(mobileDictUpdateFrequency: mobileDictUpdateFrequency,
-                  networkDictUpdateFrequency: networkDictUpdateFrequency,
-                  deviceInfoMonitor: DeviceInfoMonitor())
-    }
-
     /// Initializes a newly allocated PlatformContext object with custom update frequency for mobile and network properties and a custom device info monitor
     /// - Parameters:
     ///   - mobileDictUpdateFrequency: Minimal gap between subsequent updates of mobile platform information
     ///   - networkDictUpdateFrequency: Minimal gap between subsequent updates of network platform information
     ///   - deviceInfoMonitor: Device monitor for fetching platform information
     /// - Returns: a PlatformContext object
-    init(mobileDictUpdateFrequency: TimeInterval, networkDictUpdateFrequency: TimeInterval, deviceInfoMonitor: DeviceInfoMonitor) {
+    init(mobileDictUpdateFrequency: TimeInterval = 0.1, networkDictUpdateFrequency: TimeInterval = 10.0, deviceInfoMonitor: DeviceInfoMonitor = DeviceInfoMonitor()) {
         self.mobileDictUpdateFrequency = mobileDictUpdateFrequency
         self.networkDictUpdateFrequency = networkDictUpdateFrequency
         self.deviceInfoMonitor = deviceInfoMonitor
diff --git a/Sources/Snowplow/Payload/SelfDescribingJson.swift b/Sources/Snowplow/Payload/SelfDescribingJson.swift
index a453dcfea..b8140d100 100644
--- a/Sources/Snowplow/Payload/SelfDescribingJson.swift
+++ b/Sources/Snowplow/Payload/SelfDescribingJson.swift
@@ -93,7 +93,7 @@ public class SelfDescribingJson: NSObject {
     /// - Returns: An SPSelfDescribingJson.
     @objc
     public convenience init(schema: String, andPayload data: Payload) {
-        self.init(schema: schema, andData: data.dictionary as? NSObject)
+        self.init(schema: schema, andData: data.dictionary as NSObject?)
     }
 
     /// Initializes a newly allocated SPSelfDescribingJson.
@@ -103,7 +103,7 @@ public class SelfDescribingJson: NSObject {
     /// - Returns: An SPSelfDescribingJson.
     @objc
     public convenience init(schema: String, andSelfDescribingJson data: SelfDescribingJson) {
-        self.init(schema: schema, andData: data.dictionary as? NSObject)
+        self.init(schema: schema, andData: data.dictionary as NSObject?)
     }
 
     /// Sets the data field of the self-describing JSON.
@@ -117,13 +117,13 @@ public class SelfDescribingJson: NSObject {
     /// - Parameter data: An SPPayload to be nested into the data.
     @objc
     public func setData(withPayload data: Payload) {
-        return setData(withObject: data.dictionary as? NSObject)
+        return setData(withObject: data.dictionary as NSObject?)
     }
 
     /// Sets the data field of the self-describing JSON.
     /// - Parameter data: A self-describing JSON to be nested into the data.
     @objc
     public func setData(withSelfDescribingJson data: SelfDescribingJson) {
-        return setData(withObject: data.dictionary as? NSObject)
+        return setData(withObject: data.dictionary as NSObject?)
     }
 }
diff --git a/Sources/Snowplow/Tracker/SessionState.swift b/Sources/Snowplow/Tracker/SessionState.swift
index 6b8197ceb..e1e462b55 100644
--- a/Sources/Snowplow/Tracker/SessionState.swift
+++ b/Sources/Snowplow/Tracker/SessionState.swift
@@ -45,10 +45,10 @@ public class SessionState: NSObject, State {
 
     class func buildSessionDictionary(withFirstEventId firstEventId: String?, firstEventTimestamp: String?, currentSessionId: String, previousSessionId: String?, sessionIndex: Int, userId: String, storage: String) -> [String : NSObject] {
         var dictionary: [String : NSObject] = [:]
-        dictionary[kSPSessionPreviousId] = previousSessionId as? NSObject ?? NSNull()
+        dictionary[kSPSessionPreviousId] = previousSessionId as NSObject? ?? NSNull()
         dictionary[kSPSessionId] = currentSessionId as NSObject
-        dictionary[kSPSessionFirstEventId] = firstEventId as? NSObject
-        dictionary[kSPSessionFirstEventTimestamp] = firstEventTimestamp as? NSObject
+        dictionary[kSPSessionFirstEventId] = firstEventId as NSObject?
+        dictionary[kSPSessionFirstEventTimestamp] = firstEventTimestamp as NSObject?
         dictionary[kSPSessionIndex] = NSNumber(value: sessionIndex)
         dictionary[kSPSessionStorage] = storage as NSObject
         dictionary[kSPSessionUserId] = userId as NSObject
diff --git a/Tests/Configurations/TestRemoteConfiguration.swift b/Tests/Configurations/TestRemoteConfiguration.swift
index 11ac82aba..f8eb16cb3 100644
--- a/Tests/Configurations/TestRemoteConfiguration.swift
+++ b/Tests/Configurations/TestRemoteConfiguration.swift
@@ -19,8 +19,8 @@
 //  License: Apache License Version 2.0
 //
 
-import Nocilla
 import XCTest
+import Mocker
 @testable import SnowplowTracker
 
 class TestRemoteConfiguration: XCTestCase {
@@ -73,17 +73,15 @@ class TestRemoteConfiguration: XCTestCase {
         XCTAssertEqual("testUserId", subjectConfiguration?.userId)
     }
 
+#if !os(watchOS) // Mocker seems not to currently work on watchOS
     func testDownloadConfiguration() {
         let endpoint = "https://fake-snowplow.io/config.json"
 
-        LSNocilla.sharedInstance().start()
-        _ = stubRequest("GET", endpoint as NSString)?
-            .andReturn(200)?
-            .withHeaders(
-                [ "Content-Type": "application/json" ]
-            )?
-            .withBody(
-                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}" as NSString)
+        let mock = Mock(url: URL(string: endpoint)!, dataType: .json, statusCode: 200, data: [
+            .get: "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}".data(using: .utf8)!
+        ])
+        mock.register()
+        
         let expectation = XCTestExpectation()
 
         let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
@@ -95,9 +93,10 @@ class TestRemoteConfiguration: XCTestCase {
         })
 
         wait(for: [expectation], timeout: 10)
-        LSNocilla.sharedInstance().stop()
     }
+#endif
 
+#if os(iOS) || os(macOS)
     func testCache() {
         let bundle = ConfigurationBundle(namespace: "namespace", networkConfiguration: NetworkConfiguration(endpoint: "endpoint"))
         let expected = FetchedConfigurationBundle(schema: "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0", configurationVersion: 12)
@@ -122,6 +121,7 @@ class TestRemoteConfiguration: XCTestCase {
         XCTAssertEqual(expectedBundle.networkConfiguration?.endpoint, configBundle?.networkConfiguration?.endpoint)
         XCTAssertNil(configBundle?.trackerConfiguration)
     }
+#endif
 
     func testProvider_notDownloading_fails() {
         // prepare test
@@ -129,8 +129,11 @@ class TestRemoteConfiguration: XCTestCase {
         let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
         let cache = ConfigurationCache(remoteConfiguration: remoteConfig)
         cache.clear()
-        LSNocilla.sharedInstance().start()
-        _ = stubRequest("GET", endpoint as NSString).andReturn(404)
+        
+        // mock endpoint
+        let mock = Mock(url: URL(string: endpoint)!, dataType: .json, statusCode: 404, data: [.get: Data()])
+        mock.register()
+        
         // test
         let expectation = XCTestExpectation()
         let provider = ConfigurationProvider(remoteConfiguration: remoteConfig)
@@ -139,9 +142,6 @@ class TestRemoteConfiguration: XCTestCase {
         })
         let result = XCTWaiter.wait(for: [expectation], timeout: 5)
         XCTAssertEqual(XCTWaiter.Result.timedOut, result)
-
-        // close test
-        LSNocilla.sharedInstance().stop()
     }
 
     func testProvider_downloadOfWrongSchema_fails() {
@@ -150,14 +150,11 @@ class TestRemoteConfiguration: XCTestCase {
         let remoteConfig = RemoteConfiguration(endpoint: endpoint, method: .get)
         let cache = ConfigurationCache(remoteConfiguration: remoteConfig)
         cache.clear()
-        LSNocilla.sharedInstance().start()
-        _ = stubRequest("GET", endpoint as NSString)?
-            .andReturn(200)?
-            .withHeaders([
-                "Content-Type": "application/json"
-            ])?
-            .withBody(
-                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/2-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}" as NSString)
+
+        let mock = Mock(url: URL(string: endpoint)!, dataType: .json, statusCode: 200, data: [
+            .get: "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/2-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}".data(using: .utf8)!
+        ])
+        mock.register()
         
         // test
         let expectation = XCTestExpectation()
@@ -167,11 +164,9 @@ class TestRemoteConfiguration: XCTestCase {
         })
         let result = XCTWaiter.wait(for: [expectation], timeout: 5)
         XCTAssertEqual(XCTWaiter.Result.timedOut, result)
-
-        // close test
-        LSNocilla.sharedInstance().stop()
     }
 
+#if os(iOS) || os(macOS)
     func testProvider_downloadSameConfigVersionThanCached_dontUpdate() {
         // prepare test
         let endpoint = "https://fake-snowplow.io/config.json"
@@ -185,14 +180,10 @@ class TestRemoteConfiguration: XCTestCase {
         cache.write(cached)
         Thread.sleep(forTimeInterval: 5) // wait to write on cache
 
-        LSNocilla.sharedInstance().start()
-        _ = stubRequest("GET", endpoint as NSString)?
-            .andReturn(200)?
-            .withHeaders([
-                "Content-Type": "application/json"
-            ] as [NSString: NSString])?
-            .withBody(
-                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}" as NSString)
+        let mock = Mock(url: URL(string: endpoint)!, dataType: .json, statusCode: 200, data: [
+            .get: "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}".data(using: .utf8)!
+        ])
+        mock.register()
 
         // test
         let expectation = XCTestExpectation()
@@ -210,9 +201,6 @@ class TestRemoteConfiguration: XCTestCase {
         let result = XCTWaiter.wait(for: [expectation], timeout: 5)
         XCTAssertEqual(XCTWaiter.Result.timedOut, result)
         XCTAssertEqual(1, i)
-
-        // close test
-        LSNocilla.sharedInstance().stop()
     }
 
     func testProvider_downloadHigherConfigVersionThanCached_doUpdate() {
@@ -227,15 +215,11 @@ class TestRemoteConfiguration: XCTestCase {
         cached.configurationBundle = [bundle]
         cache.write(cached)
         Thread.sleep(forTimeInterval: 5) // wait to write on cache
-
-        LSNocilla.sharedInstance().start()
-        _ = stubRequest("GET", endpoint as NSString)?
-            .andReturn(200)?
-            .withHeaders([
-                "Content-Type": "application/json"
-            ] as [NSString : NSString])?
-            .withBody(
-                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}" as NSString)
+        
+        let mock = Mock(url: URL(string: endpoint)!, dataType: .json, statusCode: 200, data: [
+            .get: "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}".data(using: .utf8)!
+        ])
+        mock.register()
 
         // test
         let expectation = XCTestExpectation()
@@ -255,9 +239,6 @@ class TestRemoteConfiguration: XCTestCase {
         let result = XCTWaiter.wait(for: [expectation], timeout: 5)
         XCTAssertEqual(XCTWaiter.Result.timedOut, result)
         XCTAssertEqual(2, i)
-
-        // close test
-        LSNocilla.sharedInstance().stop()
     }
 
     func testProvider_justRefresh_downloadSameConfigVersionThanCached_dontUpdate() {
@@ -273,15 +254,10 @@ class TestRemoteConfiguration: XCTestCase {
         cache.write(cached)
         Thread.sleep(forTimeInterval: 5) // wait to write on cache
         
-        // stub request for configuration (return version 1)
-        LSNocilla.sharedInstance().start()
-        _ = stubRequest("GET", endpoint as NSString)?
-            .andReturn(200)?
-            .withHeaders([
-                "Content-Type": "application/json"
-            ] as [NSString : NSString])?
-            .withBody(
-                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}" as NSString)
+        let mock = Mock(url: URL(string: endpoint)!, dataType: .json, statusCode: 200, data: [
+            .get: "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}".data(using: .utf8)!
+        ])
+        mock.register()
 
         // test
         let provider = ConfigurationProvider(remoteConfiguration: remoteConfig)
@@ -298,9 +274,6 @@ class TestRemoteConfiguration: XCTestCase {
         })
         let result = XCTWaiter.wait(for: [expectation], timeout: 5)
         XCTAssertEqual(XCTWaiter.Result.timedOut, result)
-
-        // close test
-        LSNocilla.sharedInstance().stop()
     }
 
     func testDoesntUseCachedConfigurationIfDifferentRemoteEndpoint() {
@@ -319,14 +292,10 @@ class TestRemoteConfiguration: XCTestCase {
         Thread.sleep(forTimeInterval: 5) // wait to write on cache
 
         // stub request for configuration (return version 1)
-        LSNocilla.sharedInstance().start()
-        _ = stubRequest("GET", endpoint as NSString)?
-            .andReturn(200)?
-            .withHeaders([
-                "Content-Type": "application/json"
-            ] as [NSString : NSString])?
-            .withBody(
-                "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}" as NSString)
+        let mock = Mock(url: URL(string: endpoint)!, dataType: .json, statusCode: 200, data: [
+            .get: "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}".data(using: .utf8)!
+        ])
+        mock.register()
 
         // initialize tracker with remote config
         let expectation = XCTestExpectation()
@@ -339,9 +308,10 @@ class TestRemoteConfiguration: XCTestCase {
         })
 
         wait(for: [expectation], timeout: 10)
-        LSNocilla.sharedInstance().stop()
     }
     
+#endif
+    
     // TODO: Replace LSNocilla as it's unreliable and unsupported. It causes this test failure.
     /*
     - (void)testConfigurationProvider_justRefresh_downloadHigherConfigVersionThanCached_doUpdate {
diff --git a/Tests/TestGeneratedJsons.swift b/Tests/TestGeneratedJsons.swift
deleted file mode 100644
index 850cd932a..000000000
--- a/Tests/TestGeneratedJsons.swift
+++ /dev/null
@@ -1,390 +0,0 @@
-//
-//  TestGeneratedJsons.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
-
-import XCTest
-@testable import SnowplowTracker
-
-let IGLU_PATH = "http://raw.githubusercontent.com/snowplow/iglu-central/master/schemas/"
-
-class TestGeneratedJsons: XCTestCase {
-    private var validator: IGLUClient?
-
-    override func setUp() {
-        super.setUp()
-        validator = IGLUClient(jsonString: getJSONAsString(withFilePath: "iglu_resolver.json"), andBundles: [Bundle(for: TestGeneratedJsons.self)])
-    }
-
-    override func tearDown() {
-        validator = nil
-        super.tearDown()
-    }
-
-    func testScreenContextJson() {
-        let stateMachine = ScreenStateMachine()
-        let fakeEvent = TrackerEvent(event: Structured(category: "fake", action: "fake"), state: nil)
-        let screenState = ScreenState(name: "name", type: "type", screenId: nil, transitionType: "transition", topViewControllerClassName: "topVCname", viewControllerClassName: "VCname")
-        let entities = stateMachine.entities(from: fakeEvent, state: screenState)
-        let screenContext = entities?.first
-        XCTAssertNotNil(screenContext)
-        XCTAssertTrue(validator!.validateJson(screenContext?.dictionary))
-    }
-
-    // TODO: this test fails for reasons I don't understand
-//    func testClientSessionContextJson() {
-//        let session = Session(foregroundTimeout: 1800, andBackgroundTimeout: 1800)
-//        let data = session.getDictWithEventId(Utilities.getUUIDString(), eventTimestamp: 1654496481346, userAnonymisation: false)
-//        let json = SelfDescribingJson(schema: kSPSessionContextSchema, andDictionary: data!).getAsDictionary()
-//        XCTAssertTrue(validator!.validateJson(json))
-//    }
-
-    //#pragma clang diagnostic push
-    //#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-    func testPlatformContextJson() {
-        let subject = Subject(platformContext: true, andGeoContext: true)
-        let data = subject.getPlatformDict(withUserAnonymisation: false)?.dictionary
-        var json: [String : NSObject]?
-        #if os(iOS)
-        json = SelfDescribingJson(schema: kSPMobileContextSchema, andDictionary: data!).dictionary
-        #else
-        json = SelfDescribingJson(schema: kSPDesktopContextSchema, andDictionary: data!).dictionary
-        #endif
-        XCTAssertTrue(validator!.validateJson(json))
-    }
-
-    func testGeoContextJson() {
-        let subject = Subject(platformContext: false, andGeoContext: true)
-        subject.geoLongitude = NSNumber(value: 5)
-        subject.geoLatitude = NSNumber(value: 89.2)
-        subject.geoTimestamp = NSNumber(value: 5)
-        subject.geoLatitudeLongitudeAccuracy = NSNumber(value: 5.5)
-        subject.geoSpeed = NSNumber(value: 6.2)
-        subject.geoBearing = NSNumber(value: 82.3)
-        subject.geoAltitude = NSNumber(value: 62.3)
-        subject.geoAltitudeAccuracy = NSNumber(value: 16.3)
-        let data = subject.getGeoLocationDict()
-        let json = SelfDescribingJson(schema: kSPGeoContextSchema, andDictionary: data!).dictionary
-        XCTAssertTrue(validator!.validateJson(json))
-    }
-
-    //#pragma clang diagnostic pop
-
-    func testGdprContextJson() {
-        let gdpr = GDPRContext(
-            basis: .consent,
-            documentId: "id",
-            documentVersion: "version",
-            documentDescription: "description")
-        XCTAssertTrue(validator!.validateJson(gdpr.context.dictionary))
-    }
-
-    func testStructuredEventPayloadJson() {
-        let tracker = getTracker("acme.fake.url")
-        tracker.base64Encoded = false
-        let event = Structured(category: "DemoCategory", action: "DemoAction")
-        event.label = "DemoLabel"
-        event.property = "DemoProperty"
-        event.value = NSNumber(value: 5)
-
-        // Check that the final payload passes validation
-        let trackerEvent = TrackerEvent(event: event, state: nil)
-        let data = tracker.payload(with: trackerEvent).dictionary
-
-        let dataArray = [data] as NSObject
-        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
-
-        XCTAssertTrue(validator!.validateJson(json))
-    }
-
-    func testUnstructuredEventPayloadJson() {
-        let tracker = getTracker("acme.fake.url")
-        tracker.base64Encoded = false
-        var input: [String : NSObject] = [:]
-        input["level"] = NSNumber(value: 23)
-        input["score"] = NSNumber(value: 56473)
-        let sdj = SelfDescribingJson(
-            schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
-            andDictionary: input)
-        let event = SelfDescribing(eventData: sdj)
-
-        // Check that the final payload passes validation
-        let trackerEvent = TrackerEvent(event: event, state: nil)
-        let data = tracker.payload(with: trackerEvent).dictionary
-
-        let dataArray = [data] as NSObject
-        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
-
-        XCTAssertTrue(validator!.validateJson(json))
-
-        // Check that the nested unstructured event passes validation
-        let ue_pr = data?["ue_pr"] as? String
-        var unstructDictionary: [AnyHashable : Any]? = nil
-        do {
-            if let aData = ue_pr?.data(using: .utf8) {
-                unstructDictionary = try JSONSerialization.jsonObject(with: aData, options: []) as? [AnyHashable : Any]
-            }
-        } catch {
-        }
-
-        XCTAssertTrue(validator!.validateJson(unstructDictionary))
-    }
-
-    func testSelfDescribingEventPayloadJson() {
-        let tracker = getTracker("acme.fake.url")
-        tracker.base64Encoded = false
-        var input: [String : NSObject] = [:]
-        input["level"] = NSNumber(value: 23)
-        input["score"] = NSNumber(value: 56473)
-        let sdj = SelfDescribingJson(
-            schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
-            andDictionary: input)
-        let event = SelfDescribing(eventData: sdj)
-
-        // Check that the final payload passes validation
-        let trackerEvent = TrackerEvent(event: event, state: nil)
-        let data = tracker.payload(with: trackerEvent).dictionary
-
-        let dataArray = [data] as NSObject
-        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
-
-        XCTAssertTrue(validator!.validateJson(json))
-
-        // Check that the nested unstructured event passes validation
-        let ue_pr = data?["ue_pr"] as? String
-        var unstructDictionary: [AnyHashable : Any]? = nil
-        do {
-            if let aData = ue_pr?.data(using: .utf8) {
-                unstructDictionary = try JSONSerialization.jsonObject(with: aData, options: []) as? [AnyHashable : Any]
-            }
-        } catch {
-        }
-
-        XCTAssertTrue(validator!.validateJson(unstructDictionary))
-    }
-
-    func testConsentWithdrawnEventPayloadJson() {
-        let event = ConsentWithdrawn()
-        event.documentDescription = "Description"
-        event.documentId = "1234"
-        event.version = "10"
-        event.all = false
-        event.name = "Name"
-
-        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testConsentDocumentEventPayloadJson() {
-        let event = ConsentDocument(documentId: "1234", version: "10")
-        event.documentDescription = "Description"
-        event.name = "Name"
-
-        let sdj = event.payload.dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testConsentGrantedEventPayloadJson() {
-        let event = ConsentGranted(expiry: "2012-04-23T18:25:43.511Z", documentId: "1234", version: "10")
-        event.documentDescription = "Description"
-        event.name = "Name"
-
-        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testPageViewEventPayloadJson() {
-        let tracker = getTracker("acme.fake.url")
-        let event = PageView(pageUrl: "DemoPageUrl")
-        event.pageTitle = "DemoPageTitle"
-        event.referrer = "DemoPageReferrer"
-
-        // Check that the final payload passes validation
-        let trackerEvent = TrackerEvent(event: event, state: nil)
-        let data = tracker.payload(with: trackerEvent).dictionary
-
-        let dataArray = [data] as NSObject
-        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
-
-        XCTAssertTrue(validator!.validateJson(json))
-    }
-
-    func testEcommerceEventPayloadJson() {
-        let tracker = getTracker("acme.fake.url")
-
-        let transactionID = "6a8078be"
-        var itemArray: [EcommerceItem] = []
-        let item = EcommerceItem(sku: "DemoItemSku", price: 0.75, quantity: 1)
-        item.name = "DemoItemName"
-        item.category = "DemoItemCategory"
-        item.currency = "USD"
-
-        itemArray.append(item)
-        let event = Ecommerce(orderId: transactionID, totalValue: 350, items: itemArray)
-        event.affiliation = "DemoTranAffiliation"
-        // TODO: incompatible properties with ObjC
-        //    event.taxValue = 10;
-        //    event.shipping = 15;
-        event.city = "Boston"
-        event.state = "Massachusetts"
-        event.country = "USA"
-        event.currency = "USD"
-
-        // Check that the main payload passes validation
-        let trackerEvent = TrackerEvent(event: event, state: nil)
-        var data = tracker.payload(with: trackerEvent).dictionary
-
-        var dataArray = [data] as NSObject
-        var json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
-
-        XCTAssertTrue(validator!.validateJson(json))
-
-        // Check that the item payload passes validation
-        data = tracker.payload(with: trackerEvent).dictionary
-
-        dataArray = [data] as NSObject
-        json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
-
-        XCTAssertTrue(validator!.validateJson(json))
-    }
-
-    func testTimingEventJson() {
-        let event = Timing(category: "DemoTimingCategory", variable: "DemoTimingVariable", timing: 5)
-        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testScreenViewEventJson() {
-        let event = ScreenView(name: "DemoScreenName", screenId: UUID())
-        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testPushNotificationEventJson() {
-        var attachments: [AnyHashable] = []
-        attachments.append(
-            [
-                kSPPnAttachmentId: "identifier",
-                kSPPnAttachmentUrl: "url",
-                kSPPnAttachmentType: "type"
-            ])
-        
-        let userInfo = [
-            "aps": [
-                "alert": [
-                    "title": "test title",
-                    "body": "test",
-                    "loc-key": "test key"
-                ],
-                "content-available": NSNumber(value: 0)
-            ] as NSObject
-        ]
-        
-        let content = NotificationContent(title: "title", body: "body", badge: NSNumber(value: 5))
-        content.subtitle = "subtitle"
-        content.sound = "sound"
-        content.launchImageName = "launchImageName"
-        content.userInfo = userInfo
-
-        let event = PushNotification(date: "date", action: "action", trigger: "PUSH", category: "category", thread: "thread", notification: content)
-
-        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testMessageNotificationEventJson() {
-        let userInfo = [
-            "aps": [
-                "alert": [
-                    "title": "test title",
-                    "body": "test",
-                    "loc-key": "test key"
-                ],
-                "content-available": NSNumber(value: 0)
-            ] as NSObject
-        ]
-        let event = MessageNotification.messageNotification(userInfo: userInfo, defaultTitle: nil, defaultBody: nil)
-        let sdj = SelfDescribingJson(schema: event!.schema, andDictionary: event!.payload).dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testApplicationInstallJson() {
-        let installEvent = SelfDescribingJson(schema: kSPApplicationInstallSchema, andDictionary: [String : NSObject]())
-        let json = installEvent.dictionary
-        XCTAssertTrue(validator!.validateJson(json))
-    }
-
-    func testApplicationContextJson() {
-        let json = Utilities.getApplicationContext(withVersion: "testversion", andBuild: "testbuild")
-        XCTAssertTrue(validator!.validateJson(json.dictionary))
-    }
-
-    func testErrorEventJson() {
-        let event = SNOWError(message: "some error message")
-        event.name = "some exception name"
-        event.stackTrace = "some stack trace"
-        let sdj = SelfDescribingJson(schema: event.schema, andDictionary: event.payload).dictionary
-        XCTAssertTrue(validator!.validateJson(sdj))
-    }
-
-    func testFinalEventPayloadJson() {
-        let tracker = getTracker("acme.fake.url")
-        let event = PageView(pageUrl: "DemoPageUrl")
-        event.pageTitle = "DemoPageTitle"
-        event.referrer = "DemoPageReferrer"
-
-        // Check that the final payload passes validation
-        let trackerEvent = TrackerEvent(event: event, state: nil)
-        let data = tracker.payload(with: trackerEvent).dictionary
-
-        let dataArray = [data] as NSObject
-        let json = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: dataArray).dictionary
-        XCTAssertTrue(validator!.validateJson(json))
-
-        // Check that the nested context json passes validation
-        let contextsJson = data?["co"] as? String
-        var contextDictionary: [AnyHashable : Any]? = nil
-        if let aData = contextsJson?.data(using: .utf8) {
-            contextDictionary = try? JSONSerialization.jsonObject(with: aData, options: []) as? [AnyHashable : Any]
-        }
-        XCTAssertTrue(validator!.validateJson(contextDictionary))
-    }
-
-    func getJSONAsString(withFilePath filePath: String?) -> String? {
-        let path = Bundle(for: TestGeneratedJsons.self).path(forResource: filePath, ofType: nil, inDirectory: "Products")
-        if let data = NSData(contentsOfFile: path ?? "") as Data? {
-            return String(data: data, encoding: .utf8)
-        }
-        return nil
-    }
-
-    func getTracker(_ url: String) -> Tracker {
-        let endpoint = "https://\(url)"
-        let networkConfig = NetworkConfiguration(endpoint: endpoint, method: .post)
-        let trackerConfig = TrackerConfiguration(appId: "anAppId")
-        trackerConfig.platformContext = true
-        trackerConfig.geoLocationContext = true
-        trackerConfig.base64Encoding = false
-        trackerConfig.sessionContext = true
-        let serviceProvider = ServiceProvider(namespace: "aNamespace", network: networkConfig, configurations: [trackerConfig])
-        return serviceProvider.tracker
-    }
-}
diff --git a/Tests/TestNetworkConnection.swift b/Tests/TestNetworkConnection.swift
index 68de1de60..6db11ed64 100644
--- a/Tests/TestNetworkConnection.swift
+++ b/Tests/TestNetworkConnection.swift
@@ -19,7 +19,7 @@
 //  License: Apache License Version 2.0
 //
 
-import Nocilla
+import Mocker
 import XCTest
 @testable import SnowplowTracker
 
@@ -28,21 +28,17 @@ let TEST_URL_ENDPOINT = "acme.test.url.com"
 class TestNetworkConnection: XCTestCase {
     override func setUp() {
         super.setUp()
-        if LSNocilla.sharedInstance().isStarted {
-            LSNocilla.sharedInstance().stop()
-        }
-        LSNocilla.sharedInstance().start()
+        Mocker.removeAll()
     }
 
     override func tearDown() {
         super.tearDown()
-        LSNocilla.sharedInstance().clearStubs()
-        LSNocilla.sharedInstance().stop()
     }
 
+#if !os(watchOS) // Mocker seems not to currently work on watchOS
     func testGetRequestWithSuccess() {
-        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
-        _ = stubRequest("GET", regex).andReturn(200)
+        let endpoint = "https://\(TEST_URL_ENDPOINT)/i"
+        Mock(url: URL(string: endpoint)!, ignoreQuery: true, dataType: .json, statusCode: 200, data: [.get: Data()]).register()
         
         let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .get)
 
@@ -56,10 +52,10 @@ class TestNetworkConnection: XCTestCase {
         XCTAssertTrue(result.isSuccessful)
         XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
     }
-
+    
     func testGetRequestWithNoSuccess() {
-        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
-        _ = stubRequest("GET", regex).andReturn(404)
+        let endpoint = "https://\(TEST_URL_ENDPOINT)/i"
+        Mock(url: URL(string: endpoint)!, ignoreQuery: true, dataType: .json, statusCode: 404, data: [.get: Data()]).register()
 
         let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .get)
         
@@ -73,10 +69,10 @@ class TestNetworkConnection: XCTestCase {
         XCTAssertFalse(result.isSuccessful)
         XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
     }
-
+    
     func testPostRequestWithSuccess() {
-        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
-        _ = stubRequest("POST", regex).andReturn(200)
+        let endpoint = "https://\(TEST_URL_ENDPOINT)/com.snowplowanalytics.snowplow/tp2"
+        Mock(url: URL(string: endpoint)!, ignoreQuery: true, dataType: .json, statusCode: 200, data: [.post: Data()]).register()
 
         let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
         
@@ -90,10 +86,10 @@ class TestNetworkConnection: XCTestCase {
         XCTAssertTrue(result.isSuccessful)
         XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
     }
-
+    
     func testPostRequestWithNoSuccess() {
-        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
-        _ = stubRequest("POST", regex).andReturn(404)
+        let endpoint = "https://\(TEST_URL_ENDPOINT)/com.snowplowanalytics.snowplow/tp2"
+        Mock(url: URL(string: endpoint)!, ignoreQuery: true, dataType: .json, statusCode: 404, data: [.post: Data()]).register()
 
         let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
 
@@ -107,7 +103,8 @@ class TestNetworkConnection: XCTestCase {
         XCTAssertFalse(result.isSuccessful)
         XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
     }
-
+#endif
+    
     func testFreeEndpoint_GetHttpsUrl() {
         let connection = DefaultNetworkConnection(urlString: "acme.test.url.com", httpMethod: .post)
         XCTAssertTrue(connection.urlEndpoint!.absoluteString.hasPrefix("https://acme.test.url.com"))
@@ -128,15 +125,17 @@ class TestNetworkConnection: XCTestCase {
         XCTAssertTrue((connection.urlEndpoint?.absoluteString == "http://acme.test.url.com/i"))
     }
 
+#if !os(watchOS) // Mocker seems not to currently work on watchOS
     func testDoesntAddHeaderWithoutServerAnonymisation() {
-        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
-        _ = stubRequest("POST", regex).withHeader(
-            "SP-Anonymous",
-            "*")?.andReturn(
-            500)
-        _ = stubRequest("POST", regex).andReturn(
-            200)
-
+        let endpoint = "https://\(TEST_URL_ENDPOINT)/com.snowplowanalytics.snowplow/tp2"
+        var mock = Mock(url: URL(string: endpoint)!, ignoreQuery: true, dataType: .json, statusCode: 200, data: [.post: Data()])
+        let requestExpectation = expectation(description: "Checked the request")
+        mock.onRequest = { request, postBodyArguments in
+            XCTAssertNil(request.value(forHTTPHeaderField: "SP-Anonymous"))
+            requestExpectation.fulfill()
+        }
+        mock.register()
+        
         let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
         connection.serverAnonymisation = false
 
@@ -149,15 +148,19 @@ class TestNetworkConnection: XCTestCase {
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
         XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+        wait(for: [requestExpectation], timeout: 2.0)
     }
 
     func testAddsHeaderForServerAnonymisationForPostRequest() {
-        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
-        _ = stubRequest("POST", regex).withHeader(
-            "SP-Anonymous",
-            "*")?.andReturn(
-            200)
-
+        let endpoint = "https://\(TEST_URL_ENDPOINT)/com.snowplowanalytics.snowplow/tp2"
+        var mock = Mock(url: URL(string: endpoint)!, ignoreQuery: true, dataType: .json, statusCode: 200, data: [.post: Data()])
+        let requestExpectation = expectation(description: "Checked the request")
+        mock.onRequest = { request, postBodyArguments in
+            XCTAssertEqual("*", request.value(forHTTPHeaderField: "SP-Anonymous"))
+            requestExpectation.fulfill()
+        }
+        mock.register()
+        
         let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .post)
         connection.serverAnonymisation = true
 
@@ -170,15 +173,19 @@ class TestNetworkConnection: XCTestCase {
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
         XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+        wait(for: [requestExpectation], timeout: 2.0)
     }
 
     func testAddsHeaderForServerAnonymisationForGetRequest() {
-        let regex = try? NSRegularExpression(pattern: "^\("https")://\(TEST_URL_ENDPOINT)/i?(.*?)")
-       _ = stubRequest("GET", regex).withHeader(
-            "SP-Anonymous",
-            "*")?.andReturn(
-            200)
-
+        let endpoint = "https://\(TEST_URL_ENDPOINT)/i"
+        var mock = Mock(url: URL(string: endpoint)!, ignoreQuery: true, dataType: .json, statusCode: 200, data: [.get: Data()])
+        let requestExpectation = expectation(description: "Checked the request")
+        mock.onRequest = { request, postBodyArguments in
+            XCTAssertEqual("*", request.value(forHTTPHeaderField: "SP-Anonymous"))
+            requestExpectation.fulfill()
+        }
+        mock.register()
+        
         let connection = DefaultNetworkConnection(urlString: TEST_URL_ENDPOINT, httpMethod: .get)
         connection.serverAnonymisation = true
 
@@ -191,5 +198,7 @@ class TestNetworkConnection: XCTestCase {
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
         XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+        wait(for: [requestExpectation], timeout: 2.0)
     }
+#endif
 }
diff --git a/Tests/TestPlatformContext.swift b/Tests/TestPlatformContext.swift
index 598f98c37..4a9b338e4 100644
--- a/Tests/TestPlatformContext.swift
+++ b/Tests/TestPlatformContext.swift
@@ -22,21 +22,20 @@
 import XCTest
 @testable import SnowplowTracker
 
+#if os(iOS)
 class TestPlatformContext: XCTestCase {
     func testContainsPlatformInfo() {
-        let context = PlatformContext()
+        let context = PlatformContext(deviceInfoMonitor: MockDeviceInfoMonitor())
         let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary
         XCTAssertNotNil(platformDict)
         XCTAssertNotNil(platformDict)
     }
 
     func testContainsMobileInfo() {
-        #if os(iOS)
-        let context = PlatformContext()
+        let context = PlatformContext(deviceInfoMonitor: MockDeviceInfoMonitor())
         let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary
         XCTAssertNotNil(platformDict)
         XCTAssertNotNil(platformDict)
-        #endif
     }
 
     func testAddsAllMockedInfo() {
@@ -56,15 +55,9 @@ class TestPlatformContext: XCTestCase {
         XCTAssertEqual("wifi" as NSObject, platformDict[kSPMobileNetworkType])
         XCTAssertEqual(NSNumber(value: 20), platformDict[kSPMobileBatteryLevel])
         XCTAssertEqual("charging" as NSObject, platformDict[kSPMobileBatteryState])
-        XCTAssertEqual(NSNumber(value: false), platformDict[kSPMobileLowPowerMode])
-        XCTAssertEqual(NSNumber(value: 100000), platformDict[kSPMobilePhysicalMemory])
-        XCTAssertEqual(NSNumber(value: 1000), platformDict[kSPMobileAppAvailableMemory])
-        XCTAssertEqual(NSNumber(value: 9000), platformDict[kSPMobileAvailableStorage])
-        XCTAssertEqual(NSNumber(value: 900000), platformDict[kSPMobileTotalStorage])
     }
 
     func testUpdatesMobileInfo() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
@@ -75,11 +68,9 @@ class TestPlatformContext: XCTestCase {
         _ = context.fetchPlatformDict(withUserAnonymisation: false)
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("appAvailableMemory"))
-        #endif
     }
 
     func testDoesntUpdateMobileInfoWithinUpdateWindow() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 1000, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
@@ -90,11 +81,9 @@ class TestPlatformContext: XCTestCase {
         _ = context.fetchPlatformDict(withUserAnonymisation: false)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
-        #endif
     }
 
     func testUpdatesNetworkInfo() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 1, networkDictUpdateFrequency: 0, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
@@ -105,11 +94,9 @@ class TestPlatformContext: XCTestCase {
         _ = context.fetchPlatformDict(withUserAnonymisation: false)
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("networkType"))
-        #endif
     }
 
     func testDoesntUpdateNetworkInfoWithinUpdateWindow() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1000, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
@@ -120,11 +107,9 @@ class TestPlatformContext: XCTestCase {
         _ = context.fetchPlatformDict(withUserAnonymisation: false)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
-        #endif
     }
 
     func testDoesntUpdateNonEphemeralInfo() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 0, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
@@ -135,11 +120,9 @@ class TestPlatformContext: XCTestCase {
         _ = context.fetchPlatformDict(withUserAnonymisation: false)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("totalStorage"))
-        #endif
     }
 
     func testDoesntUpdateIdfaAndIdfvIfNotNil() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
@@ -147,11 +130,9 @@ class TestPlatformContext: XCTestCase {
         _ = context.fetchPlatformDict(withUserAnonymisation: false)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfv"))
-        #endif
     }
 
     func testUpdatesIdfaAndIdfvIfNil() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         deviceInfoMonitor.customAppleIdfa = nil
         deviceInfoMonitor.customAppleIdfv = nil
@@ -161,11 +142,9 @@ class TestPlatformContext: XCTestCase {
         _ = context.fetchPlatformDict(withUserAnonymisation: false)
         XCTAssertEqual(2, deviceInfoMonitor.accessCount("appleIdfa"))
         XCTAssertEqual(2, deviceInfoMonitor.accessCount("appleIdfv"))
-        #endif
     }
 
     func testAnonymisesUserIdentifiers() {
-        #if os(iOS)
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         guard let platformDict = context.fetchPlatformDict(withUserAnonymisation: true).dictionary else {
@@ -173,7 +152,6 @@ class TestPlatformContext: XCTestCase {
         }
         XCTAssertNil(platformDict[kSPMobileAppleIdfa])
         XCTAssertNil(platformDict[kSPMobileAppleIdfv])
-        #endif
     }
 
 //    func testPerformanceOfFetchingNetworkDict() {
@@ -195,3 +173,4 @@ class TestPlatformContext: XCTestCase {
 //        })
 //    }
 }
+#endif
diff --git a/Tests/TestSQLiteEventStore.swift b/Tests/TestSQLiteEventStore.swift
index 869ba1dce..0db24fb29 100644
--- a/Tests/TestSQLiteEventStore.swift
+++ b/Tests/TestSQLiteEventStore.swift
@@ -19,6 +19,8 @@
 //  License: Apache License Version 2.0
 //
 
+#if os(iOS) || os(macOS)
+
 import XCTest
 @testable import SnowplowTracker
 
@@ -152,3 +154,5 @@ class TestSQLiteEventStore: XCTestCase {
         XCTAssertEqual(2, eventStore2.count())
     }
 }
+
+#endif
diff --git a/Tests/Tests-bridging-header.h b/Tests/Tests-bridging-header.h
deleted file mode 100644
index 04eecfe6d..000000000
--- a/Tests/Tests-bridging-header.h
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-//  Tests-bridging-header.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
-//
-//  This program is licensed to you under the Apache License Version 2.0,
-//  and you may not use this file except in compliance with the Apache License
-//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
-//  http://www.apache.org/licenses/LICENSE-2.0.
-//
-//  Unless required by applicable law or agreed to in writing,
-//  software distributed under the Apache License Version 2.0 is distributed on
-//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-//  express or implied. See the Apache License Version 2.0 for the specific
-//  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
-
-#ifndef Tests_bridging_header_h
-#define Tests_bridging_header_h
-
-#import <SnowplowIgluClient/IGLUClient.h>
-
-#endif /* Tests_bridging_header_h */

From db8bc27fe038e575ab3a6427bf02b7649920d56f Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Wed, 7 Dec 2022 11:33:53 +0100
Subject: [PATCH 04/19] Add tests using Micro for payload validation (close
 #736)

PR #738
---
 .github/workflows/build.yml                   |  23 +-
 .slather.yml                                  |   4 -
 Sources/Core/Tracker/StateFuture.swift        |  27 +--
 Sources/Core/Tracker/StateManager.swift       |   2 +-
 Sources/Core/Tracker/TrackerState.swift       |   2 +-
 Sources/Snowplow/Events/SelfDescribing.swift  |   5 +
 .../TestRemoteConfiguration.swift             |   4 +
 .../Integration/TestTrackEventsToMicro.swift  | 199 ++++++++++++++++
 Tests/TestNetworkConnection.swift             |   6 +-
 Tests/Utils/Micro.swift                       | 213 ++++++++++++++++++
 10 files changed, 460 insertions(+), 25 deletions(-)
 delete mode 100644 .slather.yml
 create mode 100644 Tests/Integration/TestTrackEventsToMicro.swift
 create mode 100644 Tests/Utils/Micro.swift

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index efda17635..41bd94af9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -59,6 +59,26 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
 
+      # -- Micro --
+      - name: Cache Micro
+        id: cache-micro
+        uses: actions/cache@v3
+        with:
+          path: micro.jar
+          key: ${{ runner.os }}-micro
+
+      - name: Get micro
+        if: steps.cache-micro.outputs.cache-hit != 'true'
+        run: curl -o micro.jar -L https://github.com/snowplow-incubator/snowplow-micro/releases/download/micro-1.3.4/snowplow-micro-1.3.4.jar
+
+      - name: Run Micro in background
+        run: java -jar micro.jar &
+
+      - name: Wait on Micro endpoint
+        timeout-minutes: 2
+        run: while ! nc -z '0.0.0.0' 9090; do sleep 1; done
+      # -- Micro --
+
       - name: Select Xcode Version
         run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer
 
@@ -68,7 +88,8 @@ jobs:
             -scheme SnowplowTracker \
             -sdk "${{ matrix.sdk }}" \
             -destination "${{ matrix.destination }}" \
-            clean test | xcpretty
+            -quiet \
+            clean test
 
   build_objc_demo_app:
     name: "ObjC demo (iOS ${{ matrix.version.ios }})"
diff --git a/.slather.yml b/.slather.yml
deleted file mode 100644
index 4be050905..000000000
--- a/.slather.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-coverage_service: coveralls
-xcodeproj: Snowplow.xcodeproj
-workspace: Snowplow.xcworkspace
-scheme: Snowplow-iOS
diff --git a/Sources/Core/Tracker/StateFuture.swift b/Sources/Core/Tracker/StateFuture.swift
index 8e20ebf28..91d65c485 100644
--- a/Sources/Core/Tracker/StateFuture.swift
+++ b/Sources/Core/Tracker/StateFuture.swift
@@ -27,19 +27,6 @@ import Foundation
 /// For this reason, the StateFuture can be the head of StateFuture chain which will collapse once the StateFuture
 /// head is asked to get the real state value.
 class StateFuture: NSObject {
-    var state: State? {
-        objc_sync_enter(self)
-        defer { objc_sync_exit(self) }
-        if computedState == nil {
-            if let stateMachine = stateMachine, let event = event {
-                computedState = stateMachine.transition(from: event, state: previousState?.state)
-            }
-            event = nil
-            previousState = nil
-            stateMachine = nil
-        }
-        return computedState
-    }
     private var event: Event?
     private var previousState: StateFuture?
     private var stateMachine: StateMachineProtocol?
@@ -51,4 +38,18 @@ class StateFuture: NSObject {
         self.previousState = previousState
         self.stateMachine = stateMachine
     }
+    
+    func computeState() -> State? {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        if computedState == nil {
+            if let stateMachine = stateMachine, let event = event {
+                computedState = stateMachine.transition(from: event, state: previousState?.computeState())
+                previousState = nil
+                self.event = nil
+                self.stateMachine = nil
+            }
+        }
+        return computedState
+    }
 }
diff --git a/Sources/Core/Tracker/StateManager.swift b/Sources/Core/Tracker/StateManager.swift
index db750dd1e..19e6ad25f 100644
--- a/Sources/Core/Tracker/StateManager.swift
+++ b/Sources/Core/Tracker/StateManager.swift
@@ -100,7 +100,7 @@ class StateManager: NSObject {
                  externally)
                  Remove the early state-computation only when these two problems are fixed.
                  */
-                _ = currentStateFuture.state // Early state-computation
+                _ = currentStateFuture.computeState() // Early state-computation
             }
         }
         return trackerState.snapshot()
diff --git a/Sources/Core/Tracker/TrackerState.swift b/Sources/Core/Tracker/TrackerState.swift
index 245dc6bff..3947c13b5 100644
--- a/Sources/Core/Tracker/TrackerState.swift
+++ b/Sources/Core/Tracker/TrackerState.swift
@@ -58,7 +58,7 @@ class TrackerState: NSObject, TrackerStateSnapshot {
     // Protocol SPTrackerStateSnapshot
 
     func state(withIdentifier stateIdentifier: String) -> State? {
-        return stateFuture(withIdentifier: stateIdentifier)?.state
+        return stateFuture(withIdentifier: stateIdentifier)?.computeState()
     }
 
     func state(withStateMachine stateMachine: StateMachineProtocol) -> State? {
diff --git a/Sources/Snowplow/Events/SelfDescribing.swift b/Sources/Snowplow/Events/SelfDescribing.swift
index 5311ba3fa..b4f80a73b 100644
--- a/Sources/Snowplow/Events/SelfDescribing.swift
+++ b/Sources/Snowplow/Events/SelfDescribing.swift
@@ -59,4 +59,9 @@ public class SelfDescribing: SelfDescribingAbstract {
         self._schema = schema
         self._payload = payload
     }
+    
+    public init(schema: String, payload: [String : String]) {
+        self._schema = schema
+        self._payload = payload as [String : NSObject]
+    }
 }
diff --git a/Tests/Configurations/TestRemoteConfiguration.swift b/Tests/Configurations/TestRemoteConfiguration.swift
index f8eb16cb3..a223de00c 100644
--- a/Tests/Configurations/TestRemoteConfiguration.swift
+++ b/Tests/Configurations/TestRemoteConfiguration.swift
@@ -24,6 +24,10 @@ import Mocker
 @testable import SnowplowTracker
 
 class TestRemoteConfiguration: XCTestCase {
+    override func tearDown() {
+        Mocker.removeAll()
+    }
+    
     func testJSONToConfigurations() {
         let config = """
             {"$schema":"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0","configurationVersion":12,"configurationBundle": [\
diff --git a/Tests/Integration/TestTrackEventsToMicro.swift b/Tests/Integration/TestTrackEventsToMicro.swift
new file mode 100644
index 000000000..7f4195b8e
--- /dev/null
+++ b/Tests/Integration/TestTrackEventsToMicro.swift
@@ -0,0 +1,199 @@
+//
+//  TestWithMicro.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+import SnowplowTracker
+
+class TestTrackEventsToMicro: XCTestCase {
+    var tracker: TrackerController?
+    
+    override func setUp() {
+        tracker = Snowplow.createTracker(namespace: "ns", network: NetworkConfiguration(endpoint: Micro.endpoint))!
+        
+        Micro.setUpMockerIgnores()
+        wait(for: [Micro.reset()], timeout: Micro.timeout)
+    }
+    
+    func testTrackStructuredEvent() {
+        let event = Structured(category: "shop", action: "add-to-basket")
+        event.label = "Add To Basket"
+        event.property = "pcs"
+        event.value = 2.0
+        track(event)
+        
+        wait(for: [
+            Micro.expectCounts(good: 1),
+            Micro.expectPrimitiveEvent() { actual in
+                XCTAssertEqual("shop", actual.se_category)
+                XCTAssertEqual("add-to-basket", actual.se_action)
+                XCTAssertEqual("Add To Basket", actual.se_label)
+                XCTAssertEqual("pcs", actual.se_property)
+                XCTAssertEqual(2.0, actual.se_value)
+            }
+        ], timeout: Micro.timeout)
+    }
+    
+    func testTrackSelfDescribing() {
+        let event = SelfDescribing(
+            schema: "iglu:com.snowplowanalytics.snowplow/screen_view/jsonschema/1-0-0",
+            payload: [
+                "name": "test", "id": "something else"
+            ]
+        )
+        track(event)
+        
+        wait(for: [
+            Micro.expectCounts(good: 1),
+            Micro.expectSelfDescribingEvent() { (actual: ScreenViewExpected) in
+                XCTAssertEqual("test", actual.name)
+                XCTAssertEqual("something else", actual.id)
+            }
+        ], timeout: Micro.timeout)
+    }
+    
+    func testTrackScreenViews() {
+        // track the first screen view
+        track(ScreenView(name: "screen1", screenId: UUID()))
+        wait(for: [Micro.expectCounts(good: 1)], timeout: Micro.timeout)
+        wait(for: [Micro.reset()], timeout: Micro.timeout)
+        
+        // track the second screen view and check reference to previous
+        track(ScreenView(name: "screen2", screenId: UUID()))
+        wait(for: [
+            Micro.expectCounts(good: 1),
+            Micro.expectSelfDescribingEvent() { (actual: ScreenViewExpected) in
+                XCTAssertEqual("screen2", actual.name)
+                XCTAssertEqual("screen1", actual.previousName)
+            }
+        ], timeout: Micro.timeout)
+        wait(for: [Micro.reset()], timeout: Micro.timeout)
+        
+        // track another event and check screen context
+        track(Timing(category: "cat", variable: "var", timing: 10))
+        wait(for: [
+            Micro.expectEventContext(
+                schema: "iglu:com.snowplowanalytics.mobile/screen/jsonschema/1-0-0"
+            ) { (actual: ScreenContextExpected) in
+                XCTAssertEqual("screen2", actual.name)
+            }
+        ], timeout: Micro.timeout)
+    }
+    
+    func testTrackDeepLink() {
+        // track the deep link received event
+        let deepLink = DeepLinkReceived(url: "https://snowplow.io")
+        deepLink.referrer = "https://plowsnow.io"
+        track(deepLink)
+        wait(for: [
+            Micro.expectSelfDescribingEvent() { (actual: DeepLinkExpected) in
+                XCTAssertEqual("https://snowplow.io", actual.url)
+                XCTAssertEqual("https://plowsnow.io", actual.referrer)
+            }
+        ], timeout: Micro.timeout)
+        wait(for: [Micro.reset()], timeout: Micro.timeout)
+        
+        // track a screen view and check references to the deep link
+        track(ScreenView(name: "screen", screenId: UUID()))
+        wait(for: [
+            // deep link info in payload
+            Micro.expectPrimitiveEvent() { actual in
+                XCTAssertEqual("https://snowplow.io", actual.page_url)
+                XCTAssertEqual("https://plowsnow.io", actual.page_referrer)
+            },
+            // deep link info in context entity
+            Micro.expectEventContext(
+                schema: "iglu:com.snowplowanalytics.mobile/deep_link/jsonschema/1-0-0"
+            ) { (actual: DeepLinkExpected) in
+                XCTAssertEqual("https://snowplow.io", actual.url)
+                XCTAssertEqual("https://plowsnow.io", actual.referrer)
+            }
+        ], timeout: Micro.timeout)
+    }
+    
+    func testSessionTracking() {
+        // track the first event
+        track(Structured(category: "cat", action: "act"))
+        var userId: String?, sessionId: String?
+        wait(for: [
+            Micro.expectEventContext(
+                schema: "iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2"
+            ) { (actual: SessionExpected) in
+                userId = actual.userId
+                sessionId = actual.sessionId
+            }
+        ], timeout: Micro.timeout)
+        wait(for: [Micro.reset()], timeout: Micro.timeout)
+        
+        // track the second event in the same session
+        track(Structured(category: "cat", action: "act"))
+        wait(for: [
+            Micro.expectEventContext(
+                schema: "iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2"
+            ) { (actual: SessionExpected) in
+                XCTAssertEqual(userId, actual.userId)
+                XCTAssertEqual(sessionId, actual.sessionId)
+            }
+        ], timeout: Micro.timeout)
+        wait(for: [Micro.reset()], timeout: Micro.timeout)
+        
+        // start a new session and track event
+        tracker!.session!.startNewSession()
+        track(Structured(category: "cat", action: "act"))
+        wait(for: [
+            Micro.expectEventContext(
+                schema: "iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2"
+            ) { (actual: SessionExpected) in
+                XCTAssertEqual(userId, actual.userId)
+                XCTAssertNotEqual(sessionId, actual.sessionId)
+            }
+        ], timeout: Micro.timeout)
+    }
+    
+    private func track(_ event: Event) {
+        _ = tracker!.track(event)
+        tracker!.emitter!.flush()
+    }
+}
+
+struct ScreenViewExpected: Codable {
+    let name: String
+    let id: String
+    let type: String?
+    let previousName: String?
+    let previousId: String?
+    let previousType: String?
+    let transitionType: String?
+}
+
+struct ScreenContextExpected: Codable {
+    let name: String
+    let id: String
+}
+
+struct DeepLinkExpected: Codable {
+    let url: String
+    let referrer: String?
+}
+
+struct SessionExpected: Codable {
+    let sessionId: String
+    let userId: String
+}
diff --git a/Tests/TestNetworkConnection.swift b/Tests/TestNetworkConnection.swift
index 6db11ed64..bb2f563c3 100644
--- a/Tests/TestNetworkConnection.swift
+++ b/Tests/TestNetworkConnection.swift
@@ -26,13 +26,9 @@ import XCTest
 let TEST_URL_ENDPOINT = "acme.test.url.com"
 
 class TestNetworkConnection: XCTestCase {
-    override func setUp() {
-        super.setUp()
-        Mocker.removeAll()
-    }
-
     override func tearDown() {
         super.tearDown()
+        Mocker.removeAll()
     }
 
 #if !os(watchOS) // Mocker seems not to currently work on watchOS
diff --git a/Tests/Utils/Micro.swift b/Tests/Utils/Micro.swift
new file mode 100644
index 000000000..e3963717a
--- /dev/null
+++ b/Tests/Utils/Micro.swift
@@ -0,0 +1,213 @@
+//
+//  Micro.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Michael Hadam
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+import XCTest
+import Mocker
+
+class Micro {
+    
+    static let timeout = 10.0
+    static let retryDelay = 0.5
+    static let maxNumberOfRetries = 20
+    static let endpoint = "http://0.0.0.0:9090"
+    
+    class func setUpMockerIgnores() {
+        Mocker.ignore(URL(string: "\(endpoint)/micro/good")!)
+        Mocker.ignore(URL(string: "\(endpoint)/micro/reset")!)
+        Mocker.ignore(URL(string: "\(endpoint)/micro/all")!)
+        Mocker.ignore(URL(string: "\(endpoint)/com.snowplowanalytics.snowplow/tp2")!)
+        Mocker.ignore(URL(string: "\(endpoint)/i")!)
+    }
+    
+    class func reset() -> XCTestExpectation {
+        let expectation = XCTestExpectation(description: "Reset Micro")
+        let url = URLRequest(url: URL(string: "\(endpoint)/micro/reset")!)
+        let task = URLSession.shared.dataTask(with: url) { data, response, error in
+            if let error = error {
+                XCTFail("Failed to reset Micro: \(error).")
+            } else {
+                _ = expectCounts(good: 0, bad: 0, expectation: expectation)
+            }
+        }
+        task.resume()
+        return expectation
+    }
+    
+    class func expectCounts(
+        good: Int = 0,
+        bad: Int = 0,
+        expectation: XCTestExpectation? = nil,
+        numberOfRetries: Int = 0) -> XCTestExpectation {
+        let expectation = expectation ?? XCTestExpectation(description: "Count of good and bad events")
+
+        let url = URLRequest(url: URL(string: "\(endpoint)/micro/all")!)
+        let task = URLSession.shared.dataTask(with: url) { data, response, error in
+            if let error = error {
+                XCTFail("Failed to request Micro: \(error).")
+            } else if let data = data,
+                      let res = try? JSONDecoder().decode(AllResponse.self, from: data) {
+                if res.good == good && res.bad == bad {
+                    expectation.fulfill()
+                } else if numberOfRetries < maxNumberOfRetries {
+                    DispatchQueue.main.asyncAfter(deadline: .now() + retryDelay) {
+                        _ = expectCounts(good: good,
+                                     bad: bad,
+                                     expectation: expectation,
+                                     numberOfRetries: numberOfRetries + 1)
+                    }
+                } else {
+                    XCTFail("Didn't find the expected event counts in Micro")
+                }
+            } else {
+                XCTFail("Failed to parse response from Micro")
+            }
+        }
+        task.resume()
+        
+        return expectation
+    }
+    
+    class func expectSelfDescribingEvent<T: Codable>(numberOfRetries: Int = 0,
+                                                     completion: @escaping (T)->()) -> XCTestExpectation {
+        return expectEvent() { (event: SelfDescribingResponse<T>) in
+            completion(event.unstruct_event.data.data)
+        }
+    }
+    
+    class func expectPrimitiveEvent(numberOfRetries: Int = 0,
+                                    completion: @escaping (PrimitiveResponse)->()) -> XCTestExpectation {
+        return expectEvent() { (event: PrimitiveResponse) in
+            completion(event)
+        }
+    }
+    
+    class func expectEventContext<T: Codable>(schema: String,
+                                              completion: @escaping (T)->()) -> XCTestExpectation {
+        return expectEvent() { (event: WithContextResponse<T>) in
+            if let entity = event.contexts.data.filter({ $0.schema == schema }).map({ $0.data! }).first {
+                completion(entity)
+            } else {
+                XCTFail("Failed to find the context entity in response")
+            }
+        }
+    }
+    
+    private class func expectEvent<T: Codable>(expectation: XCTestExpectation? = nil,
+                                               numberOfRetries: Int = 0,
+                                               completion: @escaping (T)->()) -> XCTestExpectation {
+        let expectation = expectation ?? XCTestExpectation(description: "Expected event")
+        
+        DispatchQueue.main.asyncAfter(deadline: .now() + retryDelay) {
+            let url = URLRequest(url: URL(string: "\(endpoint)/micro/good")!)
+            let task = URLSession.shared.dataTask(with: url) { data, response, error in
+                if let error = error {
+                    XCTFail("Failed to request Micro: \(error).")
+                } else if let data = data {
+                    if let items = try? JSONDecoder().decode([GoodResponse<T>].self, from: data),
+                       let item = items.first {
+                        completion(item.event)
+                        expectation.fulfill()
+                    } else if numberOfRetries < maxNumberOfRetries {
+                        _ = expectEvent(expectation: expectation,
+                                        numberOfRetries: numberOfRetries + 1,
+                                        completion: completion)
+                    } else {
+                        XCTFail("Didn't find the expected event in Micro")
+                    }
+                } else {
+                    XCTFail("Failed to parse response from Micro")
+                }
+            }
+            task.resume()
+        }
+        return expectation
+    }
+}
+
+struct AllResponse: Codable {
+    let good: Int
+    let bad: Int
+}
+
+struct PrimitiveResponse: Codable {
+    let se_category: String?
+    let se_action: String?
+    let se_label: String?
+    let se_property: String?
+    let se_value: Double?
+    let page_url: String?
+    let page_referrer: String?
+}
+
+struct SelfDescribingResponse<T: Codable>: Codable {
+    let unstruct_event: UnstructEventResponse<T>
+}
+
+struct UnstructEventResponse<T: Codable>: Codable {
+    let data: SelfDescribingDataResponse<T>
+}
+
+struct ContextEntityWrapper<T: Codable>: Decodable {
+    let entity: T?
+}
+
+extension ContextEntityWrapper {
+    init(from decoder: Decoder) throws {
+        let container = try decoder.singleValueContainer()
+        entity = try? container.decode(T.self)
+    }
+}
+
+struct ContextEntityResponse<T: Codable>: Codable {
+    let schema: String
+    let data: T?
+}
+
+extension ContextEntityResponse {
+    private enum CodingKeys: String, CodingKey {
+        case schema = "schema"
+        case data = "data"
+    }
+    
+    init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        
+        schema = try container.decode(String.self, forKey: .schema)
+        data = try? container.decode(T.self, forKey: .data)
+    }
+}
+
+struct ContextsResponse<T: Codable>: Codable {
+    let data: [ContextEntityResponse<T>]
+}
+
+struct WithContextResponse<T: Codable>: Codable {
+    let contexts: ContextsResponse<T>
+}
+
+struct SelfDescribingDataResponse<T: Codable>: Codable {
+    let data: T
+}
+
+struct GoodResponse<T: Codable>: Codable {
+    let event: T
+}

From 973090c8a589f52f0a6d28d08cc6b9eb3e16fee8 Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Mon, 12 Dec 2022 13:13:03 +0100
Subject: [PATCH 05/19] Add API docs using Swift-DocC (close #739)

PR #741
---
 .doxygen/Doxyfile         | 2553 -------------------------------------
 .github/workflows/doc.yml |   24 +-
 .gitignore                |    3 +-
 Package.resolved          |    9 +
 Package.swift             |    5 +
 README.md                 |    2 +-
 6 files changed, 24 insertions(+), 2572 deletions(-)
 delete mode 100644 .doxygen/Doxyfile

diff --git a/.doxygen/Doxyfile b/.doxygen/Doxyfile
deleted file mode 100644
index 08824afa9..000000000
--- a/.doxygen/Doxyfile
+++ /dev/null
@@ -1,2553 +0,0 @@
-# Doxyfile 1.8.18
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a double hash (##) is considered a comment and is placed in
-# front of the TAG it is preceding.
-#
-# All text after a single hash (#) is considered a comment and will be ignored.
-# The format is:
-# TAG = value [value, ...]
-# For lists, items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (\" \").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the configuration
-# file that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
-# The default value is: UTF-8.
-
-DOXYFILE_ENCODING      = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
-# double-quotes, unless you are using Doxywizard) that should identify the
-# project for which the documentation is generated. This name is used in the
-# title of most generated pages and in a few other places.
-# The default value is: My Project.
-
-PROJECT_NAME           = "Snowplow iOS Tracker"
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
-# could be handy for archiving the generated documentation or if some version
-# control system is used.
-
-PROJECT_NUMBER         = SNOWPLOW_TRACKER_VERSION
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer a
-# quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF          =
-
-# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
-# in the documentation. The maximum height of the logo should not exceed 55
-# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
-# the logo to the output directory.
-
-PROJECT_LOGO           =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
-# into which the generated documentation will be written. If a relative path is
-# entered, it will be relative to the location where doxygen was started. If
-# left blank the current directory will be used.
-
-OUTPUT_DIRECTORY       =
-
-# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
-# directories (in 2 levels) under the output directory of each output format and
-# will distribute the generated files over these directories. Enabling this
-# option can be useful when feeding doxygen a huge amount of source files, where
-# putting all generated files in the same directory would otherwise causes
-# performance problems for the file system.
-# The default value is: NO.
-
-CREATE_SUBDIRS         = NO
-
-# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
-# characters to appear in the names of generated files. If set to NO, non-ASCII
-# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
-# U+3044.
-# The default value is: NO.
-
-ALLOW_UNICODE_NAMES    = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
-# The default value is: English.
-
-OUTPUT_LANGUAGE        = English
-
-# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all generated output in the proper direction.
-# Possible values are: None, LTR, RTL and Context.
-# The default value is: None.
-
-OUTPUT_TEXT_DIRECTION  = None
-
-# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
-# descriptions after the members that are listed in the file and class
-# documentation (similar to Javadoc). Set to NO to disable this.
-# The default value is: YES.
-
-BRIEF_MEMBER_DESC      = YES
-
-# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
-# description of a member or function before the detailed description
-#
-# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-# The default value is: YES.
-
-REPEAT_BRIEF           = NO
-
-# This tag implements a quasi-intelligent brief description abbreviator that is
-# used to form the text in various listings. Each string in this list, if found
-# as the leading text of the brief description, will be stripped from the text
-# and the result, after processing the whole list, is used as the annotated
-# text. Otherwise, the brief description is used as-is. If left blank, the
-# following values are used ($name is automatically replaced with the name of
-# the entity):The $name class, The $name widget, The $name file, is, provides,
-# specifies, contains, represents, a, an and the.
-
-ABBREVIATE_BRIEF       = "The $name class" \
-                         "The $name widget" \
-                         "The $name file" \
-                         is \
-                         provides \
-                         specifies \
-                         contains \
-                         represents \
-                         a \
-                         an \
-                         the
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# doxygen will generate a detailed section even if there is only a brief
-# description.
-# The default value is: NO.
-
-ALWAYS_DETAILED_SEC    = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-# The default value is: NO.
-
-INLINE_INHERITED_MEMB  = NO
-
-# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
-# before files name in the file list and in the header files. If set to NO the
-# shortest path that makes the file name unique will be used
-# The default value is: YES.
-
-FULL_PATH_NAMES        = YES
-
-# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
-# Stripping is only done if one of the specified strings matches the left-hand
-# part of the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the path to
-# strip.
-#
-# Note that you can specify absolute paths here, but also relative paths, which
-# will be relative from the directory where doxygen is started.
-# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-
-STRIP_FROM_PATH        =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
-# path mentioned in the documentation of a class, which tells the reader which
-# header file to include in order to use a class. If left blank only the name of
-# the header file containing the class definition is used. Otherwise one should
-# specify the list of include paths that are normally passed to the compiler
-# using the -I flag.
-
-STRIP_FROM_INC_PATH    =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
-# less readable) file names. This can be useful is your file systems doesn't
-# support long names like on DOS, Mac, or CD-ROM.
-# The default value is: NO.
-
-SHORT_NAMES            = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
-# first line (until the first dot) of a Javadoc-style comment as the brief
-# description. If set to NO, the Javadoc-style will behave just like regular Qt-
-# style comments (thus requiring an explicit @brief command for a brief
-# description.)
-# The default value is: NO.
-
-JAVADOC_AUTOBRIEF      = NO
-
-# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
-# such as
-# /***************
-# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
-# Javadoc-style will behave just like regular comments and it will not be
-# interpreted by doxygen.
-# The default value is: NO.
-
-JAVADOC_BANNER         = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
-# line (until the first dot) of a Qt-style comment as the brief description. If
-# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
-# requiring an explicit \brief command for a brief description.)
-# The default value is: NO.
-
-QT_AUTOBRIEF           = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
-# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
-# a brief description. This used to be the default behavior. The new default is
-# to treat a multi-line C++ comment block as a detailed description. Set this
-# tag to YES if you prefer the old behavior instead.
-#
-# Note that setting this tag to YES also means that rational rose comments are
-# not recognized any more.
-# The default value is: NO.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
-# documentation from any documented member that it re-implements.
-# The default value is: YES.
-
-INHERIT_DOCS           = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
-# page for each member. If set to NO, the documentation of a member will be part
-# of the file/class/namespace that contains it.
-# The default value is: NO.
-
-SEPARATE_MEMBER_PAGES  = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
-# uses this value to replace tabs by spaces in code fragments.
-# Minimum value: 1, maximum value: 16, default value: 4.
-
-TAB_SIZE               = 4
-
-# This tag can be used to specify a number of aliases that act as commands in
-# the documentation. An alias has the form:
-# name=value
-# For example adding
-# "sideeffect=@par Side Effects:\n"
-# will allow you to put the command \sideeffect (or @sideeffect) in the
-# documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines (in the resulting output). You can put ^^ in the value part of an
-# alias to insert a newline as if a physical newline was in the original file.
-# When you need a literal { or } or , in the value part of an alias you have to
-# escape them by means of a backslash (\), this can lead to conflicts with the
-# commands \{ and \} for these it is advised to use the version @{ and @} or use
-# a double escape (\\{ and \\})
-
-ALIASES                =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
-# only. Doxygen will then generate output that is more tailored for C. For
-# instance, some of the names that are used will be different. The list of all
-# members will be omitted, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_FOR_C  = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
-# Python sources only. Doxygen will then generate output that is more tailored
-# for that language. For instance, namespaces will be presented as packages,
-# qualified scopes will look different, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_JAVA   = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources. Doxygen will then generate output that is tailored for Fortran.
-# The default value is: NO.
-
-OPTIMIZE_FOR_FORTRAN   = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for VHDL.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_VHDL   = NO
-
-# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
-# sources only. Doxygen will then generate output that is more tailored for that
-# language. For instance, namespaces will be presented as modules, types will be
-# separated into more groups, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_SLICE  = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
-# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
-# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
-# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
-# tries to guess whether the code is fixed or free formatted code, this is the
-# default for Fortran type files). For instance to make doxygen treat .inc files
-# as Fortran files (default is PHP), and .f files as C (default is Fortran),
-# use: inc=Fortran f=C.
-#
-# Note: For files without extension you can use no_extension as a placeholder.
-#
-# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
-
-EXTENSION_MAPPING      =
-
-# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
-# according to the Markdown format, which allows for more readable
-# documentation. See https://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you can
-# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
-# case of backward compatibilities issues.
-# The default value is: YES.
-
-MARKDOWN_SUPPORT       = YES
-
-# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
-# to that level are automatically included in the table of contents, even if
-# they do not have an id attribute.
-# Note: This feature currently applies only to Markdown headings.
-# Minimum value: 0, maximum value: 99, default value: 5.
-# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
-
-TOC_INCLUDE_HEADINGS   = 5
-
-# When enabled doxygen tries to link words that correspond to documented
-# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by putting a % sign in front of the word or
-# globally by setting AUTOLINK_SUPPORT to NO.
-# The default value is: YES.
-
-AUTOLINK_SUPPORT       = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should set this
-# tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string);
-# versus func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-# The default value is: NO.
-
-BUILTIN_STL_SUPPORT    = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-# The default value is: NO.
-
-CPP_CLI_SUPPORT        = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
-# will parse them like normal C++ but will assume all classes use public instead
-# of private inheritance when no explicit protection keyword is present.
-# The default value is: NO.
-
-SIP_SUPPORT            = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES will make
-# doxygen to replace the get and set methods by a property in the documentation.
-# This will only work if the methods are indeed getting or setting a simple
-# type. If this is not the case, or you want to show the methods anyway, you
-# should set this option to NO.
-# The default value is: YES.
-
-IDL_PROPERTY_SUPPORT   = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-# The default value is: NO.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
-# If one adds a struct or class to a group and this option is enabled, then also
-# any nested class or struct is added to the same group. By default this option
-# is disabled and one has to add nested compounds explicitly via \ingroup.
-# The default value is: NO.
-
-GROUP_NESTED_COMPOUNDS = NO
-
-# Set the SUBGROUPING tag to YES to allow class member groups of the same type
-# (for instance a group of public functions) to be put as a subgroup of that
-# type (e.g. under the Public Functions section). Set it to NO to prevent
-# subgrouping. Alternatively, this can be done per class using the
-# \nosubgrouping command.
-# The default value is: YES.
-
-SUBGROUPING            = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
-# are shown inside the group in which they are included (e.g. using \ingroup)
-# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
-# and RTF).
-#
-# Note that this feature does not work in combination with
-# SEPARATE_MEMBER_PAGES.
-# The default value is: NO.
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
-# with only public data fields or simple typedef fields will be shown inline in
-# the documentation of the scope in which they are defined (i.e. file,
-# namespace, or group documentation), provided this scope is documented. If set
-# to NO, structs, classes, and unions are shown on a separate page (for HTML and
-# Man pages) or section (for LaTeX and RTF).
-# The default value is: NO.
-
-INLINE_SIMPLE_STRUCTS  = NO
-
-# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
-# enum is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically be
-# useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-# The default value is: NO.
-
-TYPEDEF_HIDES_STRUCT   = NO
-
-# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can be
-# an expensive process and often the same symbol appears multiple times in the
-# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
-# doxygen will become slower. If the cache is too large, memory is wasted. The
-# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
-# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
-# symbols. At the end of a run doxygen will report the cache usage and suggest
-# the optimal cache size from a speed point of view.
-# Minimum value: 0, maximum value: 9, default value: 0.
-
-LOOKUP_CACHE_SIZE      = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
-# documentation are documented, even if no documentation was available. Private
-# class members and static file members will be hidden unless the
-# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
-# Note: This will also disable the warnings about undocumented members that are
-# normally produced when WARNINGS is set to YES.
-# The default value is: NO.
-
-EXTRACT_ALL            = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
-# be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PRIVATE        = NO
-
-# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
-# methods of a class will be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PRIV_VIRTUAL   = NO
-
-# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
-# scope will be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PACKAGE        = NO
-
-# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
-# included in the documentation.
-# The default value is: NO.
-
-EXTRACT_STATIC         = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
-# locally in source files will be included in the documentation. If set to NO,
-# only classes defined in header files are included. Does not have any effect
-# for Java sources.
-# The default value is: YES.
-
-EXTRACT_LOCAL_CLASSES  = YES
-
-# This flag is only useful for Objective-C code. If set to YES, local methods,
-# which are defined in the implementation section but not in the interface are
-# included in the documentation. If set to NO, only methods in the interface are
-# included.
-# The default value is: NO.
-
-EXTRACT_LOCAL_METHODS  = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base name of
-# the file that contains the anonymous namespace. By default anonymous namespace
-# are hidden.
-# The default value is: NO.
-
-EXTRACT_ANON_NSPACES   = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
-# undocumented members inside documented classes or files. If set to NO these
-# members will be included in the various overviews, but no documentation
-# section is generated. This option has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_MEMBERS     = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy. If set
-# to NO, these classes will be included in the various overviews. This option
-# has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_CLASSES     = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# declarations. If set to NO, these declarations will be included in the
-# documentation.
-# The default value is: NO.
-
-HIDE_FRIEND_COMPOUNDS  = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
-# documentation blocks found inside the body of a function. If set to NO, these
-# blocks will be appended to the function's detailed documentation block.
-# The default value is: NO.
-
-HIDE_IN_BODY_DOCS      = NO
-
-# The INTERNAL_DOCS tag determines if documentation that is typed after a
-# \internal command is included. If the tag is set to NO then the documentation
-# will be excluded. Set it to YES to include the internal documentation.
-# The default value is: NO.
-
-INTERNAL_DOCS          = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES, upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# (including Cygwin) ands Mac users are advised to set this option to NO.
-# The default value is: system dependent.
-
-CASE_SENSE_NAMES       = NO
-
-# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
-# their full class and namespace scopes in the documentation. If set to YES, the
-# scope will be hidden.
-# The default value is: NO.
-
-HIDE_SCOPE_NAMES       = YES
-
-# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
-# append additional text to a page's title, such as Class Reference. If set to
-# YES the compound reference will be hidden.
-# The default value is: NO.
-
-HIDE_COMPOUND_REFERENCE= NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
-# the files that are included by a file in the documentation of that file.
-# The default value is: YES.
-
-SHOW_INCLUDE_FILES     = YES
-
-# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
-# grouped member an include statement to the documentation, telling the reader
-# which file to include in order to use the member.
-# The default value is: NO.
-
-SHOW_GROUPED_MEMB_INC  = NO
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
-# files with double quotes in the documentation rather than with sharp brackets.
-# The default value is: NO.
-
-FORCE_LOCAL_INCLUDES   = NO
-
-# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
-# documentation for inline members.
-# The default value is: YES.
-
-INLINE_INFO            = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
-# (detailed) documentation of file and class members alphabetically by member
-# name. If set to NO, the members will appear in declaration order.
-# The default value is: YES.
-
-SORT_MEMBER_DOCS       = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
-# descriptions of file, namespace and class members alphabetically by member
-# name. If set to NO, the members will appear in declaration order. Note that
-# this will also influence the order of the classes in the class list.
-# The default value is: NO.
-
-SORT_BRIEF_DOCS        = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
-# (brief and detailed) documentation of class members so that constructors and
-# destructors are listed first. If set to NO the constructors will appear in the
-# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
-# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
-# member documentation.
-# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
-# detailed member documentation.
-# The default value is: NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
-# of group names into alphabetical order. If set to NO the group names will
-# appear in their defined order.
-# The default value is: NO.
-
-SORT_GROUP_NAMES       = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
-# fully-qualified names, including namespaces. If set to NO, the class list will
-# be sorted only by class name, not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the alphabetical
-# list.
-# The default value is: NO.
-
-SORT_BY_SCOPE_NAME     = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
-# type resolution of all parameters of a function it will reject a match between
-# the prototype and the implementation of a member function even if there is
-# only one candidate or it is obvious which candidate to choose by doing a
-# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
-# accept a match between prototype and implementation in such cases.
-# The default value is: NO.
-
-STRICT_PROTO_MATCHING  = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
-# list. This list is created by putting \todo commands in the documentation.
-# The default value is: YES.
-
-GENERATE_TODOLIST      = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
-# list. This list is created by putting \test commands in the documentation.
-# The default value is: YES.
-
-GENERATE_TESTLIST      = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
-# list. This list is created by putting \bug commands in the documentation.
-# The default value is: YES.
-
-GENERATE_BUGLIST       = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
-# the deprecated list. This list is created by putting \deprecated commands in
-# the documentation.
-# The default value is: YES.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional documentation
-# sections, marked by \if <section_label> ... \endif and \cond <section_label>
-# ... \endcond blocks.
-
-ENABLED_SECTIONS       =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
-# initial value of a variable or macro / define can have for it to appear in the
-# documentation. If the initializer consists of more lines than specified here
-# it will be hidden. Use a value of 0 to hide initializers completely. The
-# appearance of the value of individual variables and macros / defines can be
-# controlled using \showinitializer or \hideinitializer command in the
-# documentation regardless of this setting.
-# Minimum value: 0, maximum value: 10000, default value: 30.
-
-MAX_INITIALIZER_LINES  = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
-# the bottom of the documentation of classes and structs. If set to YES, the
-# list will mention the files that were used to generate the documentation.
-# The default value is: YES.
-
-SHOW_USED_FILES        = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
-# will remove the Files entry from the Quick Index and from the Folder Tree View
-# (if specified).
-# The default value is: YES.
-
-SHOW_FILES             = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
-# page. This will remove the Namespaces entry from the Quick Index and from the
-# Folder Tree View (if specified).
-# The default value is: YES.
-
-SHOW_NAMESPACES        = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command command input-file, where command is the value of the
-# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
-# by doxygen. Whatever the program writes to standard output is used as the file
-# version. For an example see the documentation.
-
-FILE_VERSION_FILTER    =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option. You can
-# optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
-#
-# Note that if you run doxygen from a directory containing a file called
-# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
-# tag is left empty.
-
-LAYOUT_FILE            =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
-# the reference definitions. This must be a list of .bib files. The .bib
-# extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
-# For LaTeX the style of the bibliography can be controlled using
-# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
-# search path. See also \cite for info how to create references.
-
-CITE_BIB_FILES         =
-
-#---------------------------------------------------------------------------
-# Configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated to
-# standard output by doxygen. If QUIET is set to YES this implies that the
-# messages are off.
-# The default value is: NO.
-
-QUIET                  = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
-# this implies that the warnings are on.
-#
-# Tip: Turn warnings on while writing the documentation.
-# The default value is: YES.
-
-WARNINGS               = YES
-
-# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
-# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
-# will automatically be disabled.
-# The default value is: YES.
-
-WARN_IF_UNDOCUMENTED   = YES
-
-# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
-# The default value is: YES.
-
-WARN_IF_DOC_ERROR      = YES
-
-# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
-# are documented, but have no documentation for their parameters or return
-# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation. If
-# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
-# The default value is: NO.
-
-WARN_NO_PARAMDOC       = NO
-
-# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
-# a warning is encountered.
-# The default value is: NO.
-
-WARN_AS_ERROR          = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that doxygen
-# can produce. The string should contain the $file, $line, and $text tags, which
-# will be replaced by the file and line number from which the warning originated
-# and the warning text. Optionally the format may contain $version, which will
-# be replaced by the version of the file (if it could be obtained via
-# FILE_VERSION_FILTER)
-# The default value is: $file:$line: $text.
-
-WARN_FORMAT            = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning and error
-# messages should be written. If left blank the output is written to standard
-# error (stderr).
-
-WARN_LOGFILE           =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag is used to specify the files and/or directories that contain
-# documented source files. You may enter file names like myfile.cpp or
-# directories like /usr/src/myproject. Separate the files or directories with
-# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
-# Note: If this tag is empty the current directory is searched.
-
-INPUT                  = ../README.md ../Snowplow
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
-# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
-# possible encodings.
-# The default value is: UTF-8.
-
-INPUT_ENCODING         = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
-# *.h) to filter out the source-files in the directories.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# read by doxygen.
-#
-# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
-# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
-# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
-# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
-# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
-# *.vhdl, *.ucf, *.qsf and *.ice.
-
-FILE_PATTERNS          = *.c \
-                         *.cc \
-                         *.cxx \
-                         *.cpp \
-                         *.c++ \
-                         *.java \
-                         *.ii \
-                         *.ixx \
-                         *.ipp \
-                         *.i++ \
-                         *.inl \
-                         *.idl \
-                         *.ddl \
-                         *.odl \
-                         *.h \
-                         *.hh \
-                         *.hxx \
-                         *.hpp \
-                         *.h++ \
-                         *.cs \
-                         *.d \
-                         *.php \
-                         *.php4 \
-                         *.php5 \
-                         *.phtml \
-                         *.inc \
-                         *.m \
-                         *.markdown \
-                         *.md \
-                         *.mm \
-                         *.dox \
-                         *.doc \
-                         *.txt \
-                         *.py \
-                         *.pyw \
-                         *.f90 \
-                         *.f95 \
-                         *.f03 \
-                         *.f08 \
-                         *.f18 \
-                         *.f \
-                         *.for \
-                         *.vhd \
-                         *.vhdl \
-                         *.ucf \
-                         *.qsf \
-                         *.ice
-
-# The RECURSIVE tag can be used to specify whether or not subdirectories should
-# be searched for input files as well.
-# The default value is: NO.
-
-RECURSIVE              = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-#
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
-EXCLUDE                =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-# The default value is: NO.
-
-EXCLUDE_SYMLINKS       = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories.
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories for example use the pattern */test/*
-
-EXCLUDE_PATTERNS       = */*Tests/*
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */test/*
-
-EXCLUDE_SYMBOLS        = Test*
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or directories
-# that contain example code fragments that are included (see the \include
-# command).
-
-EXAMPLE_PATH           =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
-# *.h) to filter out the source-files in the directories. If left blank all
-# files are included.
-
-EXAMPLE_PATTERNS       = *
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude commands
-# irrespective of the value of the RECURSIVE tag.
-# The default value is: NO.
-
-EXAMPLE_RECURSIVE      = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or directories
-# that contain images that are to be included in the documentation (see the
-# \image command).
-
-IMAGE_PATH             =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command:
-#
-# <filter> <input-file>
-#
-# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
-# name of an input file. Doxygen will then use the output that the filter
-# program writes to standard output. If FILTER_PATTERNS is specified, this tag
-# will be ignored.
-#
-# Note that the filter must not add or remove lines; it is applied before the
-# code is scanned, but not when the output code is generated. If lines are added
-# or removed, the anchors will not be placed correctly.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# properly processed by doxygen.
-
-INPUT_FILTER           =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form: pattern=filter
-# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
-# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
-# patterns match the file name, INPUT_FILTER is applied.
-#
-# Note that for custom extensions or not directly supported extensions you also
-# need to set EXTENSION_MAPPING for the extension otherwise the files are not
-# properly processed by doxygen.
-
-FILTER_PATTERNS        =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will also be used to filter the input files that are used for
-# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
-# The default value is: NO.
-
-FILTER_SOURCE_FILES    = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
-# it is also possible to disable source filtering for a specific pattern using
-# *.ext= (so without naming a filter).
-# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-
-FILTER_SOURCE_PATTERNS =
-
-# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page
-# (index.html). This can be useful if you have a project on for instance GitHub
-# and want to reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE = README.md
-
-#---------------------------------------------------------------------------
-# Configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
-# generated. Documented entities will be cross-referenced with these sources.
-#
-# Note: To get rid of all source code in the generated output, make sure that
-# also VERBATIM_HEADERS is set to NO.
-# The default value is: NO.
-
-SOURCE_BROWSER         = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body of functions,
-# classes and enums directly into the documentation.
-# The default value is: NO.
-
-INLINE_SOURCES         = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
-# special comment blocks from generated source code fragments. Normal C, C++ and
-# Fortran comments will always remain visible.
-# The default value is: YES.
-
-STRIP_CODE_COMMENTS    = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# entity all documented functions referencing it will be listed.
-# The default value is: NO.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES then for each documented function
-# all documented entities called/used by that function will be listed.
-# The default value is: NO.
-
-REFERENCES_RELATION    = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
-# to YES then the hyperlinks from functions in REFERENCES_RELATION and
-# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
-# link to the documentation.
-# The default value is: YES.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
-# source code will show a tooltip with additional information such as prototype,
-# brief description and links to the definition and documentation. Since this
-# will make the HTML file larger and loading of large files a bit slower, you
-# can opt to disable this feature.
-# The default value is: YES.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-SOURCE_TOOLTIPS        = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code will
-# point to the HTML generated by the htags(1) tool instead of doxygen built-in
-# source browser. The htags tool is part of GNU's global source tagging system
-# (see https://www.gnu.org/software/global/global.html). You will need version
-# 4.8.6 or higher.
-#
-# To use it do the following:
-# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
-# - Make sure the INPUT points to the root of the source tree
-# - Run doxygen as normal
-#
-# Doxygen will invoke htags (and that will in turn invoke gtags), so these
-# tools must be available from the command line (i.e. in the search path).
-#
-# The result: instead of the source browser generated by doxygen, the links to
-# source code will now point to the output of htags.
-# The default value is: NO.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-USE_HTAGS              = NO
-
-# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
-# verbatim copy of the header file for each class for which an include is
-# specified. Set to NO to disable this.
-# See also: Section \class.
-# The default value is: YES.
-
-VERBATIM_HEADERS       = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
-# compounds will be generated. Enable this if the project contains a lot of
-# classes, structs, unions or interfaces.
-# The default value is: YES.
-
-ALPHABETICAL_INDEX     = YES
-
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX    = 5
-
-# In case all classes in a project start with a common prefix, all classes will
-# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
-# can be used to specify a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-IGNORE_PREFIX          =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
-# The default value is: YES.
-
-GENERATE_HTML          = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_OUTPUT            = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
-# generated HTML page (for example: .htm, .php, .asp).
-# The default value is: .html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FILE_EXTENSION    = .html
-
-# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
-# each generated HTML page. If the tag is left blank doxygen will generate a
-# standard header.
-#
-# To get valid HTML the header file that includes any scripts and style sheets
-# that doxygen needs, which is dependent on the configuration options used (e.g.
-# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
-# default header using
-# doxygen -w html new_header.html new_footer.html new_stylesheet.css
-# YourConfigFile
-# and then modify the file new_header.html. See also section "Doxygen usage"
-# for information on how to generate the default header that doxygen normally
-# uses.
-# Note: The header is subject to change so you typically have to regenerate the
-# default header when upgrading to a newer version of doxygen. For a description
-# of the possible markers and block names see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_HEADER            = 
-
-# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
-# generated HTML page. If the tag is left blank doxygen will generate a standard
-# footer. See HTML_HEADER for more information on how to generate a default
-# footer and what special commands can be used inside the footer. See also
-# section "Doxygen usage" for information on how to generate the default footer
-# that doxygen normally uses.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FOOTER            = 
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
-# sheet that is used by each HTML page. It can be used to fine-tune the look of
-# the HTML output. If left blank doxygen will generate a default style sheet.
-# See also section "Doxygen usage" for information on how to generate the style
-# sheet that doxygen normally uses.
-# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
-# it is more robust and this tag (HTML_STYLESHEET) will in the future become
-# obsolete.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_STYLESHEET        = 
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
-# cascading style sheets that are included after the standard style sheets
-# created by doxygen. Using this option one can overrule certain style aspects.
-# This is preferred over using HTML_STYLESHEET since it does not replace the
-# standard style sheet and is therefore more robust against future updates.
-# Doxygen will copy the style sheet files to the output directory.
-# Note: The order of the extra style sheet files is of importance (e.g. the last
-# style sheet in the list overrules the setting of the previous ones in the
-# list). For an example see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_STYLESHEET  = 
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
-# files will be copied as-is; there are no commands or markers available.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_FILES       = 
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
-# will adjust the colors in the style sheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
-# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
-# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
-# purple, and 360 is red again.
-# Minimum value: 0, maximum value: 359, default value: 220.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_HUE    = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
-# value of 255 will produce the most vivid colors.
-# Minimum value: 0, maximum value: 255, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_SAT    = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
-# luminance component of the colors in the HTML output. Values below 100
-# gradually make the output lighter, whereas values above 100 make the output
-# darker. The value divided by 100 is the actual gamma applied, so 80 represents
-# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
-# change the gamma.
-# Minimum value: 40, maximum value: 240, default value: 80.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_GAMMA  = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting this
-# to YES can help to show when doxygen was last run and thus if the
-# documentation is up to date.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_TIMESTAMP         = NO
-
-# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
-# documentation will contain a main index with vertical navigation menus that
-# are dynamically created via JavaScript. If disabled, the navigation index will
-# consists of multiple levels of tabs that are statically embedded in every HTML
-# page. Disable this option to support browsers that do not have JavaScript,
-# like the Qt help browser.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_MENUS     = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_SECTIONS  = NO
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
-# shown in the various tree structured indices initially; the user can expand
-# and collapse entries dynamically later on. Doxygen will expand the tree to
-# such a level that at most the specified number of entries are visible (unless
-# a fully collapsed tree already exceeds this amount). So setting the number of
-# entries 1 will produce a full collapsed tree by default. 0 is a special value
-# representing an infinite number of entries and will result in a full expanded
-# tree by default.
-# Minimum value: 0, maximum value: 9999, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files will be
-# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: https://developer.apple.com/xcode/), introduced with OSX
-# 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
-# genXcode/_index.html for more information.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_DOCSET        = NO
-
-# This tag determines the name of the docset feed. A documentation feed provides
-# an umbrella under which multiple documentation sets from a single provider
-# (such as a company or product suite) can be grouped.
-# The default value is: Doxygen generated docs.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_FEEDNAME        = "Doxygen generated docs"
-
-# This tag specifies a string that should uniquely identify the documentation
-# set bundle. This should be a reverse domain-name style string, e.g.
-# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_BUNDLE_ID       = org.doxygen.Project
-
-# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-# The default value is: org.doxygen.Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
-
-# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
-# The default value is: Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_NAME  = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
-# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
-# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
-#
-# The HTML Help Workshop contains a compiler that can convert all HTML output
-# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
-# files are now used as the Windows 98 help format, and will replace the old
-# Windows help format (.hlp) on all Windows platforms in the future. Compressed
-# HTML files also contain an index, a table of contents, and you can search for
-# words in the documentation. The HTML workshop also contains a viewer for
-# compressed HTML files.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_HTMLHELP      = NO
-
-# The CHM_FILE tag can be used to specify the file name of the resulting .chm
-# file. You can add a path in front of the file if the result should not be
-# written to the html output directory.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_FILE               =
-
-# The HHC_LOCATION tag can be used to specify the location (absolute path
-# including file name) of the HTML help compiler (hhc.exe). If non-empty,
-# doxygen will try to run the HTML help compiler on the generated index.hhp.
-# The file has to be specified with full path.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-HHC_LOCATION           =
-
-# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-GENERATE_CHI           = NO
-
-# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
-# and project file content.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_INDEX_ENCODING     =
-
-# The BINARY_TOC flag controls whether a binary table of contents is generated
-# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
-# enables the Previous and Next buttons.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-BINARY_TOC             = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members to
-# the table of contents of the HTML help documentation and to the tree view.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-TOC_EXPAND             = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
-# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
-# (.qch) of the generated HTML documentation.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_QHP           = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
-# the file name of the resulting .qch file. The path specified is relative to
-# the HTML output folder.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QCH_FILE               =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
-# Project output. For more information please see Qt Help Project / Namespace
-# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_NAMESPACE          = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
-# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
-# folders).
-# The default value is: doc.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_VIRTUAL_FOLDER     = doc
-
-# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
-# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_NAME   =
-
-# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_ATTRS  =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_SECT_FILTER_ATTRS  =
-
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHG_LOCATION           =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
-# generated, together with the HTML files, they form an Eclipse help plugin. To
-# install this plugin and make it available under the help contents menu in
-# Eclipse, the contents of the directory containing the HTML and XML files needs
-# to be copied into the plugins directory of eclipse. The name of the directory
-# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
-# After copying Eclipse needs to be restarted before the help appears.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_ECLIPSEHELP   = NO
-
-# A unique identifier for the Eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have this
-# name. Each documentation set should have its own identifier.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
-
-ECLIPSE_DOC_ID         = org.doxygen.Project
-
-# If you want full control over the layout of the generated HTML pages it might
-# be necessary to disable the index and replace it with your own. The
-# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
-# of each HTML page. A value of NO enables the index and the value YES disables
-# it. Since the tabs in the index contain the same information as the navigation
-# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-DISABLE_INDEX          = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information. If the tag
-# value is set to YES, a side panel will be generated containing a tree-like
-# index structure (just like the one that is generated for HTML Help). For this
-# to work a browser that supports JavaScript, DHTML, CSS and frames is required
-# (i.e. any modern browser). Windows users are probably better off using the
-# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_TREEVIEW      = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
-# doxygen will group on one line in the generated HTML documentation.
-#
-# Note that a value of 0 will completely suppress the enum values from appearing
-# in the overview section.
-# Minimum value: 0, maximum value: 20, default value: 4.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-ENUM_VALUES_PER_LINE   = 4
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
-# to set the initial width (in pixels) of the frame in which the tree is shown.
-# Minimum value: 0, maximum value: 1500, default value: 250.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-TREEVIEW_WIDTH         = 250
-
-# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
-# external symbols imported via tag files in a separate window.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-EXT_LINKS_IN_WINDOW    = NO
-
-# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
-# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
-# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
-# the HTML output. These images will generally look nicer at scaled resolutions.
-# Possible values are: png The default and svg Looks nicer but requires the
-# pdf2svg tool.
-# The default value is: png.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FORMULA_FORMAT    = png
-
-# Use this tag to change the font size of LaTeX formulas included as images in
-# the HTML documentation. When you change the font size after a successful
-# doxygen run you need to manually remove any form_*.png images from the HTML
-# output directory to force them to be regenerated.
-# Minimum value: 8, maximum value: 50, default value: 10.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_FONTSIZE       = 10
-
-# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are not
-# supported properly for IE 6.0, but are supported on all modern browsers.
-#
-# Note that when changing this option you need to delete any form_*.png files in
-# the HTML output directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_TRANSPARENT    = YES
-
-# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
-# to create new LaTeX commands to be used in formulas as building blocks. See
-# the section "Including formulas" for details.
-
-FORMULA_MACROFILE      =
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# https://www.mathjax.org) which uses client side JavaScript for the rendering
-# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
-# installed or if you want to formulas look prettier in the HTML output. When
-# enabled you may also need to install MathJax separately and configure the path
-# to it using the MATHJAX_RELPATH option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-USE_MATHJAX            = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
-# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
-# The default value is: HTML-CSS.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_FORMAT         = HTML-CSS
-
-# When MathJax is enabled you need to specify the location relative to the HTML
-# output directory using the MATHJAX_RELPATH option. The destination directory
-# should contain the MathJax.js script. For instance, if the mathjax directory
-# is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
-# Content Delivery Network so you can quickly see the result without installing
-# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_RELPATH        = https://cdn.jsdelivr.net/npm/mathjax@2
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
-# extension names that should be enabled during MathJax rendering. For example
-# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_EXTENSIONS     =
-
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
-# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
-# example see the documentation.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_CODEFILE       =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
-# the HTML output. The underlying search engine uses javascript and DHTML and
-# should work on any modern browser. Note that when using HTML help
-# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
-# there is already a search function so this one should typically be disabled.
-# For large projects the javascript based search engine can be slow, then
-# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
-# search using the keyboard; to jump to the search box use <access key> + S
-# (what the <access key> is depends on the OS and browser, but it is typically
-# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
-# key> to jump into the search results window, the results can be navigated
-# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
-# the search. The filter options can be selected when the cursor is inside the
-# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
-# to select a filter and <Enter> or <escape> to activate or cancel the filter
-# option.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-SEARCHENGINE           = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using JavaScript. There
-# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
-# setting. When disabled, doxygen will generate a PHP script for searching and
-# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
-# and searching needs to be provided by external tools. See the section
-# "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SERVER_BASED_SEARCH    = NO
-
-# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
-# script for searching. Instead the search results are written to an XML file
-# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
-# search results.
-#
-# Doxygen ships with an example indexer (doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/).
-#
-# See the section "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH        = NO
-
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will return the search results when EXTERNAL_SEARCH is enabled.
-#
-# Doxygen ships with an example indexer (doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/). See the section "External Indexing and
-# Searching" for details.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHENGINE_URL       =
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
-# search data is written to a file for indexing by an external tool. With the
-# SEARCHDATA_FILE tag the name of this file can be specified.
-# The default file is: searchdata.xml.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHDATA_FILE        = searchdata.xml
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
-# projects and redirect the results back to the right project.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH_ID     =
-
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
-# projects other than the one defined by this configuration file, but that are
-# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
-# to a relative location where the documentation can be found. The format is:
-# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTRA_SEARCH_MAPPINGS  =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
-# The default value is: YES.
-
-GENERATE_LATEX         = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: latex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_OUTPUT           = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked.
-#
-# Note that when not enabling USE_PDFLATEX the default is latex when enabling
-# USE_PDFLATEX the default is pdflatex and when in the later case latex is
-# chosen this is overwritten by pdflatex. For specific output languages the
-# default can have been set differently, this depends on the implementation of
-# the output language.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_CMD_NAME         =
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
-# index for LaTeX.
-# Note: This tag is used in the Makefile / make.bat.
-# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
-# (.tex).
-# The default file is: makeindex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-MAKEINDEX_CMD_NAME     = makeindex
-
-# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
-# generate index for LaTeX. In case there is no backslash (\) as first character
-# it will be automatically added in the LaTeX code.
-# Note: This tag is used in the generated output file (.tex).
-# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
-# The default value is: makeindex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_MAKEINDEX_CMD    = makeindex
-
-# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
-# documents. This may be useful for small projects and may help to save some
-# trees in general.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-COMPACT_LATEX          = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used by the
-# printer.
-# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
-# 14 inches) and executive (7.25 x 10.5 inches).
-# The default value is: a4.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PAPER_TYPE             = a4
-
-# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
-# that should be included in the LaTeX output. The package can be specified just
-# by its name or with the correct syntax as to be used with the LaTeX
-# \usepackage command. To get the times font for instance you can specify :
-# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
-# To use the option intlimits with the amsmath package you can specify:
-# EXTRA_PACKAGES=[intlimits]{amsmath}
-# If left blank no extra packages will be included.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-EXTRA_PACKAGES         =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
-# generated LaTeX document. The header should contain everything until the first
-# chapter. If it is left blank doxygen will generate a standard header. See
-# section "Doxygen usage" for information on how to let doxygen write the
-# default header to a separate file.
-#
-# Note: Only use a user-defined header if you know what you are doing! The
-# following commands have a special meaning inside the header: $title,
-# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
-# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
-# string, for the replacement values of the other commands the user is referred
-# to HTML_HEADER.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_HEADER           =
-
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
-# generated LaTeX document. The footer should contain everything after the last
-# chapter. If it is left blank doxygen will generate a standard footer. See
-# LATEX_HEADER for more information on how to generate a default footer and what
-# special commands can be used inside the footer.
-#
-# Note: Only use a user-defined footer if you know what you are doing!
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_FOOTER           =
-
-# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
-# LaTeX style sheets that are included after the standard style sheets created
-# by doxygen. Using this option one can overrule certain style aspects. Doxygen
-# will copy the style sheet files to the output directory.
-# Note: The order of the extra style sheet files is of importance (e.g. the last
-# style sheet in the list overrules the setting of the previous ones in the
-# list).
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_EXTRA_STYLESHEET =
-
-# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the LATEX_OUTPUT output
-# directory. Note that the files will be copied as-is; there are no commands or
-# markers available.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_EXTRA_FILES      =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
-# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
-# contain links (just like the HTML output) instead of page references. This
-# makes the output suitable for online browsing using a PDF viewer.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PDF_HYPERLINKS         = YES
-
-# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES, to get a
-# higher quality PDF documentation.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-USE_PDFLATEX           = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
-# command to the generated LaTeX files. This will instruct LaTeX to keep running
-# if errors occur, instead of asking the user for help. This option is also used
-# when generating formulas in HTML.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BATCHMODE        = NO
-
-# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
-# index chapters (such as File Index, Compound Index, etc.) in the output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_HIDE_INDICES     = NO
-
-# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
-# code with syntax highlighting in the LaTeX output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_SOURCE_CODE      = NO
-
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. See
-# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
-# The default value is: plain.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BIB_STYLE        = plain
-
-# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
-# page will contain the date and time when the page was generated. Setting this
-# to NO can help when comparing the output of multiple runs.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_TIMESTAMP        = NO
-
-# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
-# path from which the emoji images will be read. If a relative path is entered,
-# it will be relative to the LATEX_OUTPUT directory. If left blank the
-# LATEX_OUTPUT directory will be used.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_EMOJI_DIRECTORY  =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
-# RTF output is optimized for Word 97 and may not look too pretty with other RTF
-# readers/editors.
-# The default value is: NO.
-
-GENERATE_RTF           = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: rtf.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_OUTPUT             = rtf
-
-# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
-# documents. This may be useful for small projects and may help to save some
-# trees in general.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-COMPACT_RTF            = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
-# contain hyperlink fields. The RTF file will contain links (just like the HTML
-# output) instead of page references. This makes the output suitable for online
-# browsing using Word or some other Word compatible readers that support those
-# fields.
-#
-# Note: WordPad (write) and others do not support links.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_HYPERLINKS         = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# configuration file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-#
-# See also section "Doxygen usage" for information on how to generate the
-# default style sheet that doxygen normally uses.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_STYLESHEET_FILE    =
-
-# Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's configuration file. A template extensions file can be
-# generated using doxygen -e rtf extensionFile.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_EXTENSIONS_FILE    =
-
-# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
-# with syntax highlighting in the RTF output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_SOURCE_CODE        = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
-# classes and files.
-# The default value is: NO.
-
-GENERATE_MAN           = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it. A directory man3 will be created inside the directory specified by
-# MAN_OUTPUT.
-# The default directory is: man.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_OUTPUT             = man
-
-# The MAN_EXTENSION tag determines the extension that is added to the generated
-# man pages. In case the manual section does not start with a number, the number
-# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
-# optional.
-# The default value is: .3.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_EXTENSION          = .3
-
-# The MAN_SUBDIR tag determines the name of the directory created within
-# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
-# MAN_EXTENSION with the initial . removed.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_SUBDIR             =
-
-# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
-# will generate one additional man file for each entity documented in the real
-# man page(s). These additional files only source the real man page, but without
-# them the man command would be unable to find the correct page.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_LINKS              = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
-# captures the structure of the code including all documentation.
-# The default value is: NO.
-
-GENERATE_XML           = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: xml.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_OUTPUT             = xml
-
-# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
-# listings (including syntax highlighting and cross-referencing information) to
-# the XML output. Note that enabling this will significantly increase the size
-# of the XML output.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_PROGRAMLISTING     = YES
-
-# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
-# namespace members in file scope as well, matching the HTML output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_NS_MEMB_FILE_SCOPE = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the DOCBOOK output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
-# that can be used to generate PDF.
-# The default value is: NO.
-
-GENERATE_DOCBOOK       = NO
-
-# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
-# front of it.
-# The default directory is: docbook.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_OUTPUT         = docbook
-
-# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
-# program listings (including syntax highlighting and cross-referencing
-# information) to the DOCBOOK output. Note that enabling this will significantly
-# increase the size of the DOCBOOK output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_PROGRAMLISTING = NO
-
-#---------------------------------------------------------------------------
-# Configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
-# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
-# the structure of the code including all documentation. Note that this feature
-# is still experimental and incomplete at the moment.
-# The default value is: NO.
-
-GENERATE_AUTOGEN_DEF   = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
-# file that captures the structure of the code including all documentation.
-#
-# Note that this feature is still experimental and incomplete at the moment.
-# The default value is: NO.
-
-GENERATE_PERLMOD       = NO
-
-# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
-# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
-# output from the Perl module output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_LATEX          = NO
-
-# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
-# formatted so it can be parsed by a human reader. This is useful if you want to
-# understand what is going on. On the other hand, if this tag is set to NO, the
-# size of the Perl module output will be much smaller and Perl will parse it
-# just the same.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_PRETTY         = YES
-
-# The names of the make variables in the generated doxyrules.make file are
-# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
-# so different doxyrules.make files included by the same Makefile don't
-# overwrite each other's variables.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
-# C-preprocessor directives found in the sources and include files.
-# The default value is: YES.
-
-ENABLE_PREPROCESSING   = YES
-
-# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
-# in the source code. If set to NO, only conditional compilation will be
-# performed. Macro expansion can be done in a controlled way by setting
-# EXPAND_ONLY_PREDEF to YES.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-MACRO_EXPANSION        = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
-# the macro expansion is limited to the macros specified with the PREDEFINED and
-# EXPAND_AS_DEFINED tags.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_ONLY_PREDEF     = NO
-
-# If the SEARCH_INCLUDES tag is set to YES, the include files in the
-# INCLUDE_PATH will be searched if a #include is found.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-SEARCH_INCLUDES        = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by the
-# preprocessor.
-# This tag requires that the tag SEARCH_INCLUDES is set to YES.
-
-INCLUDE_PATH           =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will be
-# used.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-INCLUDE_FILE_PATTERNS  =
-
-# The PREDEFINED tag can be used to specify one or more macro names that are
-# defined before the preprocessor is started (similar to the -D option of e.g.
-# gcc). The argument of the tag is a list of macros of the form: name or
-# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
-# is assumed. To prevent a macro definition from being undefined via #undef or
-# recursively expanded use the := operator instead of the = operator.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-PREDEFINED             =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
-# tag can be used to specify a list of macro names that should be expanded. The
-# macro definition that is found in the sources will be used. Use the PREDEFINED
-# tag if you want to use a different macro definition that overrules the
-# definition found in the source code.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_AS_DEFINED      =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
-# remove all references to function-like macros that are alone on a line, have
-# an all uppercase name, and do not end with a semicolon. Such function macros
-# are typically used for boiler-plate code, and will confuse the parser if not
-# removed.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-SKIP_FUNCTION_MACROS   = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES tag can be used to specify one or more tag files. For each tag
-# file the location of the external documentation should be added. The format of
-# a tag file without this location is as follows:
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where loc1 and loc2 can be relative or absolute paths or URLs. See the
-# section "Linking to external documentation" for more information about the use
-# of tag files.
-# Note: Each tag file must have a unique name (where the name does NOT include
-# the path). If a tag file is not located in the directory in which doxygen is
-# run, you must also specify the path to the tagfile here.
-
-TAGFILES               =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
-# tag file that is based on the input files it reads. See section "Linking to
-# external documentation" for more information about the usage of tag files.
-
-GENERATE_TAGFILE       =
-
-# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
-# the class index. If set to NO, only the inherited external classes will be
-# listed.
-# The default value is: NO.
-
-ALLEXTERNALS           = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will be
-# listed.
-# The default value is: YES.
-
-EXTERNAL_GROUPS        = YES
-
-# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
-# the related pages index. If set to NO, only the current project's pages will
-# be listed.
-# The default value is: YES.
-
-EXTERNAL_PAGES         = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
-# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
-# NO turns the diagrams off. Note that this option also works with HAVE_DOT
-# disabled, but it is recommended to install and use dot, since it yields more
-# powerful graphs.
-# The default value is: YES.
-
-CLASS_DIAGRAMS         = YES
-
-# You can include diagrams made with dia in doxygen documentation. Doxygen will
-# then run dia to produce the diagram and insert it in the documentation. The
-# DIA_PATH tag allows you to specify the directory where the dia binary resides.
-# If left empty dia is assumed to be found in the default search path.
-
-DIA_PATH               =
-
-# If set to YES the inheritance and collaboration graphs will hide inheritance
-# and usage relations if the target is undocumented or is not a class.
-# The default value is: YES.
-
-HIDE_UNDOC_RELATIONS   = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz (see:
-# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
-# Bell Labs. The other options in this section have no effect if this option is
-# set to NO
-# The default value is: NO.
-
-HAVE_DOT               = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
-# to run in parallel. When set to 0 doxygen will base this on the number of
-# processors available in the system. You can set it explicitly to a value
-# larger than 0 to get control over the balance between CPU load and processing
-# speed.
-# Minimum value: 0, maximum value: 32, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_NUM_THREADS        = 0
-
-# When you want a differently looking font in the dot files that doxygen
-# generates you can specify the font name using DOT_FONTNAME. You need to make
-# sure dot is able to find the font, which can be done by putting it in a
-# standard location or by setting the DOTFONTPATH environment variable or by
-# setting DOT_FONTPATH to the directory containing the font.
-# The default value is: Helvetica.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTNAME           = Helvetica
-
-# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
-# dot graphs.
-# Minimum value: 4, maximum value: 24, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTSIZE           = 10
-
-# By default doxygen will tell dot to use the default font as specified with
-# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
-# the path where dot can find it using this tag.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTPATH           =
-
-# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
-# each documented class showing the direct and indirect inheritance relations.
-# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CLASS_GRAPH            = YES
-
-# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
-# graph for each documented class showing the direct and indirect implementation
-# dependencies (inheritance, containment, and class references variables) of the
-# class with other documented classes.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-COLLABORATION_GRAPH    = YES
-
-# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
-# groups, showing the direct groups dependencies.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GROUP_GRAPHS           = YES
-
-# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LOOK               = NO
-
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
-# class node. If there are many fields or methods and many nodes the graph may
-# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
-# number of items for each type to make the size more manageable. Set this to 0
-# for no limit. Note that the threshold may be exceeded by 50% before the limit
-# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
-# but if the number exceeds 15, the total amount of fields shown is limited to
-# 10.
-# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LIMIT_NUM_FIELDS   = 10
-
-# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
-# collaboration graphs will show the relations between templates and their
-# instances.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-TEMPLATE_RELATIONS     = NO
-
-# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
-# YES then doxygen will generate a graph for each documented file showing the
-# direct and indirect include dependencies of the file with other documented
-# files.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDE_GRAPH          = YES
-
-# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
-# set to YES then doxygen will generate a graph for each documented file showing
-# the direct and indirect include dependencies of the file with other documented
-# files.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDED_BY_GRAPH      = YES
-
-# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
-# dependency graph for every global function or class method.
-#
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable call graphs for selected
-# functions only using the \callgraph command. Disabling a call graph can be
-# accomplished by means of the command \hidecallgraph.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALL_GRAPH             = NO
-
-# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
-# dependency graph for every global function or class method.
-#
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable caller graphs for selected
-# functions only using the \callergraph command. Disabling a caller graph can be
-# accomplished by means of the command \hidecallergraph.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALLER_GRAPH           = NO
-
-# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
-# hierarchy of all classes instead of a textual one.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GRAPHICAL_HIERARCHY    = YES
-
-# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
-# dependencies a directory has on other directories in a graphical way. The
-# dependency relations are determined by the #include relations between the
-# files in the directories.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DIRECTORY_GRAPH        = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. For an explanation of the image formats see the section
-# output formats in the documentation of the dot tool (Graphviz (see:
-# http://www.graphviz.org/)).
-# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
-# to make the SVG files visible in IE 9+ (other browsers do not have this
-# requirement).
-# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
-# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
-# png:gdiplus:gdiplus.
-# The default value is: png.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_IMAGE_FORMAT       = png
-
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-#
-# Note that this requires a modern browser other than Internet Explorer. Tested
-# and working are Firefox, Chrome, Safari, and Opera.
-# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
-# the SVG files visible. Older versions of IE do not have SVG support.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INTERACTIVE_SVG        = NO
-
-# The DOT_PATH tag can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_PATH               =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the \dotfile
-# command).
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOTFILE_DIRS           =
-
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the \mscfile
-# command).
-
-MSCFILE_DIRS           =
-
-# The DIAFILE_DIRS tag can be used to specify one or more directories that
-# contain dia files that are included in the documentation (see the \diafile
-# command).
-
-DIAFILE_DIRS           =
-
-# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
-# path where java can find the plantuml.jar file. If left blank, it is assumed
-# PlantUML is not used or called during a preprocessing step. Doxygen will
-# generate a warning when it encounters a \startuml command in this case and
-# will not generate output for the diagram.
-
-PLANTUML_JAR_PATH      =
-
-# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
-# configuration file for plantuml.
-
-PLANTUML_CFG_FILE      =
-
-# When using plantuml, the specified paths are searched for files specified by
-# the !include statement in a plantuml block.
-
-PLANTUML_INCLUDE_PATH  =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
-# that will be shown in the graph. If the number of nodes in a graph becomes
-# larger than this value, doxygen will truncate the graph, which is visualized
-# by representing a node as a red box. Note that doxygen if the number of direct
-# children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
-# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-# Minimum value: 0, maximum value: 10000, default value: 50.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_GRAPH_MAX_NODES    = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
-# generated by dot. A depth value of 3 means that only nodes reachable from the
-# root by following a path via at most 3 edges will be shown. Nodes that lay
-# further from the root node will be omitted. Note that setting this option to 1
-# or 2 may greatly reduce the computation time needed for large code bases. Also
-# note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-# Minimum value: 0, maximum value: 1000, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-MAX_DOT_GRAPH_DEPTH    = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not seem
-# to support this out of the box.
-#
-# Warning: Depending on the platform used, enabling this option may lead to
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
-# read).
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_TRANSPARENT        = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10) support
-# this, this feature is disabled by default.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_MULTI_TARGETS      = NO
-
-# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
-# explaining the meaning of the various boxes and arrows in the dot generated
-# graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GENERATE_LEGEND        = YES
-
-# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
-# files that are used to generate the various graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_CLEANUP            = YES
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index b3cb8b927..724fe321f 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -6,31 +6,21 @@ on:
 
 jobs:
   docs:
-    runs-on: ubuntu-latest
+    runs-on: macos-latest
     steps:
       - name: Checkout
         uses: actions/checkout@v2
 
-      - name: Get version
-        id: get_version
-        run: |
-          echo $(cat VERSION)
-          echo ::set-output name=VERSION::$(cat VERSION)
-
-      - name: Update Doxyfile version
-        env:
-          VERSION: ${{ steps.get_version.outputs.VERSION }}
-        run: sed -i -e "s|SNOWPLOW_TRACKER_VERSION|${VERSION}|g" ./.doxygen/Doxyfile
+      - name: Set up Swift
+        uses: swift-actions/setup-swift@v1
 
-      - name: Doxygen
-        uses: mattnotmitt/doxygen-action@v1
-        with:
-          working-directory: '.doxygen'
-          doxyfile-path: './Doxyfile'
+      - name: Generate documentation
+        run: |
+          swift package --allow-writing-to-directory docs generate-documentation --target SnowplowTracker --disable-indexing --output-path docs --transform-for-static-hosting --hosting-base-path snowplow-objc-tracker
     
       - name: Deploy to GitHub Pages
         if: success()
         uses: peaceiris/actions-gh-pages@v3
         with:
           github_token: ${{ secrets.GITHUB_TOKEN }}
-          publish_dir: ./.doxygen/html
+          publish_dir: ./docs
diff --git a/.gitignore b/.gitignore
index 73dc2c3e4..ffd88b982 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,7 +39,8 @@ Carthage/
 Frameworks/
 
 # Documentation
-/docs/html/
+/docs
 
 # Swift Package Manager
 .swiftpm
+.build
diff --git a/Package.resolved b/Package.resolved
index 0d5690d38..90cd82c2e 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -18,6 +18,15 @@
           "revision": "5d86f27a8f80d4ba388bc1a379a3c2289a1f3d18",
           "version": "2.6.0"
         }
+      },
+      {
+        "package": "SwiftDocCPlugin",
+        "repositoryURL": "https://github.com/apple/swift-docc-plugin",
+        "state": {
+          "branch": null,
+          "revision": "3303b164430d9a7055ba484c8ead67a52f7b74f6",
+          "version": "1.0.0"
+        }
       }
     ]
   },
diff --git a/Package.swift b/Package.swift
index 17509b0a7..50f680904 100644
--- a/Package.swift
+++ b/Package.swift
@@ -33,3 +33,8 @@ let package = Package(
             path: "Tests")
     ]
 )
+#if swift(>=5.6)
+package.dependencies += [
+    .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
+]
+#endif
diff --git a/README.md b/README.md
index 702fcbdf8..2abfc9097 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@ limitations under the License.
 [tech-docs]: https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/mobile-trackers/
 [tech-docs-image]: https://d3i6fms1cm1j0i.cloudfront.net/github/images/techdocs.png
 
-[api-docs]: https://snowplow.github.io/snowplow-objc-tracker/
+[api-docs]: https://snowplow.github.io/snowplow-objc-tracker/documentation/snowplowtracker/snowplow/
 
 [contributing-image]: https://d3i6fms1cm1j0i.cloudfront.net/github/images/contributing.png
 

From 79082629df26be871cd6f26d37fb0769b30c78e2 Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Mon, 12 Dec 2022 13:46:39 +0100
Subject: [PATCH 06/19] Update API comments for Swift-DocC and add missing
 comments (close #740)

PR #742
---
 Sources/Core/Utils/Utilities.swift            |  7 +--
 .../Configurations/Configuration.swift        |  1 +
 .../Configurations/NetworkConfiguration.swift |  3 +-
 .../Configurations/SessionConfiguration.swift |  1 +
 .../Configurations/SubjectConfiguration.swift |  1 +
 .../Configurations/TrackerConfiguration.swift |  1 +
 Sources/Snowplow/Emitter/EventStore.swift     |  1 +
 .../Snowplow/Entities/DeepLinkEntity.swift    | 10 ++--
 .../Snowplow/Entities/LifecycleEntity.swift   |  4 +-
 Sources/Snowplow/Events/Background.swift      |  6 ++-
 Sources/Snowplow/Events/ConsentDocument.swift |  4 +-
 Sources/Snowplow/Events/ConsentGranted.swift  | 24 +++++----
 .../Snowplow/Events/ConsentWithdrawn.swift    | 10 ++--
 .../Snowplow/Events/DeepLinkReceived.swift    | 14 +++---
 Sources/Snowplow/Events/Ecommerce.swift       |  5 +-
 Sources/Snowplow/Events/EcommerceItem.swift   |  5 +-
 Sources/Snowplow/Events/EventBase.swift       |  6 ++-
 Sources/Snowplow/Events/Foreground.swift      |  6 ++-
 .../Snowplow/Events/MessageNotification.swift | 15 +++---
 .../MessageNotificationAttachment.swift       |  3 +-
 Sources/Snowplow/Events/PageView.swift        | 11 ++---
 .../Snowplow/Events/PushNotification.swift    | 46 ++++++++++++++---
 Sources/Snowplow/Events/SNOWError.swift       | 12 ++++-
 Sources/Snowplow/Events/ScreenView.swift      | 13 ++---
 Sources/Snowplow/Events/SelfDescribing.swift  | 49 +++++++++----------
 Sources/Snowplow/Events/Structured.swift      | 17 ++++++-
 Sources/Snowplow/Events/Timing.swift          | 12 +++--
 Sources/Snowplow/Events/TrackerError.swift    | 16 +++++-
 Sources/Snowplow/Snowplow.swift               | 32 ++++++++++--
 .../Snowplow/Tracker/InspectableEvent.swift   |  4 +-
 30 files changed, 230 insertions(+), 109 deletions(-)

diff --git a/Sources/Core/Utils/Utilities.swift b/Sources/Core/Utils/Utilities.swift
index 3292f2aef..4e9f15d5d 100644
--- a/Sources/Core/Utils/Utilities.swift
+++ b/Sources/Core/Utils/Utilities.swift
@@ -119,10 +119,11 @@ class Utilities {
         return Bundle.main.bundleIdentifier
     }
     
-    /// @brief URL encodes a dictionary as key=value pairs separated by &, so that it can be used in a query-string.
-    /// This method can encode string, numbers, and bool values, and not embedded arrays or dictionaries.
+    /// URL encodes a dictionary as key=value pairs separated by &, so that it can be used in a query-string.
+    ///
+    /// This method can encode string, numbers, and bool values, but not embedded arrays or dictionaries.
     /// It encodes bool as 1 and 0.
-    /// @return The url encoded string of the dictionary.
+    /// - Returns: The url encoded string of the dictionary.
     class func urlEncode(_ dictionary: [String : NSObject]) -> String {
         return dictionary.map { (key: String, value: NSObject) in
             "\(self.urlEncode(key))=\(self.urlEncode(value.description))"
diff --git a/Sources/Snowplow/Configurations/Configuration.swift b/Sources/Snowplow/Configurations/Configuration.swift
index 1691da887..bab1ac899 100644
--- a/Sources/Snowplow/Configurations/Configuration.swift
+++ b/Sources/Snowplow/Configurations/Configuration.swift
@@ -20,6 +20,7 @@
 
 import Foundation
 
+/// Common parent class for configuration classes.
 @objc(SPConfiguration)
 public class Configuration: NSObject, NSCopying, NSSecureCoding {
     @objc
diff --git a/Sources/Snowplow/Configurations/NetworkConfiguration.swift b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
index 64a5b034b..4a4dab6d0 100644
--- a/Sources/Snowplow/Configurations/NetworkConfiguration.swift
+++ b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
@@ -20,8 +20,7 @@
 
 import Foundation
 
-/// Represents the network communication configuration
-/// allowing the tracker to be able to send events to the Snowplow collector.
+/// Represents the network communication configuration allowing the tracker to be able to send events to the Snowplow collector.
 @objc(SPNetworkConfiguration)
 public class NetworkConfiguration: Configuration {
     /// URL (without schema/protocol) used to send events to the collector.
diff --git a/Sources/Snowplow/Configurations/SessionConfiguration.swift b/Sources/Snowplow/Configurations/SessionConfiguration.swift
index 75f7206de..6d8184702 100644
--- a/Sources/Snowplow/Configurations/SessionConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SessionConfiguration.swift
@@ -20,6 +20,7 @@
 
 import Foundation
 
+/// Configuration for session management.
 @objc(SPSessionConfigurationProtocol)
 public protocol SessionConfigurationProtocol: AnyObject {
     /// The amount of time that can elapse before the
diff --git a/Sources/Snowplow/Configurations/SubjectConfiguration.swift b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
index a96ca9edd..c70aeae9f 100644
--- a/Sources/Snowplow/Configurations/SubjectConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
@@ -21,6 +21,7 @@
 import CoreGraphics
 import Foundation
 
+/// Configuration for the current user and device information that is tracked along with events.
 @objc(SPSubjectConfigurationProtocol)
 public protocol SubjectConfigurationProtocol: AnyObject {
     /// The custom UserID.
diff --git a/Sources/Snowplow/Configurations/TrackerConfiguration.swift b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
index 7e31c9d28..27323d278 100644
--- a/Sources/Snowplow/Configurations/TrackerConfiguration.swift
+++ b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
@@ -21,6 +21,7 @@
 
 import Foundation
 
+/// Configuration of tracker properties.
 @objc(SPTrackerConfigurationProtocol)
 public protocol TrackerConfigurationProtocol: AnyObject {
     /// Identifer of the app.
diff --git a/Sources/Snowplow/Emitter/EventStore.swift b/Sources/Snowplow/Emitter/EventStore.swift
index fbe89fb19..8a00894fd 100644
--- a/Sources/Snowplow/Emitter/EventStore.swift
+++ b/Sources/Snowplow/Emitter/EventStore.swift
@@ -21,6 +21,7 @@
 
 import Foundation
 
+/// Protocol to implement storage for events that are queued to be sent.
 @objc(SPEventStore)
 public protocol EventStore: NSObjectProtocol {
     /// Adds an event to the store.
diff --git a/Sources/Snowplow/Entities/DeepLinkEntity.swift b/Sources/Snowplow/Entities/DeepLinkEntity.swift
index 8c84ff69e..c01420dae 100644
--- a/Sources/Snowplow/Entities/DeepLinkEntity.swift
+++ b/Sources/Snowplow/Entities/DeepLinkEntity.swift
@@ -21,17 +21,21 @@
 import Foundation
 
 /// Entity that indicates a deep-link has been received and processed.
+///
+/// Schema: `iglu:com.snowplowanalytics.mobile/deep_link/jsonschema/1-0-0`
 @objc(SPDeepLinkEntity)
 public class DeepLinkEntity: SelfDescribingJson {
     @objc
-    public static let schema = "iglu:com.snowplowanalytics.mobile/deep_link/jsonschema/1-0-0"
+    static let schema = "iglu:com.snowplowanalytics.mobile/deep_link/jsonschema/1-0-0"
     @objc
-    public static let paramReferrer = "referrer"
+    static let paramReferrer = "referrer"
     @objc
-    public static let paramUrl = "url"
+    static let paramUrl = "url"
     
+    /// URL in the received deep-link
     @objc
     public var url: String
+    /// Referrer URL, source of this deep-link
     @objc
     public var referrer: String?
 
diff --git a/Sources/Snowplow/Entities/LifecycleEntity.swift b/Sources/Snowplow/Entities/LifecycleEntity.swift
index 2ea704f58..cfbf88f14 100644
--- a/Sources/Snowplow/Entities/LifecycleEntity.swift
+++ b/Sources/Snowplow/Entities/LifecycleEntity.swift
@@ -20,11 +20,13 @@
 
 import Foundation
 
-/// Entity that indicates the state of the app is visible (foreground) when the event is tracked.
 let kSPLifecycleEntitySchema = "iglu:com.snowplowanalytics.mobile/application_lifecycle/jsonschema/1-0-0"
 let kSPLifecycleEntityParamIndex = "index"
 let kSPLifecycleEntityParamIsVisible = "isVisible"
 
+/// Entity that indicates the state of the app is visible (foreground) when the event is tracked.
+///
+/// Schema: `iglu:com.snowplowanalytics.mobile/application_lifecycle/jsonschema/1-0-0`
 @objc(SPLifecycleEntity)
 public class LifecycleEntity: SelfDescribingJson {
 
diff --git a/Sources/Snowplow/Events/Background.swift b/Sources/Snowplow/Events/Background.swift
index 608d2e905..99ae63632 100644
--- a/Sources/Snowplow/Events/Background.swift
+++ b/Sources/Snowplow/Events/Background.swift
@@ -22,6 +22,8 @@
 import Foundation
 
 /// A background transition event.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0`
 @objc(SPBackground)
 public class Background: SelfDescribingAbstract {
     /// Index indicating the current transition.
@@ -35,11 +37,11 @@ public class Background: SelfDescribingAbstract {
         self.index = index
     }
 
-    override public var schema: String {
+    override var schema: String {
         return kSPBackgroundSchema
     }
 
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [AnyHashable : Any] = [:]
         payload[kSPBackgroundIndex] = NSNumber(value: index)
         return payload as? [String : NSObject] ?? [:]
diff --git a/Sources/Snowplow/Events/ConsentDocument.swift b/Sources/Snowplow/Events/ConsentDocument.swift
index d3debbf8c..510861326 100644
--- a/Sources/Snowplow/Events/ConsentDocument.swift
+++ b/Sources/Snowplow/Events/ConsentDocument.swift
@@ -22,6 +22,8 @@
 import Foundation
 
 /// A consent document event.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0`
 @objc(SPConsentDocument)
 public class ConsentDocument: NSObject {
     /// Identifier of the document.
@@ -48,7 +50,7 @@ public class ConsentDocument: NSObject {
     }
 
     /// Returns the payload.
-    public var payload: SelfDescribingJson {
+    var payload: SelfDescribingJson {
         var event: [String : String] = [:]
         event[kSPCdId] = documentId
         event[kSPCdVersion] = version
diff --git a/Sources/Snowplow/Events/ConsentGranted.swift b/Sources/Snowplow/Events/ConsentGranted.swift
index d420489c6..274fa2351 100644
--- a/Sources/Snowplow/Events/ConsentGranted.swift
+++ b/Sources/Snowplow/Events/ConsentGranted.swift
@@ -22,6 +22,8 @@
 import Foundation
 
 /// A consent granted event.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/consent_granted/jsonschema/1-0-0`
 @objc(SPConsentGranted)
 public class ConsentGranted: SelfDescribingAbstract {
     /// Expiration of the consent.
@@ -40,6 +42,8 @@ public class ConsentGranted: SelfDescribingAbstract {
     @objc
     public var documentDescription: String?
     /// Other attached documents.
+    ///
+    /// Schema for the documents: `iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0`
     @objc
     public var documents: [SelfDescribingJson]?
 
@@ -55,16 +59,6 @@ public class ConsentGranted: SelfDescribingAbstract {
         self.version = version
     }
 
-    override public var schema: String {
-        return kSPConsentGrantedSchema
-    }
-
-    override public var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[KSPCgExpiry] = expiry as NSObject
-        return payload
-    }
-
     /// Retuns the full list of attached documents.
     @objc
     public var allDocuments: [SelfDescribingJson] {
@@ -85,6 +79,16 @@ public class ConsentGranted: SelfDescribingAbstract {
         return results
     }
 
+    override var schema: String {
+        return kSPConsentGrantedSchema
+    }
+
+    override var payload: [String : NSObject] {
+        var payload: [String : NSObject] = [:]
+        payload[KSPCgExpiry] = expiry as NSObject
+        return payload
+    }
+
     override func beginProcessing(withTracker tracker: Tracker) {
         contexts.append(contentsOf: allDocuments) // TODO: Only the user should modify the public contexts property
     }
diff --git a/Sources/Snowplow/Events/ConsentWithdrawn.swift b/Sources/Snowplow/Events/ConsentWithdrawn.swift
index d947f1a5e..cb59df0a5 100644
--- a/Sources/Snowplow/Events/ConsentWithdrawn.swift
+++ b/Sources/Snowplow/Events/ConsentWithdrawn.swift
@@ -22,6 +22,8 @@
 import Foundation
 
 /// A consent withdrawn event.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/consent_withdrawn/jsonschema/1-0-0`
 @objc(SPConsentWithdrawn)
 public class ConsentWithdrawn: SelfDescribingAbstract {
     /// Consent to all.
@@ -40,21 +42,23 @@ public class ConsentWithdrawn: SelfDescribingAbstract {
     @objc
     public var documentDescription: String?
     /// Other documents.
+    ///
+    /// Schema for the documents: `iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0`
     @objc
     public var documents: [SelfDescribingJson]?
 
-    public override var schema: String {
+    override var schema: String {
         return kSPConsentWithdrawnSchema
     }
 
-    public override var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         return [
             KSPCwAll: all ? NSNumber(value: true) : NSNumber(value: false)
         ]
     }
 
     @objc
-    public var allDocuments: [SelfDescribingJson] {
+    var allDocuments: [SelfDescribingJson] {
         var results: [SelfDescribingJson] = []
         guard let documentId = documentId, let version = version else { return results }
 
diff --git a/Sources/Snowplow/Events/DeepLinkReceived.swift b/Sources/Snowplow/Events/DeepLinkReceived.swift
index 09404729c..b4bfc5467 100644
--- a/Sources/Snowplow/Events/DeepLinkReceived.swift
+++ b/Sources/Snowplow/Events/DeepLinkReceived.swift
@@ -21,6 +21,8 @@
 import Foundation
 
 /// A deep-link received in the app.
+///
+/// Schema: `iglu:com.snowplowanalytics.mobile/deep_link_received/jsonschema/1-0-0`
 @objc(SPDeepLinkReceived)
 public class DeepLinkReceived: SelfDescribingAbstract {
     /// Referrer URL, source of this deep-link.
@@ -31,32 +33,32 @@ public class DeepLinkReceived: SelfDescribingAbstract {
     public var url: String
 
     /// Creates a deep-link received event.
-    /// @param url URL in the received deep-link.
+    /// - Parameter url: URL in the received deep-link.
     @objc
     public init(url: String) {
         self.url = url
     }
     
     @objc
-    public class var schema: String {
+    class var schema: String {
         return "iglu:com.snowplowanalytics.mobile/deep_link_received/jsonschema/1-0-0"
     }
 
     @objc
-    public class var paramUrl: String {
+    class var paramUrl: String {
         return "url"
     }
 
     @objc
-    public class var paramReferrer: String {
+    class var paramReferrer: String {
         return "referrer"
     }
 
-    public override var schema: String {
+    override var schema: String {
         return DeepLinkReceived.schema
     }
 
-    public override var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         if let referrer = referrer {
             payload[DeepLinkReceived.paramReferrer] = referrer as NSObject
diff --git a/Sources/Snowplow/Events/Ecommerce.swift b/Sources/Snowplow/Events/Ecommerce.swift
index 3b36affd0..ef21819e2 100644
--- a/Sources/Snowplow/Events/Ecommerce.swift
+++ b/Sources/Snowplow/Events/Ecommerce.swift
@@ -61,12 +61,11 @@ public class Ecommerce : PrimitiveAbstract {
         self.items = items ?? []
     }
 
-    @objc
-    override public var eventName: String {
+    override var eventName: String {
         return kSPEventEcomm
     }
 
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPEcommTotal] = String(format: "%.02f", totalValue) as NSObject
         if let taxValue = taxValue {
diff --git a/Sources/Snowplow/Events/EcommerceItem.swift b/Sources/Snowplow/Events/EcommerceItem.swift
index d1d7ef3c6..97ab77946 100644
--- a/Sources/Snowplow/Events/EcommerceItem.swift
+++ b/Sources/Snowplow/Events/EcommerceItem.swift
@@ -52,12 +52,11 @@ public class EcommerceItem : PrimitiveAbstract {
         self.quantity = quantity
     }
 
-    @objc
-    override public var eventName: String {
+    override var eventName: String {
         return kSPEventEcommItem
     }
 
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPEcommItemId] = orderId as NSObject?
         payload[kSPEcommItemSku] = sku as NSObject
diff --git a/Sources/Snowplow/Events/EventBase.swift b/Sources/Snowplow/Events/EventBase.swift
index 165a8752f..d352806d7 100644
--- a/Sources/Snowplow/Events/EventBase.swift
+++ b/Sources/Snowplow/Events/EventBase.swift
@@ -27,11 +27,13 @@ public class Event: NSObject {
     /// The user event timestamp in milliseconds (epoch time).
     @objc
     public var trueTimestamp: Date?
+    
     /// The contexts attached to the event.
     @objc
     public var contexts: [SelfDescribingJson] = []
+    
     /// The payload of the event.
-    public var payload: [String : NSObject] {
+    var payload: [String : NSObject] {
         NSException(
             name: .internalInconsistencyException,
             reason: "You must override \(NSStringFromSelector(#function)) in a subclass",
@@ -55,7 +57,7 @@ public class Event: NSObject {
 public class SelfDescribingAbstract: Event {
     /// The schema of the event.
     @objc
-    public var schema: String {
+    var schema: String {
         NSException(
             name: .internalInconsistencyException,
             reason: "You must override \(NSStringFromSelector(#function)) in a subclass",
diff --git a/Sources/Snowplow/Events/Foreground.swift b/Sources/Snowplow/Events/Foreground.swift
index fa8abcc45..ef8c2fe57 100644
--- a/Sources/Snowplow/Events/Foreground.swift
+++ b/Sources/Snowplow/Events/Foreground.swift
@@ -22,6 +22,8 @@
 import Foundation
 
 /// A foreground transition event.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/application_foreground/jsonschema/1-0-0`
 @objc(SPForeground)
 public class Foreground: SelfDescribingAbstract {
     /// Indicate the current transition.
@@ -35,11 +37,11 @@ public class Foreground: SelfDescribingAbstract {
         self.index = index
     }
 
-    override public var schema: String {
+    override var schema: String {
         return kSPForegroundSchema
     }
 
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPForegroundIndex] = NSNumber(value: index)
         return payload
diff --git a/Sources/Snowplow/Events/MessageNotification.swift b/Sources/Snowplow/Events/MessageNotification.swift
index a0b8c838a..648a8338a 100644
--- a/Sources/Snowplow/Events/MessageNotification.swift
+++ b/Sources/Snowplow/Events/MessageNotification.swift
@@ -57,6 +57,8 @@ func triggerToString(_ trigger: MessageNotificationTrigger) -> String {
 }
 
 /// An event that represents the reception of a push notification (or a locally generated one).
+///
+/// Schema: `iglu:com.snowplowanalytics.mobile/message_notification/jsonschema/1-0-0`
 @objc(SPMessageNotification)
 public class MessageNotification : SelfDescribingAbstract {
     /// The action associated with the notification.
@@ -117,9 +119,9 @@ public class MessageNotification : SelfDescribingAbstract {
     
     /// Creates a Message Notification event that represents a push notification or a local notification.
     /// @note The custom data of the push notification have to be tracked separately in custom entities that can be attached to this event.
-    /// @param title Title of message notification.
-    /// @param body Body content of the message notification.
-    /// @param trigger The trigger that raised this notification: remote notification (push), position related (location), date-time related (calendar, timeInterval) or app generated (other).
+    /// - Parameter title: Title of message notification.
+    /// - Parameter body: Body content of the message notification.
+    /// - Parameter trigger: The trigger that raised this notification: remote notification (push), position related (location), date-time related (calendar, timeInterval) or app generated (other).
     @objc
     public init(title: String, body: String, trigger: MessageNotificationTrigger) {
         self.title = title
@@ -127,8 +129,7 @@ public class MessageNotification : SelfDescribingAbstract {
         self.trigger = trigger
     }
     
-    @objc
-    public class func messageNotification(userInfo: [String: NSObject], defaultTitle: String?, defaultBody: String?) -> MessageNotification? {
+    class func messageNotification(userInfo: [String: NSObject], defaultTitle: String?, defaultBody: String?) -> MessageNotification? {
         guard let aps = userInfo["aps"] as? [String : NSObject] else {
             return nil
         }
@@ -156,11 +157,11 @@ public class MessageNotification : SelfDescribingAbstract {
         return event
     }
     
-    public override var schema: String {
+    override var schema: String {
         return kSPMessageNotificationSchema
     }
     
-    public override var payload: [String: NSObject] {
+    override var payload: [String: NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPMessageNotificationParamAction] = action as NSObject?
         if let attachments = attachments {
diff --git a/Sources/Snowplow/Events/MessageNotificationAttachment.swift b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
index 095b05deb..08c8264ce 100644
--- a/Sources/Snowplow/Events/MessageNotificationAttachment.swift
+++ b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
@@ -42,8 +42,7 @@ public class MessageNotificationAttachment : NSObject {
         self.url = url
     }
     
-    @objc
-    public var data: [String : NSObject] {
+    var data: [String : NSObject] {
         return [
             kSPMessageNotificationAttachmentParamIdentifier: identifer as NSObject,
             kSPMessageNotificationAttachmentParamType: type as NSObject,
diff --git a/Sources/Snowplow/Events/PageView.swift b/Sources/Snowplow/Events/PageView.swift
index 5ba37f865..920c8c390 100644
--- a/Sources/Snowplow/Events/PageView.swift
+++ b/Sources/Snowplow/Events/PageView.swift
@@ -36,20 +36,19 @@ public class PageView : PrimitiveAbstract {
     public var referrer: String?
 
     /// Creates a Page View event
-    /// @param pageUrl Page URL
-    /// @param pageTitle Page title
-    /// @param referrer Page referrer URL
+    /// - Parameter pageUrl: Page URL
+    /// - Parameter pageTitle: Page title
+    /// - Parameter referrer: Page referrer URL
     @objc
     public init(pageUrl: String) {
         self.pageUrl = pageUrl
     }
     
-    @objc
-    override public var eventName: String {
+    override var eventName: String {
         return kSPEventPageView
     }
     
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String: NSObject] = [
             kSPPageUrl: pageUrl as NSObject
         ]
diff --git a/Sources/Snowplow/Events/PushNotification.swift b/Sources/Snowplow/Events/PushNotification.swift
index 114526cc4..2f8214f24 100644
--- a/Sources/Snowplow/Events/PushNotification.swift
+++ b/Sources/Snowplow/Events/PushNotification.swift
@@ -23,21 +23,37 @@ import Foundation
 import UserNotifications
 #endif
 
+/// Push notification event.
+///
+/// Schema: `iglu:com.apple/notification_event/jsonschema/1-0-1`
 @objc(SPPushNotification)
 public class PushNotification : SelfDescribingAbstract {
+    /// The delivery date of the notification.
     @objc
     public var date: String
+    /// The action associated with the notification.
     @objc
     public var action: String
+    /// The trigger that raised this notification: remote notification (`PUSH`), position related (`LOCATION`), date-time related (`CALENDAR`, `TIME_INTERVAL`).
     @objc
     public var trigger: String
+    /// The category associated to the notification.
     @objc
     public var category: String
+    /// An identifier for the thread.
     @objc
     public var thread: String
+    /// Notification content
     @objc
     public var notification: NotificationContent?
 
+    /// Creates a notification event
+    /// - Parameter date: The delivery date of the notification.
+    /// - Parameter action: The action associated with the notification.
+    /// - Parameter trigger: The trigger that raised this notification: remote notification (`PUSH`), position related (`LOCATION`), date-time related (`CALENDAR`, `TIME_INTERVAL`).
+    /// - Parameter category: The category associated to the notification.
+    /// - Parameter thread: An identifier for the thread.
+    /// - Parameter notification: Notification content.
     @objc
     public init(date: String, action: String, trigger: String, category: String, thread: String, notification: NotificationContent?) {
         self.date = date
@@ -50,6 +66,13 @@ public class PushNotification : SelfDescribingAbstract {
 
     #if os(iOS)
 
+    /// Creates a notification event
+    /// - Parameter date: The delivery date of the notification.
+    /// - Parameter action: The action associated with the notification.
+    /// - Parameter notificationTrigger: The trigger that raised this notification: remote notification (`PUSH`), position related (`LOCATION`), date-time related (`CALENDAR`, `TIME_INTERVAL`).
+    /// - Parameter category: The category associated to the notification.
+    /// - Parameter thread: An identifier for the thread.
+    /// - Parameter notification: Notification content.
     @objc
     public init(date: String, action: String, notificationTrigger trigger: UNNotificationTrigger?, category: String, thread: String, notification: NotificationContent?) {
         self.date = date
@@ -60,8 +83,7 @@ public class PushNotification : SelfDescribingAbstract {
         self.notification = notification
     }
 
-    @objc
-    public class func string(from trigger: UNNotificationTrigger?) -> String {
+    class func string(from trigger: UNNotificationTrigger?) -> String {
         var triggerType = "UNKNOWN"
         if let trigger = trigger {
             let triggerClass = NSStringFromClass(type(of: trigger).self)
@@ -80,11 +102,11 @@ public class PushNotification : SelfDescribingAbstract {
 
     #endif
 
-    public override var schema: String {
+    override var schema: String {
         return kSPPushNotificationSchema
     }
 
-    public override var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var data: [String: NSObject] = [
             kSPPushTrigger: trigger as NSObject,
             kSPPushAction: action as NSObject,
@@ -99,25 +121,38 @@ public class PushNotification : SelfDescribingAbstract {
 
 // MARK:- SPNotificationContent
 
+/// Content for a notification.
 @objc(SPNotificationContent)
 public class NotificationContent : NSObject {
+    /// Title of message notification.
     @objc
     public var title: String
+    /// Body content of the message notification.
     @objc
     public var body: String
+    /// The number that the app’s icon displays.
     @objc
     public var badge: NSNumber?
+    /// The notification's subtitle.
     @objc
     public var subtitle: String?
+    /// The sound played when the device receives the notification.
     @objc
     public var sound: String?
+    /// The name of the image or storyboard to use when your app launches because of the notification.
     @objc
     public var launchImageName: String?
+    /// The custom data associated with the notification.
     @objc
     public var userInfo: [String : NSObject]?
+    /// Attachments added to the notification (they can be part of the data object).
     @objc
     public var attachments: [NSObject]?
 
+    /// Creates a notification content
+    /// - Parameter title: Title of message notification.
+    /// - Parameter body: Body content of the message notification.
+    /// - Parameter badge: The number that the app’s icon displays.
     @objc
     public init(title: String, body: String, badge: NSNumber?) {
         self.title = title
@@ -125,8 +160,7 @@ public class NotificationContent : NSObject {
         self.badge = badge
     }
 
-    @objc
-    public var payload: [String : NSObject] {
+    var payload: [String : NSObject] {
         var event: [String : NSObject] = [:]
         event[kSPPnTitle] = title as NSObject
         event[kSPPnBody] = body as NSObject
diff --git a/Sources/Snowplow/Events/SNOWError.swift b/Sources/Snowplow/Events/SNOWError.swift
index 807afc999..df24b1ad5 100644
--- a/Sources/Snowplow/Events/SNOWError.swift
+++ b/Sources/Snowplow/Events/SNOWError.swift
@@ -21,25 +21,33 @@
 
 import Foundation
 
+/// Error event tracked by exception autotracking.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-2`
 @objc(SPSNOWError)
 public class SNOWError: SelfDescribingAbstract {
+    /// Error message
     @objc
     public var message: String
+    /// Error name
     @objc
     public var name: String?
+    /// Stacktrace for the error
     @objc
     public var stackTrace: String?
     
+    /// Creates a SNOWError event.
+    /// - Parameter message: Error message
     @objc
     public init(message: String) {
         self.message = message
     }
     
-    override public var schema: String {
+    override var schema: String {
         return kSPErrorSchema
     }
 
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPErrorMessage] = message as NSObject
         payload[kSPErrorStackTrace] = stackTrace as NSObject?
diff --git a/Sources/Snowplow/Events/ScreenView.swift b/Sources/Snowplow/Events/ScreenView.swift
index a51078b65..06f383994 100644
--- a/Sources/Snowplow/Events/ScreenView.swift
+++ b/Sources/Snowplow/Events/ScreenView.swift
@@ -36,6 +36,8 @@ public enum ScreenType : Int {
 }
 
 /// A screenview event.
+///
+/// Schema: `iglu:com.snowplowanalytics.mobile/screen_view/jsonschema/1-0-0`
 @objc(SPScreenView)
 public class ScreenView: SelfDescribingAbstract {
     /// Name of the screen.
@@ -67,19 +69,19 @@ public class ScreenView: SelfDescribingAbstract {
     public var topViewControllerClassName: String?
 
     /// Creates a screenview event.
-    /// @param name Name of the screen.
-    /// @param screenId Identifier of the screen.
+    /// - Parameter name: Name of the screen.
+    /// - Parameter screenId: Identifier of the screen.
     @objc
     public init(name: String, screenId: UUID?) {
         self.screenId = screenId ?? UUID()
         self.name = name
     }
 
-    public override var schema: String {
+    override var schema: String {
         return kSPScreenViewSchema
     }
 
-    public override var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPSvName] = name as NSObject
         payload[kSPSvScreenId] = screenId.uuidString as NSObject
@@ -91,8 +93,7 @@ public class ScreenView: SelfDescribingAbstract {
         return payload
     }
 
-    @objc
-    public class func stringWithScreenType(_ screenType: ScreenType) -> String? {
+    class func stringWithScreenType(_ screenType: ScreenType) -> String? {
         let arr = [
             "Default",
             "Navigation",
diff --git a/Sources/Snowplow/Events/SelfDescribing.swift b/Sources/Snowplow/Events/SelfDescribing.swift
index b4f80a73b..334f2b77d 100644
--- a/Sources/Snowplow/Events/SelfDescribing.swift
+++ b/Sources/Snowplow/Events/SelfDescribing.swift
@@ -24,31 +24,6 @@ import Foundation
 /// A self-describing event.
 @objc(SPSelfDescribing)
 public class SelfDescribing: SelfDescribingAbstract {
-    @objc
-    public var eventData: SelfDescribingJson {
-        set {
-            schema = newValue.schema
-            payload = newValue.data as! [String : NSObject]
-        }
-        get {
-            return SelfDescribingJson(schema: schema, andDictionary: payload)
-        }
-    }
-    private var _schema: String
-    @objc
-    override public var schema: String {
-        get { return _schema }
-        set { _schema = newValue }
-    }
-    private var _payload: [String: NSObject]
-    @objc
-    override public var payload: [String : NSObject] {
-        get { return _payload }
-        set {
-            _payload = newValue
-        }
-    }
-
     @objc
     public convenience init(eventData: SelfDescribingJson) {
         self.init(schema: eventData.schema, payload: eventData.data as! [String : NSObject])
@@ -64,4 +39,28 @@ public class SelfDescribing: SelfDescribingAbstract {
         self._schema = schema
         self._payload = payload as [String : NSObject]
     }
+    
+    private var _schema: String
+    override var schema: String {
+        get { return _schema }
+        set { _schema = newValue }
+    }
+    
+    private var _payload: [String: NSObject]
+    override var payload: [String : NSObject] {
+        get { return _payload }
+        set {
+            _payload = newValue
+        }
+    }
+    
+    var eventData: SelfDescribingJson {
+        set {
+            schema = newValue.schema
+            payload = newValue.data as! [String : NSObject]
+        }
+        get {
+            return SelfDescribingJson(schema: schema, andDictionary: payload)
+        }
+    }
 }
diff --git a/Sources/Snowplow/Events/Structured.swift b/Sources/Snowplow/Events/Structured.swift
index c184a3896..a637e488a 100644
--- a/Sources/Snowplow/Events/Structured.swift
+++ b/Sources/Snowplow/Events/Structured.swift
@@ -24,14 +24,27 @@ import Foundation
 /// A structured event.
 @objc(SPStructured)
 public class Structured: PrimitiveAbstract {
+    /// Name for the group of objects you want to track e.g. "media", "ecomm".
     @objc
     public var category: String
+    /// Defines the type of user interaction for the web object.
+    ///
+    /// E.g., "play-video", "add-to-basket".
     @objc
     public var action: String
+    /// Identifies the specific object being actioned.
+    ///
+    /// E.g., ID of the video being played, or the SKU or the product added-to-basket.
     @objc
     public var label: String?
+    /// Describes the object or the action performed on it.
+    ///
+    /// This might be the quantity of an item added to basket
     @objc
     public var property: String?
+    /// Quantifies or further describes the user action.
+    ///
+    /// This might be the price of an item added-to-basket, or the starting time of the video where play was just pressed.
     @objc
     public var value: NSNumber?
 
@@ -42,11 +55,11 @@ public class Structured: PrimitiveAbstract {
     }
 
     @objc
-    override public var eventName: String {
+    override var eventName: String {
         return kSPEventStructured
     }
 
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPStuctCategory] = category as NSObject
         payload[kSPStuctAction] = action as NSObject
diff --git a/Sources/Snowplow/Events/Timing.swift b/Sources/Snowplow/Events/Timing.swift
index a1672d82b..a8f46b58c 100644
--- a/Sources/Snowplow/Events/Timing.swift
+++ b/Sources/Snowplow/Events/Timing.swift
@@ -22,6 +22,8 @@
 import Foundation
 
 /// A timing event.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/timing/jsonschema/1-0-0`
 @objc(SPTiming)
 public class Timing: SelfDescribingAbstract {
     /// The timing category
@@ -38,9 +40,9 @@ public class Timing: SelfDescribingAbstract {
     public var label: String?
 
     /// Creates a timing event
-    /// @param category The timing category
-    /// @param variable The timing variable
-    /// @param timing The time
+    /// - Parameter category: The timing category
+    /// - Parameter variable: The timing variable
+    /// - Parameter timing: The time
     @objc
     public init(category: String, variable: String, timing: Int) {
         self.category = category
@@ -48,11 +50,11 @@ public class Timing: SelfDescribingAbstract {
         self.timing = timing
     }
 
-    public override var schema: String {
+    override var schema: String {
         return kSPUserTimingsSchema
     }
 
-    public override var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPUtCategory] = category as NSObject
         payload[kSPUtVariable] = variable as NSObject
diff --git a/Sources/Snowplow/Events/TrackerError.swift b/Sources/Snowplow/Events/TrackerError.swift
index 6ed30f579..cbd0504c1 100644
--- a/Sources/Snowplow/Events/TrackerError.swift
+++ b/Sources/Snowplow/Events/TrackerError.swift
@@ -25,17 +25,29 @@ let kMaxExceptionNameLength = 1024
 
 import Foundation
 
+/// Tracker error event used in diagnostic autotracking.
+///
+/// Schema: `iglu:com.snowplowanalytics.snowplow/diagnostic_error/jsonschema/1-0-0`
 @objc(SPTrackerError)
 public class TrackerError : SelfDescribingAbstract {
+    /// Class name or source where the error appeared.
     @objc
     public var source: String
+    /// Message of the error.
     @objc
     public var message: String
+    /// Error involved in the error.
     @objc
     public var error: Error?
+    /// Exception involved in the error.
     @objc
     public var exception: NSException?
     
+    /// Create tracker error.
+    /// - Parameter source: Class name or source where the error appeared.
+    /// - Parameter message: Message of the error.
+    /// - Parameter error: Error involved in the error.
+    /// - Parameter exception: Exception involved in the error.
     @objc
     public init(source: String, message: String, error: Error? = nil, exception: NSException? = nil) {
         self.source = source
@@ -44,11 +56,11 @@ public class TrackerError : SelfDescribingAbstract {
         self.exception = exception
     }
     
-    override public var schema: String {
+    override var schema: String {
         return kSPDiagnosticErrorSchema
     }
     
-    override public var payload: [String : NSObject] {
+    override var payload: [String : NSObject] {
         var payload: [String : NSObject] = [:]
         payload[kSPDiagnosticErrorClassName] = source as NSObject
         payload[kSPDiagnosticErrorMessage] = truncate(message, maxLength: kMaxMessageLength) as NSObject
diff --git a/Sources/Snowplow/Snowplow.swift b/Sources/Snowplow/Snowplow.swift
index 0613f09f2..1ba48a459 100644
--- a/Sources/Snowplow/Snowplow.swift
+++ b/Sources/Snowplow/Snowplow.swift
@@ -25,6 +25,20 @@ import WebKit
 #endif
 
 /// Entry point to instance a new Snowplow tracker.
+///
+/// The following example initializes a tracker instance using the ``createTracker(namespace:network:)`` method and tracks a ``SelfDescribing`` event:
+///
+/// ```swift
+/// let tracker = Snowplow.createTracker(
+///     namespace: "ns1",
+///     endpoint: "https://collector.example.com"
+/// )
+/// let event = SelfDescribing(
+///     schema: "iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1",
+///     payload: ["targetUrl": "http://a-target-url.com"]
+/// )
+/// tracker?.track(event)
+/// ```
 @objc(SPSnowplow)
 public class Snowplow: NSObject {
     private static var serviceProviderInstances: [String : ServiceProvider] = [:]
@@ -34,6 +48,7 @@ public class Snowplow: NSObject {
     /// Remote Configuration
 
     /// Setup a single or a set of tracker instances which will be used inside the app to track events.
+    ///
     /// The app can run multiple tracker instances which will be identified by string `namespaces`.
     /// The trackers configuration is automatically download from the endpoint indicated in the `RemoteConfiguration`
     /// passed as argument. For more details see `RemoteConfiguration`.
@@ -68,6 +83,7 @@ public class Snowplow: NSObject {
     }
 
     /// Reconfigure, create or delete the trackers based on the configuration downloaded remotely.
+    ///
     /// The trackers configuration is automatically download from the endpoint indicated in the `RemoteConfiguration`
     /// previously used to setup the trackers.
     ///
@@ -96,6 +112,7 @@ public class Snowplow: NSObject {
     /// Standard Configuration
 
     /// Create a new tracker instance which will be used inside the app to track events.
+    ///
     /// The app can run multiple tracker instances which will be identified by string `namespaces`.
     /// The tracker will be configured with default setting and only the collector endpoint URL need
     /// to be passed for the configuration.
@@ -118,12 +135,13 @@ public class Snowplow: NSObject {
     ///   - method: The method for the requests to the collector (GET or POST).
     /// - Returns: The tracker instance created.
     @objc
-    public class func createTracker(namespace: String, endpoint: String, method: HttpMethodOptions) -> TrackerController? {
+    public class func createTracker(namespace: String, endpoint: String, method: HttpMethodOptions = .post) -> TrackerController? {
         let networkConfiguration = NetworkConfiguration(endpoint: endpoint, method: method)
         return createTracker(namespace: namespace, network: networkConfiguration, configurations: [])
     }
 
     /// Create a new tracker instance which will be used inside the app to track events.
+    ///
     /// The app can run multiple tracker instances which will be identified by string `namespaces`.
     /// The tracker will be configured with default setting and only the collector endpoint URL need
     /// to be passed for the configuration.
@@ -151,6 +169,7 @@ public class Snowplow: NSObject {
     }
 
     /// Create a new tracker instance which will be used inside the app to track events.
+    ///
     /// The app can run multiple tracker instances which will be identified by string `namespaces`.
     /// The tracker will be configured with default setting and only the collector endpoint URL need
     /// to be passed for the configuration.
@@ -186,6 +205,8 @@ public class Snowplow: NSObject {
         }
     }
 
+    /// Get the default tracker instance.
+    ///
     /// The default tracker instance is the first created in the app, but that can be overridden programmatically
     /// calling `setTrackerAsDefault(TrackerController)`.
     @objc
@@ -203,6 +224,7 @@ public class Snowplow: NSObject {
     }
 
     /// Set the passed tracker as default tracker if it's registered as an active tracker in the app.
+    ///
     /// If the passed instance is of a tracker which is already removed (see `removeTracker`) then it can't become the new default tracker
     /// and the operation fails.
     ///
@@ -221,7 +243,8 @@ public class Snowplow: NSObject {
         return false
     }
 
-    /// A tracker can be removed from the active trackers of the app.
+    /// Remove a tracker from the active trackers of the app.
+    ///
     /// Once it has been removed it can't be added again or set as default.
     /// The unique way to resume a removed tracker is creating a new tracker with same namespace and
     /// same configurations.
@@ -246,8 +269,10 @@ public class Snowplow: NSObject {
     }
 
     /// Remove all the trackers.
+    ///
     /// The removed tracker is always stopped.
-    /// See `removeTracker(TrackerController)`
+    ///
+    /// See ``remove(tracker:)`` to remove  a specific tracker.
     @objc
     public class func removeAllTrackers() {
         objc_sync_enter(self)
@@ -269,6 +294,7 @@ public class Snowplow: NSObject {
     #if os(iOS) || os(macOS)
 
     /// Subscribe to events tracked in a Web view using the Snowplow WebView tracker JavaScript library.
+    ///
     /// - Parameter webViewConfiguration: Configuration of the Web view to subscribe to events from
     @objc
     public class func subscribeToWebViewEvents(with webViewConfiguration: WKWebViewConfiguration) {
diff --git a/Sources/Snowplow/Tracker/InspectableEvent.swift b/Sources/Snowplow/Tracker/InspectableEvent.swift
index fd31b0fb4..6c64586e9 100644
--- a/Sources/Snowplow/Tracker/InspectableEvent.swift
+++ b/Sources/Snowplow/Tracker/InspectableEvent.swift
@@ -37,8 +37,8 @@ public protocol InspectableEvent {
     @objc
     var state: TrackerStateSnapshot { get }
     /// Add payload values to the event.
-    /// @param payload Map of values to add to the event payload.
-    /// @return Whether or not the values have been successfully added to the event payload.
+    /// - Parameter payload: Map of values to add to the event payload.
+    /// - Returns: Whether or not the values have been successfully added to the event payload.
     @objc
     func addPayloadValues(_ payload: [String : NSObject]) -> Bool
 }

From 12cc0cf4c8c1685b8e921b76b0154b4819a298fd Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Mon, 19 Dec 2022 08:25:12 +0100
Subject: [PATCH 07/19] Add a closure to tracker configuration that enables
 retrieving IDFA value and replaces the use of SNOWPLOW_IDFA_ENABLED macro
 (close #678)

PR #743
---
 Sources/Core/Subject/PlatformContext.swift    | 10 +-
 Sources/Core/Subject/Subject.swift            |  8 +-
 Sources/Core/Tracker/ServiceProvider.swift    |  1 +
 Sources/Core/Tracker/Tracker.swift            |  8 +-
 .../Tracker/TrackerConfigurationUpdate.swift  | 48 ++++++----
 .../Core/Tracker/TrackerControllerImpl.swift  | 27 ++----
 Sources/Core/Utils/DeviceInfoMonitor.swift    | 82 +----------------
 .../Configurations/TrackerConfiguration.swift |  9 ++
 Tests/Legacy Tests/LegacyTestSubject.swift    |  8 +-
 Tests/TestPlatformContext.swift               | 91 ++++++++++++++-----
 Tests/TestSubject.swift                       |  6 +-
 Tests/Utils/MockDeviceInfoMonitor.swift       |  6 --
 12 files changed, 142 insertions(+), 162 deletions(-)

diff --git a/Sources/Core/Subject/PlatformContext.swift b/Sources/Core/Subject/PlatformContext.swift
index 29e9d3445..35a08430e 100644
--- a/Sources/Core/Subject/PlatformContext.swift
+++ b/Sources/Core/Subject/PlatformContext.swift
@@ -52,7 +52,7 @@ class PlatformContext {
 
     /// Updates and returns payload dictionary with device context information.
     /// - Parameter userAnonymisation: Whether to anonymise user identifiers (IDFA values)
-    func fetchPlatformDict(withUserAnonymisation userAnonymisation: Bool) -> Payload {
+    func fetchPlatformDict(userAnonymisation: Bool, advertisingIdentifierRetriever: (() -> UUID?)?) -> Payload {
         #if os(iOS)
         objc_sync_enter(self)
         let now = Date().timeIntervalSince1970
@@ -71,6 +71,11 @@ class PlatformContext {
             copy.addValueToPayload(nil, forKey: kSPMobileAppleIdfv)
             return copy
         } else {
+            if let retriever = advertisingIdentifierRetriever {
+                if platformDict.dictionary?[kSPMobileAppleIdfa] == nil {
+                    platformDict.addValueToPayload(retriever()?.uuidString, forKey: kSPMobileAppleIdfa)
+                }
+            }
             return platformDict
         }
     }
@@ -104,9 +109,6 @@ class PlatformContext {
         lastUpdatedEphemeralMobileDict = Date().timeIntervalSince1970
 
         if let currentDict = platformDict.dictionary {
-            if currentDict[kSPMobileAppleIdfa] == nil {
-                platformDict.addValueToPayload(deviceInfoMonitor.appleIdfa, forKey: kSPMobileAppleIdfa)
-            }
             if currentDict[kSPMobileAppleIdfv] == nil {
                 platformDict.addValueToPayload(deviceInfoMonitor.appleIdfv, forKey: kSPMobileAppleIdfv)
             }
diff --git a/Sources/Core/Subject/Subject.swift b/Sources/Core/Subject/Subject.swift
index 07152ce16..9ed50fa13 100644
--- a/Sources/Core/Subject/Subject.swift
+++ b/Sources/Core/Subject/Subject.swift
@@ -298,7 +298,7 @@ class Subject : NSObject {
 
     //#pragma clang diagnostic pop
 
-    func getStandardDict(withUserAnonymisation userAnonymisation: Bool) -> Payload? {
+    func getStandardDict(userAnonymisation: Bool) -> Payload? {
         if userAnonymisation {
             var copy = standardDict.dictionary ?? [:]
             copy.removeValue(forKey: kSPUid)
@@ -313,9 +313,11 @@ class Subject : NSObject {
     /// Gets all platform dictionary pairs to decorate event with. Returns nil if not enabled.
     /// - Parameter userAnonymisation: Whether to anonymise user identifiers
     /// - Returns: A SPPayload with all platform specific pairs.
-    func getPlatformDict(withUserAnonymisation userAnonymisation: Bool) -> Payload? {
+    func getPlatformDict(userAnonymisation: Bool, advertisingIdentifierRetriever: (() -> UUID?)?) -> Payload? {
         if platformContext {
-            return platformContextManager.fetchPlatformDict(withUserAnonymisation: userAnonymisation)
+            return platformContextManager.fetchPlatformDict(
+                userAnonymisation: userAnonymisation,
+                advertisingIdentifierRetriever: advertisingIdentifierRetriever)
         } else {
             return nil
         }
diff --git a/Sources/Core/Tracker/ServiceProvider.swift b/Sources/Core/Tracker/ServiceProvider.swift
index b3426dc04..aff795503 100644
--- a/Sources/Core/Tracker/ServiceProvider.swift
+++ b/Sources/Core/Tracker/ServiceProvider.swift
@@ -294,6 +294,7 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
             tracker.installEvent = trackerConfig.installAutotracking
             tracker.trackerDiagnostic = trackerConfig.diagnosticAutotracking
             tracker.userAnonymisation = trackerConfig.userAnonymisation
+            tracker.advertisingIdentifierRetriever = trackerConfig.advertisingIdentifierRetriever
             if let config = gcConfig {
                 tracker.globalContextGenerators = config.contextGenerators
             }
diff --git a/Sources/Core/Tracker/Tracker.swift b/Sources/Core/Tracker/Tracker.swift
index f5c296f83..d516ba129 100644
--- a/Sources/Core/Tracker/Tracker.swift
+++ b/Sources/Core/Tracker/Tracker.swift
@@ -297,6 +297,8 @@ class Tracker: NSObject {
     var isTracking: Bool {
         return dataCollection
     }
+    
+    var advertisingIdentifierRetriever: (() -> UUID?)?
 
     init(trackerNamespace: String,
          appId: String?,
@@ -539,7 +541,7 @@ class Tracker: NSObject {
             payload.addValueToPayload(String(format: "%lld", ttInMilliSeconds), forKey: kSPTrueTimestamp)
         }
         payload.addDictionaryToPayload(trackerData)
-        if let subjectDict = subject?.getStandardDict(withUserAnonymisation: userAnonymisation)?.dictionary {
+        if let subjectDict = subject?.getStandardDict(userAnonymisation: userAnonymisation)?.dictionary {
             payload.addDictionaryToPayload(subjectDict)
         }
         payload.addValueToPayload(devicePlatformToString(devicePlatform), forKey: kSPPlatform)
@@ -611,7 +613,9 @@ class Tracker: NSObject {
 
     func addBasicContexts(toContexts contexts: inout [SelfDescribingJson], eventId: String, eventTimestamp: Int64, isService: Bool) {
         if subject != nil {
-            if let platformDict = subject?.getPlatformDict(withUserAnonymisation: userAnonymisation)?.dictionary {
+            if let platformDict = subject?.getPlatformDict(
+                userAnonymisation: userAnonymisation,
+                advertisingIdentifierRetriever: advertisingIdentifierRetriever)?.dictionary {
                 contexts.append(SelfDescribingJson(schema: platformContextSchema, andDictionary: platformDict))
             }
             if let geoLocationDict = subject?.getGeoLocationDict() {
diff --git a/Sources/Core/Tracker/TrackerConfigurationUpdate.swift b/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
index 958ba2e40..4c1a85dbb 100644
--- a/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
+++ b/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
@@ -22,26 +22,28 @@
 import Foundation
 
 class TrackerConfigurationUpdate: TrackerConfiguration {
+    private var appIdUpdated = false
+    private var devicePlatformUpdated = false
+    private var base64EncodingUpdated = false
+    private var logLevelUpdated = false
+    private var loggerDelegateUpdated = false
+    private var applicationContextUpdated = false
+    private var platformContextUpdated = false
+    private var geoLocationContextUpdated = false
+    private var deepLinkContextUpdated = false
+    private var sessionContextUpdated = false
+    private var screenContextUpdated = false
+    private var screenViewAutotrackingUpdated = false
+    private var lifecycleAutotrackingUpdated = false
+    private var installAutotrackingUpdated = false
+    private var exceptionAutotrackingUpdated = false
+    private var diagnosticAutotrackingUpdated = false
+    private var userAnonymisationUpdated = false
+    private var trackerVersionSuffixUpdated = false
+    private var advertisingIdentifierRetrieverUpdated = false
+    
     var sourceConfig: TrackerConfiguration?
     var isPaused = false
-    var appIdUpdated = false
-    var devicePlatformUpdated = false
-    var base64EncodingUpdated = false
-    var logLevelUpdated = false
-    var loggerDelegateUpdated = false
-    var applicationContextUpdated = false
-    var platformContextUpdated = false
-    var geoLocationContextUpdated = false
-    var deepLinkContextUpdated = false
-    var sessionContextUpdated = false
-    var screenContextUpdated = false
-    var screenViewAutotrackingUpdated = false
-    var lifecycleAutotrackingUpdated = false
-    var installAutotrackingUpdated = false
-    var exceptionAutotrackingUpdated = false
-    var diagnosticAutotrackingUpdated = false
-    var userAnonymisationUpdated = false
-    var trackerVersionSuffixUpdated = false
 
     override var appId: String {
         get {
@@ -219,4 +221,14 @@ class TrackerConfigurationUpdate: TrackerConfiguration {
             trackerVersionSuffixUpdated = true
         }
     }
+
+    override var advertisingIdentifierRetriever: (() -> UUID?)? {
+        get {
+            return ((sourceConfig == nil) || advertisingIdentifierRetrieverUpdated) ? super.advertisingIdentifierRetriever : sourceConfig?.advertisingIdentifierRetriever
+        }
+        set {
+            super.advertisingIdentifierRetriever = newValue
+            advertisingIdentifierRetrieverUpdated = true
+        }
+    }
 }
diff --git a/Sources/Core/Tracker/TrackerControllerImpl.swift b/Sources/Core/Tracker/TrackerControllerImpl.swift
index f376687f5..7a6c49bd9 100644
--- a/Sources/Core/Tracker/TrackerControllerImpl.swift
+++ b/Sources/Core/Tracker/TrackerControllerImpl.swift
@@ -78,7 +78,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.appId = newValue
-            dirtyConfig.appIdUpdated = true
             tracker.appId = newValue
         }
     }
@@ -93,7 +92,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.devicePlatform = newValue
-            dirtyConfig.devicePlatformUpdated = true
             tracker.devicePlatform = newValue
         }
     }
@@ -104,7 +102,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.base64Encoding = newValue
-            dirtyConfig.base64EncodingUpdated = true
             tracker.base64Encoded = newValue
         }
     }
@@ -115,7 +112,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.logLevel = newValue
-            dirtyConfig.logLevelUpdated = true
             tracker.logLevel = newValue
         }
     }
@@ -135,7 +131,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.applicationContext = newValue
-            dirtyConfig.applicationContextUpdated = true
             tracker.applicationContext = newValue
         }
     }
@@ -146,7 +141,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.platformContext = newValue
-            dirtyConfig.platformContextUpdated = true
             tracker.subject?.platformContext = newValue
         }
     }
@@ -157,7 +151,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.geoLocationContext = newValue
-            dirtyConfig.geoLocationContextUpdated = true
             tracker.subject?.geoLocationContext = newValue
         }
     }
@@ -168,7 +161,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.diagnosticAutotracking = newValue
-            dirtyConfig.diagnosticAutotrackingUpdated = true
             tracker.trackerDiagnostic = newValue
         }
     }
@@ -179,7 +171,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.exceptionAutotracking = newValue
-            dirtyConfig.exceptionAutotrackingUpdated = true
             tracker.exceptionEvents = newValue
         }
     }
@@ -190,7 +181,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.installAutotracking = newValue
-            dirtyConfig.installAutotrackingUpdated = true
             tracker.installEvent = newValue
         }
     }
@@ -201,7 +191,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.lifecycleAutotracking = newValue
-            dirtyConfig.lifecycleAutotrackingUpdated = true
             tracker.lifecycleEvents = newValue
         }
     }
@@ -212,7 +201,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.deepLinkContext = newValue
-            dirtyConfig.deepLinkContextUpdated = true
             tracker.deepLinkContext = newValue
         }
     }
@@ -223,7 +211,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.screenContext = newValue
-            dirtyConfig.screenContextUpdated = true
             tracker.screenContext = newValue
         }
     }
@@ -234,7 +221,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.screenViewAutotracking = newValue
-            dirtyConfig.screenViewAutotrackingUpdated = true
             tracker.autotrackScreenViews = newValue
         }
     }
@@ -245,7 +231,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.trackerVersionSuffix = newValue
-            dirtyConfig.trackerVersionSuffixUpdated = true
             if let value = newValue {
                 tracker.trackerVersionSuffix = value
             }
@@ -258,7 +243,6 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.sessionContext = newValue
-            dirtyConfig.sessionContextUpdated = true
             tracker.sessionContext = newValue
         }
     }
@@ -269,11 +253,20 @@ class TrackerControllerImpl: Controller, TrackerController {
         }
         set {
             dirtyConfig.userAnonymisation = newValue
-            dirtyConfig.userAnonymisationUpdated = true
             tracker.userAnonymisation = newValue
         }
     }
 
+    var advertisingIdentifierRetriever: (() -> UUID?)? {
+        get {
+            return tracker.advertisingIdentifierRetriever
+        }
+        set {
+            dirtyConfig.advertisingIdentifierRetriever = newValue
+            tracker.advertisingIdentifierRetriever = newValue
+        }
+    }
+
     var isTracking: Bool {
         return tracker.isTracking
     }
diff --git a/Sources/Core/Utils/DeviceInfoMonitor.swift b/Sources/Core/Utils/DeviceInfoMonitor.swift
index 64fb72779..129a5e0ba 100644
--- a/Sources/Core/Utils/DeviceInfoMonitor.swift
+++ b/Sources/Core/Utils/DeviceInfoMonitor.swift
@@ -32,95 +32,15 @@ import UIKit
 #endif
 
 class DeviceInfoMonitor {
-    /// Returns a generated string unique to each device, used only for serving advertisements. This works only if you have the AdSupport library in your project and you enable the compiler flag <code>SNOWPLOW_IDFA_ENABLED</code> to your build settings.
-    /// - Returns: A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
-    /*
-     The IDFA can be retrieved using selectors rather than proper instance methods because
-     the compiler would complain about the missing AdSupport framework.
-     As stated in the header file, this only works if you have the AdSupport library in your project.
-     If you have it and you want to use IDFA, add the compiler flag <code>SNOWPLOW_IDFA_ENABLED</code> to your build settings.
-     If you haven't AdSupport framework in your project or SNOWPLOW_IDFA_ENABLED it's not set, it just compiles returning a nil advertisingIdentifier.
-
-     Note that `advertisingIdentifier` returns a sequence of 0s when used in the simulator.
-     Use a real device if you want a proper IDFA.
-     */
-    var appleIdfa: String? {
-        #if os(iOS) || os(tvOS)
-        #if SNOWPLOW_IDFA_ENABLED
-        var errorMsg = "ASIdentifierManager not found. Please, add the AdSupport.framework if you want to use it."
-        let identifierManagerClass: AnyClass? = NSClassFromString("ASIdentifierManager")
-        if identifierManagerClass == nil {
-            logError(message: errorMsg)
-            return nil
-        }
-
-        let sharedManagerSelector = NSSelectorFromString("sharedManager")
-        if !(identifierManagerClass?.responds(to: sharedManagerSelector) ?? false) {
-            logError(message: errorMsg)
-            return nil
-        }
-
-        let identifierManager = (identifierManagerClass?.method(for: sharedManagerSelector))(identifierManagerClass, sharedManagerSelector)
-
-        if #available(iOS 14.0, *) {
-            errorMsg = "ATTrackingManager not found. Please, add the AppTrackingTransparency.framework if you want to use it."
-            let trackingManagerClass: AnyClass? = NSClassFromString("ATTrackingManager")
-            if trackingManagerClass == nil {
-                logError(message: errorMsg)
-                return nil
-            }
-
-            let trackingStatusSelector = NSSelectorFromString("trackingAuthorizationStatus")
-            if !(trackingManagerClass?.responds(to: trackingStatusSelector) ?? false) {
-                logError(message: errorMsg)
-                return nil
-            }
-
-            //notDetermined = 0, restricted = 1, denied = 2, authorized = 3
-            let authorizationStatus = (Int(trackingManagerClass?.method(for: trackingStatusSelector) ?? 0))(trackingManagerClass, trackingStatusSelector)
-
-            if authorizationStatus != 3 {
-                logDebug(message: String(format: "The user didn't let tracking of IDFA. Authorization status is: %d", authorizationStatus))
-                return nil
-            }
-        } else {
-            let isAdvertisingTrackingEnabledSelector = NSSelectorFromString("isAdvertisingTrackingEnabled")
-            if !(identifierManager?.responds(to: isAdvertisingTrackingEnabledSelector) ?? false) {
-                logError(message: errorMsg)
-                return nil
-            }
 
-            let isAdvertisingTrackingEnabled = (Bool(identifierManager?.method(for: isAdvertisingTrackingEnabledSelector) ?? false))(identifierManager, isAdvertisingTrackingEnabledSelector)
-            if !isAdvertisingTrackingEnabled {
-                logError(message: "The user didn't let tracking of IDFA.")
-                return nil
-            }
-        }
-
-        let advertisingIdentifierSelector = NSSelectorFromString("advertisingIdentifier")
-        if !(identifierManager?.responds(to: advertisingIdentifierSelector) ?? false) {
-            logError(message: "ASIdentifierManager doesn't respond to selector `advertisingIdentifier`.")
-            return nil
-        }
-
-        if let uuid = (identifierManager?.method(for: advertisingIdentifierSelector) as? UUID)(identifierManager, advertisingIdentifierSelector) {
-            return uuid.uuidString
-        }
-        #endif
-        #endif
-        return nil
-    }
-
-    /// Returns the generated identifier for vendors. More info can be found in UIDevice's identifierForVendor documentation. If you do not want to use IDFV, add the comiler flag <code>SNOWPLOW_NO_IDFV</code> to your build settings.
+    /// Returns the generated identifier for vendors. More info can be found in UIDevice's identifierForVendor documentation.
     /// - Returns: A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
     var appleIdfv: String? {
         #if os(iOS) || os(tvOS)
-        #if !SNOWPLOW_NO_IDFV
         if let idfv = UIDevice.current.identifierForVendor?.uuidString {
             return idfv
         }
         #endif
-        #endif
         return nil
     }
 
diff --git a/Sources/Snowplow/Configurations/TrackerConfiguration.swift b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
index 27323d278..3608f80f0 100644
--- a/Sources/Snowplow/Configurations/TrackerConfiguration.swift
+++ b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
@@ -80,6 +80,10 @@ public protocol TrackerConfigurationProtocol: AnyObject {
     /// @note Do not use. Internal use only.
     @objc
     var trackerVersionSuffix: String? { get set }
+    /// Closure called to retrieve the Identifier for Advertisers (IDFA) from AdSupport module
+    /// It is called repeatedly (on each tracked event) until a UUID is returned.
+    @objc
+    var advertisingIdentifierRetriever: (() -> UUID?)? { get set }
 }
 
 /// This class represents the configuration of the tracker and the core tracker properties.
@@ -143,6 +147,10 @@ public class TrackerConfiguration: Configuration, TrackerConfigurationProtocol {
     /// @note Do not use. Internal use only.
     @objc
     public var trackerVersionSuffix: String?
+    /// Closure called to retrieve the Identifier for Advertisers (IDFA) from AdSupport module
+    /// It is called repeatedly (on each tracked event) until a UUID is returned.
+    @objc
+    public var advertisingIdentifierRetriever: (() -> UUID?)?
 
     @objc
     public override init() {
@@ -234,6 +242,7 @@ public class TrackerConfiguration: Configuration, TrackerConfigurationProtocol {
         copy.diagnosticAutotracking = diagnosticAutotracking
         copy.trackerVersionSuffix = trackerVersionSuffix
         copy.userAnonymisation = userAnonymisation
+        copy.advertisingIdentifierRetriever = advertisingIdentifierRetriever
         return copy
     }
 
diff --git a/Tests/Legacy Tests/LegacyTestSubject.swift b/Tests/Legacy Tests/LegacyTestSubject.swift
index 88ed69b5c..bf46bc201 100644
--- a/Tests/Legacy Tests/LegacyTestSubject.swift	
+++ b/Tests/Legacy Tests/LegacyTestSubject.swift	
@@ -36,13 +36,13 @@ class LegacyTestSubject: XCTestCase {
 
     func testSubjectInit() {
         let subject = Subject()
-        XCTAssertNotNil(subject.getStandardDict(withUserAnonymisation: false))
+        XCTAssertNotNil(subject.getStandardDict(userAnonymisation: false))
     }
 
     func testSubjectInitWithOptions() {
         let subject = Subject(platformContext: true, andGeoContext: false)
-        XCTAssertNotNil(subject.getPlatformDict(withUserAnonymisation: false))
-        XCTAssertNotNil(subject.getStandardDict(withUserAnonymisation: false))
+        XCTAssertNotNil(subject.getPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil))
+        XCTAssertNotNil(subject.getStandardDict(userAnonymisation: false))
     }
 
     func testSubjectSetterFunctions() {
@@ -58,7 +58,7 @@ class LegacyTestSubject: XCTestCase {
         subject.networkUserId = "aNuid"
         subject.domainUserId = "aDuid"
 
-        guard var values = subject.getStandardDict(withUserAnonymisation: false)?.dictionary else {
+        guard var values = subject.getStandardDict(userAnonymisation: false)?.dictionary else {
             return XCTFail()
         }
 
diff --git a/Tests/TestPlatformContext.swift b/Tests/TestPlatformContext.swift
index 4a9b338e4..d985acf62 100644
--- a/Tests/TestPlatformContext.swift
+++ b/Tests/TestPlatformContext.swift
@@ -26,14 +26,14 @@ import XCTest
 class TestPlatformContext: XCTestCase {
     func testContainsPlatformInfo() {
         let context = PlatformContext(deviceInfoMonitor: MockDeviceInfoMonitor())
-        let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary
+        let platformDict = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil).dictionary
         XCTAssertNotNil(platformDict)
         XCTAssertNotNil(platformDict)
     }
 
     func testContainsMobileInfo() {
         let context = PlatformContext(deviceInfoMonitor: MockDeviceInfoMonitor())
-        let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary
+        let platformDict = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil).dictionary
         XCTAssertNotNil(platformDict)
         XCTAssertNotNil(platformDict)
     }
@@ -41,10 +41,11 @@ class TestPlatformContext: XCTestCase {
     func testAddsAllMockedInfo() {
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
-        guard let platformDict = context.fetchPlatformDict(withUserAnonymisation: false).dictionary else {
+        let idfa = UUID()
+        guard let platformDict = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: { idfa }).dictionary else {
             return XCTFail()
         }
-        XCTAssertEqual("appleIdfa" as NSObject, platformDict[kSPMobileAppleIdfa])
+        XCTAssertEqual(idfa.uuidString as NSObject, platformDict[kSPMobileAppleIdfa])
         XCTAssertEqual("appleIdfv" as NSObject, platformDict[kSPMobileAppleIdfv])
         XCTAssertEqual("Apple Inc." as NSObject, platformDict[kSPPlatformDeviceManu])
         XCTAssertEqual("deviceModel" as NSObject, platformDict[kSPPlatformDeviceModel])
@@ -62,10 +63,10 @@ class TestPlatformContext: XCTestCase {
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(2, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(2, deviceInfoMonitor.accessCount("appAvailableMemory"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("appAvailableMemory"))
     }
@@ -75,10 +76,10 @@ class TestPlatformContext: XCTestCase {
         let context = PlatformContext(mobileDictUpdateFrequency: 1000, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("batteryLevel"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appAvailableMemory"))
     }
@@ -88,10 +89,10 @@ class TestPlatformContext: XCTestCase {
         let context = PlatformContext(mobileDictUpdateFrequency: 1, networkDictUpdateFrequency: 0, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(2, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(2, deviceInfoMonitor.accessCount("networkType"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(3, deviceInfoMonitor.accessCount("networkType"))
     }
@@ -101,10 +102,10 @@ class TestPlatformContext: XCTestCase {
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1000, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkTechnology"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("networkType"))
     }
@@ -114,40 +115,82 @@ class TestPlatformContext: XCTestCase {
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 0, deviceInfoMonitor: deviceInfoMonitor)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("totalStorage"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("totalStorage"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("physicalMemory"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("totalStorage"))
     }
 
-    func testDoesntUpdateIdfaAndIdfvIfNotNil() {
+    func testDoesntUpdateIdfvIfNotNil() {
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
-        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfv"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
-        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfv"))
     }
 
-    func testUpdatesIdfaAndIdfvIfNil() {
+    func testUpdatesIdfvIfNil() {
         let deviceInfoMonitor = MockDeviceInfoMonitor()
-        deviceInfoMonitor.customAppleIdfa = nil
         deviceInfoMonitor.customAppleIdfv = nil
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
-        XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfa"))
         XCTAssertEqual(1, deviceInfoMonitor.accessCount("appleIdfv"))
-        _ = context.fetchPlatformDict(withUserAnonymisation: false)
-        XCTAssertEqual(2, deviceInfoMonitor.accessCount("appleIdfa"))
+        _ = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertEqual(2, deviceInfoMonitor.accessCount("appleIdfv"))
     }
+    
+    func testUpdatesIdfaIfNil() {
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        
+        guard let platformDict1 = context.fetchPlatformDict(
+            userAnonymisation: false,
+            advertisingIdentifierRetriever: { nil }
+        ).dictionary else {
+            return XCTFail()
+        }
+        XCTAssertNil(platformDict1[kSPMobileAppleIdfa])
+        
+        let idfa = UUID()
+        guard let platformDict2 = context.fetchPlatformDict(
+            userAnonymisation: false,
+            advertisingIdentifierRetriever: { idfa }
+        ).dictionary else {
+            return XCTFail()
+        }
+        XCTAssertEqual(idfa.uuidString as NSObject, platformDict2[kSPMobileAppleIdfa])
+    }
+
+    func testDoesntUpdateIdfaIfAlreadyRetrieved() {
+        let deviceInfoMonitor = MockDeviceInfoMonitor()
+        let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
+        
+        let idfa1 = UUID()
+        guard let platformDict1 = context.fetchPlatformDict(
+            userAnonymisation: false,
+            advertisingIdentifierRetriever: { idfa1 }
+        ).dictionary else {
+            return XCTFail()
+        }
+        XCTAssertEqual(idfa1.uuidString as NSObject, platformDict1[kSPMobileAppleIdfa])
+        
+        guard let platformDict2 = context.fetchPlatformDict(
+            userAnonymisation: false,
+            advertisingIdentifierRetriever: { UUID() }
+        ).dictionary else {
+            return XCTFail()
+        }
+        XCTAssertEqual(idfa1.uuidString as NSObject, platformDict2[kSPMobileAppleIdfa])
+    }
 
     func testAnonymisesUserIdentifiers() {
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
-        guard let platformDict = context.fetchPlatformDict(withUserAnonymisation: true).dictionary else {
+        guard let platformDict = context.fetchPlatformDict(
+            userAnonymisation: true,
+            advertisingIdentifierRetriever: { UUID() }
+        ).dictionary else {
             return XCTFail()
         }
         XCTAssertNil(platformDict[kSPMobileAppleIdfa])
diff --git a/Tests/TestSubject.swift b/Tests/TestSubject.swift
index 6c469e250..b5a90dc2c 100644
--- a/Tests/TestSubject.swift
+++ b/Tests/TestSubject.swift
@@ -25,14 +25,14 @@ import XCTest
 class TestSubject: XCTestCase {
     func testReturnsPlatformContextIfEnabled() {
         let subject = Subject(platformContext: true, andGeoContext: false)
-        let platformDict = subject.getPlatformDict(withUserAnonymisation: false)
+        let platformDict = subject.getPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertNotNil(platformDict)
         XCTAssertNotNil(platformDict?.dictionary?[kSPPlatformOsType])
     }
 
     func testDoesntReturnPlatformContextIfDisabled() {
         let subject = Subject(platformContext: false, andGeoContext: false)
-        let platformDict = subject.getPlatformDict(withUserAnonymisation: false)
+        let platformDict = subject.getPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertNil(platformDict)
     }
 
@@ -61,7 +61,7 @@ class TestSubject: XCTestCase {
         subject.domainUserId = "aDuid"
         subject.language = "EN"
 
-        guard let values = subject.getStandardDict(withUserAnonymisation: true)?.dictionary else {
+        guard let values = subject.getStandardDict(userAnonymisation: true)?.dictionary else {
             return XCTFail()
         }
         XCTAssertNil(values[kSPUid])
diff --git a/Tests/Utils/MockDeviceInfoMonitor.swift b/Tests/Utils/MockDeviceInfoMonitor.swift
index b4f4b6fd0..64edad210 100644
--- a/Tests/Utils/MockDeviceInfoMonitor.swift
+++ b/Tests/Utils/MockDeviceInfoMonitor.swift
@@ -24,14 +24,8 @@ import Foundation
 
 class MockDeviceInfoMonitor: DeviceInfoMonitor {
     var methodAccessCounts: [String : Int] = [:]
-    var customAppleIdfa: String? = "appleIdfa"
     var customAppleIdfv: String? = "appleIdfv"
 
-    override var appleIdfa: String? {
-        increaseMethodAccessCount("appleIdfa")
-        return customAppleIdfa
-    }
-
     override var appleIdfv: String? {
         increaseMethodAccessCount("appleIdfv")
         return customAppleIdfv

From 9e5760abf1868a299e0ca1c1b9c2f82b597a5f7c Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Tue, 20 Dec 2022 16:35:13 +0100
Subject: [PATCH 08/19] Add screen view tracking for SwiftUI (close #705)

PR #744
---
 .github/workflows/build.yml                   | 81 +++++++++---------
 .github/workflows/snyk.yml                    | 10 +--
 .gitignore                                    |  1 +
 .gitmodules                                   |  3 +
 .scripts/demo_apps_branch_name                |  1 -
 Examples                                      |  1 +
 .../ScreenViewModifier.swift                  | 73 ++++++++++++++++
 Sources/Snowplow/Events/ScreenView.swift      |  2 +-
 Sources/Snowplow/Tracker/View.swift           | 41 +++++++++
 .../Integration/TestTrackEventsToMicro.swift  | 10 ++-
 Tests/TestScreenViewModifier.swift            | 83 +++++++++++++++++++
 Tests/Utils/Micro.swift                       |  6 +-
 12 files changed, 253 insertions(+), 59 deletions(-)
 create mode 100644 .gitmodules
 delete mode 100644 .scripts/demo_apps_branch_name
 create mode 160000 Examples
 create mode 100644 Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
 create mode 100644 Sources/Snowplow/Tracker/View.swift
 create mode 100644 Tests/TestScreenViewModifier.swift

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 41bd94af9..22faadb7e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -84,12 +84,11 @@ jobs:
 
       - name: Build & Test
         run: |
-          xcodebuild \
+          set -o pipefail && xcodebuild \
             -scheme SnowplowTracker \
             -sdk "${{ matrix.sdk }}" \
             -destination "${{ matrix.destination }}" \
-            -quiet \
-            clean test
+            clean test | xcpretty
 
   build_objc_demo_app:
     name: "ObjC demo (iOS ${{ matrix.version.ios }})"
@@ -108,19 +107,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v3
-
-      - name: Get example branch name
-        id: example_branch
-        run: |
-          DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
-          echo ::set-output name=name::${DEMO_BRANCH}
-
-      - name: Checkout demo app
-        uses: actions/checkout@v3
         with:
-          repository: snowplow-incubator/snowplow-objc-tracker-examples
-          ref: ${{ steps.example_branch.outputs.name }}
-          path: examples
+          submodules: true
 
       - name: Build
         env:
@@ -129,7 +117,7 @@ jobs:
           IPHONE: ${{ matrix.version.iphone }}
           WATCH: ${{ matrix.version.watch }}
         run: |
-          cd examples/demo/ 
+          cd Examples/demo/ 
           . .scripts/setup.sh
           .scripts/test_ios_demo.sh -app SnowplowObjCDemo -podfile Podfile -ios "${BUILD_WORKSPACE_OBJC_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_OBJC_DEMO}"
 
@@ -149,19 +137,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v3
-
-      - name: Get example branch name
-        id: example_branch
-        run: |
-          DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
-          echo ::set-output name=name::${DEMO_BRANCH}
-
-      - name: Checkout demo app
-        uses: actions/checkout@v3
         with:
-          repository: snowplow-incubator/snowplow-objc-tracker-examples
-          ref: ${{ steps.example_branch.outputs.name }}
-          path: examples
+          submodules: true
 
       - name: Build
         env:
@@ -170,7 +147,7 @@ jobs:
           IPHONE: ${{ matrix.version.iphone }}
           WATCH: ${{ matrix.version.watch }}
         run: |
-          cd examples/demo/ 
+          cd Examples/demo/ 
           . .scripts/setup.sh
           .scripts/test_ios_demo.sh -app SnowplowSwiftCocoapodsDemo -podfile Podfile -ios "${BUILD_WORKSPACE_SWIFT_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_SWIFT_DEMO_IOS}" -watch "${BUILD_DEST_PAIRED}" "${BUILD_SCHEME_SWIFT_DEMO_WATCH}"
 
@@ -190,12 +167,8 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v3
-
-      - name: Get example branch name
-        id: example_branch
-        run: |
-          DEMO_BRANCH=$(cat .scripts/demo_apps_branch_name)
-          echo ::set-output name=name::${DEMO_BRANCH}
+        with:
+          submodules: true
 
       - name: Get branch name
         id: branch
@@ -212,13 +185,6 @@ jobs:
           fi
           echo ::set-output name=name::${GIT_BRANCH}
 
-      - name: Checkout demo app
-        uses: actions/checkout@v3
-        with:
-          repository: snowplow-incubator/snowplow-objc-tracker-examples
-          ref: ${{ steps.example_branch.outputs.name }}
-          path: examples
-
       - name: Build
         env:
           IOS: ${{ matrix.version.ios }}
@@ -227,6 +193,35 @@ jobs:
           WATCH: ${{ matrix.version.watch }}
           BRANCH: ${{ steps.branch.outputs.name }}
         run: |
-          cd examples/demo/
+          cd Examples/demo/
           . .scripts/setup.sh
           .scripts/test_ios_demo.sh -app SnowplowSwiftSPMDemo -spm ${BRANCH} -ios "${BUILD_WORKSPACE_SWIFT_SPM_DEMO}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_SWIFT_SPM_DEMO_IOS}"
+
+  build_iglu_central_app:
+    name: "Iglu Central (SPM) (iOS ${{ matrix.version.ios }})"
+    needs: test_framework
+    runs-on: macos-${{ matrix.version.macos }}
+    env:
+      DEVELOPER_DIR: /Applications/Xcode_${{ matrix.version.xcode }}.app/Contents/Developer
+
+    strategy:
+      fail-fast: false
+      matrix:
+        version:
+          - {ios: 15.5, iphone: iPhone 12 Pro, macos: '12', xcode: 13.4}
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+        with:
+          submodules: true
+
+      - name: Build
+        env:
+          IOS: ${{ matrix.version.ios }}
+          IPHONE: ${{ matrix.version.iphone }}
+          BRANCH: ${{ steps.branch.outputs.name }}
+        run: |
+          cd Examples/demo/
+          . .scripts/setup.sh
+          .scripts/test_ios_demo.sh -app IgluCentral -ios "${BUILD_WORKSPACE_IGLU_CENTRAL}" "${BUILD_DEST_IOS}" "${BUILD_SCHEME_IGLU_CENTRAL_IOS}"
diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml
index b4da86a17..f97237051 100644
--- a/.github/workflows/snyk.yml
+++ b/.github/workflows/snyk.yml
@@ -12,17 +12,13 @@ jobs:
     steps:
     - name: Checkout
       uses: actions/checkout@v2
-    
-    - name: Checkout demo app
-      uses: actions/checkout@v2
       with:
-        repository: snowplow-incubator/snowplow-objc-tracker-examples
-        path: examples
-      
+        submodules: true
+
     - name: Run Snyk to check for vulnerabilities in tracker
       uses: snyk/actions/cocoapods@master
       with:
         command: monitor
       env:
         SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
-        COMMAND: cd examples/demo/SnowplowSwiftCocoapodsDemo
+        COMMAND: cd Examples/demo/SnowplowSwiftCocoapodsDemo
diff --git a/.gitignore b/.gitignore
index ffd88b982..37ebac6a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 *xcuserdata*
 
 *.xccheckout
+.vscode/
 
 ### Xcode ###
 build/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..7a2d11715
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "Examples"]
+	path = Examples
+	url = https://github.com/snowplow-incubator/snowplow-objc-tracker-examples
diff --git a/.scripts/demo_apps_branch_name b/.scripts/demo_apps_branch_name
deleted file mode 100644
index f23b39592..000000000
--- a/.scripts/demo_apps_branch_name
+++ /dev/null
@@ -1 +0,0 @@
-swift
diff --git a/Examples b/Examples
new file mode 160000
index 000000000..7c6e03f53
--- /dev/null
+++ b/Examples
@@ -0,0 +1 @@
+Subproject commit 7c6e03f53fdea915f4605a4c6f006c0f6d7125f7
diff --git a/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
new file mode 100644
index 000000000..1a016031f
--- /dev/null
+++ b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
@@ -0,0 +1,73 @@
+//
+// ScreenViewModifier.swift
+// Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+#if canImport(SwiftUI)
+
+import SwiftUI
+import Foundation
+
+@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, *)
+@available(watchOS, unavailable)
+internal struct ScreenViewModifier: ViewModifier {
+    let name: String
+    let contexts: [(schema: String, data: [String: Any])]
+    let trackerNamespace: String?
+    
+    /// Transform the context entity definitions to self-describing objects
+    private var processedContexts: [SelfDescribingJson] {
+        return contexts.map({ entity in
+            if let data = entity.data as? [String : NSObject] {
+                return SelfDescribingJson(schema: entity.schema, andDictionary: data)
+            } else {
+                logError(message: "Failed to process context entity for screen view.")
+            }
+            return nil
+        }).filter({ $0 != nil }).map({ $0! })
+    }
+    
+    /// Get tracker by namespace if configured, otherwise return the default tracker
+    private var tracker: TrackerController? {
+        if let namespace = trackerNamespace {
+            return Snowplow.tracker(namespace: namespace)
+        } else {
+            return Snowplow.defaultTracker()
+        }
+    }
+
+    /// Modifies the view to track the screen view when it appears
+    func body(content: Content) -> some View {
+        content.onAppear {
+            trackScreenView()
+        }
+    }
+
+    func trackScreenView() {
+        let event = ScreenView(name: name)
+        event.contexts = processedContexts
+
+        if let tracker = tracker {
+            _ = tracker.track(event)
+        } else {
+            logError(message: "Screen view not tracked – tracker not initialized.")
+        }
+    }
+}
+
+#endif
diff --git a/Sources/Snowplow/Events/ScreenView.swift b/Sources/Snowplow/Events/ScreenView.swift
index 06f383994..1108fa427 100644
--- a/Sources/Snowplow/Events/ScreenView.swift
+++ b/Sources/Snowplow/Events/ScreenView.swift
@@ -72,7 +72,7 @@ public class ScreenView: SelfDescribingAbstract {
     /// - Parameter name: Name of the screen.
     /// - Parameter screenId: Identifier of the screen.
     @objc
-    public init(name: String, screenId: UUID?) {
+    public init(name: String, screenId: UUID? = nil) {
         self.screenId = screenId ?? UUID()
         self.name = name
     }
diff --git a/Sources/Snowplow/Tracker/View.swift b/Sources/Snowplow/Tracker/View.swift
new file mode 100644
index 000000000..c326d7701
--- /dev/null
+++ b/Sources/Snowplow/Tracker/View.swift
@@ -0,0 +1,41 @@
+//
+// View.swift
+// Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+#if canImport(SwiftUI)
+import SwiftUI
+import Foundation
+
+@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, *)
+@available(watchOS, unavailable)
+public extension View {
+    /// Sets up screen view tracking to track events when this screen appears.
+    /// - Parameter name: Name of the screen
+    /// - Parameter contexts: Context entities to attach to the event
+    /// - Returns: View with the attached modifier to track screen views
+    func snowplowScreen(name: String,
+                        contexts: [(schema: String, data: [String : Any])] = [],
+                        trackerNamespace: String? = nil) -> some View {
+        return modifier(ScreenViewModifier(name: name,
+                                           contexts: contexts,
+                                           trackerNamespace: trackerNamespace))
+    }
+}
+
+#endif
diff --git a/Tests/Integration/TestTrackEventsToMicro.swift b/Tests/Integration/TestTrackEventsToMicro.swift
index 7f4195b8e..255857e6e 100644
--- a/Tests/Integration/TestTrackEventsToMicro.swift
+++ b/Tests/Integration/TestTrackEventsToMicro.swift
@@ -26,6 +26,8 @@ class TestTrackEventsToMicro: XCTestCase {
     var tracker: TrackerController?
     
     override func setUp() {
+        super.setUp()
+        
         tracker = Snowplow.createTracker(namespace: "ns", network: NetworkConfiguration(endpoint: Micro.endpoint))!
         
         Micro.setUpMockerIgnores()
@@ -173,7 +175,7 @@ class TestTrackEventsToMicro: XCTestCase {
     }
 }
 
-struct ScreenViewExpected: Codable {
+private struct ScreenViewExpected: Codable {
     let name: String
     let id: String
     let type: String?
@@ -183,17 +185,17 @@ struct ScreenViewExpected: Codable {
     let transitionType: String?
 }
 
-struct ScreenContextExpected: Codable {
+private struct ScreenContextExpected: Codable {
     let name: String
     let id: String
 }
 
-struct DeepLinkExpected: Codable {
+private struct DeepLinkExpected: Codable {
     let url: String
     let referrer: String?
 }
 
-struct SessionExpected: Codable {
+private struct SessionExpected: Codable {
     let sessionId: String
     let userId: String
 }
diff --git a/Tests/TestScreenViewModifier.swift b/Tests/TestScreenViewModifier.swift
new file mode 100644
index 000000000..0d48f7422
--- /dev/null
+++ b/Tests/TestScreenViewModifier.swift
@@ -0,0 +1,83 @@
+//
+//  TestScreenViewModifier.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+import XCTest
+@testable import SnowplowTracker
+
+#if canImport(SwiftUI)
+#if os(iOS) || os(tvOS) || os(macOS)
+
+class TestScreenViewModifier: XCTestCase {
+    var tracker: TrackerController?
+    
+    override func setUp() {
+        super.setUp()
+        
+        tracker = Snowplow.createTracker(namespace: "ns",
+                                         network: NetworkConfiguration(endpoint: Micro.endpoint))!
+        
+        Micro.setUpMockerIgnores()
+        wait(for: [Micro.reset()], timeout: Micro.timeout)
+    }
+    
+    override func tearDown() {
+        super.tearDown()
+    }
+    
+    func testTracksScreenViewWithContextEntity() {
+        if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, *) {
+            let modifier = ScreenViewModifier(
+                name: "screen-1",
+                contexts: [
+                    (
+                        schema: "iglu:com.snowplowanalytics.iglu/anything-a/jsonschema/1-0-0",
+                        data: [
+                            "works": true
+                        ]
+                    )
+                ],
+                trackerNamespace: "ns"
+            )
+            modifier.trackScreenView()
+            
+            wait(for: [
+                Micro.expectSelfDescribingEvent() { (actual: ScreenViewExpected) in
+                    XCTAssertEqual("screen-1", actual.name)
+                },
+                Micro.expectEventContext(schema: "iglu:com.snowplowanalytics.iglu/anything-a/jsonschema/1-0-0") { (actual: AnythingEntityExpected) in
+                    XCTAssertTrue(actual.works)
+                }
+            ], timeout: Micro.timeout)
+        }
+    }
+}
+
+private struct ScreenViewExpected: Codable {
+    let name: String
+}
+
+private struct AnythingEntityExpected: Codable {
+    let works: Bool
+}
+
+#endif
+#endif
diff --git a/Tests/Utils/Micro.swift b/Tests/Utils/Micro.swift
index e3963717a..4c1d4e10e 100644
--- a/Tests/Utils/Micro.swift
+++ b/Tests/Utils/Micro.swift
@@ -27,7 +27,7 @@ class Micro {
     
     static let timeout = 10.0
     static let retryDelay = 0.5
-    static let maxNumberOfRetries = 20
+    static let maxNumberOfRetries = 19
     static let endpoint = "http://0.0.0.0:9090"
     
     class func setUpMockerIgnores() {
@@ -75,7 +75,7 @@ class Micro {
                                      numberOfRetries: numberOfRetries + 1)
                     }
                 } else {
-                    XCTFail("Didn't find the expected event counts in Micro")
+                    XCTFail("Didn't find the expected event counts in Micro, actual: \(String(data: data, encoding: .utf8)!)")
                 }
             } else {
                 XCTFail("Failed to parse response from Micro")
@@ -131,7 +131,7 @@ class Micro {
                                         numberOfRetries: numberOfRetries + 1,
                                         completion: completion)
                     } else {
-                        XCTFail("Didn't find the expected event in Micro")
+                        XCTFail("Didn't find the expected event in Micro, actual: \(String(data: data, encoding: .utf8)!)")
                     }
                 } else {
                     XCTFail("Failed to parse response from Micro")

From a0f8a49af0c9ebde0fa23d6778db5ce4c4f82c5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matu=CC=81s=CC=8C=20Tomlein?= <matus.tomlein@gmail.com>
Date: Tue, 20 Dec 2022 16:41:04 +0100
Subject: [PATCH 09/19] Prepare for 5.0.0-alpha.2 release

---
 CHANGELOG                           | 11 ++++++++++-
 Examples                            |  2 +-
 SnowplowTracker.podspec             |  2 +-
 Sources/Core/TrackerConstants.swift |  8 ++++----
 VERSION                             |  2 +-
 5 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 65315fcd1..aa1c3d188 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,14 @@
+Version 5.0.0-alpha.2 (2022-12-21)
+----------------------------------
+Add screen view tracking for SwiftUI (#705)
+Drop Carthage and build using Swift Package Manager (#735)
+Add tests using Micro for payload validation (#736)
+Add a closure to tracker configuration that enables retrieving IDFA value and replaces the use of SNOWPLOW_IDFA_ENABLED macro (#678)
+Update API comments for Swift-DocC and add missing comments (#740)
+Add API docs using Swift-DocC (#739)
+
 Version 5.0.0-alpha.1 (2022-11-30)
---------------------------
+----------------------------------
 Migrate to Swift (#732)
 
 Version 4.1.0 (2022-11-16)
diff --git a/Examples b/Examples
index 7c6e03f53..dfeb71c6f 160000
--- a/Examples
+++ b/Examples
@@ -1 +1 @@
-Subproject commit 7c6e03f53fdea915f4605a4c6f006c0f6d7125f7
+Subproject commit dfeb71c6fdc20e6487e36370c48ddce7d769846e
diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec
index 1e550d69a..9eb238028 100644
--- a/SnowplowTracker.podspec
+++ b/SnowplowTracker.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = "SnowplowTracker"
-    s.version          = "5.0.0-alpha.1"
+    s.version          = "5.0.0-alpha.2"
     s.summary          = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
     s.description      = <<-DESC
     Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
diff --git a/Sources/Core/TrackerConstants.swift b/Sources/Core/TrackerConstants.swift
index da8a9555f..b16cc2217 100644
--- a/Sources/Core/TrackerConstants.swift
+++ b/Sources/Core/TrackerConstants.swift
@@ -23,13 +23,13 @@ import Foundation
 
 // --- Version
 #if os(iOS)
-let kSPVersion = "ios-5.0.0-alpha.1"
+let kSPVersion = "ios-5.0.0-alpha.2"
 #elseif os(tvOS)
-let kSPVersion = "tvos-5.0.0-alpha.1"
+let kSPVersion = "tvos-5.0.0-alpha.2"
 #elseif os(watchOS)
-let kSPVersion = "watchos-5.0.0-alpha.1"
+let kSPVersion = "watchos-5.0.0-alpha.2"
 #else
-let kSPVersion = "osx-5.0.0-alpha.1"
+let kSPVersion = "osx-5.0.0-alpha.2"
 #endif
 
 // --- Session Dictionary keys
diff --git a/VERSION b/VERSION
index 5d200d543..42d68e545 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.0.0-alpha.1
+5.0.0-alpha.2

From 803c541aa1b64ce533bb0e178c5117059bef511d Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Tue, 31 Jan 2023 14:17:13 +0100
Subject: [PATCH 10/19] Refactor APIs to replace usage of NSObject in
 dictionaries with the Any type (close #748)

PR #749
---
 Examples                                      |   2 +-
 Sources/Core/Emitter/Emitter.swift            |   8 +-
 Sources/Core/Logger/Logger.swift              |  10 +-
 .../ConfigurationFetcher.swift                |   2 +-
 .../FetchedConfigurationBundle.swift          |   4 +-
 .../Core/ScreenViewTracking/ScreenState.swift |   2 +-
 .../ScreenStateMachine.swift                  |  10 +-
 .../ScreenViewModifier.swift                  |   9 +-
 Sources/Core/Session/Session.swift            |  16 +-
 Sources/Core/Storage/MemoryEventStore.swift   |   6 +-
 Sources/Core/Storage/SQLiteEventStore.swift   |   8 +-
 Sources/Core/Subject/PlatformContext.swift    |  62 ++++----
 Sources/Core/Subject/Subject.swift            |  74 ++++-----
 .../Core/Tracker/DeepLinkStateMachine.swift   |   2 +-
 Sources/Core/Tracker/LifecycleState.swift     |   8 +-
 .../Core/Tracker/LifecycleStateMachine.swift  |   8 +-
 Sources/Core/Tracker/ServiceProvider.swift    |   2 +-
 Sources/Core/Tracker/Tracker.swift            |  77 +++++----
 Sources/Core/Tracker/TrackerEvent.swift       |   6 +-
 .../Core/Tracker/WebViewMessageHandler.swift  |   4 +-
 Sources/Core/Utils/DataPersistence.swift      |  17 +-
 Sources/Core/Utils/DeviceInfoMonitor.swift    |   6 +-
 Sources/Core/Utils/Utilities.swift            |   6 +-
 .../Configurations/Configuration.swift        |   2 +-
 .../Configurations/ConfigurationBundle.swift  |  10 +-
 .../Configurations/NetworkConfiguration.swift |   2 +-
 .../Configurations/SessionConfiguration.swift |   2 +-
 .../Configurations/SubjectConfiguration.swift |   2 +-
 .../Configurations/TrackerConfiguration.swift |   2 +-
 Sources/Snowplow/Emitter/EventStore.swift     |   2 +-
 .../Snowplow/Entities/DeepLinkEntity.swift    |  12 +-
 .../Snowplow/Entities/LifecycleEntity.swift   |  16 +-
 Sources/Snowplow/Events/Background.swift      |   8 +-
 Sources/Snowplow/Events/ConsentGranted.swift  |   8 +-
 .../Snowplow/Events/ConsentWithdrawn.swift    |   4 +-
 .../Snowplow/Events/DeepLinkReceived.swift    |  10 +-
 Sources/Snowplow/Events/Ecommerce.swift       |  22 +--
 Sources/Snowplow/Events/EcommerceItem.swift   |  18 +--
 Sources/Snowplow/Events/EventBase.swift       |   2 +-
 Sources/Snowplow/Events/Foreground.swift      |   8 +-
 .../Snowplow/Events/MessageNotification.swift |  54 ++++---
 .../MessageNotificationAttachment.swift       |   8 +-
 Sources/Snowplow/Events/PageView.swift        |  10 +-
 .../Snowplow/Events/PushNotification.swift    |  68 ++++----
 Sources/Snowplow/Events/SNOWError.swift       |  12 +-
 Sources/Snowplow/Events/ScreenView.swift      |  18 +--
 Sources/Snowplow/Events/SelfDescribing.swift  |  15 +-
 Sources/Snowplow/Events/Structured.swift      |  14 +-
 Sources/Snowplow/Events/Timing.swift          |  12 +-
 Sources/Snowplow/Events/TrackerError.swift    |  14 +-
 Sources/Snowplow/Network/Request.swift        |  23 ++-
 Sources/Snowplow/Network/RequestResult.swift  |   4 +-
 Sources/Snowplow/Payload/Payload.swift        |  61 +++-----
 .../Snowplow/Payload/SelfDescribingJson.swift |  55 +++----
 Sources/Snowplow/Snowplow.swift               |   2 +-
 .../Snowplow/Tracker/InspectableEvent.swift   |   4 +-
 Sources/Snowplow/Tracker/SessionState.swift   |  24 +--
 .../Tracker/StateMachineProtocol.swift        |   2 +-
 .../TestRemoteConfiguration.swift             |   2 +-
 .../TestTrackerConfiguration.swift            |  14 +-
 .../Global Contexts/TestGlobalContexts.swift  |  14 +-
 Tests/Legacy Tests/LegacyTestSubject.swift    |  56 ++++---
 Tests/Legacy Tests/LegacyTestTracker.swift    |  12 +-
 Tests/TestDataPersistence.swift               |  28 ++--
 Tests/TestEvents.swift                        |  44 +++---
 Tests/TestLifecycleState.swift                |  10 +-
 Tests/TestMemoryEventStore.swift              |   3 +-
 Tests/TestNetworkConnection.swift             |  14 +-
 Tests/TestPayload.swift                       | 136 +++++++---------
 Tests/TestPlatformContext.swift               |  62 +++-----
 Tests/TestRequest.swift                       |   2 +-
 Tests/TestRequestResult.swift                 |   8 +-
 Tests/TestSQLiteEventStore.swift              |  11 +-
 Tests/TestScreenState.swift                   |  12 +-
 Tests/TestSelfDescribingJson.swift            |  73 +++++----
 Tests/TestServiceProvider.swift               |   2 +-
 Tests/TestSession.swift                       | 148 +++++++++---------
 Tests/TestStateManager.swift                  |  32 ++--
 Tests/TestSubject.swift                       |  16 +-
 Tests/TestWebViewMessageHandler.swift         |   4 +-
 Tests/Utils/MockEventStore.swift              |   6 +-
 81 files changed, 754 insertions(+), 834 deletions(-)

diff --git a/Examples b/Examples
index dfeb71c6f..b2b7a3317 160000
--- a/Examples
+++ b/Examples
@@ -1 +1 @@
-Subproject commit dfeb71c6fdc20e6487e36370c48ddce7d769846e
+Subproject commit b2b7a3317a00727a88217e3c5451c49eb38f5a3b
diff --git a/Sources/Core/Emitter/Emitter.swift b/Sources/Core/Emitter/Emitter.swift
index 915de3b9b..88986a54a 100644
--- a/Sources/Core/Emitter/Emitter.swift
+++ b/Sources/Core/Emitter/Emitter.swift
@@ -406,7 +406,7 @@ class Emitter: NSObject, EmitterEventProcessing {
         var successCount = 0
         var failedWillRetryCount = 0
         var failedWontRetryCount = 0
-        var removableEvents: [NSNumber] = []
+        var removableEvents: [Int64] = []
 
         for result in sendResults ?? [] {
             let resultIndexArray = result.storeIds
@@ -469,18 +469,18 @@ class Emitter: NSObject, EmitterEventProcessing {
             var i = 0
             while i < events.count {
                 var eventArray: [Payload] = []
-                var indexArray: [NSNumber] = []
+                var indexArray: [Int64] = []
 
                 let iUntil = min(i + bufferOption.rawValue, events.count)
                 for j in i..<iUntil {
                     let event = events[j]
 
                     let payload = event.payload
-                    let emitterEventId = NSNumber(value: event.storeId)
+                    let emitterEventId = event.storeId
                     addSendingTime(to: payload, timestamp: sendingTime)
 
                     if isOversize(payload) {
-                        let request = Request(payload: payload, emitterEventId: emitterEventId.int64Value, oversize: true)
+                        let request = Request(payload: payload, emitterEventId: emitterEventId, oversize: true)
                         requests.append(request)
                     } else if isOversize(payload, previousPayloads: eventArray) {
                         let request = Request(payloads: eventArray, emitterEventIds: indexArray)
diff --git a/Sources/Core/Logger/Logger.swift b/Sources/Core/Logger/Logger.swift
index b37ab98dc..8b055d16c 100644
--- a/Sources/Core/Logger/Logger.swift
+++ b/Sources/Core/Logger/Logger.swift
@@ -129,11 +129,11 @@ class Logger: NSObject {
         }
 
         // Construct userInfo
-        var userInfo: [String : NSObject] = [:]
-        userInfo["tag"] = tag as NSObject
-        userInfo["message"] = message as NSObject
-        userInfo["error"] = error as NSObject?
-        userInfo["exception"] = exception as NSObject?
+        var userInfo: [String : Any] = [:]
+        userInfo["tag"] = tag
+        userInfo["message"] = message
+        userInfo["error"] = error
+        userInfo["exception"] = exception
 
         // Send notification to tracker
         NotificationCenter.default.post(
diff --git a/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift b/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
index 9ff84b2b9..b0b6e072c 100644
--- a/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
+++ b/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
@@ -49,7 +49,7 @@ class ConfigurationFetcher: NSObject {
     }
 
     func resolveRequest(with data: Data) {
-        if let jsonObject = try? JSONSerialization.jsonObject(with: data) as? [String : NSObject],
+        if let jsonObject = try? JSONSerialization.jsonObject(with: data) as? [String : Any],
            let fetchedConfigurationBundle = FetchedConfigurationBundle(dictionary: jsonObject) {
             onFetchCallback(fetchedConfigurationBundle, ConfigurationState.fetched)
         }
diff --git a/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
index a19156325..01718e48a 100644
--- a/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
+++ b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
@@ -31,7 +31,7 @@ class FetchedConfigurationBundle: Configuration {
         self.configurationVersion = configurationVersion
     }
     
-    init?(dictionary: [String : NSObject]) {
+    init?(dictionary: [String : Any]) {
         guard let schema = dictionary["$schema"] as? String else {
             logDebug(message: "Error assigning: schema")
             return nil
@@ -42,7 +42,7 @@ class FetchedConfigurationBundle: Configuration {
             return nil
         }
         self.configurationVersion = configurationVersion
-        guard let bundles = dictionary["configurationBundle"] as? [[String : NSObject]] else {
+        guard let bundles = dictionary["configurationBundle"] as? [[String : Any]] else {
             logDebug(message: "Error assigning: configurationBundle")
             return nil
         }
diff --git a/Sources/Core/ScreenViewTracking/ScreenState.swift b/Sources/Core/ScreenViewTracking/ScreenState.swift
index 117caa53d..d155c00a9 100644
--- a/Sources/Core/ScreenViewTracking/ScreenState.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenState.swift
@@ -47,7 +47,7 @@ class ScreenState: NSObject, State, NSCopying {
     ///   - theTopControllerName: The top view controller class name
     ///   - theControllerName: The view controller class name
     required init(name theName: String, type theType: String?, screenId theScreenId: String?, transitionType theTransitionType: String?, topViewControllerClassName theTopControllerName: String?, viewControllerClassName theControllerName: String?) {
-            name = theName
+        name = theName
         if theScreenId == nil {
             screenId = UUID().uuidString
         } else {
diff --git a/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
index 06a62433c..dec9e1e4c 100644
--- a/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
@@ -54,13 +54,13 @@ class ScreenStateMachine: StateMachineProtocol {
         return nil
     }
 
-    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]? {
         if let state = state as? ScreenState {
             let previousState = state.previousState
-            var addedValues: [String : NSObject] = [:]
-            addedValues[kSPSvPreviousName] = previousState?.name as NSObject?
-            addedValues[kSPSvPreviousType] = previousState?.type as NSObject?
-            addedValues[kSPSvPreviousScreenId] = previousState?.screenId as NSObject?
+            var addedValues: [String : Any] = [:]
+            addedValues[kSPSvPreviousName] = previousState?.name
+            addedValues[kSPSvPreviousType] = previousState?.type
+            addedValues[kSPSvPreviousScreenId] = previousState?.screenId
             return addedValues
         }
         return nil
diff --git a/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
index 1a016031f..0c9ff8d1f 100644
--- a/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
@@ -33,13 +33,8 @@ internal struct ScreenViewModifier: ViewModifier {
     /// Transform the context entity definitions to self-describing objects
     private var processedContexts: [SelfDescribingJson] {
         return contexts.map({ entity in
-            if let data = entity.data as? [String : NSObject] {
-                return SelfDescribingJson(schema: entity.schema, andDictionary: data)
-            } else {
-                logError(message: "Failed to process context entity for screen view.")
-            }
-            return nil
-        }).filter({ $0 != nil }).map({ $0! })
+            return SelfDescribingJson(schema: entity.schema, andDictionary: entity.data)
+        })
     }
     
     /// Get tracker by namespace if configured, otherwise return the default tracker
diff --git a/Sources/Core/Session/Session.swift b/Sources/Core/Session/Session.swift
index baeb2c2b2..afd19fd9a 100644
--- a/Sources/Core/Session/Session.swift
+++ b/Sources/Core/Session/Session.swift
@@ -76,9 +76,9 @@ class Session {
             dataPersistence = DataPersistence.getFor(namespace: namespace)
         }
         let storedSessionDict = dataPersistence?.session
-        userId = Session.retrieveUserId(withSessionDict: storedSessionDict)
+        userId = Session.retrieveUserId(sessionDict: storedSessionDict)
         if var storedSessionDict = storedSessionDict {
-            storedSessionDict[kSPSessionUserId] = userId as NSObject?
+            storedSessionDict[kSPSessionUserId] = userId
             state = SessionState(storedState: storedSessionDict)
             dataPersistence?.session = storedSessionDict
         }
@@ -128,12 +128,12 @@ class Session {
     ///   - firstEventTimestamp: Device created timestamp of the first event of the session
     ///   - userAnonymisation: Whether to anonymise user identifiers
     /// - Returns: a SnowplowPayload containing the session dictionary
-    func getDictWithEventId(_ eventId: String?, eventTimestamp: Int64, userAnonymisation: Bool) -> [String : NSObject]? {
-        var context: [String : NSObject]? = nil
+    func getDictWithEventId(_ eventId: String?, eventTimestamp: Int64, userAnonymisation: Bool) -> [String : Any]? {
+        var context: [String : Any]? = nil
         objc_sync_enter(self)
         if isSessionCheckerEnabled {
             if shouldUpdate() {
-                update(withEventId: eventId, eventTimestamp: eventTimestamp)
+                update(eventId: eventId, eventTimestamp: eventTimestamp)
                 if let onSessionStateUpdate = onSessionStateUpdate, let state = state {
                     DispatchQueue.global(qos: .default).async {
                         onSessionStateUpdate(state)
@@ -152,7 +152,7 @@ class Session {
         if userAnonymisation {
             // mask the user identifier
             var copy = context
-            copy?[kSPSessionUserId] = kSPSessionAnonymousUserId as NSObject
+            copy?[kSPSessionUserId] = kSPSessionAnonymousUserId
             copy?[kSPSessionPreviousId] = nil
             return copy
         } else {
@@ -163,7 +163,7 @@ class Session {
 
     // MARK: - Private
 
-    private static func retrieveUserId(withSessionDict sessionDict: [String : NSObject]?) -> String {
+    private static func retrieveUserId(sessionDict: [String : Any]?) -> String {
         var userId = sessionDict?[kSPSessionUserId] as? String ?? Utilities.getUUIDString()
         // Session_UserID is available only if the session context is enabled.
         // In a future version we would like to make it available even if the session context is disabled.
@@ -192,7 +192,7 @@ class Session {
         return now < lastAccess || Int(now - lastAccess) > timeout
     }
 
-    private func update(withEventId eventId: String?, eventTimestamp: Int64) {
+    private func update(eventId: String?, eventTimestamp: Int64) {
         isNewSession = false
         let sessionIndex = (state?.sessionIndex ?? 0) + 1
         let eventISOTimestamp = Utilities.timestamp(toISOString: eventTimestamp)
diff --git a/Sources/Core/Storage/MemoryEventStore.swift b/Sources/Core/Storage/MemoryEventStore.swift
index 3ee6e93f1..215c5d935 100644
--- a/Sources/Core/Storage/MemoryEventStore.swift
+++ b/Sources/Core/Storage/MemoryEventStore.swift
@@ -86,10 +86,10 @@ class MemoryEventStore: NSObject, EventStore {
     }
 
     func removeEvent(withId storeId: Int64) -> Bool {
-        return removeEvents(withIds: [NSNumber(value: storeId)])
+        return removeEvents(withIds: [storeId])
     }
 
-    func removeEvents(withIds storeIds: [NSNumber]) -> Bool {
+    func removeEvents(withIds storeIds: [Int64]) -> Bool {
         objc_sync_enter(self)
         defer { objc_sync_exit(self) }
         var itemsToRemove: [EmitterEvent] = []
@@ -97,7 +97,7 @@ class MemoryEventStore: NSObject, EventStore {
             guard let item = item as? EmitterEvent else {
                 continue
             }
-            if storeIds.contains(NSNumber(value: item.storeId)) {
+            if storeIds.contains(item.storeId) {
                 itemsToRemove.append(item)
             }
         }
diff --git a/Sources/Core/Storage/SQLiteEventStore.swift b/Sources/Core/Storage/SQLiteEventStore.swift
index 68dbd95b8..062ac9a6c 100644
--- a/Sources/Core/Storage/SQLiteEventStore.swift
+++ b/Sources/Core/Storage/SQLiteEventStore.swift
@@ -143,11 +143,11 @@ class SQLiteEventStore: NSObject, EventStore {
         return res
     }
 
-    func removeEvents(withIds storeIds: [NSNumber]) -> Bool {
+    func removeEvents(withIds storeIds: [Int64]) -> Bool {
         var res = false
         queue?.inDatabase({ db in
             if db.open() && storeIds.count != 0 {
-                let ids = storeIds.map { $0.stringValue }.joined(separator: ",")
+                let ids = storeIds.map { String(describing: $0) }.joined(separator: ",")
                 logDebug(message: String(format: "Removing [%@] from database now.", ids))
                 let query = String(format: _queryDeleteIds, ids)
                 res = db.executeUpdate(query, withArgumentsIn: [])
@@ -232,7 +232,7 @@ class SQLiteEventStore: NSObject, EventStore {
                 if let s = try? db.executeQuery(_querySelectId, values: [id_]) {
                     while s.next() {
                         if let data = s.data(forColumn: "eventData"),
-                           let dict = try? JSONSerialization.jsonObject(with: data) as? [String: NSObject] {
+                           let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
                             let payload = Payload(dictionary: dict)
                             event = EmitterEvent(payload: payload, storeId: id_)
                         }
@@ -265,7 +265,7 @@ class SQLiteEventStore: NSObject, EventStore {
                     while s.next() {
                         let index = s.longLongInt(forColumn: "ID")
                         if let data = s.data(forColumn: "eventData"),
-                           let dict = try? JSONSerialization.jsonObject(with: data) as? [String: NSObject] {
+                           let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
                             let payload = Payload(dictionary: dict)
                             let event = EmitterEvent(payload: payload, storeId: index)
                             res.append(event)
diff --git a/Sources/Core/Subject/PlatformContext.swift b/Sources/Core/Subject/PlatformContext.swift
index 35a08430e..db5fb756f 100644
--- a/Sources/Core/Subject/PlatformContext.swift
+++ b/Sources/Core/Subject/PlatformContext.swift
@@ -66,14 +66,14 @@ class PlatformContext {
         #endif
         if userAnonymisation {
             // mask user identifiers
-            let copy = Payload(dictionary: platformDict.dictionary ?? [:])
-            copy.addValueToPayload(nil, forKey: kSPMobileAppleIdfa)
-            copy.addValueToPayload(nil, forKey: kSPMobileAppleIdfv)
+            let copy = Payload(dictionary: platformDict.dictionary)
+            copy[kSPMobileAppleIdfa] = nil
+            copy[kSPMobileAppleIdfv] = nil
             return copy
         } else {
             if let retriever = advertisingIdentifierRetriever {
-                if platformDict.dictionary?[kSPMobileAppleIdfa] == nil {
-                    platformDict.addValueToPayload(retriever()?.uuidString, forKey: kSPMobileAppleIdfa)
+                if platformDict.dictionary[kSPMobileAppleIdfa] == nil {
+                    platformDict[kSPMobileAppleIdfa] = retriever()?.uuidString
                 }
             }
             return platformDict
@@ -84,10 +84,10 @@ class PlatformContext {
 
     func setPlatformDict() {
         platformDict = Payload()
-        platformDict.addValueToPayload(deviceInfoMonitor.osType, forKey: kSPPlatformOsType)
-        platformDict.addValueToPayload(deviceInfoMonitor.osVersion, forKey: kSPPlatformOsVersion)
-        platformDict.addValueToPayload(deviceInfoMonitor.deviceVendor, forKey: kSPPlatformDeviceManu)
-        platformDict.addValueToPayload(deviceInfoMonitor.deviceModel, forKey: kSPPlatformDeviceModel)
+        platformDict[kSPPlatformOsType] = deviceInfoMonitor.osType
+        platformDict[kSPPlatformOsVersion] = deviceInfoMonitor.osVersion
+        platformDict[kSPPlatformDeviceManu] = deviceInfoMonitor.deviceVendor
+        platformDict[kSPPlatformDeviceModel] = deviceInfoMonitor.deviceModel
 
         #if os(iOS)
         setMobileDict()
@@ -95,11 +95,11 @@ class PlatformContext {
     }
 
     func setMobileDict() {
-        platformDict.addValueToPayload(deviceInfoMonitor.carrierName, forKey: kSPMobileCarrier)
+        platformDict[kSPMobileCarrier] = deviceInfoMonitor.carrierName
         if let totalStorage = deviceInfoMonitor.totalStorage {
-            platformDict.addNumericValueToPayload(NSNumber(value: totalStorage), forKey: kSPMobileTotalStorage)
+            platformDict[kSPMobileTotalStorage] = totalStorage
         }
-        platformDict.addNumericValueToPayload(NSNumber(value: deviceInfoMonitor.physicalMemory), forKey: kSPMobilePhysicalMemory)
+        platformDict[kSPMobilePhysicalMemory] = deviceInfoMonitor.physicalMemory
         
         setEphemeralMobileDict()
         setEphemeralNetworkDict()
@@ -108,31 +108,29 @@ class PlatformContext {
     func setEphemeralMobileDict() {
         lastUpdatedEphemeralMobileDict = Date().timeIntervalSince1970
 
-        if let currentDict = platformDict.dictionary {
-            if currentDict[kSPMobileAppleIdfv] == nil {
-                platformDict.addValueToPayload(deviceInfoMonitor.appleIdfv, forKey: kSPMobileAppleIdfv)
-            }
-            
-            if let batteryLevel = deviceInfoMonitor.batteryLevel {
-                platformDict.addNumericValueToPayload(NSNumber(value: batteryLevel), forKey: kSPMobileBatteryLevel)
-            }
-            platformDict.addValueToPayload(deviceInfoMonitor.batteryState, forKey: kSPMobileBatteryState)
-            if let isLowPowerModeEnabled = deviceInfoMonitor.isLowPowerModeEnabled {
-                platformDict.addNumericValueToPayload(NSNumber(value: isLowPowerModeEnabled), forKey: kSPMobileLowPowerMode)
-            }
-            if let availableStorage = deviceInfoMonitor.availableStorage {
-                platformDict.addNumericValueToPayload(NSNumber(value: availableStorage), forKey: kSPMobileAvailableStorage)
-            }
-            if let appAvailableMemory = deviceInfoMonitor.appAvailableMemory {
-                platformDict.addNumericValueToPayload(NSNumber(value: appAvailableMemory), forKey: kSPMobileAppAvailableMemory)
-            }
+        if platformDict[kSPMobileAppleIdfv] == nil {
+            platformDict[kSPMobileAppleIdfv] = deviceInfoMonitor.appleIdfv
+        }
+        
+        if let batteryLevel = deviceInfoMonitor.batteryLevel {
+            platformDict[kSPMobileBatteryLevel] = batteryLevel
+        }
+        platformDict[kSPMobileBatteryState] = deviceInfoMonitor.batteryState
+        if let isLowPowerModeEnabled = deviceInfoMonitor.isLowPowerModeEnabled {
+            platformDict[kSPMobileLowPowerMode] = isLowPowerModeEnabled
+        }
+        if let availableStorage = deviceInfoMonitor.availableStorage {
+            platformDict[kSPMobileAvailableStorage] = availableStorage
+        }
+        if let appAvailableMemory = deviceInfoMonitor.appAvailableMemory {
+            platformDict[kSPMobileAppAvailableMemory] = appAvailableMemory
         }
     }
 
     func setEphemeralNetworkDict() {
         lastUpdatedEphemeralNetworkDict = Date().timeIntervalSince1970
 
-        platformDict.addValueToPayload(deviceInfoMonitor.networkTechnology, forKey: kSPMobileNetworkTech)
-        platformDict.addValueToPayload(deviceInfoMonitor.networkType, forKey: kSPMobileNetworkType)
+        platformDict[kSPMobileNetworkTech] = deviceInfoMonitor.networkTechnology
+        platformDict[kSPMobileNetworkType] = deviceInfoMonitor.networkType
     }
 }
diff --git a/Sources/Core/Subject/Subject.swift b/Sources/Core/Subject/Subject.swift
index 9ed50fa13..4384d0401 100644
--- a/Sources/Core/Subject/Subject.swift
+++ b/Sources/Core/Subject/Subject.swift
@@ -24,9 +24,9 @@ import Foundation
 /// @class Subject
 /// This class is used to access and persist user information, it represents the current user being tracked.
 class Subject : NSObject {
-    private var standardDict = Payload()
+    private var standardDict: [String : String] = [:]
     private var platformContextManager = PlatformContext()
-    private var geoLocationDict: [String : NSObject] = [:]
+    private var geoDict: [String : NSObject] = [:]
 
     var platformContext = false
     var geoLocationContext = false
@@ -43,7 +43,7 @@ class Subject : NSObject {
         }
         set(uid) {
             _userId = uid
-            standardDict.addValueToPayload(uid, forKey: kSPUid)
+            standardDict[kSPUid] = uid
         }
     }
 
@@ -54,7 +54,7 @@ class Subject : NSObject {
         }
         set(nuid) {
             _networkUserId = nuid
-            standardDict.addValueToPayload(nuid, forKey: kSPNetworkUid)
+            standardDict[kSPNetworkUid] = nuid
         }
     }
 
@@ -66,7 +66,7 @@ class Subject : NSObject {
         }
         set(duid) {
             _domainUserId = duid
-            standardDict.addValueToPayload(duid, forKey: kSPDomainUid)
+            standardDict[kSPDomainUid] = duid
         }
     }
 
@@ -78,7 +78,7 @@ class Subject : NSObject {
         }
         set(useragent) {
             _useragent = useragent
-            standardDict.addValueToPayload(useragent, forKey: kSPUseragent)
+            standardDict[kSPUseragent] = useragent
         }
     }
 
@@ -90,7 +90,7 @@ class Subject : NSObject {
         }
         set(ip) {
             _ipAddress = ip
-            standardDict.addValueToPayload(ip, forKey: kSPIpAddress)
+            standardDict[kSPIpAddress] = ip
         }
     }
 
@@ -102,7 +102,7 @@ class Subject : NSObject {
         }
         set(timezone) {
             _timezone = timezone
-            standardDict.addValueToPayload(timezone, forKey: kSPTimezone)
+            standardDict[kSPTimezone] = timezone
         }
     }
 
@@ -114,7 +114,7 @@ class Subject : NSObject {
         }
         set(lang) {
             _language = lang
-            standardDict.addValueToPayload(lang, forKey: kSPLanguage)
+            standardDict[kSPLanguage] = lang
         }
     }
 
@@ -127,7 +127,7 @@ class Subject : NSObject {
         set(depth) {
             _colorDepth = depth
             let res = "\(depth?.stringValue ?? "")"
-            standardDict.addValueToPayload(res, forKey: kSPColorDepth)
+            standardDict[kSPColorDepth] = res
         }
     }
 
@@ -140,9 +140,9 @@ class Subject : NSObject {
             _screenResolution = newValue
             if let size = newValue {
                 let res = "\((NSNumber(value: size.width)).stringValue)x\((NSNumber(value: size.height)).stringValue)"
-                standardDict.addValueToPayload(res, forKey: kSPResolution)
+                standardDict[kSPResolution] = res
             } else {
-                standardDict.addValueToPayload(nil, forKey: kSPResolution)
+                standardDict.removeValue(forKey: kSPResolution)
             }
         }
     }
@@ -156,9 +156,9 @@ class Subject : NSObject {
             _screenViewPort = newValue
             if let size = newValue {
                 let res = "\((NSNumber(value: size.width)).stringValue)x\((NSNumber(value: size.height)).stringValue)"
-                standardDict.addValueToPayload(res, forKey: kSPViewPort)
+                standardDict[kSPViewPort] = res
             } else {
-                standardDict.addValueToPayload(nil, forKey: kSPViewPort)
+                standardDict.removeValue(forKey: kSPViewPort)
             }
             
         }
@@ -171,79 +171,79 @@ class Subject : NSObject {
     /// Latitude value for the geolocation context.
     var geoLatitude: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoLatitude] as? NSNumber
+            return geoDict[kSPGeoLatitude] as? NSNumber
         }
         set(latitude) {
-            geoLocationDict[kSPGeoLatitude] = latitude
+            geoDict[kSPGeoLatitude] = latitude
         }
     }
 
     /// Longitude value for the geo context.
     var geoLongitude: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoLongitude] as? NSNumber
+            return geoDict[kSPGeoLongitude] as? NSNumber
         }
         set(longitude) {
-            geoLocationDict[kSPGeoLongitude] = longitude
+            geoDict[kSPGeoLongitude] = longitude
         }
     }
 
     /// LatitudeLongitudeAccuracy value for the geolocation context.
     var geoLatitudeLongitudeAccuracy: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoLatLongAccuracy] as? NSNumber
+            return geoDict[kSPGeoLatLongAccuracy] as? NSNumber
         }
         set(latitudeLongitudeAccuracy) {
-            geoLocationDict[kSPGeoLatLongAccuracy] = latitudeLongitudeAccuracy
+            geoDict[kSPGeoLatLongAccuracy] = latitudeLongitudeAccuracy
         }
     }
 
     /// Altitude value for the geolocation context.
     var geoAltitude: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoAltitude] as? NSNumber
+            return geoDict[kSPGeoAltitude] as? NSNumber
         }
         set(altitude) {
-            geoLocationDict[kSPGeoAltitude] = altitude
+            geoDict[kSPGeoAltitude] = altitude
         }
     }
 
     /// AltitudeAccuracy value for the geolocation context.
     var geoAltitudeAccuracy: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoAltitudeAccuracy] as? NSNumber
+            return geoDict[kSPGeoAltitudeAccuracy] as? NSNumber
         }
         set(altitudeAccuracy) {
-            geoLocationDict[kSPGeoAltitudeAccuracy] = altitudeAccuracy
+            geoDict[kSPGeoAltitudeAccuracy] = altitudeAccuracy
         }
     }
 
     var geoBearing: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoBearing] as? NSNumber
+            return geoDict[kSPGeoBearing] as? NSNumber
         }
         set(bearing) {
-            geoLocationDict[kSPGeoBearing] = bearing
+            geoDict[kSPGeoBearing] = bearing
         }
     }
 
     /// Speed value for the geolocation context.
     var geoSpeed: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoSpeed] as? NSNumber
+            return geoDict[kSPGeoSpeed] as? NSNumber
         }
         set(speed) {
-            geoLocationDict[kSPGeoSpeed] = speed
+            geoDict[kSPGeoSpeed] = speed
         }
     }
 
     /// Timestamp value for the geolocation context.
     var geoTimestamp: NSNumber? {
         get {
-            return geoLocationDict[kSPGeoTimestamp] as? NSNumber
+            return geoDict[kSPGeoTimestamp] as? NSNumber
         }
         set(timestamp) {
-            geoLocationDict[kSPGeoTimestamp] = timestamp
+            geoDict[kSPGeoTimestamp] = timestamp
         }
     }
 
@@ -298,14 +298,14 @@ class Subject : NSObject {
 
     //#pragma clang diagnostic pop
 
-    func getStandardDict(userAnonymisation: Bool) -> Payload? {
+    func standardDict(userAnonymisation: Bool) -> [String : String] {
         if userAnonymisation {
-            var copy = standardDict.dictionary ?? [:]
+            var copy = standardDict
             copy.removeValue(forKey: kSPUid)
             copy.removeValue(forKey: kSPDomainUid)
             copy.removeValue(forKey: kSPNetworkUid)
             copy.removeValue(forKey: kSPIpAddress)
-            return Payload(dictionary: copy)
+            return copy
         }
         return standardDict
     }
@@ -313,7 +313,7 @@ class Subject : NSObject {
     /// Gets all platform dictionary pairs to decorate event with. Returns nil if not enabled.
     /// - Parameter userAnonymisation: Whether to anonymise user identifiers
     /// - Returns: A SPPayload with all platform specific pairs.
-    func getPlatformDict(userAnonymisation: Bool, advertisingIdentifierRetriever: (() -> UUID?)?) -> Payload? {
+    func platformDict(userAnonymisation: Bool, advertisingIdentifierRetriever: (() -> UUID?)?) -> Payload? {
         if platformContext {
             return platformContextManager.fetchPlatformDict(
                 userAnonymisation: userAnonymisation,
@@ -325,10 +325,10 @@ class Subject : NSObject {
 
     /// Gets the geolocation dictionary if the required keys are available. Returns nil if not enabled.
     /// - Returns: A dictionary with key-value pairs of the geolocation context.
-    func getGeoLocationDict() -> [String : NSObject]? {
+    public var geoLocationDict: [String : NSObject]? {
         if geoLocationContext {
-            if geoLocationDict[kSPGeoLatitude] != nil && geoLocationDict[kSPGeoLongitude] != nil {
-                return geoLocationDict
+            if geoDict[kSPGeoLatitude] != nil && geoDict[kSPGeoLongitude] != nil {
+                return geoDict
             } else {
                 logDebug(message: "GeoLocation missing required fields; cannot get.")
                 return nil
diff --git a/Sources/Core/Tracker/DeepLinkStateMachine.swift b/Sources/Core/Tracker/DeepLinkStateMachine.swift
index 746783741..abbabf105 100644
--- a/Sources/Core/Tracker/DeepLinkStateMachine.swift
+++ b/Sources/Core/Tracker/DeepLinkStateMachine.swift
@@ -77,7 +77,7 @@ class DeepLinkStateMachine: StateMachineProtocol {
         return nil
     }
 
-    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]? {
         return nil
     }
 }
diff --git a/Sources/Core/Tracker/LifecycleState.swift b/Sources/Core/Tracker/LifecycleState.swift
index 7e8264990..425e62cca 100644
--- a/Sources/Core/Tracker/LifecycleState.swift
+++ b/Sources/Core/Tracker/LifecycleState.swift
@@ -22,16 +22,14 @@ import Foundation
 
 class LifecycleState: NSObject, State {
     private(set) var isForeground = false
-    private(set) var index: NSNumber?
+    private(set) var index: Int
 
-    init(asForegroundWithIndex index: NSNumber?) {
-        super.init()
+    init(asForegroundWithIndex index: Int) {
         isForeground = true
         self.index = index
     }
 
-    init(asBackgroundWithIndex index: NSNumber?) {
-        super.init()
+    init(asBackgroundWithIndex index: Int) {
         isForeground = false
         self.index = index
     }
diff --git a/Sources/Core/Tracker/LifecycleStateMachine.swift b/Sources/Core/Tracker/LifecycleStateMachine.swift
index c1d4e31d7..c459068d8 100644
--- a/Sources/Core/Tracker/LifecycleStateMachine.swift
+++ b/Sources/Core/Tracker/LifecycleStateMachine.swift
@@ -30,10 +30,10 @@ class LifecycleStateMachine: StateMachineProtocol {
 
     func transition(from event: Event, state currentState: State?) -> State? {
         if let e = event as? Foreground {
-            return LifecycleState(asForegroundWithIndex: NSNumber(value: e.index))
+            return LifecycleState(asForegroundWithIndex: e.index)
         }
         if let e = event as? Background {
-            return LifecycleState(asBackgroundWithIndex: NSNumber(value: e.index))
+            return LifecycleState(asBackgroundWithIndex: e.index)
         }
         return nil
     }
@@ -50,7 +50,7 @@ class LifecycleStateMachine: StateMachineProtocol {
         }
         if let s = state as? LifecycleState {
             let entity = LifecycleEntity(isVisible: s.isForeground)
-            entity.index = s.index
+            entity.index = NSNumber(value: s.index)
             return [entity]
         }
         return nil
@@ -60,7 +60,7 @@ class LifecycleStateMachine: StateMachineProtocol {
         return []
     }
 
-    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]? {
         return nil
     }
 }
diff --git a/Sources/Core/Tracker/ServiceProvider.swift b/Sources/Core/Tracker/ServiceProvider.swift
index aff795503..f78d274d5 100644
--- a/Sources/Core/Tracker/ServiceProvider.swift
+++ b/Sources/Core/Tracker/ServiceProvider.swift
@@ -136,7 +136,7 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
         let _ = tracker // Build tracker to initialize NotificationCenter receivers
     }
 
-    func reset(withConfigurations configurations: [Configuration]) {
+    func reset(configurations: [Configuration]) {
         stopServices()
         resetConfigurationUpdates()
         processConfigurations(configurations)
diff --git a/Sources/Core/Tracker/Tracker.swift b/Sources/Core/Tracker/Tracker.swift
index d516ba129..0c3c93f81 100644
--- a/Sources/Core/Tracker/Tracker.swift
+++ b/Sources/Core/Tracker/Tracker.swift
@@ -30,9 +30,9 @@ func uncaughtExceptionHandler(_ exception: NSException) {
         if message.count == 0 { return }
     
         // Construct userInfo
-        var userInfo: [String : NSObject] = [:]
-        userInfo["message"] = message as NSObject
-        userInfo["stacktrace"] = stacktrace as NSObject
+        var userInfo: [String : Any] = [:]
+        userInfo["message"] = message
+        userInfo["stacktrace"] = stacktrace
     
         // Send notification to tracker
         NotificationCenter.default.post(
@@ -60,7 +60,7 @@ class Tracker: NSObject {
     private(set) var currentScreenState: ScreenState?
     /// List of tags associated to global contexts.
     
-    private var trackerData: [String : NSObject]? = nil
+    private var trackerData: [String : String]? = nil
     func setTrackerData() {
         var trackerVersion = kSPVersion
         if trackerVersionSuffix.count != 0 {
@@ -72,9 +72,9 @@ class Tracker: NSObject {
             }
         }
         trackerData = [
-            kSPTrackerVersion : trackerVersion as NSObject,
-            kSPNamespace : trackerNamespace as NSObject,
-            kSPAppId : appId as NSObject
+            kSPTrackerVersion : trackerVersion,
+            kSPNamespace : trackerNamespace,
+            kSPAppId : appId
         ]
     }
 
@@ -366,7 +366,7 @@ class Tracker: NSObject {
             if !installTracker.isNewInstall && previousTimestamp == nil {
                 return
             }
-            let data: [String: NSObject] = [:]
+            let data: [String: Any] = [:]
             let installEvent = SelfDescribingJson(schema: kSPApplicationInstallSchema, andDictionary: data)
             let event = SelfDescribing(eventData: installEvent)
             event.trueTimestamp = previousTimestamp // it can be nil
@@ -540,8 +540,10 @@ class Tracker: NSObject {
             let ttInMilliSeconds = Int64(trueTimestamp.timeIntervalSince1970 * 1000)
             payload.addValueToPayload(String(format: "%lld", ttInMilliSeconds), forKey: kSPTrueTimestamp)
         }
-        payload.addDictionaryToPayload(trackerData)
-        if let subjectDict = subject?.getStandardDict(userAnonymisation: userAnonymisation)?.dictionary {
+        if let trackerData = trackerData {
+            payload.addDictionaryToPayload(trackerData)
+        }
+        if let subjectDict = subject?.standardDict(userAnonymisation: userAnonymisation) {
             payload.addDictionaryToPayload(subjectDict)
         }
         payload.addValueToPayload(devicePlatformToString(devicePlatform), forKey: kSPPlatform)
@@ -557,18 +559,15 @@ class Tracker: NSObject {
 
         if let schema = event.schema {
             let eventPayload = event.payload
-            let data = SelfDescribingJson(schema: schema, andData: eventPayload as NSObject)
-            if let data = data.dictionary as NSObject? {
-                let unstructuredEventPayload: [String : NSObject] = [
-                    kSPSchema: kSPUnstructSchema as NSObject,
-                    kSPData: data
-                ]
-                payload.addDictionaryToPayload(
-                    unstructuredEventPayload,
-                    base64Encoded: base64Encoded,
-                    typeWhenEncoded: kSPUnstructuredEncoded,
-                    typeWhenNotEncoded: kSPUnstructured)
-            }
+            let data = SelfDescribingJson(schema: schema, andData: eventPayload)
+            let unstructuredEventPayload = SelfDescribingJson.dictionary(
+                schema: kSPUnstructSchema,
+                data: data.dictionary)
+            payload.addDictionaryToPayload(
+                unstructuredEventPayload,
+                base64Encoded: base64Encoded,
+                typeWhenEncoded: kSPUnstructuredEncoded,
+                typeWhenNotEncoded: kSPUnstructured)
         }
     }
 
@@ -591,9 +590,9 @@ class Tracker: NSObject {
         } else if event.schema == kSPScreenViewSchema {
             for entity in contexts {
                 if entity.schema == DeepLinkEntity.schema {
-                    let data = entity.data as? [AnyHashable : Any]
-                    url = data?[DeepLinkEntity.paramUrl] as? String
-                    referrer = data?[DeepLinkEntity.paramReferrer] as? String
+                    let data = entity.data
+                    url = data[DeepLinkEntity.paramUrl] as? String
+                    referrer = data[DeepLinkEntity.paramReferrer] as? String
                     break
                 }
             }
@@ -613,12 +612,12 @@ class Tracker: NSObject {
 
     func addBasicContexts(toContexts contexts: inout [SelfDescribingJson], eventId: String, eventTimestamp: Int64, isService: Bool) {
         if subject != nil {
-            if let platformDict = subject?.getPlatformDict(
+            if let platformDict = subject?.platformDict(
                 userAnonymisation: userAnonymisation,
                 advertisingIdentifierRetriever: advertisingIdentifierRetriever)?.dictionary {
                 contexts.append(SelfDescribingJson(schema: platformContextSchema, andDictionary: platformDict))
             }
-            if let geoLocationDict = subject?.getGeoLocationDict() {
+            if let geoLocationDict = subject?.geoLocationDict {
                 contexts.append(SelfDescribingJson(schema: kSPGeoContextSchema, andDictionary: geoLocationDict))
             }
         }
@@ -663,21 +662,17 @@ class Tracker: NSObject {
         if contexts.count == 0 {
             return
         }
-        var data: [[String : NSObject]] = []
-        for context in contexts {
-            if let dict = context.dictionary {
-                data.append(dict)
-            }
-        }
 
-        let finalContext = SelfDescribingJson(schema: kSPContextSchema, andData: data as NSObject)
-        if let dict = finalContext.dictionary {
-            payload.addDictionaryToPayload(
-                dict,
-                base64Encoded: base64Encoded,
-                typeWhenEncoded: kSPContextEncoded,
-                typeWhenNotEncoded: kSPContext)
-        }
+        let dict: [String : Any] = [
+            kSPSchema: kSPContextSchema,
+            kSPData: contexts.map { $0.dictionary }
+        ]
+
+        payload.addDictionaryToPayload(
+            dict,
+            base64Encoded: base64Encoded,
+            typeWhenEncoded: kSPContextEncoded,
+            typeWhenNotEncoded: kSPContext)
     }
 
     deinit {
diff --git a/Sources/Core/Tracker/TrackerEvent.swift b/Sources/Core/Tracker/TrackerEvent.swift
index f546fbc5b..5754f94b0 100644
--- a/Sources/Core/Tracker/TrackerEvent.swift
+++ b/Sources/Core/Tracker/TrackerEvent.swift
@@ -23,8 +23,8 @@ import Foundation
 
 class TrackerEvent : InspectableEvent {
     
-    private var _payload: [String: NSObject]
-    var payload: [String: NSObject] {
+    private var _payload: [String: Any]
+    var payload: [String: Any] {
         get { return _payload }
         set { _payload = newValue }
     }
@@ -69,7 +69,7 @@ class TrackerEvent : InspectableEvent {
         }
     }
 
-    func addPayloadValues(_ payload: [String : NSObject]) -> Bool {
+    func addPayloadValues(_ payload: [String : Any]) -> Bool {
         var result = true
         for (key, obj) in payload {
             if self.payload[key] == nil {
diff --git a/Sources/Core/Tracker/WebViewMessageHandler.swift b/Sources/Core/Tracker/WebViewMessageHandler.swift
index d9a38c750..4a3a9c78f 100644
--- a/Sources/Core/Tracker/WebViewMessageHandler.swift
+++ b/Sources/Core/Tracker/WebViewMessageHandler.swift
@@ -62,7 +62,7 @@ class WebViewMessageHandler: NSObject, WKScriptMessageHandler {
 
     func trackSelfDescribing(_ event: [AnyHashable : Any], withContext context: [[AnyHashable : Any]], andTrackers trackers: [String]) {
         if let schema = event["schema"] as? String,
-           let payload = event["data"] as? [String : NSObject] {
+           let payload = event["data"] as? [String : Any] {
             let selfDescribing = SelfDescribing(schema: schema, payload: payload)
             track(selfDescribing, withContext: context, andTrackers: trackers)
         }
@@ -156,7 +156,7 @@ class WebViewMessageHandler: NSObject, WKScriptMessageHandler {
 
         for entityJson in context {
             if let schema = entityJson["schema"] as? String,
-               let payload = entityJson["data"] as? [String : NSObject] {
+               let payload = entityJson["data"] as? [String : Any] {
                 let entity = SelfDescribingJson(schema: schema, andDictionary: payload)
                 contextEntities.append(entity)
             }
diff --git a/Sources/Core/Utils/DataPersistence.swift b/Sources/Core/Utils/DataPersistence.swift
index 186233cad..96ff948d5 100644
--- a/Sources/Core/Utils/DataPersistence.swift
+++ b/Sources/Core/Utils/DataPersistence.swift
@@ -29,28 +29,28 @@ let kSessionFilenameV1 = "session.dict"
 let kSessionFilenamePrefixV2_2 = "session"
 var sessionKey = "session"
 
-class DataPersistence: NSObject {
-    var data: [String : [String : NSObject]] {
+class DataPersistence {
+    var data: [String : [String : Any]] {
         get {
             objc_sync_enter(self)
             defer { objc_sync_exit(self) }
             if !isStoredOnFile {
-                return ((UserDefaults.standard.dictionary(forKey: userDefaultsKey) ?? [:]) as? [String : [String : NSObject]]) ?? [:]
+                return ((UserDefaults.standard.dictionary(forKey: userDefaultsKey) ?? [:]) as? [String : [String : Any]]) ?? [:]
             }
-            var result: [String : [String : NSObject]]? = nil
+            var result: [String : [String : Any]]? = nil
             if let fileUrl = fileUrl {
-                result = NSDictionary(contentsOf: fileUrl) as Dictionary? as? [String : [String : NSObject]]
+                result = NSDictionary(contentsOf: fileUrl) as? [String : [String : Any]]
             }
 
             if result == nil {
                 // Initialise
                 result = [:]
-                var sessionDict: [AnyHashable : Any] = [:]
+                var sessionDict: [String : Any] = [:]
                 // Add missing fields
                 sessionDict[kSPSessionFirstEventId] = ""
                 sessionDict[kSPSessionStorage] = "LOCAL_STORAGE"
                 // Wrap up
-                result?[sessionKey] = sessionDict as? [String : NSObject]
+                result?[sessionKey] = sessionDict
                 if let result = result, let fileUrl = fileUrl {
                     let _ = storeDictionary(result, fileURL: fileUrl)
                 }
@@ -69,7 +69,7 @@ class DataPersistence: NSObject {
         }
     }
 
-    var session: [String : NSObject]? {
+    var session: [String : Any]? {
         get {
             return (data)[sessionKey]
         }
@@ -91,7 +91,6 @@ class DataPersistence: NSObject {
     private var fileUrl: URL?
 
     init(namespace escapedNamespace: String, storedOnFile isStoredOnFile: Bool) {
-        super.init()
         self.escapedNamespace = escapedNamespace
         userDefaultsKey = "\(kSPSessionDictionaryPrefix)_\(escapedNamespace)"
 #if !(os(tvOS) || os(watchOS))
diff --git a/Sources/Core/Utils/DeviceInfoMonitor.swift b/Sources/Core/Utils/DeviceInfoMonitor.swift
index 129a5e0ba..69f57f3cb 100644
--- a/Sources/Core/Utils/DeviceInfoMonitor.swift
+++ b/Sources/Core/Utils/DeviceInfoMonitor.swift
@@ -184,7 +184,7 @@ class DeviceInfoMonitor {
 
     /// Returns battery state for the device.
     /// - Returns: One of "charging", "full", "unplugged" or NULL
-    var batteryState: String {
+    var batteryState: String? {
         #if os(iOS)
         switch UIDevice.current.batteryState {
         case .charging:
@@ -194,10 +194,10 @@ class DeviceInfoMonitor {
         case .unplugged:
             return "unplugged"
         default:
-            return ""
+            return nil
         }
         #else
-        return ""
+        return nil
         #endif
     }
 
diff --git a/Sources/Core/Utils/Utilities.swift b/Sources/Core/Utils/Utilities.swift
index 4e9f15d5d..978c18bae 100644
--- a/Sources/Core/Utils/Utilities.swift
+++ b/Sources/Core/Utils/Utilities.swift
@@ -124,9 +124,9 @@ class Utilities {
     /// This method can encode string, numbers, and bool values, but not embedded arrays or dictionaries.
     /// It encodes bool as 1 and 0.
     /// - Returns: The url encoded string of the dictionary.
-    class func urlEncode(_ dictionary: [String : NSObject]) -> String {
-        return dictionary.map { (key: String, value: NSObject) in
-            "\(self.urlEncode(key))=\(self.urlEncode(value.description))"
+    class func urlEncode(_ dictionary: [String : Any]) -> String {
+        return dictionary.map { (key: String, value: Any) in
+            "\(self.urlEncode(key))=\(self.urlEncode(String(describing: value)))"
         }.joined(separator: "&")
     }
 
diff --git a/Sources/Snowplow/Configurations/Configuration.swift b/Sources/Snowplow/Configurations/Configuration.swift
index bab1ac899..822288179 100644
--- a/Sources/Snowplow/Configurations/Configuration.swift
+++ b/Sources/Snowplow/Configurations/Configuration.swift
@@ -24,7 +24,7 @@ import Foundation
 @objc(SPConfiguration)
 public class Configuration: NSObject, NSCopying, NSSecureCoding {
     @objc
-    public convenience init?(dictionary: [String : NSObject]) {
+    public convenience init?(dictionary: [String : Any]) {
         self.init()
     }
 
diff --git a/Sources/Snowplow/Configurations/ConfigurationBundle.swift b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
index 8d20b5d54..19154a9e8 100644
--- a/Sources/Snowplow/Configurations/ConfigurationBundle.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
@@ -64,23 +64,23 @@ public class ConfigurationBundle: Configuration {
     }
 
     @objc
-    public init?(dictionary: [String : NSObject]) {
+    public init?(dictionary: [String : Any]) {
         if let namespace = dictionary["namespace"] as? String {
             self.namespace = namespace
         } else {
             logDebug(message: "Error assigning: namespace")
             return nil
         }
-        if let config = dictionary["networkConfiguration"] as? [String : NSObject] {
+        if let config = dictionary["networkConfiguration"] as? [String : Any] {
             networkConfiguration = NetworkConfiguration(dictionary: config)
         }
-        if let config = dictionary["trackerConfiguration"] as? [String : NSObject] {
+        if let config = dictionary["trackerConfiguration"] as? [String : Any] {
             trackerConfiguration = TrackerConfiguration(dictionary: config)
         }
-        if let config = dictionary["subjectConfiguration"] as? [String : NSObject] {
+        if let config = dictionary["subjectConfiguration"] as? [String : Any] {
             subjectConfiguration = SubjectConfiguration(dictionary: config)
         }
-        if let config = dictionary["sessionConfiguration"] as? [String: NSObject] {
+        if let config = dictionary["sessionConfiguration"] as? [String: Any] {
             sessionConfiguration = SessionConfiguration(dictionary: config)
         }
     }
diff --git a/Sources/Snowplow/Configurations/NetworkConfiguration.swift b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
index 4a4dab6d0..99ddae2a0 100644
--- a/Sources/Snowplow/Configurations/NetworkConfiguration.swift
+++ b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
@@ -48,7 +48,7 @@ public class NetworkConfiguration: Configuration {
 
     /// Allow endpoint and method only.
     @objc
-    public convenience init?(dictionary: [String : NSObject]) {
+    public convenience init?(dictionary: [String : Any]) {
         if let endpoint = dictionary["endpoint"] as? String {
             let method = dictionary["method"] as? String
             let httpMethod = (method == "get") ? HttpMethodOptions.get : HttpMethodOptions.post
diff --git a/Sources/Snowplow/Configurations/SessionConfiguration.swift b/Sources/Snowplow/Configurations/SessionConfiguration.swift
index 6d8184702..baf2e0c65 100644
--- a/Sources/Snowplow/Configurations/SessionConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SessionConfiguration.swift
@@ -73,7 +73,7 @@ public class SessionConfiguration: Configuration, SessionConfigurationProtocol {
     }
 
     @objc
-    public convenience init?(dictionary: [String : NSObject]) {
+    public convenience init?(dictionary: [String : Any]) {
         let foregroundTimeout = dictionary["foregroundTimeout"] as? Int ?? TrackerDefaults.foregroundTimeout
         let backgroundTimeout = dictionary["backgroundTimeout"] as? Int ?? TrackerDefaults.backgroundTimeout
         self.init(foregroundTimeoutInSeconds: foregroundTimeout, backgroundTimeoutInSeconds: backgroundTimeout)
diff --git a/Sources/Snowplow/Configurations/SubjectConfiguration.swift b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
index c70aeae9f..a04206760 100644
--- a/Sources/Snowplow/Configurations/SubjectConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
@@ -95,7 +95,7 @@ public protocol SubjectConfigurationProtocol: AnyObject {
 /// The contexts to track can be enabled in the `TrackerConfiguration` class.
 @objc(SPSubjectConfiguration)
 public class SubjectConfiguration: Configuration, SubjectConfigurationProtocol {
-    convenience init(dictionary: [String : NSObject]) {
+    convenience init(dictionary: [String : Any]) {
         self.init()
         if let userId = dictionary["userId"] as? String { self.userId = userId }
         if let networkUserId = dictionary["networkUserId"] as? String { self.networkUserId = networkUserId }
diff --git a/Sources/Snowplow/Configurations/TrackerConfiguration.swift b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
index 3608f80f0..e93a33ba4 100644
--- a/Sources/Snowplow/Configurations/TrackerConfiguration.swift
+++ b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
@@ -164,7 +164,7 @@ public class TrackerConfiguration: Configuration, TrackerConfigurationProtocol {
     }
 
     @objc
-    public convenience init?(dictionary: [String : NSObject]) {
+    public convenience init?(dictionary: [String : Any]) {
         self.init()
         if let appId = dictionary["appId"] as? String {
             self.appId = appId
diff --git a/Sources/Snowplow/Emitter/EventStore.swift b/Sources/Snowplow/Emitter/EventStore.swift
index 8a00894fd..8991b11a6 100644
--- a/Sources/Snowplow/Emitter/EventStore.swift
+++ b/Sources/Snowplow/Emitter/EventStore.swift
@@ -37,7 +37,7 @@ public protocol EventStore: NSObjectProtocol {
     /// - Parameter storeIds: the events' identifiers in the store.
     /// - Returns: a boolean of success to remove.
     @objc
-    func removeEvents(withIds storeIds: [NSNumber]) -> Bool
+    func removeEvents(withIds storeIds: [Int64]) -> Bool
     /// Empties the store of all the events.
     /// - Returns: a boolean of success to remove.
     @objc
diff --git a/Sources/Snowplow/Entities/DeepLinkEntity.swift b/Sources/Snowplow/Entities/DeepLinkEntity.swift
index c01420dae..866e3d6f5 100644
--- a/Sources/Snowplow/Entities/DeepLinkEntity.swift
+++ b/Sources/Snowplow/Entities/DeepLinkEntity.swift
@@ -42,16 +42,16 @@ public class DeepLinkEntity: SelfDescribingJson {
     @objc
     public init(url: String) {
         self.url = url
-        super.init(schema: DeepLinkEntity.schema, andData: nil)
+        super.init(schema: DeepLinkEntity.schema, andData: [:])
     }
 
     @objc
-    override public var data: NSObject? {
+    override public var data: [String : Any] {
         get {
-            var data: [String: NSObject] = [:]
-            data[DeepLinkEntity.paramUrl] = url as NSObject
-            data[DeepLinkEntity.paramReferrer] = referrer as NSObject?
-            return data as NSObject
+            var data: [String: Any] = [:]
+            data[DeepLinkEntity.paramUrl] = url
+            data[DeepLinkEntity.paramReferrer] = referrer
+            return data
         }
         set {}
     }
diff --git a/Sources/Snowplow/Entities/LifecycleEntity.swift b/Sources/Snowplow/Entities/LifecycleEntity.swift
index cfbf88f14..6815175d1 100644
--- a/Sources/Snowplow/Entities/LifecycleEntity.swift
+++ b/Sources/Snowplow/Entities/LifecycleEntity.swift
@@ -32,23 +32,19 @@ public class LifecycleEntity: SelfDescribingJson {
 
     @objc
     public init(isVisible: Bool) {
-        var parameters: [String : NSObject] = [:]
-        parameters[kSPLifecycleEntityParamIsVisible] = NSNumber(value: isVisible)
-        super.init(schema: kSPLifecycleEntitySchema, andData: parameters as NSObject)
+        var parameters: [String : Any] = [:]
+        parameters[kSPLifecycleEntityParamIsVisible] = isVisible
+        super.init(schema: kSPLifecycleEntitySchema, andData: parameters)
     }
 
     @objc
     public var index: NSNumber? {
         set {
-            if let data = data,
-               var parameters = data as? [String : NSObject] {
-                parameters[kSPLifecycleEntityParamIndex] = newValue
-            }
+            data[kSPLifecycleEntityParamIndex] = newValue?.intValue
         }
         get {
-            if let data = data,
-               let parameters = data as? [String : NSObject] {
-                return parameters[kSPLifecycleEntityParamIndex] as? NSNumber
+            if let value = data[kSPLifecycleEntityParamIndex] as? Int {
+                return NSNumber(value: value)
             }
             return nil
         }
diff --git a/Sources/Snowplow/Events/Background.swift b/Sources/Snowplow/Events/Background.swift
index 99ae63632..bd927c2bc 100644
--- a/Sources/Snowplow/Events/Background.swift
+++ b/Sources/Snowplow/Events/Background.swift
@@ -41,9 +41,9 @@ public class Background: SelfDescribingAbstract {
         return kSPBackgroundSchema
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [AnyHashable : Any] = [:]
-        payload[kSPBackgroundIndex] = NSNumber(value: index)
-        return payload as? [String : NSObject] ?? [:]
+    override var payload: [String : Any] {
+        return [
+            kSPBackgroundIndex: index
+        ]
     }
 }
diff --git a/Sources/Snowplow/Events/ConsentGranted.swift b/Sources/Snowplow/Events/ConsentGranted.swift
index 274fa2351..297faf0c9 100644
--- a/Sources/Snowplow/Events/ConsentGranted.swift
+++ b/Sources/Snowplow/Events/ConsentGranted.swift
@@ -83,10 +83,10 @@ public class ConsentGranted: SelfDescribingAbstract {
         return kSPConsentGrantedSchema
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[KSPCgExpiry] = expiry as NSObject
-        return payload
+    override var payload: [String : Any] {
+        return [
+            KSPCgExpiry: expiry
+        ]
     }
 
     override func beginProcessing(withTracker tracker: Tracker) {
diff --git a/Sources/Snowplow/Events/ConsentWithdrawn.swift b/Sources/Snowplow/Events/ConsentWithdrawn.swift
index cb59df0a5..272999bf9 100644
--- a/Sources/Snowplow/Events/ConsentWithdrawn.swift
+++ b/Sources/Snowplow/Events/ConsentWithdrawn.swift
@@ -51,9 +51,9 @@ public class ConsentWithdrawn: SelfDescribingAbstract {
         return kSPConsentWithdrawnSchema
     }
 
-    override var payload: [String : NSObject] {
+    override var payload: [String : Any] {
         return [
-            KSPCwAll: all ? NSNumber(value: true) : NSNumber(value: false)
+            KSPCwAll: all
         ]
     }
 
diff --git a/Sources/Snowplow/Events/DeepLinkReceived.swift b/Sources/Snowplow/Events/DeepLinkReceived.swift
index b4bfc5467..47788b1ba 100644
--- a/Sources/Snowplow/Events/DeepLinkReceived.swift
+++ b/Sources/Snowplow/Events/DeepLinkReceived.swift
@@ -38,7 +38,7 @@ public class DeepLinkReceived: SelfDescribingAbstract {
     public init(url: String) {
         self.url = url
     }
-    
+
     @objc
     class var schema: String {
         return "iglu:com.snowplowanalytics.mobile/deep_link_received/jsonschema/1-0-0"
@@ -58,12 +58,12 @@ public class DeepLinkReceived: SelfDescribingAbstract {
         return DeepLinkReceived.schema
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
         if let referrer = referrer {
-            payload[DeepLinkReceived.paramReferrer] = referrer as NSObject
+            payload[DeepLinkReceived.paramReferrer] = referrer
         }
-        payload[DeepLinkReceived.paramUrl] = url as NSObject
+        payload[DeepLinkReceived.paramUrl] = url
         return payload
     }
 }
diff --git a/Sources/Snowplow/Events/Ecommerce.swift b/Sources/Snowplow/Events/Ecommerce.swift
index ef21819e2..fe89770a2 100644
--- a/Sources/Snowplow/Events/Ecommerce.swift
+++ b/Sources/Snowplow/Events/Ecommerce.swift
@@ -65,21 +65,21 @@ public class Ecommerce : PrimitiveAbstract {
         return kSPEventEcomm
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPEcommTotal] = String(format: "%.02f", totalValue) as NSObject
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPEcommTotal] = String(format: "%.02f", totalValue)
         if let taxValue = taxValue {
-            payload[kSPEcommTax] = String(format: "%.02f", taxValue.doubleValue) as NSObject
+            payload[kSPEcommTax] = String(format: "%.02f", taxValue.doubleValue)
         }
         if let shipping = shipping {
-            payload[kSPEcommShipping] = String(format: "%.02f", shipping.doubleValue) as NSObject
+            payload[kSPEcommShipping] = String(format: "%.02f", shipping.doubleValue)
         }
-        payload[kSPEcommId] = orderId as NSObject
-        payload[kSPEcommAffiliation] = affiliation as NSObject?
-        payload[kSPEcommCity] = city as NSObject?
-        payload[kSPEcommState] = state as NSObject?
-        payload[kSPEcommCountry] = country as NSObject?
-        payload[kSPEcommCurrency] = currency as NSObject?
+        payload[kSPEcommId] = orderId
+        payload[kSPEcommAffiliation] = affiliation
+        payload[kSPEcommCity] = city
+        payload[kSPEcommState] = state
+        payload[kSPEcommCountry] = country
+        payload[kSPEcommCurrency] = currency
         return payload
     }
 
diff --git a/Sources/Snowplow/Events/EcommerceItem.swift b/Sources/Snowplow/Events/EcommerceItem.swift
index 97ab77946..9f0346f28 100644
--- a/Sources/Snowplow/Events/EcommerceItem.swift
+++ b/Sources/Snowplow/Events/EcommerceItem.swift
@@ -56,15 +56,15 @@ public class EcommerceItem : PrimitiveAbstract {
         return kSPEventEcommItem
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPEcommItemId] = orderId as NSObject?
-        payload[kSPEcommItemSku] = sku as NSObject
-        payload[kSPEcommItemName] = name as NSObject?
-        payload[kSPEcommItemCategory] = category as NSObject?
-        payload[kSPEcommItemCurrency] = currency as NSObject?
-        payload[kSPEcommItemPrice] = String(format: "%.02f", price) as NSObject
-        payload[kSPEcommItemQuantity] = String(format: "%ld", quantity) as NSObject
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPEcommItemId] = orderId
+        payload[kSPEcommItemSku] = sku
+        payload[kSPEcommItemName] = name
+        payload[kSPEcommItemCategory] = category
+        payload[kSPEcommItemCurrency] = currency
+        payload[kSPEcommItemPrice] = String(format: "%.02f", price)
+        payload[kSPEcommItemQuantity] = String(format: "%ld", quantity)
         return payload
     }
 }
diff --git a/Sources/Snowplow/Events/EventBase.swift b/Sources/Snowplow/Events/EventBase.swift
index d352806d7..f038b89a3 100644
--- a/Sources/Snowplow/Events/EventBase.swift
+++ b/Sources/Snowplow/Events/EventBase.swift
@@ -33,7 +33,7 @@ public class Event: NSObject {
     public var contexts: [SelfDescribingJson] = []
     
     /// The payload of the event.
-    var payload: [String : NSObject] {
+    var payload: [String : Any] {
         NSException(
             name: .internalInconsistencyException,
             reason: "You must override \(NSStringFromSelector(#function)) in a subclass",
diff --git a/Sources/Snowplow/Events/Foreground.swift b/Sources/Snowplow/Events/Foreground.swift
index ef8c2fe57..019de493c 100644
--- a/Sources/Snowplow/Events/Foreground.swift
+++ b/Sources/Snowplow/Events/Foreground.swift
@@ -41,9 +41,9 @@ public class Foreground: SelfDescribingAbstract {
         return kSPForegroundSchema
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPForegroundIndex] = NSNumber(value: index)
-        return payload
+    override var payload: [String : Any] {
+        return [
+            kSPForegroundIndex: index
+        ]
     }
 }
diff --git a/Sources/Snowplow/Events/MessageNotification.swift b/Sources/Snowplow/Events/MessageNotification.swift
index 648a8338a..e0b0f2af4 100644
--- a/Sources/Snowplow/Events/MessageNotification.swift
+++ b/Sources/Snowplow/Events/MessageNotification.swift
@@ -129,11 +129,11 @@ public class MessageNotification : SelfDescribingAbstract {
         self.trigger = trigger
     }
     
-    class func messageNotification(userInfo: [String: NSObject], defaultTitle: String?, defaultBody: String?) -> MessageNotification? {
-        guard let aps = userInfo["aps"] as? [String : NSObject] else {
+    class func messageNotification(userInfo: [String: Any], defaultTitle: String?, defaultBody: String?) -> MessageNotification? {
+        guard let aps = userInfo["aps"] as? [String : Any] else {
             return nil
         }
-        guard let alert = aps["alert"] as? [String : NSObject] else {
+        guard let alert = aps["alert"] as? [String : Any] else {
             return nil
         }
         // alert fields
@@ -151,7 +151,11 @@ public class MessageNotification : SelfDescribingAbstract {
         // aps fields
         event.notificationCount = aps["badge"] as? Int
         event.sound = aps["sound"] as? String
-        event.contentAvailable = aps["content-available"] as? Bool
+        if let contentAvailable = aps["content-available"] as? Bool {
+            event.contentAvailable = contentAvailable
+        } else if let contentAvailable = aps["content-available"] as? Int {
+            event.contentAvailable = contentAvailable > 0
+        }
         event.category = aps["category"] as? String
         event.threadIdentifier = aps["thread-id"] as? String
         return event
@@ -161,35 +165,35 @@ public class MessageNotification : SelfDescribingAbstract {
         return kSPMessageNotificationSchema
     }
     
-    override var payload: [String: NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPMessageNotificationParamAction] = action as NSObject?
+    override var payload: [String: Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPMessageNotificationParamAction] = action
         if let attachments = attachments {
-            payload[kSPMessageNotificationParamMessageNotificationAttachments] = attachments.map { $0.data } as NSObject
+            payload[kSPMessageNotificationParamMessageNotificationAttachments] = attachments.map { $0.data }
         }
-        payload[kSPMessageNotificationParamBody] = body as NSObject?
+        payload[kSPMessageNotificationParamBody] = body
         if let bodyLocArgs = bodyLocArgs {
-            payload[kSPMessageNotificationParamBodyLocArgs] = bodyLocArgs as NSObject
+            payload[kSPMessageNotificationParamBodyLocArgs] = bodyLocArgs
         }
-        payload[kSPMessageNotificationParamBodyLocKey] = bodyLocKey as NSObject?
-        payload[kSPMessageNotificationParamCategory] = category as NSObject?
+        payload[kSPMessageNotificationParamBodyLocKey] = bodyLocKey
+        payload[kSPMessageNotificationParamCategory] = category
         if let contentAvailable = contentAvailable {
-            payload[kSPMessageNotificationParamContentAvailable] = NSNumber(value: contentAvailable)
+            payload[kSPMessageNotificationParamContentAvailable] = contentAvailable
         }
-        payload[kSPMessageNotificationParamGroup] = group as NSObject?
-        payload[kSPMessageNotificationParamIcon] = icon as NSObject?
-        payload[kSPMessageNotificationParamNotificationCount] = notificationCount as NSObject?
-        payload[kSPMessageNotificationParamNotificationTimestamp] = notificationTimestamp as NSObject?
-        payload[kSPMessageNotificationParamSound] = sound as NSObject?
-        payload[kSPMessageNotificationParamSubtitle] = subtitle as NSObject?
-        payload[kSPMessageNotificationParamTag] = tag as NSObject?
-        payload[kSPMessageNotificationParamThreadIdentifier] = threadIdentifier as NSObject?
-        payload[kSPMessageNotificationParamTitle] = title as NSObject?
+        payload[kSPMessageNotificationParamGroup] = group
+        payload[kSPMessageNotificationParamIcon] = icon
+        payload[kSPMessageNotificationParamNotificationCount] = notificationCount
+        payload[kSPMessageNotificationParamNotificationTimestamp] = notificationTimestamp
+        payload[kSPMessageNotificationParamSound] = sound
+        payload[kSPMessageNotificationParamSubtitle] = subtitle
+        payload[kSPMessageNotificationParamTag] = tag
+        payload[kSPMessageNotificationParamThreadIdentifier] = threadIdentifier
+        payload[kSPMessageNotificationParamTitle] = title
         if let titleLocArgs = titleLocArgs {
-            payload[kSPMessageNotificationParamTitleLocArgs] = titleLocArgs as NSObject
+            payload[kSPMessageNotificationParamTitleLocArgs] = titleLocArgs
         }
-        payload[kSPMessageNotificationParamTitleLocKey] = titleLocKey as NSObject?
-        payload[kSPMessageNotificationParamTrigger] = triggerToString(trigger) as NSObject
+        payload[kSPMessageNotificationParamTitleLocKey] = titleLocKey
+        payload[kSPMessageNotificationParamTrigger] = triggerToString(trigger)
         return payload
     }
 }
diff --git a/Sources/Snowplow/Events/MessageNotificationAttachment.swift b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
index 08c8264ce..f814e152f 100644
--- a/Sources/Snowplow/Events/MessageNotificationAttachment.swift
+++ b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
@@ -42,11 +42,11 @@ public class MessageNotificationAttachment : NSObject {
         self.url = url
     }
     
-    var data: [String : NSObject] {
+    var data: [String : Any] {
         return [
-            kSPMessageNotificationAttachmentParamIdentifier: identifer as NSObject,
-            kSPMessageNotificationAttachmentParamType: type as NSObject,
-            kSPMessageNotificationAttachmentParamUrl: url as NSObject
+            kSPMessageNotificationAttachmentParamIdentifier: identifer,
+            kSPMessageNotificationAttachmentParamType: type,
+            kSPMessageNotificationAttachmentParamUrl: url
         ]
     }
 }
diff --git a/Sources/Snowplow/Events/PageView.swift b/Sources/Snowplow/Events/PageView.swift
index 920c8c390..e65c8145e 100644
--- a/Sources/Snowplow/Events/PageView.swift
+++ b/Sources/Snowplow/Events/PageView.swift
@@ -48,12 +48,12 @@ public class PageView : PrimitiveAbstract {
         return kSPEventPageView
     }
     
-    override var payload: [String : NSObject] {
-        var payload: [String: NSObject] = [
-            kSPPageUrl: pageUrl as NSObject
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [
+            kSPPageUrl: pageUrl
         ]
-        payload[kSPPageTitle] = pageTitle as NSObject?
-        payload[kSPPageRefr] = referrer as NSObject?
+        payload[kSPPageTitle] = pageTitle
+        payload[kSPPageRefr] = referrer
         return payload
     }
 }
diff --git a/Sources/Snowplow/Events/PushNotification.swift b/Sources/Snowplow/Events/PushNotification.swift
index 2f8214f24..cb2e564d6 100644
--- a/Sources/Snowplow/Events/PushNotification.swift
+++ b/Sources/Snowplow/Events/PushNotification.swift
@@ -106,15 +106,15 @@ public class PushNotification : SelfDescribingAbstract {
         return kSPPushNotificationSchema
     }
 
-    override var payload: [String : NSObject] {
-        var data: [String: NSObject] = [
-            kSPPushTrigger: trigger as NSObject,
-            kSPPushAction: action as NSObject,
-            kSPPushDeliveryDate: date as NSObject,
-            kSPPushCategoryId: category as NSObject,
-            kSPPushThreadId: thread as NSObject
+    override var payload: [String : Any] {
+        var data: [String: Any] = [
+            kSPPushTrigger: trigger,
+            kSPPushAction: action,
+            kSPPushDeliveryDate: date,
+            kSPPushCategoryId: category,
+            kSPPushThreadId: thread
         ]
-        if let notification = notification?.payload { data[kSPPushNotificationParam] = notification as NSObject }
+        if let notification = notification?.payload { data[kSPPushNotificationParam] = notification }
         return data
     }
 }
@@ -144,7 +144,7 @@ public class NotificationContent : NSObject {
     public var launchImageName: String?
     /// The custom data associated with the notification.
     @objc
-    public var userInfo: [String : NSObject]?
+    public var userInfo: [String : Any]?
     /// Attachments added to the notification (they can be part of the data object).
     @objc
     public var attachments: [NSObject]?
@@ -160,51 +160,51 @@ public class NotificationContent : NSObject {
         self.badge = badge
     }
 
-    var payload: [String : NSObject] {
-        var event: [String : NSObject] = [:]
-        event[kSPPnTitle] = title as NSObject
-        event[kSPPnBody] = body as NSObject
-        event[kSPPnBadge] = badge
+    var payload: [String : Any] {
+        var event: [String : Any] = [:]
+        event[kSPPnTitle] = title
+        event[kSPPnBody] = body
+        event[kSPPnBadge] = badge?.intValue
         if let subtitle = subtitle {
-            event[kSPPnSubtitle] = subtitle as NSObject
+            event[kSPPnSubtitle] = subtitle
         }
         if let sound = sound {
-            event[kSPPnSound] = sound as NSObject
+            event[kSPPnSound] = sound
         }
         if let launchImageName = launchImageName {
-            event[kSPPnLaunchImageName] = launchImageName as NSObject
+            event[kSPPnLaunchImageName] = launchImageName
         }
         if let userInfo = userInfo {
-            // modify contentAvailable value "1" and "0" to @YES and @NO to comply with schema
-            if var aps = userInfo["aps"] as? [NSString : NSObject],
-               let contentAvailable = aps["contentAvailable"] as? NSNumber {
+            // modify contentAvailable value 1 and 0 to true and false to comply with schema
+            if var aps = userInfo["aps"] as? [String : Any],
+               let contentAvailable = aps["contentAvailable"] as? Int {
 
-                if contentAvailable == NSNumber(value: 1) {
-                    aps["contentAvailable"] = NSNumber(value: true)
-                } else if contentAvailable == NSNumber(value: 0) {
-                    aps["contentAvailable"] = NSNumber(value: false)
+                if contentAvailable == 1 {
+                    aps["contentAvailable"] = true
+                } else if contentAvailable == 0 {
+                    aps["contentAvailable"] = false
                 }
                 var newUserInfo = userInfo
-                newUserInfo["aps"] = aps as NSObject
-                event[kSPPnUserInfo] = newUserInfo as NSObject
+                newUserInfo["aps"] = aps
+                event[kSPPnUserInfo] = newUserInfo
+            } else {
+                event[kSPPnUserInfo] = userInfo
             }
         }
         if let attachments = attachments {
-            var converting: [[AnyHashable : Any]] = []
-            for attachment in attachments {
-                var newAttachment: [String : NSObject] = [:]
-                if let value = attachment.value(forKey: "identifier") as? NSObject {
+            event[kSPPnAttachments] = attachments.map { (attachment: NSObject) -> [String : Any] in
+                var newAttachment: [String : Any] = [:]
+                if let value = attachment.value(forKey: "identifier") {
                     newAttachment[kSPPnAttachmentId] = value
                 }
-                if let value = attachment.value(forKey: "URL") as? NSObject {
+                if let value = attachment.value(forKey: "URL") {
                     newAttachment[kSPPnAttachmentUrl] = value
                 }
-                if let value = attachment.value(forKey: "type") as? NSObject {
+                if let value = attachment.value(forKey: "type") {
                     newAttachment[kSPPnAttachmentType] = value
                 }
-                converting.append(newAttachment)
+                return newAttachment
             }
-            event[kSPPnAttachments] = converting as NSObject
         }
         return event // copyItems: true
     }
diff --git a/Sources/Snowplow/Events/SNOWError.swift b/Sources/Snowplow/Events/SNOWError.swift
index df24b1ad5..00e296fa1 100644
--- a/Sources/Snowplow/Events/SNOWError.swift
+++ b/Sources/Snowplow/Events/SNOWError.swift
@@ -47,12 +47,12 @@ public class SNOWError: SelfDescribingAbstract {
         return kSPErrorSchema
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPErrorMessage] = message as NSObject
-        payload[kSPErrorStackTrace] = stackTrace as NSObject?
-        payload[kSPErrorName] = name as NSObject?
-        payload[kSPErrorLanguage] = "SWIFT" as NSObject
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPErrorMessage] = message
+        payload[kSPErrorStackTrace] = stackTrace
+        payload[kSPErrorName] = name
+        payload[kSPErrorLanguage] = "SWIFT"
         return payload
     }
 }
diff --git a/Sources/Snowplow/Events/ScreenView.swift b/Sources/Snowplow/Events/ScreenView.swift
index 1108fa427..10cd2d1a0 100644
--- a/Sources/Snowplow/Events/ScreenView.swift
+++ b/Sources/Snowplow/Events/ScreenView.swift
@@ -81,15 +81,15 @@ public class ScreenView: SelfDescribingAbstract {
         return kSPScreenViewSchema
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPSvName] = name as NSObject
-        payload[kSPSvScreenId] = screenId.uuidString as NSObject
-        if let type = type { payload[kSPSvType] = type as NSObject }
-        if let previousName = previousName { payload[kSPSvPreviousName] = previousName as NSObject }
-        if let previousType = previousType { payload[kSPSvPreviousType] = previousType as NSObject }
-        if let previousId = previousId { payload[kSPSvPreviousScreenId] = previousId as NSObject }
-        if let transitionType = transitionType { payload[kSPSvTransitionType] = transitionType as NSObject }
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPSvName] = name
+        payload[kSPSvScreenId] = screenId.uuidString
+        if let type = type { payload[kSPSvType] = type }
+        if let previousName = previousName { payload[kSPSvPreviousName] = previousName }
+        if let previousType = previousType { payload[kSPSvPreviousType] = previousType }
+        if let previousId = previousId { payload[kSPSvPreviousScreenId] = previousId }
+        if let transitionType = transitionType { payload[kSPSvTransitionType] = transitionType }
         return payload
     }
 
diff --git a/Sources/Snowplow/Events/SelfDescribing.swift b/Sources/Snowplow/Events/SelfDescribing.swift
index 334f2b77d..535459b4d 100644
--- a/Sources/Snowplow/Events/SelfDescribing.swift
+++ b/Sources/Snowplow/Events/SelfDescribing.swift
@@ -26,28 +26,23 @@ import Foundation
 public class SelfDescribing: SelfDescribingAbstract {
     @objc
     public convenience init(eventData: SelfDescribingJson) {
-        self.init(schema: eventData.schema, payload: eventData.data as! [String : NSObject])
+        self.init(schema: eventData.schema, payload: eventData.data)
     }
 
     @objc
-    public init(schema: String, payload: [String : NSObject]) {
+    public init(schema: String, payload: [String : Any]) {
         self._schema = schema
         self._payload = payload
     }
     
-    public init(schema: String, payload: [String : String]) {
-        self._schema = schema
-        self._payload = payload as [String : NSObject]
-    }
-    
     private var _schema: String
     override var schema: String {
         get { return _schema }
         set { _schema = newValue }
     }
     
-    private var _payload: [String: NSObject]
-    override var payload: [String : NSObject] {
+    private var _payload: [String : Any]
+    override var payload: [String : Any] {
         get { return _payload }
         set {
             _payload = newValue
@@ -57,7 +52,7 @@ public class SelfDescribing: SelfDescribingAbstract {
     var eventData: SelfDescribingJson {
         set {
             schema = newValue.schema
-            payload = newValue.data as! [String : NSObject]
+            payload = newValue.data
         }
         get {
             return SelfDescribingJson(schema: schema, andDictionary: payload)
diff --git a/Sources/Snowplow/Events/Structured.swift b/Sources/Snowplow/Events/Structured.swift
index a637e488a..2ada8ff08 100644
--- a/Sources/Snowplow/Events/Structured.swift
+++ b/Sources/Snowplow/Events/Structured.swift
@@ -59,14 +59,14 @@ public class Structured: PrimitiveAbstract {
         return kSPEventStructured
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPStuctCategory] = category as NSObject
-        payload[kSPStuctAction] = action as NSObject
-        if let label = label { payload[kSPStuctLabel] = label as NSObject }
-        if let property = property { payload[kSPStuctProperty] = property as NSObject }
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPStuctCategory] = category
+        payload[kSPStuctAction] = action
+        if let label = label { payload[kSPStuctLabel] = label }
+        if let property = property { payload[kSPStuctProperty] = property }
         if let value = value {
-            payload[kSPStuctValue] = String(format: "%.17g", value.doubleValue) as NSObject
+            payload[kSPStuctValue] = String(format: "%.17g", value.doubleValue)
         }
         return payload
     }
diff --git a/Sources/Snowplow/Events/Timing.swift b/Sources/Snowplow/Events/Timing.swift
index a8f46b58c..7e3fdc033 100644
--- a/Sources/Snowplow/Events/Timing.swift
+++ b/Sources/Snowplow/Events/Timing.swift
@@ -54,12 +54,12 @@ public class Timing: SelfDescribingAbstract {
         return kSPUserTimingsSchema
     }
 
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPUtCategory] = category as NSObject
-        payload[kSPUtVariable] = variable as NSObject
-        payload[kSPUtTiming] = NSNumber(value: timing)
-        if let label = label { payload[kSPUtLabel] = label as NSObject }
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPUtCategory] = category
+        payload[kSPUtVariable] = variable
+        payload[kSPUtTiming] = timing
+        if let label = label { payload[kSPUtLabel] = label }
         return payload
     }
 }
diff --git a/Sources/Snowplow/Events/TrackerError.swift b/Sources/Snowplow/Events/TrackerError.swift
index cbd0504c1..75e3a3b49 100644
--- a/Sources/Snowplow/Events/TrackerError.swift
+++ b/Sources/Snowplow/Events/TrackerError.swift
@@ -60,19 +60,19 @@ public class TrackerError : SelfDescribingAbstract {
         return kSPDiagnosticErrorSchema
     }
     
-    override var payload: [String : NSObject] {
-        var payload: [String : NSObject] = [:]
-        payload[kSPDiagnosticErrorClassName] = source as NSObject
-        payload[kSPDiagnosticErrorMessage] = truncate(message, maxLength: kMaxMessageLength) as NSObject
+    override var payload: [String : Any] {
+        var payload: [String : Any] = [:]
+        payload[kSPDiagnosticErrorClassName] = source
+        payload[kSPDiagnosticErrorMessage] = truncate(message, maxLength: kMaxMessageLength)
         if let error = error {
-            payload[kSPDiagnosticErrorExceptionName] = error as NSObject
+            payload[kSPDiagnosticErrorExceptionName] = error
         }
         if let exception = exception {
-            payload[kSPDiagnosticErrorExceptionName] = truncate(exception.name.rawValue, maxLength: kMaxExceptionNameLength) as NSObject
+            payload[kSPDiagnosticErrorExceptionName] = truncate(exception.name.rawValue, maxLength: kMaxExceptionNameLength)
             let symbols = (exception).callStackSymbols
             if symbols.count != 0 {
                 let stackTrace = "Stacktrace:\n\(symbols)"
-                payload[kSPDiagnosticErrorStack] = truncate(stackTrace, maxLength: kMaxStackLength) as NSObject
+                payload[kSPDiagnosticErrorStack] = truncate(stackTrace, maxLength: kMaxStackLength)
             }
         }
         return payload
diff --git a/Sources/Snowplow/Network/Request.swift b/Sources/Snowplow/Network/Request.swift
index d9d86617f..4c42dbd76 100644
--- a/Sources/Snowplow/Network/Request.swift
+++ b/Sources/Snowplow/Network/Request.swift
@@ -26,7 +26,7 @@ public class Request: NSObject {
     @objc
     public private(set) var payload: Payload?
     @objc
-    public private(set) var emitterEventIds: [NSNumber]?
+    public private(set) var emitterEventIds: [Int64]
     @objc
     public private(set) var oversize = false
     @objc
@@ -37,33 +37,30 @@ public class Request: NSObject {
     }
 
     init(payload: Payload, emitterEventId: Int64, oversize: Bool) {
+        emitterEventIds = [emitterEventId]
         super.init()
         self.payload = payload
-        emitterEventIds = [NSNumber(value: emitterEventId)]
         customUserAgent = userAgent(from: payload)
         self.oversize = oversize
     }
 
-    init(payloads: [Payload], emitterEventIds: [NSNumber]) {
+    init(payloads: [Payload], emitterEventIds: [Int64]) {
+        self.emitterEventIds = emitterEventIds
         super.init()
         var tempUserAgent: String? = nil
-        var payloadData: [[String : NSObject]] = []
+        var payloadData: [[String : Any]] = []
         for payload in payloads {
-            if let data = payload.dictionary {
-                payloadData.append(data)
-            }
+            payloadData.append(payload.dictionary)
             tempUserAgent = userAgent(from: payload)
         }
-        let payloadBundle = SelfDescribingJson(schema: kSPPayloadDataSchema, andData: payloadData as NSObject)
-        if let payloadBundleDict = payloadBundle.dictionary {
-            payload = Payload(dictionary: payloadBundleDict)
-        }
-        self.emitterEventIds = emitterEventIds
+        let payloadBundle = SelfDescribingJson.dictionary(schema: kSPPayloadDataSchema,
+                                                          data: payloadData)
+        payload = Payload(dictionary: payloadBundle)
         customUserAgent = tempUserAgent
         oversize = false
     }
 
     func userAgent(from payload: Payload) -> String? {
-        return (payload.dictionary?[kSPUseragent] as? String)
+        return (payload.dictionary[kSPUseragent] as? String)
     }
 }
diff --git a/Sources/Snowplow/Network/RequestResult.swift b/Sources/Snowplow/Network/RequestResult.swift
index ea484915c..e78de9b03 100644
--- a/Sources/Snowplow/Network/RequestResult.swift
+++ b/Sources/Snowplow/Network/RequestResult.swift
@@ -30,7 +30,7 @@ public class RequestResult: NSObject {
     public private(set) var isOversize: Bool
     /// Returns the stored index array, needed to remove the events after sending.
     @objc
-    public private(set) var storeIds: [NSNumber]?
+    public private(set) var storeIds: [Int64]?
 
     @objc
     public convenience override init() {
@@ -42,7 +42,7 @@ public class RequestResult: NSObject {
     ///   - statusCode: HTTP status code from collector response
     ///   - storeIds: the event indexes in the database
     @objc
-    public init(statusCode: NSNumber?, oversize isOversize: Bool, storeIds: [NSNumber]?) {
+    public init(statusCode: NSNumber?, oversize isOversize: Bool, storeIds: [Int64]?) {
         self.statusCode = statusCode?.intValue
         self.isOversize = isOversize
         self.storeIds = storeIds
diff --git a/Sources/Snowplow/Payload/Payload.swift b/Sources/Snowplow/Payload/Payload.swift
index 40cd10db3..3cd21d7e1 100644
--- a/Sources/Snowplow/Payload/Payload.swift
+++ b/Sources/Snowplow/Payload/Payload.swift
@@ -23,14 +23,13 @@ import Foundation
 
 @objc(SPPayload)
 public class Payload: NSObject {
-    private var payload: [String : NSObject] = [:]
+    private var payload: [String : Any] = [:]
     @objc
     public var allowDiagnostic = true
     
     /// Returns the payload of that particular SPPayload object.
     /// - Returns: NSDictionary of data in the object.
-    @objc
-    public var dictionary: [String : NSObject]? {
+    public var dictionary: [String : Any] {
         objc_sync_enter(self)
         defer { objc_sync_exit(self) }
         return payload
@@ -50,7 +49,7 @@ public class Payload: NSObject {
 
     @objc
     override public var description: String {
-        return dictionary?.description ?? ""
+        return dictionary.description
     }
 
     ///  Initializes a newly allocated SPPayload
@@ -64,7 +63,7 @@ public class Payload: NSObject {
     ///  - Parameter dictionary: An object of NSDictionary.
     ///  - Returns: A SnowplowPayload.
     @objc
-    public init(dictionary: [String : NSObject]) {
+    public init(dictionary: [String : Any]) {
         super.init()
         payload = dictionary
     }
@@ -74,48 +73,28 @@ public class Payload: NSObject {
     /// - value: A NSString value
     /// - key: A key of type NSString
     @objc
-    public func addValueToPayload(_ value: String?, forKey key: String) {
+    public func addValueToPayload(_ value: Any?, forKey key: String) {
         objc_sync_enter(self)
-        if value == nil || value?.count == 0 {
+        if value == nil {
             if payload[key] != nil {
                 payload.removeValue(forKey: key)
             }
         } else {
-            payload[key] = value as NSObject?
-        }
-        objc_sync_exit(self)
-    }
-
-    /// Adds a simple name-value pair into the SPPayload intance.
-    /// - Parameters:
-    /// - value: A NSNumber value
-    /// - key: A key of type NSString
-    @objc
-    public func addNumericValueToPayload(_ value: NSNumber?, forKey key: String) {
-        objc_sync_enter(self)
-        if let value = value {
             payload[key] = value
-        } else if payload[key] != nil {
-            payload.removeValue(forKey: key)
         }
         objc_sync_exit(self)
     }
 
     ///  Adds a dictionary of attributes to be appended into the SPPayload instance. It does NOT overwrite the existing data in the object.
-    ///  All attribute values must be NSString types to be added; all others are discarded.
+    ///  All attribute values must be String types to be added; all others are discarded.
     ///  - Parameter dictionary: An object of NSDictionary.
     @objc
-    public func addDictionaryToPayload(_ dictionary: [String : NSObject]?) {
-        if dictionary == nil {
-            return
-        }
-        (dictionary as NSDictionary?)?.enumerateKeysAndObjects({ [self] key, value, stop in
-            if value is NSString {
-                if let key = key as? String {
-                    addValueToPayload(value as? String, forKey: key)
-                }
+    public func addDictionaryToPayload(_ dictionary: [String : Any]) {
+        for (key, value) in dictionary {
+            if value is String {
+                addValueToPayload(value, forKey: key)
             }
-        })
+        }
     }
 
     ///  Adds a dictionary of attributes to be appended into the SPPayload instance. Gives you the option to Base64 encode the data before adding it into the object.
@@ -131,7 +110,7 @@ public class Payload: NSObject {
         typeWhenEncoded typeEncoded: String?,
         typeWhenNotEncoded typeNotEncoded: String?
     ) {
-        guard let _ = try? JSONSerialization.jsonObject(with: json) as? [String : NSObject] else { return }
+        guard let _ = try? JSONSerialization.jsonObject(with: json) as? [String : Any] else { return }
         if encode {
             guard let typeEncoded = typeEncoded else { return }
             var encodedString = json.base64EncodedString(options: [])
@@ -185,7 +164,7 @@ public class Payload: NSObject {
     ///  - typeNotEncoded: If the data is NOT going to be encoded, the result will be a value of the key in typeWhenNotEncoded.
     @objc
     public func addDictionaryToPayload(
-        _ dictionary: [String : NSObject],
+        _ dictionary: [String : Any],
         base64Encoded encode: Bool,
         typeWhenEncoded typeEncoded: String?,
         typeWhenNotEncoded typeNotEncoded: String?
@@ -198,4 +177,16 @@ public class Payload: NSObject {
             typeWhenEncoded: typeEncoded,
             typeWhenNotEncoded: typeNotEncoded)
     }
+    
+    /// Shorthand to set and get payload values for keys.
+    /// Setting a null value will remove the key from payload.
+    @objc
+    public subscript(key: String) -> Any? {
+        get {
+            dictionary[key]
+        }
+        set {
+            addValueToPayload(newValue, forKey: key)
+        }
+    }
 }
diff --git a/Sources/Snowplow/Payload/SelfDescribingJson.swift b/Sources/Snowplow/Payload/SelfDescribingJson.swift
index b8140d100..e5929a399 100644
--- a/Sources/Snowplow/Payload/SelfDescribingJson.swift
+++ b/Sources/Snowplow/Payload/SelfDescribingJson.swift
@@ -33,26 +33,20 @@ public class SelfDescribingJson: NSObject {
     
     /// Data of the self-describing JSON.
     @objc
-    public var data: NSObject?
+    public var data: [String : Any]
 
     /// Returns the internal NSDictionary of the self-describing JSON.
     /// - Returns: The self-describing JSON as an NSDictionary.
     @objc
-    public var dictionary: [String : NSObject]? {
-        if let data = data {
-            return [
-                kSPSchema: schema as NSObject,
-                kSPData: data
-            ]
-        }
-        return nil
+    public var dictionary: [String : Any] {
+        return SelfDescribingJson.dictionary(schema: schema, data: data)
     }
 
     /// Returns a string description of the internal dictionary.
     /// - Returns: The description of the dictionary.
     @objc
     override public var description: String {
-        return dictionary?.description ?? ""
+        return dictionary.description
     }
 
     /// Initializes a newly allocated SPSelfDescribingJson.
@@ -61,10 +55,10 @@ public class SelfDescribingJson: NSObject {
     ///   - data: Data to set for data field of the self-describing JSON, should be an NSDictionary.
     /// - Returns: An SPSelfDescribingJson.
     @objc
-    public init(schema: String, andData data: NSObject?) {
+    public init(schema: String, andData data: [String : Any]) {
         self.schema = schema
+        self.data = data
         super.init()
-        setData(withObject: data)
     }
 
     /// Initializes a newly allocated SPSelfDescribingJson.
@@ -73,17 +67,8 @@ public class SelfDescribingJson: NSObject {
     ///   - data: Dictionary to set for data field of the self-describing JSON.
     /// - Returns: An SPSelfDescribingJson.
     @objc
-    public convenience init(schema: String, andDictionary data: [String : NSObject]) {
-        self.init(schema: schema, andData: data as NSObject)
-    }
-
-    /// Initializes a newly allocated SPSelfDescribingJson.
-    /// - Parameters:
-    ///   - schema: A valid schema string.
-    ///   - data: Dictionary to set for data field of the self-describing JSON.
-    /// - Returns: An SPSelfDescribingJson.
-    public convenience init(schema: String, andDictionary data: [String : String]) {
-        self.init(schema: schema, andDictionary: data as [String : NSObject])
+    public convenience init(schema: String, andDictionary data: [String : Any]) {
+        self.init(schema: schema, andData: data)
     }
 
     /// Initializes a newly allocated SPSelfDescribingJson.
@@ -92,8 +77,8 @@ public class SelfDescribingJson: NSObject {
     ///   - data: Payload to set for data field of the self-describing JSON.
     /// - Returns: An SPSelfDescribingJson.
     @objc
-    public convenience init(schema: String, andPayload data: Payload) {
-        self.init(schema: schema, andData: data.dictionary as NSObject?)
+    public convenience init(schema: String, andPayload payload: Payload) {
+        self.init(schema: schema, andData: payload.dictionary)
     }
 
     /// Initializes a newly allocated SPSelfDescribingJson.
@@ -103,27 +88,27 @@ public class SelfDescribingJson: NSObject {
     /// - Returns: An SPSelfDescribingJson.
     @objc
     public convenience init(schema: String, andSelfDescribingJson data: SelfDescribingJson) {
-        self.init(schema: schema, andData: data.dictionary as NSObject?)
-    }
-
-    /// Sets the data field of the self-describing JSON.
-    /// - Parameter data: An NSObject to be nested into the data.
-    @objc
-    public func setData(withObject data: NSObject?) {
-        self.data = data
+        self.init(schema: schema, andData: data.dictionary)
     }
 
     /// Sets the data field of the self-describing JSON.
     /// - Parameter data: An SPPayload to be nested into the data.
     @objc
     public func setData(withPayload data: Payload) {
-        return setData(withObject: data.dictionary as NSObject?)
+        self.data = data.dictionary
     }
 
     /// Sets the data field of the self-describing JSON.
     /// - Parameter data: A self-describing JSON to be nested into the data.
     @objc
     public func setData(withSelfDescribingJson data: SelfDescribingJson) {
-        return setData(withObject: data.dictionary as NSObject?)
+        self.data = data.dictionary
+    }
+    
+    class func dictionary(schema: String, data: Any) -> [String: Any] {
+        return [
+            kSPSchema: schema,
+            kSPData: data
+        ]
     }
 }
diff --git a/Sources/Snowplow/Snowplow.swift b/Sources/Snowplow/Snowplow.swift
index 1ba48a459..6796b33ad 100644
--- a/Sources/Snowplow/Snowplow.swift
+++ b/Sources/Snowplow/Snowplow.swift
@@ -196,7 +196,7 @@ public class Snowplow: NSObject {
     @objc
     public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [Configuration]) -> TrackerController? {
         if let serviceProvider = serviceProviderInstances[namespace] {
-            serviceProvider.reset(withConfigurations: configurations + [networkConfiguration])
+            serviceProvider.reset(configurations: configurations + [networkConfiguration])
             return serviceProvider.trackerController
         } else {
             let serviceProvider = ServiceProvider(namespace: namespace, network: networkConfiguration, configurations: configurations)
diff --git a/Sources/Snowplow/Tracker/InspectableEvent.swift b/Sources/Snowplow/Tracker/InspectableEvent.swift
index 6c64586e9..d61e7e5a9 100644
--- a/Sources/Snowplow/Tracker/InspectableEvent.swift
+++ b/Sources/Snowplow/Tracker/InspectableEvent.swift
@@ -32,7 +32,7 @@ public protocol InspectableEvent {
     var eventName: String? { get }
     /// The payload of the event
     @objc
-    var payload: [String : NSObject] { get }
+    var payload: [String : Any] { get }
     /// The tracker state at the time the event was sent.
     @objc
     var state: TrackerStateSnapshot { get }
@@ -40,5 +40,5 @@ public protocol InspectableEvent {
     /// - Parameter payload: Map of values to add to the event payload.
     /// - Returns: Whether or not the values have been successfully added to the event payload.
     @objc
-    func addPayloadValues(_ payload: [String : NSObject]) -> Bool
+    func addPayloadValues(_ payload: [String : Any]) -> Bool
 }
diff --git a/Sources/Snowplow/Tracker/SessionState.swift b/Sources/Snowplow/Tracker/SessionState.swift
index e1e462b55..9581bf7e1 100644
--- a/Sources/Snowplow/Tracker/SessionState.swift
+++ b/Sources/Snowplow/Tracker/SessionState.swift
@@ -38,20 +38,20 @@ public class SessionState: NSObject, State {
     @objc
     public private(set) var userId: String
 
-    var sessionContext: [String : NSObject] {
+    var sessionContext: [String : Any] {
         return sessionDictionary
     }
-    private var sessionDictionary: [String : NSObject] = [:]
+    private var sessionDictionary: [String : Any] = [:]
 
-    class func buildSessionDictionary(withFirstEventId firstEventId: String?, firstEventTimestamp: String?, currentSessionId: String, previousSessionId: String?, sessionIndex: Int, userId: String, storage: String) -> [String : NSObject] {
-        var dictionary: [String : NSObject] = [:]
-        dictionary[kSPSessionPreviousId] = previousSessionId as NSObject? ?? NSNull()
-        dictionary[kSPSessionId] = currentSessionId as NSObject
-        dictionary[kSPSessionFirstEventId] = firstEventId as NSObject?
-        dictionary[kSPSessionFirstEventTimestamp] = firstEventTimestamp as NSObject?
-        dictionary[kSPSessionIndex] = NSNumber(value: sessionIndex)
-        dictionary[kSPSessionStorage] = storage as NSObject
-        dictionary[kSPSessionUserId] = userId as NSObject
+    class func buildSessionDictionary(withFirstEventId firstEventId: String?, firstEventTimestamp: String?, currentSessionId: String, previousSessionId: String?, sessionIndex: Int, userId: String, storage: String) -> [String : Any] {
+        var dictionary: [String : Any] = [:]
+        dictionary[kSPSessionPreviousId] = previousSessionId ?? NSNull()
+        dictionary[kSPSessionId] = currentSessionId
+        dictionary[kSPSessionFirstEventId] = firstEventId
+        dictionary[kSPSessionFirstEventTimestamp] = firstEventTimestamp
+        dictionary[kSPSessionIndex] = sessionIndex
+        dictionary[kSPSessionStorage] = storage
+        dictionary[kSPSessionUserId] = userId
         return dictionary
     }
 
@@ -74,7 +74,7 @@ public class SessionState: NSObject, State {
             storage: storage)
     }
 
-    init?(storedState: [String : NSObject]) {
+    init?(storedState: [String : Any]) {
         guard let sessionId = storedState[kSPSessionId] as? String,
               let sessionIndex = storedState[kSPSessionIndex] as? Int,
               let userId = storedState[kSPSessionUserId] as? String else {
diff --git a/Sources/Snowplow/Tracker/StateMachineProtocol.swift b/Sources/Snowplow/Tracker/StateMachineProtocol.swift
index 75f854594..cc1858735 100644
--- a/Sources/Snowplow/Tracker/StateMachineProtocol.swift
+++ b/Sources/Snowplow/Tracker/StateMachineProtocol.swift
@@ -36,5 +36,5 @@ public protocol StateMachineProtocol {
     @objc
     func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]?
     @objc
-    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]?
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]?
 }
diff --git a/Tests/Configurations/TestRemoteConfiguration.swift b/Tests/Configurations/TestRemoteConfiguration.swift
index a223de00c..e3140f7ed 100644
--- a/Tests/Configurations/TestRemoteConfiguration.swift
+++ b/Tests/Configurations/TestRemoteConfiguration.swift
@@ -44,7 +44,7 @@ class TestRemoteConfiguration: XCTestCase {
         guard let jsonData = config.data(using: .utf8) else {
             return XCTFail()
         }
-        guard let dictionary = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String : NSObject] else {
+        guard let dictionary = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String : Any] else {
             return XCTFail()
         }
         guard let fetchedConfigurationBundle = FetchedConfigurationBundle(dictionary: dictionary) else {
diff --git a/Tests/Configurations/TestTrackerConfiguration.swift b/Tests/Configurations/TestTrackerConfiguration.swift
index b75068813..a3c13d5e6 100644
--- a/Tests/Configurations/TestTrackerConfiguration.swift
+++ b/Tests/Configurations/TestTrackerConfiguration.swift
@@ -225,7 +225,7 @@ class TestTrackerConfiguration: XCTestCase {
         let payload = events.first?.payload
 
         // Check v_tracker field
-        let versionTracker = payload?.dictionary?["tv"] as? String
+        let versionTracker = payload?["tv"] as? String
         let expected = "\(kSPVersion) testWithSpace1-2-3"
         XCTAssertEqual(expected, versionTracker)
     }
@@ -262,7 +262,7 @@ class TestTrackerConfiguration: XCTestCase {
         _ = eventStore.removeAllEvents()
         XCTAssertEqual(1, events.count)
         var payload = events.first?.payload
-        var contexts = payload?.dictionary?["co"] as? String
+        var contexts = payload?["co"] as? String
         XCTAssertTrue(contexts?.contains("\"basisForProcessing\":\"contract\"") ?? false)
         XCTAssertTrue(contexts?.contains("\"documentId\":\"id1\"") ?? false)
 
@@ -282,7 +282,7 @@ class TestTrackerConfiguration: XCTestCase {
         _ = eventStore.removeAllEvents()
         XCTAssertEqual(1, events.count)
         payload = events.first?.payload
-        contexts = payload?.dictionary?["co"] as? String
+        contexts = payload?["co"] as? String
         XCTAssertFalse(contexts?.contains("\"basisForProcessing\":\"contract\"") ?? false)
         XCTAssertFalse(contexts?.contains("\"documentId\":\"id1\"") ?? false)
 
@@ -315,7 +315,7 @@ class TestTrackerConfiguration: XCTestCase {
         _ = eventStore.removeAllEvents()
         XCTAssertEqual(1, events.count)
         var payload = events.first?.payload
-        var contexts = payload?.dictionary?["co"] as? String
+        var contexts = payload?["co"] as? String
         XCTAssertFalse(contexts?.contains("\"basisForProcessing\"") ?? true)
 
         // Check gdpr can be enabled again
@@ -334,7 +334,7 @@ class TestTrackerConfiguration: XCTestCase {
         _ = eventStore.removeAllEvents()
         XCTAssertEqual(1, events.count)
         payload = events.first?.payload
-        contexts = payload?.dictionary?["co"] as? String
+        contexts = payload?["co"] as? String
         XCTAssertTrue(contexts?.contains("\"basisForProcessing\":\"contract\"") ?? false)
         XCTAssertTrue(contexts?.contains("\"documentId\":\"id1\"") ?? false)
     }
@@ -363,7 +363,7 @@ class TestTrackerConfiguration: XCTestCase {
         _ = eventStore.removeAllEvents()
         XCTAssertEqual(1, events.count)
         guard let payload = events.first?.payload,
-              let contexts = payload.dictionary?["co"] as? String else { return XCTFail() }
+              let contexts = payload["co"] as? String else { return XCTFail() }
 
         // Check empty userId in session context
         XCTAssertTrue(contexts.contains("\"userId\":\"00000000-0000-0000-0000-000000000000\""))
@@ -394,7 +394,7 @@ class TestTrackerConfiguration: XCTestCase {
         let payload = events.first?.payload
 
         // Check eid field
-        let trackedEventId = payload?.dictionary?["eid"] as? String
+        let trackedEventId = payload?["eid"] as? String
         XCTAssertTrue((eventId?.uuidString == trackedEventId))
     }
 }
diff --git a/Tests/Global Contexts/TestGlobalContexts.swift b/Tests/Global Contexts/TestGlobalContexts.swift
index cb46fdfbe..0f32c0a07 100644
--- a/Tests/Global Contexts/TestGlobalContexts.swift	
+++ b/Tests/Global Contexts/TestGlobalContexts.swift	
@@ -33,7 +33,7 @@ class GlobalContextGenerator: NSObject, ContextGenerator {
     func generator(from event: InspectableEvent) -> [SelfDescribingJson]? {
         return [
             SelfDescribingJson(schema: "schema", andDictionary: [
-                "key": "value" as NSObject
+                "key": "value"
             ])
         ]
     }
@@ -45,14 +45,14 @@ class TestGlobalContexts: XCTestCase {
     func testGlobalContexts() {
         let staticGC = GlobalContext(staticContexts: [
             SelfDescribingJson(schema: "schema", andDictionary: [
-                "key": "value" as NSObject
+                "key": "value"
             ])
         ])
         let generatorGC = GlobalContext(contextGenerator: GlobalContextGenerator())
         let blockGC = GlobalContext(generator: { event in
             return [
                 SelfDescribingJson(schema: "schemaBlock", andDictionary: [
-                    "key": "value" as NSObject
+                    "key": "value"
                 ])
             ]
         })
@@ -98,7 +98,7 @@ class TestGlobalContexts: XCTestCase {
     func testAddRemove() {
         let staticGC = GlobalContext(staticContexts: [
             SelfDescribingJson(schema: "schema", andDictionary: [
-                "key": "value" as NSObject
+                "key": "value"
             ])
         ])
         var generators: [String : GlobalContext] = [:]
@@ -129,7 +129,7 @@ class TestGlobalContexts: XCTestCase {
     func testStaticGenerator() {
         let staticGC = GlobalContext(staticContexts: [
             SelfDescribingJson(schema: "schema", andDictionary: [
-                "key": "value" as NSObject
+                "key": "value"
             ])
         ])
         var globalContexts = [
@@ -151,7 +151,7 @@ class TestGlobalContexts: XCTestCase {
         let filterMatchingGC = GlobalContext(
             staticContexts: [
                 SelfDescribingJson(schema: "schema", andDictionary: [
-                    "key": "value" as NSObject
+                    "key": "value"
                 ])
             ],
             filter: { event in
@@ -186,7 +186,7 @@ class TestGlobalContexts: XCTestCase {
 
         let rulesetGC = GlobalContext(staticContexts: [
             SelfDescribingJson(schema: "schema", andDictionary: [
-                "key": "value" as NSObject
+                "key": "value"
             ])
         ], ruleset: ruleset)
         var globalContexts = [
diff --git a/Tests/Legacy Tests/LegacyTestSubject.swift b/Tests/Legacy Tests/LegacyTestSubject.swift
index bf46bc201..3f76603e2 100644
--- a/Tests/Legacy Tests/LegacyTestSubject.swift	
+++ b/Tests/Legacy Tests/LegacyTestSubject.swift	
@@ -36,13 +36,13 @@ class LegacyTestSubject: XCTestCase {
 
     func testSubjectInit() {
         let subject = Subject()
-        XCTAssertNotNil(subject.getStandardDict(userAnonymisation: false))
+        XCTAssertNotNil(subject.standardDict(userAnonymisation: false))
     }
 
     func testSubjectInitWithOptions() {
         let subject = Subject(platformContext: true, andGeoContext: false)
-        XCTAssertNotNil(subject.getPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil))
-        XCTAssertNotNil(subject.getStandardDict(userAnonymisation: false))
+        XCTAssertNotNil(subject.platformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil))
+        XCTAssertNotNil(subject.standardDict(userAnonymisation: false))
     }
 
     func testSubjectSetterFunctions() {
@@ -58,20 +58,18 @@ class LegacyTestSubject: XCTestCase {
         subject.networkUserId = "aNuid"
         subject.domainUserId = "aDuid"
 
-        guard var values = subject.getStandardDict(userAnonymisation: false)?.dictionary else {
-            return XCTFail()
-        }
-
-        XCTAssertEqual(values[kSPUid], "aUserId" as NSObject)
-        XCTAssertTrue((values[kSPResolution] == "1920x1080" as NSObject))
-        XCTAssertTrue((values[kSPViewPort] == "1080x1920" as NSObject))
-        XCTAssertTrue((values[kSPColorDepth] == "20" as NSObject))
-        XCTAssertEqual(values[kSPTimezone], "UTC" as NSObject)
-        XCTAssertEqual(values[kSPLanguage], "EN" as NSObject)
-        XCTAssertEqual(values[kSPIpAddress], "127.0.0.1" as NSObject)
-        XCTAssertEqual(values[kSPUseragent], "aUseragent" as NSObject)
-        XCTAssertEqual(values[kSPNetworkUid], "aNuid" as NSObject)
-        XCTAssertEqual(values[kSPDomainUid], "aDuid" as NSObject)
+        let values = subject.standardDict(userAnonymisation: false)
+
+        XCTAssertEqual(values[kSPUid], "aUserId")
+        XCTAssertTrue((values[kSPResolution] == "1920x1080"))
+        XCTAssertTrue((values[kSPViewPort] == "1080x1920"))
+        XCTAssertTrue((values[kSPColorDepth] == "20"))
+        XCTAssertEqual(values[kSPTimezone], "UTC")
+        XCTAssertEqual(values[kSPLanguage], "EN")
+        XCTAssertEqual(values[kSPIpAddress], "127.0.0.1")
+        XCTAssertEqual(values[kSPUseragent], "aUseragent")
+        XCTAssertEqual(values[kSPNetworkUid], "aNuid")
+        XCTAssertEqual(values[kSPDomainUid], "aDuid")
 
         // Setup GeoLocation
         subject.geoLongitude = NSNumber(value: 5)
@@ -83,26 +81,26 @@ class LegacyTestSubject: XCTestCase {
         subject.geoAltitude = NSNumber(value: 62.3)
         subject.geoAltitudeAccuracy = NSNumber(value: 16.3)
 
-        values = subject.getGeoLocationDict()!
+        let geoValues = subject.geoLocationDict!
 
-        XCTAssertTrue((NSNumber(value: 5) == values[kSPGeoLongitude]))
-        XCTAssertTrue((NSNumber(value: 89.2) == values[kSPGeoLatitude]))
-        XCTAssertTrue((NSNumber(value: 5.5) == values[kSPGeoLatLongAccuracy]))
-        XCTAssertTrue((NSNumber(value: 6.2) == values[kSPGeoSpeed]))
-        XCTAssertTrue((NSNumber(value: 82.3) == values[kSPGeoBearing]))
-        XCTAssertTrue((NSNumber(value: 62.3) == values[kSPGeoAltitude]))
-        XCTAssertTrue((NSNumber(value: 16.3) == values[kSPGeoAltitudeAccuracy]))
-        XCTAssertTrue((NSNumber(value: 5) == values[kSPGeoTimestamp]))
+        XCTAssertTrue((NSNumber(value: 5) == geoValues[kSPGeoLongitude]))
+        XCTAssertTrue((NSNumber(value: 89.2) == geoValues[kSPGeoLatitude]))
+        XCTAssertTrue((NSNumber(value: 5.5) == geoValues[kSPGeoLatLongAccuracy]))
+        XCTAssertTrue((NSNumber(value: 6.2) == geoValues[kSPGeoSpeed]))
+        XCTAssertTrue((NSNumber(value: 82.3) == geoValues[kSPGeoBearing]))
+        XCTAssertTrue((NSNumber(value: 62.3) == geoValues[kSPGeoAltitude]))
+        XCTAssertTrue((NSNumber(value: 16.3) == geoValues[kSPGeoAltitudeAccuracy]))
+        XCTAssertTrue((NSNumber(value: 5) == geoValues[kSPGeoTimestamp]))
     }
 
     func testGeoLocationGetWithoutNeededKeys() {
         let subject = Subject(platformContext: false, andGeoContext: true)
-        XCTAssertNil(subject.getGeoLocationDict())
+        XCTAssertNil(subject.geoLocationDict)
 
         subject.geoLongitude = NSNumber(value: 5)
         subject.geoLatitude = NSNumber(value: 89.2)
 
-        XCTAssertNotNil(subject.getGeoLocationDict())
+        XCTAssertNotNil(subject.geoLocationDict)
     }
 
     func testGeoLocationWithSubjectConfiguration() {
@@ -111,7 +109,7 @@ class LegacyTestSubject: XCTestCase {
         config.geoLongitude = NSNumber(value: 24.24)
         let subject = Subject(platformContext: false, geoLocationContext: true, subjectConfiguration: config)
 
-        let values = subject.getGeoLocationDict()
+        let values = subject.geoLocationDict
 
         XCTAssertEqual(NSNumber(value: 12.12), values?[kSPGeoLatitude])
         XCTAssertEqual(NSNumber(value: 24.24), values?[kSPGeoLongitude])
diff --git a/Tests/Legacy Tests/LegacyTestTracker.swift b/Tests/Legacy Tests/LegacyTestTracker.swift
index 949963dce..776aea868 100644
--- a/Tests/Legacy Tests/LegacyTestTracker.swift	
+++ b/Tests/Legacy Tests/LegacyTestTracker.swift	
@@ -126,9 +126,9 @@ class LegacyTestTracker: XCTestCase {
         var payload = tracker.payload(with: trackerEvent)
         var payloadDict = payload.dictionary
 
-        XCTAssertEqual(payloadDict?[kSPPlatform] as? String, devicePlatformToString(.general))
-        XCTAssertEqual(payloadDict?[kSPAppId] as? String, "anAppId")
-        XCTAssertEqual(payloadDict?[kSPNamespace] as? String, "aNamespace")
+        XCTAssertEqual(payloadDict[kSPPlatform] as? String, devicePlatformToString(.general))
+        XCTAssertEqual(payloadDict[kSPAppId] as? String, "anAppId")
+        XCTAssertEqual(payloadDict[kSPNamespace] as? String, "aNamespace")
 
         // Test setting variables to new values
 
@@ -139,9 +139,9 @@ class LegacyTestTracker: XCTestCase {
         payload = tracker.payload(with: trackerEvent)
         payloadDict = payload.dictionary
 
-        XCTAssertEqual(payloadDict?[kSPPlatform] as? String, "pc")
-        XCTAssertEqual(payloadDict?[kSPAppId] as? String, "newAppId")
-        XCTAssertEqual(payloadDict?[kSPNamespace] as? String, "newNamespace")
+        XCTAssertEqual(payloadDict[kSPPlatform] as? String, "pc")
+        XCTAssertEqual(payloadDict[kSPAppId] as? String, "newAppId")
+        XCTAssertEqual(payloadDict[kSPNamespace] as? String, "newNamespace")
     }
 
     func testEventIdNotDuplicated() {
diff --git a/Tests/TestDataPersistence.swift b/Tests/TestDataPersistence.swift
index 1344cc770..55d4ada53 100644
--- a/Tests/TestDataPersistence.swift
+++ b/Tests/TestDataPersistence.swift
@@ -36,20 +36,20 @@ class TestDataPersistence: XCTestCase {
     func testDataPersistenceForNamespaceWithDifferentNamespaces() {
         let dp1 = DataPersistence.getFor(namespace: "namespace1")
         let dp2 = DataPersistence.getFor(namespace: "namespace2")
-        XCTAssertNotEqual(dp1, dp2)
+        XCTAssertFalse(dp1 === dp2)
     }
 
     func testDataPersistenceForNamespaceWithSameNamespaces() {
         let dp1 = DataPersistence.getFor(namespace: "namespace")
         let dp2 = DataPersistence.getFor(namespace: "namespace")
-        XCTAssertEqual(dp1, dp2)
+        XCTAssertTrue(dp1 === dp2)
     }
 
     func testRemoveForNamespace() {
         let dp1 = DataPersistence.getFor(namespace: "namespace")
         _ = DataPersistence.remove(withNamespace: "namespace")
         let dp2 = DataPersistence.getFor(namespace: "namespace")
-        XCTAssertNotEqual(dp1, dp2)
+        XCTAssertFalse(dp1 === dp2)
     }
 
     func testDataIsCorrectlyStored() {
@@ -63,18 +63,18 @@ class TestDataPersistence: XCTestCase {
     func commonTestDataIsCorrectlyStored(onFile isStoredOnFile: Bool) {
         let dp = DataPersistence.getFor(namespace: "namespace", storedOnFile: isStoredOnFile)
         var session = [
-            "key": "value" as NSObject
+            "key": "value"
         ]
         dp?.session = session
-        XCTAssertEqual(session, dp?.session)
-        XCTAssertEqual(session, dp?.data["session"])
+        XCTAssertEqual(session, dp?.session as! [String : String])
+        XCTAssertEqual(session, dp?.data["session"] as! [String : String])
         // Override session
         session = [
-            "key2": "value2" as NSObject
+            "key2": "value2"
         ]
         dp?.session = session
-        XCTAssertEqual(session, dp?.session)
-        XCTAssertEqual(session, dp?.data["session"])
+        XCTAssertEqual(session, dp?.session as! [String : String])
+        XCTAssertEqual(session, dp?.data["session"] as! [String : String])
     }
 
     func testDataIsStoredWithoutInterference() {
@@ -89,14 +89,14 @@ class TestDataPersistence: XCTestCase {
         let dp1 = DataPersistence.getFor(namespace: "namespace1", storedOnFile: isStoredOnFile)
         let dp2 = DataPersistence.getFor(namespace: "namespace2", storedOnFile: isStoredOnFile)
         let session = [
-            "key": "value" as NSObject
+            "key": "value"
         ]
         dp1?.session = session
         // Check dp1
-        XCTAssertEqual(session, dp1?.session)
-        XCTAssertEqual(session, dp1?.data["session"])
+        XCTAssertEqual(session, dp1?.session as? [String : String])
+        XCTAssertEqual(session, dp1?.data["session"] as? [String : String])
         // Check dp2
-        XCTAssertNotEqual(session, dp2?.session)
-        XCTAssertNotEqual(session, dp2?.data["session"])
+        XCTAssertNotEqual(session, dp2?.session as? [String : String])
+        XCTAssertNotEqual(session, dp2?.data["session"] as? [String : String])
     }
 }
diff --git a/Tests/TestEvents.swift b/Tests/TestEvents.swift
index e32839823..75d9e2f4e 100644
--- a/Tests/TestEvents.swift
+++ b/Tests/TestEvents.swift
@@ -63,7 +63,7 @@ class TestEvents: XCTestCase {
         let payload = events.first?.payload
 
         // Check v_tracker field
-        let deviceTimestamp = payload?.dictionary?["dtm"] as? String
+        let deviceTimestamp = payload?["dtm"] as? String
         let expected = String(format: "%lld", Int64(currentTimestamp.timeIntervalSince1970 * 1000))
         XCTAssertEqual(expected, deviceTimestamp)
     }
@@ -95,8 +95,8 @@ class TestEvents: XCTestCase {
         let payload = events.first?.payload
 
         // Check url and referrer fields
-        let url = payload?.dictionary?[kSPPageUrl] as? String
-        let referrer = payload?.dictionary?[kSPPageRefr] as? String
+        let url = payload?[kSPPageUrl] as? String
+        let referrer = payload?[kSPPageRefr] as? String
         XCTAssertEqual(url, "url")
         XCTAssertEqual(referrer, "referrer")
     }
@@ -132,20 +132,20 @@ class TestEvents: XCTestCase {
 
         var screenViewPayload: Payload? = nil
         for event in events {
-            if (event.payload.dictionary?["eid"] as? String) == screenViewId?.uuidString {
+            if (event.payload.dictionary["eid"] as? String) == screenViewId?.uuidString {
                 screenViewPayload = event.payload
             }
         }
         XCTAssertNotNil(screenViewPayload)
 
         // Check the DeepLink context entity properties
-        let screenViewContext = screenViewPayload?.dictionary?["co"] as? String
+        let screenViewContext = screenViewPayload?["co"] as? String
         XCTAssertTrue(screenViewContext?.contains("\"referrer\":\"the_referrer\"") ?? false)
         XCTAssertTrue(screenViewContext?.contains("\"url\":\"the_url\"") ?? false)
 
         // Check url and referrer fields for atomic table
-        let url = screenViewPayload?.dictionary?[kSPPageUrl] as? String
-        let referrer = screenViewPayload?.dictionary?[kSPPageRefr] as? String
+        let url = screenViewPayload?[kSPPageUrl] as? String
+        let referrer = screenViewPayload?[kSPPageRefr] as? String
         XCTAssertEqual(url, "the_url")
         XCTAssertEqual(referrer, "the_referrer")
     }
@@ -162,15 +162,15 @@ class TestEvents: XCTestCase {
     }
 
     func testUnstructured() {
-        var data: [String : NSObject] = [:]
-        data["level"] = NSNumber(value: 23)
-        data["score"] = NSNumber(value: 56473)
+        var data: [String : Any] = [:]
+        data["level"] = 23
+        data["score"] = 56473
         let sdj = SelfDescribingJson(
             schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
             andDictionary: data)
         let event = SelfDescribing(eventData: sdj)
         XCTAssertEqual("iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0", event.schema)
-        XCTAssertEqual(NSNumber(value: 23), event.payload["level"])
+        XCTAssertEqual(23, event.payload["level"] as? Int)
     }
 
     func testConsentWithdrawn() {
@@ -180,7 +180,7 @@ class TestEvents: XCTestCase {
         event.version = "3"
         event.documentId = "1000"
         event.documentDescription = "description"
-        XCTAssertEqual(NSNumber(value: false), event.payload["all"])
+        XCTAssertEqual(false, event.payload["all"] as? Bool)
         XCTAssertEqual(1, event.allDocuments.count)
     }
 
@@ -239,7 +239,7 @@ class TestEvents: XCTestCase {
         event.subtitle = "subtitle"
         event.sound = "sound"
         event.launchImageName = "image"
-        event.userInfo = userInfo as [String : NSObject]
+        event.userInfo = userInfo
         event.attachments = attachments as [NSObject]
         XCTAssertEqual("sound", event.payload["sound"] as? String)
     }
@@ -271,7 +271,7 @@ class TestEvents: XCTestCase {
         content.subtitle = "subtitle"
         content.sound = "sound"
         content.launchImageName = "image"
-        content.userInfo = userInfo as [String : NSObject]
+        content.userInfo = userInfo
         content.attachments = attachments as [NSObject]
 
         let event = PushNotification(
@@ -313,10 +313,10 @@ class TestEvents: XCTestCase {
         XCTAssertEqual("chime.mp3", payload["sound"] as? String)
         //    XCTAssertEqualObjects(@9, payload["notificationCount"]);
         XCTAssertEqual("category1", payload["category"] as? String)
-        let attachments = (payload["attachments"]) as? [[String : NSObject]]
+        let attachments = (payload["attachments"]) as? [[String : Any]]
         XCTAssertNotNil(attachments)
         XCTAssertEqual(1, (attachments?.count ?? 0))
-        let attachment = attachments?[0] as? [String : NSObject]
+        let attachment = attachments?[0] as? [String : Any]
         XCTAssertEqual("id", attachment?["identifier"] as? String)
         XCTAssertEqual("type", attachment?["type"] as? String)
         XCTAssertEqual("url", attachment?["url"] as? String)
@@ -332,13 +332,13 @@ class TestEvents: XCTestCase {
                     "loc-args": ["loc arg1", "loc arg2"]
                 ],
                 "sound": "chime.aiff",
-                "badge": NSNumber(value: 9),
+                "badge": 9,
                 "category": "category1",
-                "content-available": NSNumber(value: 1)
+                "content-available": 1
             ],
-            "custom-element": NSNumber(value: 1)
+            "custom-element": 1
         ]
-        let event = MessageNotification.messageNotification(userInfo: userInfo as! [String : NSObject], defaultTitle: nil, defaultBody: nil)!
+        let event = MessageNotification.messageNotification(userInfo: userInfo, defaultTitle: nil, defaultBody: nil)!
         let payload = event.payload
         XCTAssertEqual("test-title", payload["title"] as? String)
         XCTAssertEqual("test-body", payload["body"] as? String)
@@ -347,10 +347,10 @@ class TestEvents: XCTestCase {
         XCTAssertEqual(2, (locArgs?.count ?? 0))
         XCTAssertEqual("loc arg1", locArgs?[0] as? String)
         XCTAssertEqual("loc arg2", locArgs?[1] as? String)
-        XCTAssertEqual(NSNumber(value: 9), payload["notificationCount"])
+        XCTAssertEqual(9, payload["notificationCount"] as? Int)
         XCTAssertEqual("chime.aiff", payload["sound"] as? String)
         XCTAssertEqual("category1", payload["category"] as? String)
-        XCTAssertEqual(NSNumber(value: true), payload["contentAvailable"])
+        XCTAssertEqual(true, payload["contentAvailable"] as? Bool)
     }
 
     func testError() {
diff --git a/Tests/TestLifecycleState.swift b/Tests/TestLifecycleState.swift
index eeee4db40..9d36a9ac7 100644
--- a/Tests/TestLifecycleState.swift
+++ b/Tests/TestLifecycleState.swift
@@ -49,7 +49,7 @@ class TestLifecycleState: XCTestCase {
         }
         var payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        var entities = (payload?.dictionary?["co"]) as? String
+        var entities = (payload?["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains("\"isVisible\":true"))
 
@@ -60,7 +60,7 @@ class TestLifecycleState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains("\"isVisible\":false"))
 
@@ -71,7 +71,7 @@ class TestLifecycleState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?["co"]) as? String
         XCTAssertTrue(entities!.contains("\"isVisible\":false"))
 
         _ = tracker.track(Foreground(index: 1))
@@ -81,7 +81,7 @@ class TestLifecycleState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains("\"isVisible\":true"))
 
@@ -93,7 +93,7 @@ class TestLifecycleState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains("\"isVisible\":true"))
     }
diff --git a/Tests/TestMemoryEventStore.swift b/Tests/TestMemoryEventStore.swift
index 95613fec2..9007627b4 100644
--- a/Tests/TestMemoryEventStore.swift
+++ b/Tests/TestMemoryEventStore.swift
@@ -44,7 +44,8 @@ class TestMemoryEventStore: XCTestCase {
 
         XCTAssertEqual(eventStore.count(), 1)
         let events = eventStore.emittableEvents(withQueryLimit: 1)
-        XCTAssertEqual(events[0].payload.dictionary, payload.dictionary)
+        XCTAssertEqual(events[0].payload.dictionary as! [String : String],
+                       payload.dictionary as! [String : String])
         _ = eventStore.removeEvent(withId: 0)
 
         XCTAssertEqual(eventStore.count(), 0)
diff --git a/Tests/TestNetworkConnection.swift b/Tests/TestNetworkConnection.swift
index bb2f563c3..78f037a67 100644
--- a/Tests/TestNetworkConnection.swift
+++ b/Tests/TestNetworkConnection.swift
@@ -46,7 +46,7 @@ class TestNetworkConnection: XCTestCase {
         // Check successful result
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
-        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+        XCTAssertEqual(1, result.storeIds?[0])
     }
     
     func testGetRequestWithNoSuccess() {
@@ -63,7 +63,7 @@ class TestNetworkConnection: XCTestCase {
         // Check unsuccessful result
         let result = results[0]
         XCTAssertFalse(result.isSuccessful)
-        XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
+        XCTAssertEqual(1, (result.storeIds)?[0])
     }
     
     func testPostRequestWithSuccess() {
@@ -80,7 +80,7 @@ class TestNetworkConnection: XCTestCase {
         // Check successful result
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
-        XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
+        XCTAssertEqual(1, (result.storeIds)?[0])
     }
     
     func testPostRequestWithNoSuccess() {
@@ -97,7 +97,7 @@ class TestNetworkConnection: XCTestCase {
         // Check unsuccessful result
         let result = results[0]
         XCTAssertFalse(result.isSuccessful)
-        XCTAssertEqual(NSNumber(value: 1), (result.storeIds)?[0])
+        XCTAssertEqual(1, (result.storeIds)?[0])
     }
 #endif
     
@@ -143,7 +143,7 @@ class TestNetworkConnection: XCTestCase {
         // Check successful result
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
-        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+        XCTAssertEqual(1, result.storeIds?[0])
         wait(for: [requestExpectation], timeout: 2.0)
     }
 
@@ -168,7 +168,7 @@ class TestNetworkConnection: XCTestCase {
         // Check successful result
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
-        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+        XCTAssertEqual(1, result.storeIds?[0])
         wait(for: [requestExpectation], timeout: 2.0)
     }
 
@@ -193,7 +193,7 @@ class TestNetworkConnection: XCTestCase {
         // Check successful result
         let result = results[0]
         XCTAssertTrue(result.isSuccessful)
-        XCTAssertEqual(NSNumber(value: 1), result.storeIds?[0])
+        XCTAssertEqual(1, result.storeIds?[0])
         wait(for: [requestExpectation], timeout: 2.0)
     }
 #endif
diff --git a/Tests/TestPayload.swift b/Tests/TestPayload.swift
index f868a3a37..5d386e35e 100644
--- a/Tests/TestPayload.swift
+++ b/Tests/TestPayload.swift
@@ -34,182 +34,158 @@ class TestPayload: XCTestCase {
     func testInit() {
         let sample_payload = Payload()
 
-        XCTAssertEqual(
-            sample_payload.dictionary,
-            [String : NSObject]())
+        XCTAssertTrue(sample_payload.dictionary.isEmpty)
 
     }
 
     func testInitWithNSDictionary() {
-        let sample_dict: [String : NSObject] = [
-            "Key1": "Value1" as NSObject,
-            "Key2": "Value2" as NSObject
+        let sample_dict = [
+            "Key1": "Value1",
+            "Key2": "Value2"
         ]
         let sample_payload = Payload(dictionary: sample_dict)
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dict)
-        XCTAssertTrue(sample_payload.description.contains("\"Key1\": Value1"))
-        XCTAssertTrue(sample_payload.description.contains("\"Key2\": Value2"))
+        XCTAssertTrue(sample_payload.description.contains("\"Key1\": \"Value1\""))
+        XCTAssertTrue(sample_payload.description.contains("\"Key2\": \"Value2\""))
     }
 
     func testInitWithWrongDictionary() {
-        let sample_dict: [String : NSObject] = [
-            "Key1": "Value1" as NSObject,
-            "Key2": "Value2" as NSObject
+        let sample_dict = [
+            "Key1": "Value1",
+            "Key2": "Value2"
         ]
-        let sample_dict2: [String : NSObject] = [
-            "Key2": "Value1" as NSObject,
-            "Key1": "Value2" as NSObject
+        let sample_dict2 = [
+            "Key2": "Value1",
+            "Key1": "Value2"
         ]
         let sample_payload = Payload(dictionary: sample_dict)
 
         XCTAssertNotEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dict2,
             "Payload is not initialized with the correct JSON or NSDictionary")
     }
 
     func testAddValueToPayload() {
-        let sample_dict: [String : NSObject] = [
-            "Key1": "Value1" as NSObject
+        let sample_dict = [
+            "Key1": "Value1"
         ]
         let sample_payload = Payload()
         sample_payload.addValueToPayload("Value1", forKey: "Key1")
 
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dict)
     }
 
     func testAddValueToPayload2() {
-        let sample_dict: [String : NSObject] = [
-            "Key2": "Value2" as NSObject
+        let sample_dict = [
+            "Key2": "Value2"
         ]
         let sample_payload = Payload()
         sample_payload.addValueToPayload("Value1", forKey: "Key1")
 
 
         XCTAssertNotEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dict,
             "Payload should not be the same as sample_dict")
     }
 
     func testAddValueToPayload3() {
-        let sample_dict_init: [String : NSObject] = [
-            "Key1": "Value1" as NSObject
+        let sample_dict_init = [
+            "Key1": "Value1"
         ]
-        let sample_dict_final: [String : NSObject] = [
-            "Key1": "Value1" as NSObject,
-            "Key2": "Value2" as NSObject
+        let sample_dict_final = [
+            "Key1": "Value1",
+            "Key2": "Value2"
         ]
         let sample_payload = Payload(dictionary: sample_dict_init)
         sample_payload.addValueToPayload("Value2", forKey: "Key2")
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dict_final)
     }
 
     func testAddNilValueToPayload() {
         let payload = Payload()
         payload.addValueToPayload(nil, forKey: "foo")
-        XCTAssertEqual(payload.dictionary, [String : NSObject]())
+        XCTAssertTrue(payload.dictionary.isEmpty)
     }
 
     func testAddNilValueToPayloadUnsetsKey() {
         let payload = Payload(dictionary: [
-            "foo": "bar" as NSObject
+            "foo": "bar"
         ])
         payload.addValueToPayload(nil, forKey: "foo")
-        XCTAssertEqual(payload.dictionary, [String : NSObject]())
+        XCTAssertTrue(payload.dictionary.isEmpty)
     }
 
     func testAddNumericValueToPayload() {
         let sample_dict = [
-            "Key1": NSNumber(value: 100)
+            "Key1": 100
         ]
         let sample_payload = Payload()
-        sample_payload.addNumericValueToPayload(NSNumber(value: 100), forKey: "Key1")
+        sample_payload.addValueToPayload(100, forKey: "Key1")
 
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : AnyHashable],
             sample_dict)
     }
 
-    func testAddNilNumericValueToPayload() {
-        let sample_payload = Payload()
-        sample_payload.addNumericValueToPayload(nil, forKey: "Key1")
-
-
-        XCTAssertEqual(
-            sample_payload.dictionary,
-            [String : NSObject]())
-    }
-
-    func testAddNilNumericValueToPayloadUnsetsKey() {
-        let sample_payload = Payload(dictionary: [
-            "Key1": NSNumber(value: 100)
-        ])
-        sample_payload.addNumericValueToPayload(nil, forKey: "Key1")
-
-
-        XCTAssertEqual(
-            sample_payload.dictionary,
-            [String : NSObject]())
-    }
-
     func testAddDictToPayload() {
         let sample_dic = [
-            "Key1": "Value1" as NSObject
+            "Key1": "Value1"
         ]
         let sample_payload = Payload()
         sample_payload.addDictionaryToPayload(sample_dic)
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dic)
     }
 
     func testAddDictToPayload2() {
         let sample_dic = [
-            "Key1": "Value1" as NSObject
+            "Key1": "Value1"
         ]
         let sample_dic2 = [
-            "Key2": "Value2" as NSObject
+            "Key2": "Value2"
         ]
         let sample_dict_final = [
-            "Key1": "Value1" as NSObject,
-            "Key2": "Value2" as NSObject
+            "Key1": "Value1",
+            "Key2": "Value2"
         ]
         let sample_payload = Payload(dictionary: sample_dic)
         sample_payload.addDictionaryToPayload(sample_dic2)
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dict_final)
     }
 
     func testAddDictToPayload3() {
         let sample_dic = [
-            "Key1": "Value1" as NSObject
+            "Key1": "Value1"
         ]
         let sample_dic2 = [
-            "Key2": NSNumber(value: 2)
+            "Key2": 2
         ]
         let sample_dict_final = [
-            "Key1": "Value1" as NSObject
+            "Key1": "Value1"
         ]
 
         let sample_payload = Payload(dictionary: sample_dic)
         sample_payload.addDictionaryToPayload(sample_dic2)
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : AnyHashable],
             sample_dict_final)
     }
 
@@ -220,7 +196,7 @@ class TestPayload: XCTestCase {
             "Key1": "Value1"
         ]
         let sample_enc = [
-            "type_enc": "eyJLZXkxIjoiVmFsdWUxIn0" as NSObject
+            "type_enc": "eyJLZXkxIjoiVmFsdWUxIn0"
         ]
 
         // NSDictionary conversion to JSON string
@@ -234,7 +210,7 @@ class TestPayload: XCTestCase {
             typeWhenNotEncoded: "type_notenc")
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as? [String : String],
             sample_enc)
     }
 
@@ -245,7 +221,7 @@ class TestPayload: XCTestCase {
             "Key1": "Value1"
         ]
         let sample_enc = [
-            "type_notenc": "{\"Key1\":\"Value1\"}" as NSObject
+            "type_notenc": "{\"Key1\":\"Value1\"}"
         ]
 
         // NSDictionary conversion to JSON string
@@ -259,7 +235,7 @@ class TestPayload: XCTestCase {
             typeWhenNotEncoded: "type_notenc")
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_enc)
     }
 
@@ -267,7 +243,7 @@ class TestPayload: XCTestCase {
         // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
 
         let sample_enc = [
-            "type_notenc": "{\"Key1\":\"Value1\"}" as NSObject
+            "type_notenc": "{\"Key1\":\"Value1\"}"
         ]
         let json_str = "{\"Key1\":\"Value1\"}"
 
@@ -279,7 +255,7 @@ class TestPayload: XCTestCase {
             typeWhenNotEncoded: "type_notenc")
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_enc)
     }
 
@@ -287,7 +263,7 @@ class TestPayload: XCTestCase {
         // {"Key1":"Value1"} -> eyJLZXkxIjoiVmFsdWUxIn0=
 
         let sample_enc = [
-            "type_enc": "eyJLZXkxIjoiVmFsdWUxIn0" as NSObject
+            "type_enc": "eyJLZXkxIjoiVmFsdWUxIn0"
         ]
         let json_str = "{\"Key1\":\"Value1\"}"
 
@@ -299,28 +275,26 @@ class TestPayload: XCTestCase {
             typeWhenNotEncoded: "type_notenc")
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_enc)
     }
 
     func testgetPayloadAsDictionary() {
         let sample_payload = Payload()
 
-        XCTAssertEqual(
-            sample_payload.dictionary,
-            [String : NSObject]())
+        XCTAssertTrue(sample_payload.dictionary.isEmpty)
     }
 
     func testgetPayloadAsDictionary2() {
         let sample_dict = [
-            "Key1": "Value1" as NSObject
+            "Key1": "Value1"
         ]
         let sample_payload = Payload(dictionary: [
-            "Key1": "Value1" as NSObject
+            "Key1": "Value1"
         ])
 
         XCTAssertEqual(
-            sample_payload.dictionary,
+            sample_payload.dictionary as! [String : String],
             sample_dict)
     }
 }
diff --git a/Tests/TestPlatformContext.swift b/Tests/TestPlatformContext.swift
index d985acf62..dec0e1c25 100644
--- a/Tests/TestPlatformContext.swift
+++ b/Tests/TestPlatformContext.swift
@@ -42,20 +42,18 @@ class TestPlatformContext: XCTestCase {
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         let idfa = UUID()
-        guard let platformDict = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: { idfa }).dictionary else {
-            return XCTFail()
-        }
-        XCTAssertEqual(idfa.uuidString as NSObject, platformDict[kSPMobileAppleIdfa])
-        XCTAssertEqual("appleIdfv" as NSObject, platformDict[kSPMobileAppleIdfv])
-        XCTAssertEqual("Apple Inc." as NSObject, platformDict[kSPPlatformDeviceManu])
-        XCTAssertEqual("deviceModel" as NSObject, platformDict[kSPPlatformDeviceModel])
-        XCTAssertEqual("13.0.0" as NSObject, platformDict[kSPPlatformOsVersion])
-        XCTAssertEqual("ios" as NSObject, platformDict[kSPPlatformOsType])
-        XCTAssertEqual("att" as NSObject, platformDict[kSPMobileCarrier])
-        XCTAssertEqual("3g" as NSObject, platformDict[kSPMobileNetworkTech])
-        XCTAssertEqual("wifi" as NSObject, platformDict[kSPMobileNetworkType])
-        XCTAssertEqual(NSNumber(value: 20), platformDict[kSPMobileBatteryLevel])
-        XCTAssertEqual("charging" as NSObject, platformDict[kSPMobileBatteryState])
+        let platformDict = context.fetchPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: { idfa })
+        XCTAssertEqual(idfa.uuidString, platformDict[kSPMobileAppleIdfa] as? String)
+        XCTAssertEqual("appleIdfv", platformDict[kSPMobileAppleIdfv] as? String)
+        XCTAssertEqual("Apple Inc.", platformDict[kSPPlatformDeviceManu] as? String)
+        XCTAssertEqual("deviceModel", platformDict[kSPPlatformDeviceModel] as? String)
+        XCTAssertEqual("13.0.0", platformDict[kSPPlatformOsVersion] as? String)
+        XCTAssertEqual("ios", platformDict[kSPPlatformOsType] as? String)
+        XCTAssertEqual("att", platformDict[kSPMobileCarrier] as? String)
+        XCTAssertEqual("3g", platformDict[kSPMobileNetworkTech] as? String)
+        XCTAssertEqual("wifi", platformDict[kSPMobileNetworkType] as? String)
+        XCTAssertEqual(20, platformDict[kSPMobileBatteryLevel] as? Int)
+        XCTAssertEqual("charging", platformDict[kSPMobileBatteryState] as? String)
     }
 
     func testUpdatesMobileInfo() {
@@ -144,22 +142,18 @@ class TestPlatformContext: XCTestCase {
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         
-        guard let platformDict1 = context.fetchPlatformDict(
+        let platformDict1 = context.fetchPlatformDict(
             userAnonymisation: false,
             advertisingIdentifierRetriever: { nil }
-        ).dictionary else {
-            return XCTFail()
-        }
+        )
         XCTAssertNil(platformDict1[kSPMobileAppleIdfa])
         
         let idfa = UUID()
-        guard let platformDict2 = context.fetchPlatformDict(
+        let platformDict2 = context.fetchPlatformDict(
             userAnonymisation: false,
             advertisingIdentifierRetriever: { idfa }
-        ).dictionary else {
-            return XCTFail()
-        }
-        XCTAssertEqual(idfa.uuidString as NSObject, platformDict2[kSPMobileAppleIdfa])
+        )
+        XCTAssertEqual(idfa.uuidString, platformDict2[kSPMobileAppleIdfa] as? String)
     }
 
     func testDoesntUpdateIdfaIfAlreadyRetrieved() {
@@ -167,32 +161,26 @@ class TestPlatformContext: XCTestCase {
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
         
         let idfa1 = UUID()
-        guard let platformDict1 = context.fetchPlatformDict(
+        let platformDict1 = context.fetchPlatformDict(
             userAnonymisation: false,
             advertisingIdentifierRetriever: { idfa1 }
-        ).dictionary else {
-            return XCTFail()
-        }
-        XCTAssertEqual(idfa1.uuidString as NSObject, platformDict1[kSPMobileAppleIdfa])
+        )
+        XCTAssertEqual(idfa1.uuidString, platformDict1[kSPMobileAppleIdfa] as? String)
         
-        guard let platformDict2 = context.fetchPlatformDict(
+        let platformDict2 = context.fetchPlatformDict(
             userAnonymisation: false,
             advertisingIdentifierRetriever: { UUID() }
-        ).dictionary else {
-            return XCTFail()
-        }
-        XCTAssertEqual(idfa1.uuidString as NSObject, platformDict2[kSPMobileAppleIdfa])
+        )
+        XCTAssertEqual(idfa1.uuidString, platformDict2[kSPMobileAppleIdfa] as? String)
     }
 
     func testAnonymisesUserIdentifiers() {
         let deviceInfoMonitor = MockDeviceInfoMonitor()
         let context = PlatformContext(mobileDictUpdateFrequency: 0, networkDictUpdateFrequency: 1, deviceInfoMonitor: deviceInfoMonitor)
-        guard let platformDict = context.fetchPlatformDict(
+        let platformDict = context.fetchPlatformDict(
             userAnonymisation: true,
             advertisingIdentifierRetriever: { UUID() }
-        ).dictionary else {
-            return XCTFail()
-        }
+        )
         XCTAssertNil(platformDict[kSPMobileAppleIdfa])
         XCTAssertNil(platformDict[kSPMobileAppleIdfv])
     }
diff --git a/Tests/TestRequest.swift b/Tests/TestRequest.swift
index 6b9993f4f..b71f5f64a 100644
--- a/Tests/TestRequest.swift
+++ b/Tests/TestRequest.swift
@@ -226,7 +226,7 @@ class TestRequest: XCTestCase, RequestCallback {
 
     func customContext() -> [SelfDescribingJson] {
         let data = [
-            "snowplow": "demo-tracker" as NSObject
+            "snowplow": "demo-tracker"
         ]
         let context = SelfDescribingJson(
             schema: "iglu:com.acme_company/demo_ios/jsonschema/1-0-0",
diff --git a/Tests/TestRequestResult.swift b/Tests/TestRequestResult.swift
index 82735d6cb..02545d33f 100644
--- a/Tests/TestRequestResult.swift
+++ b/Tests/TestRequestResult.swift
@@ -33,8 +33,8 @@ class TestRequestResult: XCTestCase {
     }
 
     func testSuccessfulRequest() {
-        var emitterEventIds: [NSNumber]? = []
-        emitterEventIds?.append(NSNumber(value: 1))
+        var emitterEventIds: [Int64]? = []
+        emitterEventIds?.append(1)
         let result = RequestResult(statusCode: 200, oversize: false, storeIds: emitterEventIds)
 
         XCTAssertNotNil(result)
@@ -44,8 +44,8 @@ class TestRequestResult: XCTestCase {
     }
 
     func testFailedRequest() {
-        var emitterEventIds: [NSNumber]? = []
-        emitterEventIds?.append(NSNumber(value: 1))
+        var emitterEventIds: [Int64]? = []
+        emitterEventIds?.append(1)
         let result = RequestResult(statusCode: 500, oversize: false, storeIds: emitterEventIds)
         XCTAssertEqual(result.isSuccessful, false)
         XCTAssertEqual(result.shouldRetry([:]), true)
diff --git a/Tests/TestSQLiteEventStore.swift b/Tests/TestSQLiteEventStore.swift
index 0db24fb29..b3c36b674 100644
--- a/Tests/TestSQLiteEventStore.swift
+++ b/Tests/TestSQLiteEventStore.swift
@@ -49,7 +49,8 @@ class TestSQLiteEventStore: XCTestCase {
         _ = eventStore.insertEvent(payload)
 
         XCTAssertEqual(eventStore.count(), 1)
-        XCTAssertEqual(eventStore.getEventWithId(1)?.payload.dictionary, payload.dictionary)
+        XCTAssertEqual(eventStore.getEventWithId(1)?.payload.dictionary as! [String : String],
+                       payload.dictionary as! [String : String])
         XCTAssertEqual(eventStore.getLastInsertedRowId(), 1)
         _ = eventStore.removeEvent(withId: 1)
 
@@ -114,7 +115,7 @@ class TestSQLiteEventStore: XCTestCase {
     func testMigrationFromLegacyToNamespacedEventStore() {
         var eventStore = SQLiteEventStore(namespace: "aNamespace")
         eventStore.addEvent(Payload(dictionary: [
-            "key": "value" as NSObject
+            "key": "value"
         ]))
         XCTAssertEqual(1, eventStore.count())
 
@@ -136,20 +137,20 @@ class TestSQLiteEventStore: XCTestCase {
         XCTAssertTrue(FileManager.default.fileExists(atPath: newDbPath))
         XCTAssertEqual(1, eventStore.count())
         for event in eventStore.getAllEvents() ?? [] {
-            XCTAssertEqual("value", event.payload.dictionary?["key"] as? String)
+            XCTAssertEqual("value", event.payload.dictionary["key"] as? String)
         }
     }
 
     func testMultipleAccessToSameSQLiteFile() {
         let eventStore1 = SQLiteEventStore(namespace: "aNamespace")
         eventStore1.addEvent(Payload(dictionary: [
-            "key1": "value1" as NSObject
+            "key1": "value1"
         ]))
         XCTAssertEqual(1, eventStore1.count())
 
         let eventStore2 = SQLiteEventStore(namespace: "aNamespace")
         eventStore2.addEvent(Payload(dictionary: [
-            "key2": "value2" as NSObject
+            "key2": "value2"
         ]))
         XCTAssertEqual(2, eventStore2.count())
     }
diff --git a/Tests/TestScreenState.swift b/Tests/TestScreenState.swift
index e6c0468bd..e73bf025c 100644
--- a/Tests/TestScreenState.swift
+++ b/Tests/TestScreenState.swift
@@ -85,7 +85,7 @@ class TestScreenState: XCTestCase {
         }
         var payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        var entities = (payload?.dictionary?["co"]) as? String
+        var entities = (payload?.dictionary["co"]) as? String
         XCTAssertNil(entities)
 
         let uuid = UUID()
@@ -96,7 +96,7 @@ class TestScreenState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?.dictionary["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains(uuid.uuidString))
 
@@ -107,7 +107,7 @@ class TestScreenState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?.dictionary["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains(uuid.uuidString))
 
@@ -119,10 +119,10 @@ class TestScreenState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?.dictionary["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains(uuid2.uuidString))
-        let eventPayload = (payload?.dictionary?["ue_pr"]) as? String
+        let eventPayload = (payload?.dictionary["ue_pr"]) as? String
         XCTAssertNotNil(eventPayload)
         XCTAssertTrue(eventPayload!.contains(uuid.uuidString))
         XCTAssertTrue(eventPayload!.contains(uuid2.uuidString))
@@ -134,7 +134,7 @@ class TestScreenState: XCTestCase {
         }
         payload = eventStore.db[Int64(eventStore.lastInsertedRow)]
         _ = eventStore.removeAllEvents()
-        entities = (payload?.dictionary?["co"]) as? String
+        entities = (payload?.dictionary["co"]) as? String
         XCTAssertNotNil(entities)
         XCTAssertTrue(entities!.contains(uuid2.uuidString))
     }
diff --git a/Tests/TestSelfDescribingJson.swift b/Tests/TestSelfDescribingJson.swift
index 63103dd4b..8b76920ab 100644
--- a/Tests/TestSelfDescribingJson.swift
+++ b/Tests/TestSelfDescribingJson.swift
@@ -32,45 +32,47 @@ class TestSelfDescribingJson: XCTestCase {
     }
 
     func testInitWithObject() {
-        let expected = [
-            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+        let expected: [String : Any] = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0",
             "data": [
                 "hello": "world"
-            ] as NSObject
+            ]
         ]
         let data = [
-            "hello": "world" as NSObject
+            "hello": "world"
         ]
         let sdj = SelfDescribingJson(
             schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
             andDictionary: data)
-        XCTAssertEqual(expected, sdj.dictionary)
+        XCTAssertEqual(NSDictionary(dictionary: expected),
+                       NSDictionary(dictionary: sdj.dictionary))
     }
 
     func testInitWithSPPayload() {
-        let expected = [
-            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+        let expected: [String : Any] = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0",
             "data": [
                 "hello": "world"
-            ] as NSObject
+            ]
         ]
         let data = Payload()
         data.addValueToPayload("world", forKey: "hello")
         let sdj = SelfDescribingJson(
             schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
             andPayload: data)
-        XCTAssertEqual(expected, sdj.dictionary)
+        XCTAssertEqual(NSDictionary(dictionary: expected),
+                       NSDictionary(dictionary: sdj.dictionary))
     }
 
     func testInitWithSPSelfDescribingJson() {
-        let expected = [
-            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+        let expected: [String : Any] = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0",
             "data": [
                 "schema": "iglu:acme.com/nested_event/jsonschema/1-0-0",
                 "data": [
                     "hello": "world"
                 ]
-            ] as NSObject
+            ]
         ]
         let nestedData = [
             "hello": "world"
@@ -81,15 +83,16 @@ class TestSelfDescribingJson: XCTestCase {
         let sdj = SelfDescribingJson(
             schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
             andSelfDescribingJson: data)
-        XCTAssertEqual(expected, sdj.dictionary)
+        XCTAssertEqual(NSDictionary(dictionary: expected),
+                       NSDictionary(dictionary: sdj.dictionary))
     }
 
     func testUpdateSchema() {
-        let expected = [
-            "schema": "iglu:acme.com/test_event_2/jsonschema/1-0-0" as NSObject,
+        let expected: [String : Any] = [
+            "schema": "iglu:acme.com/test_event_2/jsonschema/1-0-0",
             "data": [
                 "hello": "world"
-            ] as NSObject
+            ]
         ]
         let data = [
             "hello": "world"
@@ -98,33 +101,35 @@ class TestSelfDescribingJson: XCTestCase {
             schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
             andDictionary: data)
         sdj.schema = "iglu:acme.com/test_event_2/jsonschema/1-0-0"
-        XCTAssertEqual(expected, sdj.dictionary)
+        XCTAssertEqual(NSDictionary(dictionary: expected),
+                       NSDictionary(dictionary: sdj.dictionary))
     }
-    
+
     func testUpdateDataWithObject() {
-        let expected = [
-            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+        let expected: [String : Any] = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0",
             "data": [
                 "world": "hello"
-            ] as NSObject
+            ]
         ]
         let sdj = SelfDescribingJson(
             schema: "iglu:acme.com/test_event/jsonschema/1-0-0",
             andDictionary: [
                 "hello": "world"
             ])
-        sdj.setData(withObject: [
+        sdj.data = [
             "world": "hello"
-        ] as NSObject)
-        XCTAssertEqual(expected, sdj.dictionary)
+        ]
+        XCTAssertEqual(NSDictionary(dictionary: expected),
+                       NSDictionary(dictionary: sdj.dictionary))
     }
-    
+
     func testUpdateDataWithSPPayload() {
-        let expected = [
-            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+        let expected: [String : Any] = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0",
             "data": [
                 "world": "hello"
-            ] as NSObject
+            ]
         ]
         let data = Payload()
         data.addValueToPayload("hello", forKey: "world")
@@ -134,18 +139,19 @@ class TestSelfDescribingJson: XCTestCase {
                 "hello": "world"
             ])
         sdj.setData(withPayload: data)
-        XCTAssertEqual(expected, sdj.dictionary)
+        XCTAssertEqual(NSDictionary(dictionary: expected),
+                       NSDictionary(dictionary: sdj.dictionary))
     }
 
     func testUpdateDataWithSPSelfDescribingJson() {
-        let expected = [
-            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0" as NSObject,
+        let expected: [String : Any] = [
+            "schema": "iglu:acme.com/test_event/jsonschema/1-0-0",
             "data": [
                 "schema": "iglu:acme.com/nested_event/jsonschema/1-0-0",
                 "data": [
                     "hello": "world"
                 ]
-            ] as NSObject
+            ]
         ]
         let nestedData = [
             "hello": "world"
@@ -159,7 +165,8 @@ class TestSelfDescribingJson: XCTestCase {
                 "hello": "world"
             ])
         sdj.setData(withSelfDescribingJson: data)
-        XCTAssertEqual(expected, sdj.dictionary)
+        XCTAssertEqual(NSDictionary(dictionary: expected),
+                       NSDictionary(dictionary: sdj.dictionary))
     }
 }
 
diff --git a/Tests/TestServiceProvider.swift b/Tests/TestServiceProvider.swift
index e344a7b4e..750d473ed 100644
--- a/Tests/TestServiceProvider.swift
+++ b/Tests/TestServiceProvider.swift
@@ -51,7 +51,7 @@ class TestServiceProvider: XCTestCase {
         serviceProvider.emitterController.pause()
 
         // refresh configuration
-        serviceProvider.reset(withConfigurations: [EmitterConfigurationUpdate()])
+        serviceProvider.reset(configurations: [EmitterConfigurationUpdate()])
 
         // track event and check that emitter is paused
         _ = serviceProvider.trackerController.track(Structured(category: "cat", action: "act"))
diff --git a/Tests/TestSession.swift b/Tests/TestSession.swift
index 4dff1f99d..0907df729 100644
--- a/Tests/TestSession.swift
+++ b/Tests/TestSession.swift
@@ -72,9 +72,9 @@ class TestSession: XCTestCase {
         let sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
         let sessionIndex = session.state!.sessionIndex
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.346Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
     }
 
     func testForegroundEventsOnSameSession() {
@@ -82,41 +82,41 @@ class TestSession: XCTestCase {
 
         var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
         var sessionIndex = session.state?.sessionIndex
-        let sessionId = sessionContext?[kSPSessionId]
+        let sessionId = sessionContext?[kSPSessionId] as? String
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.346Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
 
         Thread.sleep(forTimeInterval: 1)
 
         sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481347, userAnonymisation: false)
         sessionIndex = session.state?.sessionIndex
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
-        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.346Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId] as? String)
 
         Thread.sleep(forTimeInterval: 1)
 
         sessionContext = session.getDictWithEventId("event_3", eventTimestamp: 1654496481348, userAnonymisation: false)
         sessionIndex = session.state?.sessionIndex
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
-        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.346Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId] as? String)
 
         Thread.sleep(forTimeInterval: 3.1)
 
         sessionContext = session.getDictWithEventId("event_4", eventTimestamp: 1654496481349, userAnonymisation: false)
         sessionIndex = session.state?.sessionIndex
         XCTAssertEqual(2, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual("event_4" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.349Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
-        XCTAssertNotEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_4", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.349Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
+        XCTAssertNotEqual(sessionId, sessionContext?[kSPSessionId] as? String)
     }
 
     func testBackgroundEventsOnWhenLifecycleEventsDisabled() {
@@ -136,9 +136,9 @@ class TestSession: XCTestCase {
         let sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
         let sessionIndex = session?.state?.sessionIndex ?? 0
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
-        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.346Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.346Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
         XCTAssertFalse(session!.inBackground)
         XCTAssertEqual(0, session?.backgroundIndex)
     }
@@ -158,13 +158,13 @@ class TestSession: XCTestCase {
 
         session?.updateInBackground() // It sends a background event
 
-        let sessionId = session?.state?.sessionId as? NSString
+        let sessionId = session?.state?.sessionId
 
         var sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
         var sessionIndex = session?.state?.sessionIndex ?? 0
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
-        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId] as? String)
         XCTAssertTrue(session!.inBackground)
         XCTAssertEqual(1, session?.backgroundIndex)
 
@@ -173,8 +173,8 @@ class TestSession: XCTestCase {
         sessionContext = session?.getDictWithEventId("event_2", eventTimestamp: 1654496481347, userAnonymisation: false)
         sessionIndex = session?.state?.sessionIndex ?? 0
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
-        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId] as? String)
         XCTAssertTrue(session!.inBackground)
         XCTAssertEqual(1, session?.backgroundIndex)
 
@@ -183,8 +183,8 @@ class TestSession: XCTestCase {
         sessionContext = session?.getDictWithEventId("event_3", eventTimestamp: 1654496481348, userAnonymisation: false)
         sessionIndex = session?.state?.sessionIndex ?? 0
         XCTAssertEqual(1, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
-        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual(sessionId, sessionContext?[kSPSessionId] as? String)
         XCTAssertTrue(session!.inBackground)
         XCTAssertEqual(1, session?.backgroundIndex)
 
@@ -193,10 +193,10 @@ class TestSession: XCTestCase {
         sessionContext = session?.getDictWithEventId("event_4", eventTimestamp: 1654496481349, userAnonymisation: false)
         sessionIndex = session?.state?.sessionIndex ?? 0
         XCTAssertEqual(2, sessionIndex)
-        XCTAssertEqual(sessionIndex, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue ?? 0)
-        XCTAssertEqual("event_4" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.349Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
-        XCTAssertNotEqual(sessionId, sessionContext?[kSPSessionId])
+        XCTAssertEqual(sessionIndex, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_4", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.349Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
+        XCTAssertNotEqual(sessionId, sessionContext?[kSPSessionId] as? String)
         XCTAssertTrue(session!.inBackground)
         XCTAssertEqual(1, session?.backgroundIndex)
     }
@@ -214,44 +214,44 @@ class TestSession: XCTestCase {
         let session = tracker.session
 
         var sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481351, userAnonymisation: false)
-        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.351Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.351Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
         XCTAssertFalse(session!.inBackground)
         XCTAssertEqual(0, session?.backgroundIndex)
         XCTAssertEqual(0, session?.foregroundIndex)
-        var oldSessionId = sessionContext?[kSPSessionId]
+        var oldSessionId = sessionContext?[kSPSessionId] as? String
 
         session?.updateInBackground()
         Thread.sleep(forTimeInterval: 1.1)
 
         sessionContext = session?.getDictWithEventId("event_2", eventTimestamp: 1654496481352, userAnonymisation: false)
-        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId])
-        XCTAssertEqual("event_2" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.352Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId] as? String)
+        XCTAssertEqual("event_2", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.352Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
         XCTAssertTrue(session!.inBackground)
         XCTAssertEqual(1, session?.backgroundIndex)
         XCTAssertEqual(0, session?.foregroundIndex)
-        oldSessionId = sessionContext?[kSPSessionId]
+        oldSessionId = sessionContext?[kSPSessionId] as? String
 
         session?.updateInForeground()
         Thread.sleep(forTimeInterval: 1.1)
 
         sessionContext = session?.getDictWithEventId("event_3", eventTimestamp: 1654496481353, userAnonymisation: false)
-        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId])
-        XCTAssertEqual("event_3" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.353Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId] as? String)
+        XCTAssertEqual("event_3", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.353Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
         XCTAssertFalse(session!.inBackground)
         XCTAssertEqual(1, session?.backgroundIndex)
         XCTAssertEqual(1, session?.foregroundIndex)
-        oldSessionId = sessionContext?[kSPSessionId]
+        oldSessionId = sessionContext?[kSPSessionId] as? String
 
         session?.updateInBackground()
         Thread.sleep(forTimeInterval: 1.1)
 
         sessionContext = session?.getDictWithEventId("event_4", eventTimestamp: 1654496481354, userAnonymisation: false)
-        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId])
-        XCTAssertEqual("event_4" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.354Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(oldSessionId, sessionContext?[kSPSessionPreviousId] as? String)
+        XCTAssertEqual("event_4", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.354Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
         XCTAssertTrue(session!.inBackground)
         XCTAssertEqual(2, session?.backgroundIndex)
         XCTAssertEqual(1, session?.foregroundIndex)
@@ -261,27 +261,27 @@ class TestSession: XCTestCase {
         let session = Session(foregroundTimeout: 1, andBackgroundTimeout: 1, andTracker: nil)
 
         var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481355, userAnonymisation: false)
-        var prevSessionId = sessionContext?[kSPSessionId]
-        XCTAssertEqual("event_1" as NSString, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.355Z" as NSString, sessionContext?[kSPSessionFirstEventTimestamp])
+        var prevSessionId = sessionContext?[kSPSessionId] as? String
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.355Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
 
         session.stopChecker()
         Thread.sleep(forTimeInterval: 2)
 
         sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481356, userAnonymisation: false)
-        XCTAssertEqual(1, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual(prevSessionId, sessionContext?[kSPSessionId])
-        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.355Z" as NSObject, sessionContext?[kSPSessionFirstEventTimestamp])
-        prevSessionId = sessionContext?[kSPSessionId]
+        XCTAssertEqual(1, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual(prevSessionId, sessionContext?[kSPSessionId] as? String)
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.355Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
+        prevSessionId = sessionContext?[kSPSessionId] as? String
 
         session.startChecker()
 
         sessionContext = session.getDictWithEventId("event_3", eventTimestamp: 1654496481357, userAnonymisation: false)
-        XCTAssertEqual(2, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual(prevSessionId, sessionContext?[kSPSessionPreviousId])
-        XCTAssertEqual("event_3" as NSObject, sessionContext?[kSPSessionFirstEventId])
-        XCTAssertEqual("2022-06-06T06:21:21.357Z" as NSObject, sessionContext?[kSPSessionFirstEventTimestamp])
+        XCTAssertEqual(2, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual(prevSessionId, sessionContext?[kSPSessionPreviousId] as? String)
+        XCTAssertEqual("event_3", sessionContext?[kSPSessionFirstEventId] as? String)
+        XCTAssertEqual("2022-06-06T06:21:21.357Z", sessionContext?[kSPSessionFirstEventTimestamp] as? String)
     }
 
     func testBackgroundTimeBiggerThanBackgroundTimeoutCausesNewSession() {
@@ -297,7 +297,7 @@ class TestSession: XCTestCase {
         let session = tracker.session
 
         let sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481361, userAnonymisation: false)
-        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
         XCTAssertFalse(session!.inBackground)
         XCTAssertEqual(0, session?.backgroundIndex)
         XCTAssertEqual(0, session?.foregroundIndex)
@@ -328,7 +328,7 @@ class TestSession: XCTestCase {
         let session = tracker.session
 
         let sessionContext = session?.getDictWithEventId("event_1", eventTimestamp: 1654496481358, userAnonymisation: false)
-        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
         XCTAssertFalse(session!.inBackground)
         XCTAssertEqual(0, session?.backgroundIndex)
         XCTAssertEqual(0, session?.foregroundIndex)
@@ -350,13 +350,13 @@ class TestSession: XCTestCase {
         let session = Session(foregroundTimeout: 1, andBackgroundTimeout: 1, andTracker: nil)
 
         var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481359, userAnonymisation: false)
-        XCTAssertEqual("event_1" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual("event_1", sessionContext?[kSPSessionFirstEventId] as? String)
 
         Thread.sleep(forTimeInterval: 4)
 
         sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481360, userAnonymisation: false)
-        XCTAssertEqual(2, (sessionContext?[kSPSessionIndex] as? NSNumber)?.intValue)
-        XCTAssertEqual("event_2" as NSObject, sessionContext?[kSPSessionFirstEventId])
+        XCTAssertEqual(2, sessionContext?[kSPSessionIndex] as? Int)
+        XCTAssertEqual("event_2", sessionContext?[kSPSessionFirstEventId] as? String)
     }
 
     func testMultipleTrackersUpdateDifferentSessions() {
@@ -434,22 +434,22 @@ class TestSession: XCTestCase {
         let session = Session(foregroundTimeout: 3, andBackgroundTimeout: 3, andTracker: nil)
 
         var sessionContext = session.getDictWithEventId("event_1", eventTimestamp: 1654496481346, userAnonymisation: false)
-        XCTAssertEqual(NSNumber(value: 1), sessionContext?[kSPSessionEventIndex])
+        XCTAssertEqual(1, sessionContext?[kSPSessionEventIndex] as? Int)
 
         Thread.sleep(forTimeInterval: 1)
 
         sessionContext = session.getDictWithEventId("event_2", eventTimestamp: 1654496481347, userAnonymisation: false)
-        XCTAssertEqual(NSNumber(value: 2), sessionContext?[kSPSessionEventIndex])
+        XCTAssertEqual(2, sessionContext?[kSPSessionEventIndex] as? Int)
 
         Thread.sleep(forTimeInterval: 1)
 
         sessionContext = session.getDictWithEventId("event_3", eventTimestamp: 1654496481348, userAnonymisation: false)
-        XCTAssertEqual(NSNumber(value: 3), sessionContext?[kSPSessionEventIndex])
+        XCTAssertEqual(3, sessionContext?[kSPSessionEventIndex] as? Int)
 
         Thread.sleep(forTimeInterval: 3.1)
 
         sessionContext = session.getDictWithEventId("event_4", eventTimestamp: 1654496481349, userAnonymisation: false)
-        XCTAssertEqual(NSNumber(value: 1), sessionContext?[kSPSessionEventIndex])
+        XCTAssertEqual(1, sessionContext?[kSPSessionEventIndex] as? Int)
     }
 
     func testAnonymisesUserIdentifiers() {
@@ -458,11 +458,11 @@ class TestSession: XCTestCase {
         session.startNewSession() // create previous session ID reference
 
         let withoutAnonymisation = session.getDictWithEventId("event_2", eventTimestamp: 1654496481346, userAnonymisation: false)
-        XCTAssertNotEqual("00000000-0000-0000-0000-000000000000" as NSObject, withoutAnonymisation?[kSPSessionUserId])
+        XCTAssertNotEqual("00000000-0000-0000-0000-000000000000", withoutAnonymisation?[kSPSessionUserId] as? String)
         XCTAssertNotNil(withoutAnonymisation?[kSPSessionPreviousId])
 
         let withAnonymisation = session.getDictWithEventId("event_3", eventTimestamp: 1654496481347, userAnonymisation: true)
-        XCTAssertEqual("00000000-0000-0000-0000-000000000000" as NSObject, withAnonymisation?[kSPSessionUserId])
+        XCTAssertEqual("00000000-0000-0000-0000-000000000000", withAnonymisation?[kSPSessionUserId] as? String)
         XCTAssertNil(withAnonymisation?[kSPSessionPreviousId])
     }
 
@@ -476,10 +476,10 @@ class TestSession: XCTestCase {
 
     func storeAsV3_0(withNamespace namespace: String, eventId: String?, sessionId: String?, sessionIndex: Int, userId: String?) {
         let dataPersistence = DataPersistence.getFor(namespace: namespace)
-        var newSessionDict: [String : NSObject] = [:]
-        newSessionDict[kSPSessionFirstEventId] = eventId as? NSObject
-        newSessionDict[kSPSessionId] = sessionId as? NSObject
-        newSessionDict[kSPSessionIndex] = NSNumber(value: sessionIndex)
+        var newSessionDict: [String : Any] = [:]
+        newSessionDict[kSPSessionFirstEventId] = eventId
+        newSessionDict[kSPSessionId] = sessionId
+        newSessionDict[kSPSessionIndex] = sessionIndex
         dataPersistence?.session = newSessionDict
 
         //Store userId
diff --git a/Tests/TestStateManager.swift b/Tests/TestStateManager.swift
index 33769cc3f..f9f57d5bf 100644
--- a/Tests/TestStateManager.swift
+++ b/Tests/TestStateManager.swift
@@ -66,7 +66,7 @@ class MockStateMachine: StateMachineProtocol {
     func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]? {
         let mockState = state as? MockState
         let sdj = SelfDescribingJson(schema: "entity", andDictionary: [
-            "value": NSNumber(value: mockState?.value ?? 0)
+            "value": mockState?.value ?? 0
         ])
         return [sdj]
     }
@@ -75,9 +75,9 @@ class MockStateMachine: StateMachineProtocol {
         return ["event"]
     }
 
-    func payloadValues(from event: InspectableEvent, state: State?) -> [String : NSObject]? {
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]? {
         return [
-            "newParam": "value" as NSObject
+            "newParam": "value"
         ]
     }
 }
@@ -103,13 +103,13 @@ class TestStateManager: XCTestCase {
         stateManager.addOrReplaceStateMachine(MockStateMachine())
 
         let eventInc = SelfDescribing(schema: "inc", payload: [
-            "value": NSNumber(value: 1)
+            "value": 1
         ])
         let eventDec = SelfDescribing(schema: "dec", payload: [
-            "value": NSNumber(value: 2)
+            "value": 2
         ])
         let event = SelfDescribing(schema: "event", payload: [
-            "value": NSNumber(value: 3)
+            "value": 3
         ])
 
         var trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
@@ -117,7 +117,7 @@ class TestStateManager: XCTestCase {
         XCTAssertEqual(1, mockState?.value)
         var e = TrackerEvent(event: eventInc, state: trackerState)
         var entities = stateManager.entities(forProcessedEvent: e)
-        XCTAssertEqual(NSNumber(value: 1), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertEqual(1, ((entities[0].data) as? [String : Int])?["value"])
         XCTAssertTrue(stateManager.addPayloadValues(to: e))
         XCTAssertNil((e.payload)["newParam"])
 
@@ -125,7 +125,7 @@ class TestStateManager: XCTestCase {
         XCTAssertEqual(2, (trackerState?.state(withStateMachine: stateMachine) as? MockState)?.value)
         e = TrackerEvent(event: eventInc, state: trackerState)
         entities = stateManager.entities(forProcessedEvent: e)
-        XCTAssertEqual(NSNumber(value: 2), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertEqual(2, ((entities[0].data) as? [String : Int])?["value"])
         XCTAssertTrue(stateManager.addPayloadValues(to: e))
         XCTAssertNil((e.payload)["newParam"])
 
@@ -133,7 +133,7 @@ class TestStateManager: XCTestCase {
         XCTAssertEqual(1, (trackerState?.state(withStateMachine: stateMachine) as? MockState)?.value)
         e = TrackerEvent(event: eventDec, state: trackerState)
         entities = stateManager.entities(forProcessedEvent: e)
-        XCTAssertEqual(NSNumber(value: 1), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertEqual(1, ((entities[0].data) as? [String : Int])?["value"])
         XCTAssertTrue(stateManager.addPayloadValues(to: e))
         XCTAssertNil((e.payload)["newParam"])
 
@@ -141,9 +141,9 @@ class TestStateManager: XCTestCase {
         XCTAssertEqual(1, (trackerState?.state(withStateMachine: stateMachine) as? MockState)?.value)
         e = TrackerEvent(event: event, state: trackerState)
         entities = stateManager.entities(forProcessedEvent: e)
-        XCTAssertEqual(NSNumber(value: 1), ((entities[0].data) as? [String : NSNumber])?["value"])
+        XCTAssertEqual(1, ((entities[0].data) as? [String : Int])?["value"])
         XCTAssertTrue(stateManager.addPayloadValues(to: e))
-        XCTAssertEqual("value" as NSObject, (e.payload)["newParam"])
+        XCTAssertEqual("value", (e.payload)["newParam"] as? String)
     }
 
     func testAddRemoveStateMachine() {
@@ -153,7 +153,7 @@ class TestStateManager: XCTestCase {
         _ = stateManager.removeStateMachine("identifier")
 
         let eventInc = SelfDescribing(schema: "inc", payload: [
-            "value": NSNumber(value: 1)
+            "value": 1
         ])
 
         let trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
@@ -170,7 +170,7 @@ class TestStateManager: XCTestCase {
         stateManager.addOrReplaceStateMachine(MockStateMachine2())
 
         let eventInc = SelfDescribing(schema: "inc", payload: [
-            "value": NSNumber(value: 1)
+            "value": 1
         ])
 
         let trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
@@ -185,7 +185,7 @@ class TestStateManager: XCTestCase {
         stateManager.addOrReplaceStateMachine(MockStateMachine())
 
         let eventInc = SelfDescribing(schema: "inc", payload: [
-            "value": NSNumber(value: 1)
+            "value": 1
         ])
 
         let trackerState = stateManager.trackerState(forProcessedEvent: eventInc)
@@ -198,7 +198,7 @@ class TestStateManager: XCTestCase {
         let stateManager = StateManager()
         stateManager.addOrReplaceStateMachine(MockStateMachine("identifier"))
         let trackerState1 = stateManager.trackerState(forProcessedEvent: SelfDescribing(schema: "inc", payload: [
-            "value": NSNumber(value: 1)
+            "value": 1
         ]))
         XCTAssertEqual(1, (trackerState1?.state(withIdentifier: "identifier") as? MockState)?.value)
 
@@ -211,7 +211,7 @@ class TestStateManager: XCTestCase {
         let stateManager = StateManager()
         stateManager.addOrReplaceStateMachine(MockStateMachine1("identifier"))
         let trackerState1 = stateManager.trackerState(forProcessedEvent: SelfDescribing(schema: "inc", payload: [
-            "value": NSNumber(value: 1)
+            "value": 1
         ]))
         XCTAssertEqual(1, (trackerState1?.state(withIdentifier: "identifier") as? MockState)?.value)
 
diff --git a/Tests/TestSubject.swift b/Tests/TestSubject.swift
index b5a90dc2c..8f6a8e8a5 100644
--- a/Tests/TestSubject.swift
+++ b/Tests/TestSubject.swift
@@ -25,14 +25,14 @@ import XCTest
 class TestSubject: XCTestCase {
     func testReturnsPlatformContextIfEnabled() {
         let subject = Subject(platformContext: true, andGeoContext: false)
-        let platformDict = subject.getPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
+        let platformDict = subject.platformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertNotNil(platformDict)
-        XCTAssertNotNil(platformDict?.dictionary?[kSPPlatformOsType])
+        XCTAssertNotNil(platformDict?.dictionary[kSPPlatformOsType])
     }
 
     func testDoesntReturnPlatformContextIfDisabled() {
         let subject = Subject(platformContext: false, andGeoContext: false)
-        let platformDict = subject.getPlatformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
+        let platformDict = subject.platformDict(userAnonymisation: false, advertisingIdentifierRetriever: nil)
         XCTAssertNil(platformDict)
     }
 
@@ -40,7 +40,7 @@ class TestSubject: XCTestCase {
         let subject = Subject(platformContext: false, andGeoContext: true)
         subject.geoLatitude = NSNumber(value: 10.0)
         subject.geoLongitude = NSNumber(value: 10.0)
-        let geoLocationDict = subject.getGeoLocationDict()
+        let geoLocationDict = subject.geoLocationDict
         XCTAssertNotNil(geoLocationDict)
         XCTAssertNotNil(geoLocationDict)
     }
@@ -49,7 +49,7 @@ class TestSubject: XCTestCase {
         let subject = Subject(platformContext: false, andGeoContext: false)
         subject.geoLatitude = NSNumber(value: 10.0)
         subject.geoLongitude = NSNumber(value: 10.0)
-        let geoLocationDict = subject.getGeoLocationDict()
+        let geoLocationDict = subject.geoLocationDict
         XCTAssertNil(geoLocationDict)
     }
 
@@ -61,13 +61,11 @@ class TestSubject: XCTestCase {
         subject.domainUserId = "aDuid"
         subject.language = "EN"
 
-        guard let values = subject.getStandardDict(userAnonymisation: true)?.dictionary else {
-            return XCTFail()
-        }
+        let values = subject.standardDict(userAnonymisation: true)
         XCTAssertNil(values[kSPUid])
         XCTAssertNil(values[kSPIpAddress])
         XCTAssertNil(values[kSPNetworkUid])
         XCTAssertNil(values[kSPDomainUid])
-        XCTAssertEqual(values[kSPLanguage], "EN" as NSObject)
+        XCTAssertEqual(values[kSPLanguage], "EN")
     }
 }
diff --git a/Tests/TestWebViewMessageHandler.swift b/Tests/TestWebViewMessageHandler.swift
index 823b2379e..9915bbfca 100644
--- a/Tests/TestWebViewMessageHandler.swift
+++ b/Tests/TestWebViewMessageHandler.swift
@@ -66,7 +66,7 @@ class TestWebViewMessageHandler: XCTestCase {
         XCTAssertEqual(1, networkConnection?.sendingCount)
         XCTAssertEqual(1, (networkConnection?.previousRequests)?[0].count)
         let request = (networkConnection?.previousRequests)?[0][0]
-        let payload = (request?.payload?.dictionary?["data"] as? [[String: Any]])?[0]
+        let payload = (request?.payload?["data"] as? [[String: Any]])?[0]
         XCTAssert((payload?["se_ca"] as? String == "cat"))
         XCTAssert((payload?["se_ac"] as? String == "act"))
         XCTAssert((payload?["se_pr"] as? String == "prop"))
@@ -129,7 +129,7 @@ class TestWebViewMessageHandler: XCTestCase {
         XCTAssertEqual(1, networkConnection?.sendingCount)
         XCTAssertEqual(1, (networkConnection?.previousRequests)?[0].count)
         let request = (networkConnection?.previousRequests)?[0][0]
-        let payload = (request?.payload?.dictionary?["data"] as? [[String : Any]])?[0]
+        let payload = (request?.payload?["data"] as? [[String : Any]])?[0]
 
         let context = payload?["co"] as? String
         XCTAssert(context?.contains("{\"a\":\"b\"}") ?? false)
diff --git a/Tests/Utils/MockEventStore.swift b/Tests/Utils/MockEventStore.swift
index 6e8a27528..361389f91 100644
--- a/Tests/Utils/MockEventStore.swift
+++ b/Tests/Utils/MockEventStore.swift
@@ -48,10 +48,10 @@ class MockEventStore: NSObject, EventStore {
         return db.removeValue(forKey: storeId) != nil
     }
 
-    func removeEvents(withIds storeIds: [NSNumber]) -> Bool {
+    func removeEvents(withIds storeIds: [Int64]) -> Bool {
         let result = true
         for storeId in storeIds {
-            db.removeValue(forKey: storeId.int64Value)
+            db.removeValue(forKey: storeId)
         }
         return result
     }
@@ -76,7 +76,7 @@ class MockEventStore: NSObject, EventStore {
         var eventIds: [Int64] = []
         var events: [EmitterEvent] = []
         for (key, obj) in db {
-            let payloadCopy = Payload(dictionary: obj.dictionary ?? [:])
+            let payloadCopy = Payload(dictionary: obj.dictionary)
             let event = EmitterEvent(payload: payloadCopy, storeId: key)
             events.append(event)
             eventIds.append(event.storeId)

From 281b802d9f94991ae52384c5e0248c0d92ee2650 Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Tue, 31 Jan 2023 14:19:43 +0100
Subject: [PATCH 11/19] Refactor event interface and rename contexts to
 entities (close #757)

PR #758
---
 .github/workflows/build.yml                   |  2 +-
 Examples                                      |  2 +-
 .../ScreenViewModifier.swift                  |  8 +--
 .../DeepLinkState.swift                       |  2 +-
 .../DeepLinkStateMachine.swift                |  0
 .../LifecycleState.swift                      |  2 +-
 .../LifecycleStateMachine.swift               |  0
 .../Tracker => Core/StateMachine}/State.swift |  2 +-
 .../StateFuture.swift                         |  5 +-
 .../Core/StateMachine/StateMachineEvent.swift | 33 ++++++++++
 .../StateMachine}/StateMachineProtocol.swift  | 16 +++--
 .../StateManager.swift                        | 23 +++----
 .../TrackerState.swift                        |  2 +-
 .../StateMachine}/TrackerStateSnapshot.swift  |  6 +-
 Sources/Core/Tracker/Tracker.swift            | 14 ++---
 Sources/Core/Tracker/TrackerEvent.swift       | 62 +++++++++----------
 .../Core/Tracker/WebViewMessageHandler.swift  |  2 +-
 Sources/Snowplow/Events/ConsentGranted.swift  |  2 +-
 .../Snowplow/Events/ConsentWithdrawn.swift    |  2 +-
 Sources/Snowplow/Events/EventBase.swift       | 12 +++-
 .../Snowplow/Tracker/InspectableEvent.swift   | 12 ++--
 Sources/Snowplow/Tracker/View.swift           |  4 +-
 Tests/TestRequest.swift                       | 14 ++---
 Tests/TestScreenViewModifier.swift            |  2 +-
 24 files changed, 130 insertions(+), 99 deletions(-)
 rename Sources/Core/{Tracker => StateMachine}/DeepLinkState.swift (96%)
 rename Sources/Core/{Tracker => StateMachine}/DeepLinkStateMachine.swift (100%)
 rename Sources/Core/{Tracker => StateMachine}/LifecycleState.swift (96%)
 rename Sources/Core/{Tracker => StateMachine}/LifecycleStateMachine.swift (100%)
 rename Sources/{Snowplow/Tracker => Core/StateMachine}/State.swift (97%)
 rename Sources/Core/{Tracker => StateMachine}/StateFuture.swift (97%)
 create mode 100644 Sources/Core/StateMachine/StateMachineEvent.swift
 rename Sources/{Snowplow/Tracker => Core/StateMachine}/StateMachineProtocol.swift (80%)
 rename Sources/Core/{Tracker => StateMachine}/StateManager.swift (91%)
 rename Sources/Core/{Tracker => StateMachine}/TrackerState.swift (97%)
 rename Sources/{Snowplow/Tracker => Core/StateMachine}/TrackerStateSnapshot.swift (92%)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 22faadb7e..c2de3c2d8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,7 +5,7 @@ on: [push]
 jobs:
   podspec:
     name: Lint Podspec for ${{ matrix.platform }}
-    runs-on: macos-latest
+    runs-on: macos-11
     strategy:
       matrix:
         platform: [ios, osx, tvos, watchos]
diff --git a/Examples b/Examples
index b2b7a3317..a5e796d9b 160000
--- a/Examples
+++ b/Examples
@@ -1 +1 @@
-Subproject commit b2b7a3317a00727a88217e3c5451c49eb38f5a3b
+Subproject commit a5e796d9b37a2f8968deb9658c0cebd102b30bc4
diff --git a/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
index 0c9ff8d1f..7dcf3c50e 100644
--- a/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
@@ -27,12 +27,12 @@ import Foundation
 @available(watchOS, unavailable)
 internal struct ScreenViewModifier: ViewModifier {
     let name: String
-    let contexts: [(schema: String, data: [String: Any])]
+    let entities: [(schema: String, data: [String: Any])]
     let trackerNamespace: String?
     
     /// Transform the context entity definitions to self-describing objects
-    private var processedContexts: [SelfDescribingJson] {
-        return contexts.map({ entity in
+    private var processedEntities: [SelfDescribingJson] {
+        return entities.map({ entity in
             return SelfDescribingJson(schema: entity.schema, andDictionary: entity.data)
         })
     }
@@ -55,7 +55,7 @@ internal struct ScreenViewModifier: ViewModifier {
 
     func trackScreenView() {
         let event = ScreenView(name: name)
-        event.contexts = processedContexts
+        event.entities = processedEntities
 
         if let tracker = tracker {
             _ = tracker.track(event)
diff --git a/Sources/Core/Tracker/DeepLinkState.swift b/Sources/Core/StateMachine/DeepLinkState.swift
similarity index 96%
rename from Sources/Core/Tracker/DeepLinkState.swift
rename to Sources/Core/StateMachine/DeepLinkState.swift
index 778f700ea..6273b96b8 100644
--- a/Sources/Core/Tracker/DeepLinkState.swift
+++ b/Sources/Core/StateMachine/DeepLinkState.swift
@@ -21,7 +21,7 @@
 
 import Foundation
 
-class DeepLinkState: NSObject, State {
+class DeepLinkState: State {
     private(set) var url: String
     private(set) var referrer: String?
     var readyForOutput = false
diff --git a/Sources/Core/Tracker/DeepLinkStateMachine.swift b/Sources/Core/StateMachine/DeepLinkStateMachine.swift
similarity index 100%
rename from Sources/Core/Tracker/DeepLinkStateMachine.swift
rename to Sources/Core/StateMachine/DeepLinkStateMachine.swift
diff --git a/Sources/Core/Tracker/LifecycleState.swift b/Sources/Core/StateMachine/LifecycleState.swift
similarity index 96%
rename from Sources/Core/Tracker/LifecycleState.swift
rename to Sources/Core/StateMachine/LifecycleState.swift
index 425e62cca..72ec80a53 100644
--- a/Sources/Core/Tracker/LifecycleState.swift
+++ b/Sources/Core/StateMachine/LifecycleState.swift
@@ -20,7 +20,7 @@
 
 import Foundation
 
-class LifecycleState: NSObject, State {
+class LifecycleState: State {
     private(set) var isForeground = false
     private(set) var index: Int
 
diff --git a/Sources/Core/Tracker/LifecycleStateMachine.swift b/Sources/Core/StateMachine/LifecycleStateMachine.swift
similarity index 100%
rename from Sources/Core/Tracker/LifecycleStateMachine.swift
rename to Sources/Core/StateMachine/LifecycleStateMachine.swift
diff --git a/Sources/Snowplow/Tracker/State.swift b/Sources/Core/StateMachine/State.swift
similarity index 97%
rename from Sources/Snowplow/Tracker/State.swift
rename to Sources/Core/StateMachine/State.swift
index bc921cba7..ddccbe452 100644
--- a/Sources/Snowplow/Tracker/State.swift
+++ b/Sources/Core/StateMachine/State.swift
@@ -22,5 +22,5 @@
 import Foundation
 
 @objc(SPState)
-public protocol State {
+protocol State {
 }
diff --git a/Sources/Core/Tracker/StateFuture.swift b/Sources/Core/StateMachine/StateFuture.swift
similarity index 97%
rename from Sources/Core/Tracker/StateFuture.swift
rename to Sources/Core/StateMachine/StateFuture.swift
index 91d65c485..ab73f3db3 100644
--- a/Sources/Core/Tracker/StateFuture.swift
+++ b/Sources/Core/StateMachine/StateFuture.swift
@@ -26,19 +26,18 @@ import Foundation
 /// (event, previous StateFuture, StateMachine) needed to calculate the real state value.
 /// For this reason, the StateFuture can be the head of StateFuture chain which will collapse once the StateFuture
 /// head is asked to get the real state value.
-class StateFuture: NSObject {
+class StateFuture {
     private var event: Event?
     private var previousState: StateFuture?
     private var stateMachine: StateMachineProtocol?
     private var computedState: State?
 
     init(event: Event, previousState: StateFuture?, stateMachine: StateMachineProtocol) {
-        super.init()
         self.event = event
         self.previousState = previousState
         self.stateMachine = stateMachine
     }
-    
+
     func computeState() -> State? {
         objc_sync_enter(self)
         defer { objc_sync_exit(self) }
diff --git a/Sources/Core/StateMachine/StateMachineEvent.swift b/Sources/Core/StateMachine/StateMachineEvent.swift
new file mode 100644
index 000000000..65c1d979a
--- /dev/null
+++ b/Sources/Core/StateMachine/StateMachineEvent.swift
@@ -0,0 +1,33 @@
+//
+//  StateMachineEvent.swift
+//  Snowplow
+//
+//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Joshua Beemster
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// The inspectable properties of the event used to generate contexts.
+protocol StateMachineEvent {
+    /// The tracker state at the time the event was sent.
+    var state: TrackerStateSnapshot { get }
+
+    /// Add payload values to the event.
+    /// - Parameter payload: Map of values to add to the event payload.
+    /// - Returns: Whether or not the values have been successfully added to the event payload.
+    func addPayloadValues(_ payload: [String : Any]) -> Bool
+}
diff --git a/Sources/Snowplow/Tracker/StateMachineProtocol.swift b/Sources/Core/StateMachine/StateMachineProtocol.swift
similarity index 80%
rename from Sources/Snowplow/Tracker/StateMachineProtocol.swift
rename to Sources/Core/StateMachine/StateMachineProtocol.swift
index cc1858735..ce18ff37a 100644
--- a/Sources/Snowplow/Tracker/StateMachineProtocol.swift
+++ b/Sources/Core/StateMachine/StateMachineProtocol.swift
@@ -21,20 +21,18 @@
 
 import Foundation
 
-@objc(SPStateMachineProtocol)
-public protocol StateMachineProtocol {
-    @objc
+protocol StateMachineProtocol {
     var identifier: String { get }
-    @objc
     var subscribedEventSchemasForTransitions: [String] { get }
-    @objc
     var subscribedEventSchemasForEntitiesGeneration: [String] { get }
-    @objc
     var subscribedEventSchemasForPayloadUpdating: [String] { get }
-    @objc
+    
+    /// Only available for self-describing events (inheriting from SelfDescribingAbstract)
     func transition(from event: Event, state: State?) -> State?
-    @objc
+    
+    /// Available for both self-describing and primitive events (when using `*` as the schema)
     func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]?
-    @objc
+    
+    /// Only available for self-describing events (inheriting from SelfDescribingAbstract)
     func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]?
 }
diff --git a/Sources/Core/Tracker/StateManager.swift b/Sources/Core/StateMachine/StateManager.swift
similarity index 91%
rename from Sources/Core/Tracker/StateManager.swift
rename to Sources/Core/StateMachine/StateManager.swift
index 19e6ad25f..fb2360288 100644
--- a/Sources/Core/Tracker/StateManager.swift
+++ b/Sources/Core/StateMachine/StateManager.swift
@@ -21,7 +21,7 @@
 
 import Foundation
 
-class StateManager: NSObject {
+class StateManager {
     private var identifierToStateMachine: [String : StateMachineProtocol] = [:]
     private var eventSchemaToStateMachine: [String : [StateMachineProtocol]] = [:]
     private var eventSchemaToEntitiesGenerator: [String : [StateMachineProtocol]] = [:]
@@ -73,14 +73,15 @@ class StateManager: NSObject {
             stateMachine: stateMachine)
         return true
     }
-    
+
     func trackerState(forProcessedEvent event: Event) -> TrackerStateSnapshot? {
         objc_sync_enter(self)
         defer { objc_sync_exit(self) }
+
         if let sdEvent = event as? SelfDescribingAbstract {
             var stateMachines = Array(eventSchemaToStateMachine[sdEvent.schema] ?? [])
             stateMachines.append(contentsOf: eventSchemaToStateMachine["*"] ?? [])
-            
+
             for stateMachine in stateMachines {
                 let previousStateFuture = trackerState.stateFuture(withIdentifier: stateMachine.identifier)
                 let currentStateFuture = StateFuture(
@@ -106,15 +107,15 @@ class StateManager: NSObject {
         return trackerState.snapshot()
     }
 
-    func entities(forProcessedEvent event: InspectableEvent) -> [SelfDescribingJson] {
+    func entities(forProcessedEvent event: InspectableEvent & StateMachineEvent) -> [SelfDescribingJson] {
         objc_sync_enter(self)
         defer { objc_sync_exit(self) }
-        
-        guard let schema = event.schema else { return [] }
+
+        guard let schema = event.schema ?? event.eventName else { return [] }
         var result: [SelfDescribingJson] = []
         var stateMachines = eventSchemaToEntitiesGenerator[schema] ?? []
         stateMachines.append(contentsOf: eventSchemaToEntitiesGenerator["*"] ?? [])
-        
+
         for stateMachine in stateMachines {
             let state = event.state.state(withIdentifier: stateMachine.identifier)
             if let entities = stateMachine.entities(from: event, state: state) {
@@ -124,10 +125,10 @@ class StateManager: NSObject {
         return result
     }
 
-    func addPayloadValues(to event: InspectableEvent) -> Bool {
+    func addPayloadValues(to event: InspectableEvent & StateMachineEvent) -> Bool {
         objc_sync_enter(self)
         defer { objc_sync_exit(self) }
-        
+
         guard let schema = event.schema else { return true }
         var failures = 0
         var stateMachines = eventSchemaToPayloadUpdater[schema] ?? []
@@ -145,7 +146,7 @@ class StateManager: NSObject {
 
     // MARK: - Private methods
 
-    func add(toSchemaRegistry schemaRegistry: inout [String : [StateMachineProtocol]], schemas: [String], stateMachine: StateMachineProtocol?) {
+    private func add(toSchemaRegistry schemaRegistry: inout [String : [StateMachineProtocol]], schemas: [String], stateMachine: StateMachineProtocol?) {
         for eventSchema in schemas {
             var array = schemaRegistry[eventSchema] ?? []
             if let stateMachine = stateMachine {
@@ -155,7 +156,7 @@ class StateManager: NSObject {
         }
     }
 
-    func remove(fromSchemaRegistry schemaRegistry: inout [String : [StateMachineProtocol]], schemas: [String], stateMachine: StateMachineProtocol) {
+    private func remove(fromSchemaRegistry schemaRegistry: inout [String : [StateMachineProtocol]], schemas: [String], stateMachine: StateMachineProtocol) {
         for eventSchema in schemas {
             var array = schemaRegistry[eventSchema]
             array?.removeAll { $0.identifier == stateMachine.identifier }
diff --git a/Sources/Core/Tracker/TrackerState.swift b/Sources/Core/StateMachine/TrackerState.swift
similarity index 97%
rename from Sources/Core/Tracker/TrackerState.swift
rename to Sources/Core/StateMachine/TrackerState.swift
index 3947c13b5..0f11bed40 100644
--- a/Sources/Core/Tracker/TrackerState.swift
+++ b/Sources/Core/StateMachine/TrackerState.swift
@@ -22,7 +22,7 @@
 import Foundation
 
 /// The global tracker state which collects all the state generated by the tracker state machines.
-class TrackerState: NSObject, TrackerStateSnapshot {
+class TrackerState: TrackerStateSnapshot {
     private var trackerState: [String: StateFuture] = [:]
 
     /// Set a future computable state with a specific state identifier
diff --git a/Sources/Snowplow/Tracker/TrackerStateSnapshot.swift b/Sources/Core/StateMachine/TrackerStateSnapshot.swift
similarity index 92%
rename from Sources/Snowplow/Tracker/TrackerStateSnapshot.swift
rename to Sources/Core/StateMachine/TrackerStateSnapshot.swift
index 1813af2e2..a352f99a1 100644
--- a/Sources/Snowplow/Tracker/TrackerStateSnapshot.swift
+++ b/Sources/Core/StateMachine/TrackerStateSnapshot.swift
@@ -21,12 +21,10 @@
 
 import Foundation
 
-@objc(SPTrackerStateSnapshot)
-public protocol TrackerStateSnapshot {
+protocol TrackerStateSnapshot {
     /// Get a computed state with a specific state identifier
-    @objc
     func state(withIdentifier stateIdentifier: String) -> State?
+
     /// Get a computed state with a specific state machine
-    @objc
     func state(withStateMachine stateMachine: StateMachineProtocol) -> State?
 }
diff --git a/Sources/Core/Tracker/Tracker.swift b/Sources/Core/Tracker/Tracker.swift
index 0c3c93f81..b1a22651c 100644
--- a/Sources/Core/Tracker/Tracker.swift
+++ b/Sources/Core/Tracker/Tracker.swift
@@ -521,14 +521,14 @@ class Tracker: NSObject {
         } else {
             addSelfDescribingProperties(to: payload, event: event)
         }
-        var contexts = event.contexts
-        addBasicContexts(toContexts: &contexts, event: event)
-        addGlobalContexts(toContexts: &contexts, event: event)
-        addStateMachineEntities(toContexts: &contexts, event: event)
-        wrapContexts(contexts, to: payload)
+        var entities = event.entities
+        addBasicContexts(toContexts: &entities, event: event)
+        addGlobalContexts(toContexts: &entities, event: event)
+        addStateMachineEntities(toContexts: &entities, event: event)
+        wrapContexts(entities, to: payload)
         if !event.isPrimitive {
             // TODO: To remove when Atomic table refactoring is finished
-            workaround(forCampaignAttributionEnrichment: payload, event: event, contexts: &contexts)
+            workaround(forCampaignAttributionEnrichment: payload, event: event, contexts: &entities)
         }
         return payload
     }
@@ -653,7 +653,7 @@ class Tracker: NSObject {
         }
     }
 
-    func addStateMachineEntities(toContexts contexts: inout [SelfDescribingJson], event: InspectableEvent) {
+    func addStateMachineEntities(toContexts contexts: inout [SelfDescribingJson], event: InspectableEvent & StateMachineEvent) {
         let stateManagerEntities = stateManager.entities(forProcessedEvent: event)
         contexts.append(contentsOf: stateManagerEntities)
     }
diff --git a/Sources/Core/Tracker/TrackerEvent.swift b/Sources/Core/Tracker/TrackerEvent.swift
index 5754f94b0..133eea6d0 100644
--- a/Sources/Core/Tracker/TrackerEvent.swift
+++ b/Sources/Core/Tracker/TrackerEvent.swift
@@ -21,54 +21,50 @@
 
 import Foundation
 
-class TrackerEvent : InspectableEvent {
+class TrackerEvent : InspectableEvent, StateMachineEvent {
+    /// Self-describing event data or primitive event payload
+    private(set) var payload: [String: Any]
+    
+    /// Self-describing event schema
+    private(set) var schema: String?
+    
+    /// Primitive event name
+    private(set) var eventName: String?
+    
+    /// Event ID
+    private(set) var eventId: UUID
+    
+    /// List of custom as well as automatically assigned context entities
+    private(set) var entities: [SelfDescribingJson]
+    
+    private(set) var state: TrackerStateSnapshot
     
-    private var _payload: [String: Any]
-    var payload: [String: Any] {
-        get { return _payload }
-        set { _payload = newValue }
-    }
-    private var _schema: String?
-    var schema: String? {
-        get { return _schema }
-        set { _schema = newValue }
-    }
-    private var _eventName: String?
-    var eventName: String? {
-        get { return _eventName }
-        set { _eventName = newValue }
-    }
-    var eventId: UUID
     var timestamp: Int64
+    
     var trueTimestamp: Date?
-    var contexts: [SelfDescribingJson]
-    private var _state: TrackerStateSnapshot
-    var state: TrackerStateSnapshot {
-        get { return _state }
-        set { _state = newValue }
-    }
-
-    var isPrimitive: Bool
-    var isService: Bool
+    
+    private(set) var isPrimitive: Bool
+    
+    private(set) var isService: Bool
     
     init(event: Event, state: TrackerStateSnapshot? = nil) {
         eventId = UUID()
         timestamp = Int64(Date().timeIntervalSince1970 * 1000)
         trueTimestamp = event.trueTimestamp
-        contexts = event.contexts
-        _payload = event.payload
-        _state = state ?? TrackerState()
-
+        entities = event.entities
+        payload = event.payload
+        self.state = state ?? TrackerState()
+        
         isService = (event is TrackerError)
         if let abstractEvent = event as? PrimitiveAbstract {
-            _eventName = abstractEvent.eventName
+            eventName = abstractEvent.eventName
             isPrimitive = true
         } else {
-            _schema = (event as! SelfDescribingAbstract).schema
+            schema = (event as! SelfDescribingAbstract).schema
             isPrimitive = false
         }
     }
-
+    
     func addPayloadValues(_ payload: [String : Any]) -> Bool {
         var result = true
         for (key, obj) in payload {
diff --git a/Sources/Core/Tracker/WebViewMessageHandler.swift b/Sources/Core/Tracker/WebViewMessageHandler.swift
index 4a3a9c78f..a8e3281fb 100644
--- a/Sources/Core/Tracker/WebViewMessageHandler.swift
+++ b/Sources/Core/Tracker/WebViewMessageHandler.swift
@@ -139,7 +139,7 @@ class WebViewMessageHandler: NSObject, WKScriptMessageHandler {
     }
 
     func track(_ event: Event, withContext context: [[AnyHashable : Any]], andTrackers trackers: [String]) {
-        event.contexts = parseContext(context)
+        event.entities = parseContext(context)
         if trackers.count > 0 {
             for namespace in trackers {
                 if let tracker = Snowplow.tracker(namespace: namespace) {
diff --git a/Sources/Snowplow/Events/ConsentGranted.swift b/Sources/Snowplow/Events/ConsentGranted.swift
index 297faf0c9..488a63c9a 100644
--- a/Sources/Snowplow/Events/ConsentGranted.swift
+++ b/Sources/Snowplow/Events/ConsentGranted.swift
@@ -90,6 +90,6 @@ public class ConsentGranted: SelfDescribingAbstract {
     }
 
     override func beginProcessing(withTracker tracker: Tracker) {
-        contexts.append(contentsOf: allDocuments) // TODO: Only the user should modify the public contexts property
+        entities.append(contentsOf: allDocuments) // TODO: Only the user should modify the public contexts property
     }
 }
diff --git a/Sources/Snowplow/Events/ConsentWithdrawn.swift b/Sources/Snowplow/Events/ConsentWithdrawn.swift
index 272999bf9..25bc7fa87 100644
--- a/Sources/Snowplow/Events/ConsentWithdrawn.swift
+++ b/Sources/Snowplow/Events/ConsentWithdrawn.swift
@@ -78,6 +78,6 @@ public class ConsentWithdrawn: SelfDescribingAbstract {
     }
 
     override func beginProcessing(withTracker tracker: Tracker) {
-        contexts.append(contentsOf: allDocuments)
+        entities.append(contentsOf: allDocuments)
     }
 }
diff --git a/Sources/Snowplow/Events/EventBase.swift b/Sources/Snowplow/Events/EventBase.swift
index f038b89a3..9627dbe27 100644
--- a/Sources/Snowplow/Events/EventBase.swift
+++ b/Sources/Snowplow/Events/EventBase.swift
@@ -28,9 +28,17 @@ public class Event: NSObject {
     @objc
     public var trueTimestamp: Date?
     
-    /// The contexts attached to the event.
+    /// The context entities attached to the event.
     @objc
-    public var contexts: [SelfDescribingJson] = []
+    public var entities: [SelfDescribingJson] = []
+    
+    /// The context entities attached to the event.
+    @objc
+    @available(*, deprecated, renamed: "entities")
+    public var contexts: [SelfDescribingJson] {
+        get { return entities }
+        set { entities = newValue }
+    }
     
     /// The payload of the event.
     var payload: [String : Any] {
diff --git a/Sources/Snowplow/Tracker/InspectableEvent.swift b/Sources/Snowplow/Tracker/InspectableEvent.swift
index d61e7e5a9..e4f5f50c4 100644
--- a/Sources/Snowplow/Tracker/InspectableEvent.swift
+++ b/Sources/Snowplow/Tracker/InspectableEvent.swift
@@ -27,18 +27,16 @@ public protocol InspectableEvent {
     /// The schema of the event
     @objc
     var schema: String? { get }
+
     /// The name of the event
     @objc
     var eventName: String? { get }
+
     /// The payload of the event
     @objc
     var payload: [String : Any] { get }
-    /// The tracker state at the time the event was sent.
-    @objc
-    var state: TrackerStateSnapshot { get }
-    /// Add payload values to the event.
-    /// - Parameter payload: Map of values to add to the event payload.
-    /// - Returns: Whether or not the values have been successfully added to the event payload.
+
+    /// The list of context entities
     @objc
-    func addPayloadValues(_ payload: [String : Any]) -> Bool
+    var entities: [SelfDescribingJson] { get }
 }
diff --git a/Sources/Snowplow/Tracker/View.swift b/Sources/Snowplow/Tracker/View.swift
index c326d7701..c9c7b6dc4 100644
--- a/Sources/Snowplow/Tracker/View.swift
+++ b/Sources/Snowplow/Tracker/View.swift
@@ -30,10 +30,10 @@ public extension View {
     /// - Parameter contexts: Context entities to attach to the event
     /// - Returns: View with the attached modifier to track screen views
     func snowplowScreen(name: String,
-                        contexts: [(schema: String, data: [String : Any])] = [],
+                        entities: [(schema: String, data: [String : Any])] = [],
                         trackerNamespace: String? = nil) -> some View {
         return modifier(ScreenViewModifier(name: name,
-                                           contexts: contexts,
+                                           entities: entities,
                                            trackerNamespace: trackerNamespace))
     }
 }
diff --git a/Tests/TestRequest.swift b/Tests/TestRequest.swift
index b71f5f64a..b44c9b914 100644
--- a/Tests/TestRequest.swift
+++ b/Tests/TestRequest.swift
@@ -160,7 +160,7 @@ class TestRequest: XCTestCase, RequestCallback {
         event.label = "DemoLabel"
         event.property = "DemoProperty"
         event.value = NSNumber(value: 5)
-        event.contexts = customContext()
+        event.entities = customContext()
         _ = tracker_.track(event)
         return 1
     }
@@ -173,7 +173,7 @@ class TestRequest: XCTestCase, RequestCallback {
             schema: "iglu:com.acme_company/demo_ios_event/jsonschema/1-0-0",
             andDictionary: data)
         let event = SelfDescribing(eventData: sdj)
-        event.contexts = customContext()
+        event.entities = customContext()
         _ = tracker_.track(event)
         return 1
     }
@@ -182,14 +182,14 @@ class TestRequest: XCTestCase, RequestCallback {
         let event = PageView(pageUrl: "DemoPageUrl")
         event.pageTitle = "DemoPageTitle"
         event.referrer = "DemoPageReferrer"
-        event.contexts = customContext()
+        event.entities = customContext()
         _ = tracker_.track(event)
         return 1
     }
 
     func trackScreenView(with tracker_: Tracker) -> Int {
         let event = ScreenView(name: "DemoScreenName", screenId: nil)
-        event.contexts = customContext()
+        event.entities = customContext()
         _ = tracker_.track(event)
         return 1
     }
@@ -197,7 +197,7 @@ class TestRequest: XCTestCase, RequestCallback {
     func trackTimingWithCategory(with tracker_: Tracker) -> Int {
         let event = Timing(category: "DemoTimingCategory", variable: "DemoTimingVariable", timing: 5)
         event.label = "DemoTimingLabel"
-        event.contexts = customContext()
+        event.entities = customContext()
         _ = tracker_.track(event)
         return 1
     }
@@ -209,7 +209,7 @@ class TestRequest: XCTestCase, RequestCallback {
         item.name = "DemoItemName"
         item.category = "DemoItemCategory"
         item.currency = "USD"
-        item.contexts = customContext()
+        item.entities = customContext()
 
         let event = Ecommerce(orderId: transactionID, totalValue: 350, items: [item])
         event.affiliation = "DemoTranAffiliation"
@@ -219,7 +219,7 @@ class TestRequest: XCTestCase, RequestCallback {
         event.state = "Massachusetts"
         event.country = "USA"
         event.currency = "USD"
-        event.contexts = customContext()
+        event.entities = customContext()
         _ = tracker_.track(event)
         return 2
     }
diff --git a/Tests/TestScreenViewModifier.swift b/Tests/TestScreenViewModifier.swift
index 0d48f7422..f9eab5e8c 100644
--- a/Tests/TestScreenViewModifier.swift
+++ b/Tests/TestScreenViewModifier.swift
@@ -47,7 +47,7 @@ class TestScreenViewModifier: XCTestCase {
         if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, *) {
             let modifier = ScreenViewModifier(
                 name: "screen-1",
-                contexts: [
+                entities: [
                     (
                         schema: "iglu:com.snowplowanalytics.iglu/anything-a/jsonschema/1-0-0",
                         data: [

From 4c3b6ad9bd5d1c6179372cdf4e34c9920615745c Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Wed, 1 Feb 2023 16:52:57 +0100
Subject: [PATCH 12/19] Add ability to provide custom tracker plugins to
 inspect and enrich tracked events (close #750)

PR #751
---
 .../GlobalContextPluginConfiguration.swift    |  51 +++++
 .../GlobalContextsControllerImpl.swift        |  39 +++-
 .../ScreenStateMachine.swift                  |   7 +
 .../StateMachine/DeepLinkStateMachine.swift   |   7 +
 .../StateMachine/LifecycleStateMachine.swift  |   7 +
 .../StateMachine/PluginStateMachine.swift     |  91 ++++++++
 .../StateMachine/StateMachineProtocol.swift   |   4 +
 Sources/Core/StateMachine/StateManager.swift  |  26 +++
 .../Core/Tracker/PluginsControllerImpl.swift  |  36 ++++
 Sources/Core/Tracker/ServiceProvider.swift    |  46 ++--
 .../Tracker/ServiceProviderProtocol.swift     |   4 +
 Sources/Core/Tracker/Tracker.swift            | 190 ++++++-----------
 .../Core/Tracker/TrackerControllerImpl.swift  |   4 +
 Sources/Core/Tracker/TrackerEvent.swift       |  42 ++++
 .../GlobalContextsConfiguration.swift         |   9 +
 .../Configurations/PluginConfiguration.swift  | 163 +++++++++++++++
 .../Controllers/PluginsController.swift       |  32 +++
 .../Controllers/TrackerController.swift       |   3 +
 .../Global Contexts/TestGlobalContexts.swift  |  96 ++++-----
 .../Integration/TestTrackEventsToMicro.swift  |   7 +-
 Tests/TestPlugins.swift                       | 197 ++++++++++++++++++
 Tests/TestScreenViewModifier.swift            |  56 ++---
 Tests/TestServiceProvider.swift               |   4 +-
 Tests/TestStateManager.swift                  |   7 +
 24 files changed, 896 insertions(+), 232 deletions(-)
 create mode 100644 Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
 create mode 100644 Sources/Core/StateMachine/PluginStateMachine.swift
 create mode 100644 Sources/Core/Tracker/PluginsControllerImpl.swift
 create mode 100644 Sources/Snowplow/Configurations/PluginConfiguration.swift
 create mode 100644 Sources/Snowplow/Controllers/PluginsController.swift
 create mode 100644 Tests/TestPlugins.swift

diff --git a/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
new file mode 100644
index 000000000..b52ad835b
--- /dev/null
+++ b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
@@ -0,0 +1,51 @@
+//
+//  GlobalContextPluginConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class GlobalContextPluginConfiguration: Configuration, PluginConfigurationProtocol {
+    private(set) var identifier: String
+    private(set) var globalContext: GlobalContext
+    private(set) var afterTrackConfiguration: PluginAfterTrackConfiguration? = nil
+    private(set) var entitiesConfiguration: PluginEntitiesConfiguration?
+
+    init(identifier: String, globalContext: GlobalContext) {
+        self.identifier = identifier
+        self.globalContext = globalContext
+        self.entitiesConfiguration = PluginEntitiesConfiguration(closure: globalContext.contexts)
+    }
+
+    // MARK: - NSCopying
+
+    override func copy(with zone: NSZone? = nil) -> Any {
+        let copy = GlobalContextPluginConfiguration(
+            identifier: identifier,
+            globalContext: globalContext
+        )
+        return copy
+    }
+
+    // MARK: - NSCoding (No coding possible as we can't encode and decode the contextGenerators)
+
+    required convenience public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+}
diff --git a/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift b/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
index dccc68f4f..c6c5c4eb5 100644
--- a/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
+++ b/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
@@ -25,28 +25,53 @@ class GlobalContextsControllerImpl: Controller, GlobalContextsController {
 
     var contextGenerators: [String : GlobalContext] {
         get {
-            return tracker.globalContextGenerators
+            var contexts: [String : GlobalContext] = [:]
+            for configuration in pluginConfigurations {
+                contexts[configuration.identifier] = configuration.globalContext
+            }
+            return contexts
         }
         set {
-            tracker.globalContextGenerators = newValue
+            for configuration in pluginConfigurations {
+                serviceProvider.pluginsController.remove(identifier: configuration.identifier)
+            }
+            for (identifier, globalContext) in newValue {
+                let plugin = GlobalContextPluginConfiguration(identifier: identifier,
+                                                              globalContext: globalContext)
+                serviceProvider.pluginsController.add(plugin: plugin)
+            }
         }
     }
 
     func add(tag: String, contextGenerator generator: GlobalContext) -> Bool {
-        return tracker.add(generator, tag: tag)
+        if tags.contains(tag) {
+            return false
+        }
+        let plugin = GlobalContextPluginConfiguration(identifier: tag,
+                                                      globalContext: generator)
+        serviceProvider.pluginsController.add(plugin: plugin)
+        return true
     }
 
     func remove(tag: String) -> GlobalContext? {
-        return tracker.removeGlobalContext(tag)
+        let configuration = pluginConfigurations.first { configuration in
+            configuration.identifier == tag
+        }
+        serviceProvider.pluginsController.remove(identifier: tag)
+        return configuration?.globalContext
     }
 
     var tags: [String] {
-        return tracker.globalContextTags
+        return pluginConfigurations.map { $0.identifier }
     }
 
     // MARK: - Private methods
 
-    private var tracker: Tracker {
-        return serviceProvider.tracker
+    private var pluginConfigurations: [GlobalContextPluginConfiguration] {
+        return serviceProvider.pluginConfigurations.filter { configuration in
+            configuration is GlobalContextPluginConfiguration
+        }.map { configuration in
+            configuration as! GlobalContextPluginConfiguration
+        }
     }
 }
diff --git a/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
index dec9e1e4c..0c4f21bae 100644
--- a/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
@@ -37,6 +37,10 @@ class ScreenStateMachine: StateMachineProtocol {
         return [kSPScreenViewSchema]
     }
 
+    var subscribedEventSchemasForAfterTrackCallback: [String] {
+        return []
+    }
+
     func transition(from event: Event, state currentState: State?) -> State? {
         if let screenView = event as? ScreenView {
             let newState: ScreenState = screenState(from: screenView)
@@ -84,4 +88,7 @@ class ScreenStateMachine: StateMachineProtocol {
         }
         return nil
     }
+    
+    func afterTrack(event: InspectableEvent) {
+    }
 }
diff --git a/Sources/Core/StateMachine/DeepLinkStateMachine.swift b/Sources/Core/StateMachine/DeepLinkStateMachine.swift
index abbabf105..c903c956a 100644
--- a/Sources/Core/StateMachine/DeepLinkStateMachine.swift
+++ b/Sources/Core/StateMachine/DeepLinkStateMachine.swift
@@ -49,6 +49,10 @@ class DeepLinkStateMachine: StateMachineProtocol {
         return []
     }
 
+    var subscribedEventSchemasForAfterTrackCallback: [String] {
+        return []
+    }
+
     func transition(from event: Event, state: State?) -> State? {
         if let dlEvent = event as? DeepLinkReceived {
             return DeepLinkState(url: dlEvent.url, referrer: dlEvent.referrer)
@@ -80,4 +84,7 @@ class DeepLinkStateMachine: StateMachineProtocol {
     func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]? {
         return nil
     }
+    
+    func afterTrack(event: InspectableEvent) {
+    }
 }
diff --git a/Sources/Core/StateMachine/LifecycleStateMachine.swift b/Sources/Core/StateMachine/LifecycleStateMachine.swift
index c459068d8..616199a48 100644
--- a/Sources/Core/StateMachine/LifecycleStateMachine.swift
+++ b/Sources/Core/StateMachine/LifecycleStateMachine.swift
@@ -63,4 +63,11 @@ class LifecycleStateMachine: StateMachineProtocol {
     func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]? {
         return nil
     }
+
+    var subscribedEventSchemasForAfterTrackCallback: [String] {
+        return []
+    }
+
+    func afterTrack(event: InspectableEvent) {
+    }
 }
diff --git a/Sources/Core/StateMachine/PluginStateMachine.swift b/Sources/Core/StateMachine/PluginStateMachine.swift
new file mode 100644
index 000000000..07e966745
--- /dev/null
+++ b/Sources/Core/StateMachine/PluginStateMachine.swift
@@ -0,0 +1,91 @@
+//
+//  PluginStateMachine.swift
+//  Snowplow
+//
+// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+// This program is licensed to you under the Apache License Version 2.0,
+// and you may not use this file except in compliance with the Apache License
+// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+// http://www.apache.org/licenses/LICENSE-2.0.
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the Apache License Version 2.0 is distributed on
+// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the Apache License Version 2.0 for the specific
+// language governing permissions and limitations there under.
+//
+// License: Apache License Version 2.0
+//
+
+import Foundation
+
+typealias EntitiesConfiguration = (schemas: [String]?, closure: (InspectableEvent) -> ([SelfDescribingJson]))
+typealias AfterTrackConfiguration = (schemas: [String]?, closure: (InspectableEvent) -> ())
+
+class PluginStateMachine: StateMachineProtocol {
+    var identifier: String
+    private var entitiesConfiguration: EntitiesConfiguration?
+    private var afterTrackConfiguration: AfterTrackConfiguration?
+
+    init(
+        identifier: String,
+        entitiesConfiguration: EntitiesConfiguration?,
+        afterTrackConfiguration: AfterTrackConfiguration?
+    ) {
+        self.identifier = identifier
+        self.entitiesConfiguration = entitiesConfiguration
+        self.afterTrackConfiguration = afterTrackConfiguration
+    }
+
+    var subscribedEventSchemasForTransitions: [String] {
+        return []
+    }
+
+    func transition(from event: Event, state currentState: State?) -> State? {
+        return nil
+    }
+
+    var subscribedEventSchemasForEntitiesGeneration: [String] {
+        if let entitiesConfiguration = entitiesConfiguration {
+            if let schemas = entitiesConfiguration.schemas {
+                return schemas
+            } else {
+                return ["*"]
+            }
+        }
+        return []
+    }
+
+    func entities(from event: InspectableEvent, state: State?) -> [SelfDescribingJson]? {
+        if let entitiesConfiguration = entitiesConfiguration {
+            return entitiesConfiguration.closure(event)
+        }
+        return nil
+    }
+
+    var subscribedEventSchemasForPayloadUpdating: [String] {
+        return []
+    }
+
+    func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]? {
+        return nil
+    }
+
+    var subscribedEventSchemasForAfterTrackCallback: [String] {
+        if let afterTrackConfiguration = afterTrackConfiguration {
+            if let schemas = afterTrackConfiguration.schemas {
+                return schemas
+            } else {
+                return ["*"]
+            }
+        }
+        return []
+    }
+
+    func afterTrack(event: InspectableEvent) {
+        if let afterTrackConfiguration = afterTrackConfiguration {
+            afterTrackConfiguration.closure(event)
+        }
+    }
+}
diff --git a/Sources/Core/StateMachine/StateMachineProtocol.swift b/Sources/Core/StateMachine/StateMachineProtocol.swift
index ce18ff37a..dbd844f6a 100644
--- a/Sources/Core/StateMachine/StateMachineProtocol.swift
+++ b/Sources/Core/StateMachine/StateMachineProtocol.swift
@@ -26,6 +26,7 @@ protocol StateMachineProtocol {
     var subscribedEventSchemasForTransitions: [String] { get }
     var subscribedEventSchemasForEntitiesGeneration: [String] { get }
     var subscribedEventSchemasForPayloadUpdating: [String] { get }
+    var subscribedEventSchemasForAfterTrackCallback: [String] { get }
     
     /// Only available for self-describing events (inheriting from SelfDescribingAbstract)
     func transition(from event: Event, state: State?) -> State?
@@ -35,4 +36,7 @@ protocol StateMachineProtocol {
     
     /// Only available for self-describing events (inheriting from SelfDescribingAbstract)
     func payloadValues(from event: InspectableEvent, state: State?) -> [String : Any]?
+    
+    /// Available for both self-describing and primitive events (when using `*` as the schema)
+    func afterTrack(event: InspectableEvent)
 }
diff --git a/Sources/Core/StateMachine/StateManager.swift b/Sources/Core/StateMachine/StateManager.swift
index fb2360288..a5f4c5bb0 100644
--- a/Sources/Core/StateMachine/StateManager.swift
+++ b/Sources/Core/StateMachine/StateManager.swift
@@ -26,6 +26,7 @@ class StateManager {
     private var eventSchemaToStateMachine: [String : [StateMachineProtocol]] = [:]
     private var eventSchemaToEntitiesGenerator: [String : [StateMachineProtocol]] = [:]
     private var eventSchemaToPayloadUpdater: [String : [StateMachineProtocol]] = [:]
+    private var eventSchemaToAfterTrackCallback: [String : [StateMachineProtocol]] = [:]
     private var trackerState = TrackerState()
 
     func addOrReplaceStateMachine(_ stateMachine: StateMachineProtocol) {
@@ -51,6 +52,10 @@ class StateManager {
             toSchemaRegistry: &eventSchemaToPayloadUpdater,
             schemas: stateMachine.subscribedEventSchemasForPayloadUpdating,
             stateMachine: stateMachine)
+        add(
+            toSchemaRegistry: &eventSchemaToAfterTrackCallback,
+            schemas: stateMachine.subscribedEventSchemasForAfterTrackCallback,
+            stateMachine: stateMachine)
     }
 
     func removeStateMachine(_ stateMachineIdentifier: String) -> Bool {
@@ -71,6 +76,10 @@ class StateManager {
             fromSchemaRegistry: &eventSchemaToPayloadUpdater,
             schemas: stateMachine.subscribedEventSchemasForPayloadUpdating,
             stateMachine: stateMachine)
+        remove(
+            fromSchemaRegistry: &eventSchemaToAfterTrackCallback,
+            schemas: stateMachine.subscribedEventSchemasForAfterTrackCallback,
+            stateMachine: stateMachine)
         return true
     }
 
@@ -144,6 +153,23 @@ class StateManager {
         return failures == 0
     }
 
+    func afterTrack(event: InspectableEvent & StateMachineEvent) {
+        guard let schema = event.schema ?? event.eventName else { return }
+
+        objc_sync_enter(self)
+        var stateMachines = eventSchemaToAfterTrackCallback[schema] ?? []
+        stateMachines.append(contentsOf: eventSchemaToAfterTrackCallback["*"] ?? [])
+        objc_sync_exit(self)
+
+        if !stateMachines.isEmpty {
+            DispatchQueue.global(qos: .default).async {
+                for stateMachine in stateMachines {
+                    stateMachine.afterTrack(event: event)
+                }
+            }
+        }
+    }
+
     // MARK: - Private methods
 
     private func add(toSchemaRegistry schemaRegistry: inout [String : [StateMachineProtocol]], schemas: [String], stateMachine: StateMachineProtocol?) {
diff --git a/Sources/Core/Tracker/PluginsControllerImpl.swift b/Sources/Core/Tracker/PluginsControllerImpl.swift
new file mode 100644
index 000000000..0451fad44
--- /dev/null
+++ b/Sources/Core/Tracker/PluginsControllerImpl.swift
@@ -0,0 +1,36 @@
+//
+//  PluginsControllerImpl.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+class PluginsControllerImpl: Controller, PluginsController {
+    var identifiers: [String] {
+        return serviceProvider.pluginConfigurations.map { $0.identifier }
+    }
+    
+    func add(plugin: PluginConfigurationProtocol) {
+        serviceProvider.addPlugin(plugin: plugin)
+    }
+
+    func remove(identifier: String) {
+        serviceProvider.removePlugin(identifier: identifier)
+    }
+}
diff --git a/Sources/Core/Tracker/ServiceProvider.swift b/Sources/Core/Tracker/ServiceProvider.swift
index f78d274d5..5a7ab5678 100644
--- a/Sources/Core/Tracker/ServiceProvider.swift
+++ b/Sources/Core/Tracker/ServiceProvider.swift
@@ -85,14 +85,8 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
         return gdprController
     }
 
-    private var _globalContextsController: GlobalContextsControllerImpl?
     var globalContextsController: GlobalContextsControllerImpl {
-        if let controller = _globalContextsController {
-            return controller
-        }
-        let globalContextsController = makeGlobalContextsController()
-        _globalContextsController = globalContextsController
-        return globalContextsController
+        return GlobalContextsControllerImpl(serviceProvider: self)
     }
 
     private var _subjectController: SubjectControllerImpl?
@@ -110,9 +104,13 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
         _networkController = networkController
         return networkController
     }
-
+    
+    var pluginsController: PluginsControllerImpl {
+        return PluginsControllerImpl(serviceProvider: self)
+    }
+    
     // Original configurations
-    private var globalContextConfiguration: GlobalContextsConfiguration?
+    private(set) var pluginConfigurations: [PluginConfigurationProtocol] = []
 
     // Configuration updates
     private(set) var networkConfigurationUpdate = NetworkConfigurationUpdate()
@@ -169,7 +167,11 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
             } else if let configuration = configuration as? GDPRConfiguration {
                 gdprConfigurationUpdate.sourceConfig = configuration
             } else if let configuration = configuration as? GlobalContextsConfiguration {
-                globalContextConfiguration = configuration
+                for plugin in configuration.toPluginConfigurations() {
+                    pluginConfigurations.append(plugin)
+                }
+            } else if let configuration = configuration as? PluginConfigurationProtocol {
+                pluginConfigurations.append(configuration)
             }
         }
     }
@@ -189,7 +191,6 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
         _sessionController = nil
         _emitterController = nil
         _gdprController = nil
-        _globalContextsController = nil
         _subjectController = nil
         _networkController = nil
     }
@@ -266,7 +267,6 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
         
         let trackerConfig = trackerConfigurationUpdate
         let sessionConfig = sessionConfigurationUpdate
-        let gcConfig = globalContextConfiguration
         let gdprConfig = gdprConfigurationUpdate
         
         let tracker = Tracker(
@@ -295,9 +295,6 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
             tracker.trackerDiagnostic = trackerConfig.diagnosticAutotracking
             tracker.userAnonymisation = trackerConfig.userAnonymisation
             tracker.advertisingIdentifierRetriever = trackerConfig.advertisingIdentifierRetriever
-            if let config = gcConfig {
-                tracker.globalContextGenerators = config.contextGenerators
-            }
             if gdprConfig.sourceConfig != nil {
                 tracker.gdprContext = GDPRContext(
                     basis: gdprConfig.basisForProcessing,
@@ -305,6 +302,10 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
                     documentVersion: gdprConfig.documentVersion,
                     documentDescription: gdprConfig.documentDescription)
             }
+
+            for plugin in pluginConfigurations {
+                tracker.addOrReplace(stateMachine: plugin.toStateMachine())
+            }
         }
         
         if trackerConfigurationUpdate.isPaused {
@@ -341,10 +342,6 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
         return controller
     }
 
-    func makeGlobalContextsController() -> GlobalContextsControllerImpl {
-        return GlobalContextsControllerImpl(serviceProvider: self)
-    }
-
     func makeSubjectController() -> SubjectControllerImpl {
         return SubjectControllerImpl(serviceProvider: self)
     }
@@ -352,4 +349,15 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
     func makeNetworkController() -> NetworkControllerImpl {
         return NetworkControllerImpl(serviceProvider: self)
     }
+    
+    func addPlugin(plugin: PluginConfigurationProtocol) {
+        removePlugin(identifier: plugin.identifier)
+        pluginConfigurations.append(plugin)
+        tracker.addOrReplace(stateMachine: plugin.toStateMachine())
+    }
+
+    func removePlugin(identifier: String) {
+        pluginConfigurations = pluginConfigurations.filter { $0.identifier != identifier }
+        tracker.remove(stateMachineIdentifier: identifier)
+    }
 }
diff --git a/Sources/Core/Tracker/ServiceProviderProtocol.swift b/Sources/Core/Tracker/ServiceProviderProtocol.swift
index e445fd7b5..f97c2e573 100644
--- a/Sources/Core/Tracker/ServiceProviderProtocol.swift
+++ b/Sources/Core/Tracker/ServiceProviderProtocol.swift
@@ -34,10 +34,14 @@ protocol ServiceProviderProtocol: AnyObject {
     var globalContextsController: GlobalContextsControllerImpl { get }
     var subjectController: SubjectControllerImpl { get }
     var sessionController: SessionControllerImpl { get }
+    var pluginsController: PluginsControllerImpl { get }
     var networkConfigurationUpdate: NetworkConfigurationUpdate { get }
     var trackerConfigurationUpdate: TrackerConfigurationUpdate { get }
     var emitterConfigurationUpdate: EmitterConfigurationUpdate { get }
     var subjectConfigurationUpdate: SubjectConfigurationUpdate { get }
     var sessionConfigurationUpdate: SessionConfigurationUpdate { get }
     var gdprConfigurationUpdate: GDPRConfigurationUpdate { get }
+    var pluginConfigurations: [PluginConfigurationProtocol] { get }
+    func addPlugin(plugin: PluginConfigurationProtocol)
+    func removePlugin(identifier: String)
 }
diff --git a/Sources/Core/Tracker/Tracker.swift b/Sources/Core/Tracker/Tracker.swift
index b1a22651c..06177b082 100644
--- a/Sources/Core/Tracker/Tracker.swift
+++ b/Sources/Core/Tracker/Tracker.swift
@@ -58,7 +58,6 @@ class Tracker: NSObject {
     private(set) var previousScreenState: ScreenState?
     /// Current screen view state.
     private(set) var currentScreenState: ScreenState?
-    /// List of tags associated to global contexts.
     
     private var trackerData: [String : String]? = nil
     func setTrackerData() {
@@ -187,7 +186,7 @@ class Tracker: NSObject {
             objc_sync_enter(self)
             _deepLinkContext = deepLinkContext
             if deepLinkContext {
-                stateManager.addOrReplaceStateMachine(DeepLinkStateMachine())
+                addOrReplace(stateMachine: DeepLinkStateMachine())
             } else {
                 _ = stateManager.removeStateMachine(DeepLinkStateMachine.identifier)
             }
@@ -204,7 +203,7 @@ class Tracker: NSObject {
             objc_sync_enter(self)
             _screenContext = screenContext
             if screenContext {
-                stateManager.addOrReplaceStateMachine(ScreenStateMachine())
+                addOrReplace(stateMachine: ScreenStateMachine())
             } else {
                 _ = stateManager.removeStateMachine(ScreenStateMachine.identifier)
             }
@@ -253,7 +252,7 @@ class Tracker: NSObject {
             objc_sync_enter(self)
             _lifecycleEvents = lifecycleEvents
             if lifecycleEvents {
-                stateManager.addOrReplaceStateMachine(LifecycleStateMachine())
+                addOrReplace(stateMachine: LifecycleStateMachine())
             } else {
                 _ = stateManager.removeStateMachine(LifecycleStateMachine.identifier)
             }
@@ -278,12 +277,6 @@ class Tracker: NSObject {
         }
     }
 
-    var globalContextTags: [String] {
-        return Array(globalContextGenerators.keys)
-    }
-    /// Dictionary of global contexts generators.
-    var globalContextGenerators: [String : GlobalContext] = [:]
-
     /// GDPR context
     /// You can enable or disable the context by setting this property
     var gdprContext: GDPRContext?
@@ -374,37 +367,19 @@ class Tracker: NSObject {
         }
     }
     
-
-    /// Add new generator for global contexts associated with a string tag.
-    /// If the string tag has been already set the new global context is not assigned.
-    /// - Parameters:
-    ///   - generator: The global context generator.
-    ///   - tag: The tag associated to the global context.
-    /// - Returns: Weather the global context has been added.
-    func add(_ generator: GlobalContext, tag: String) -> Bool {
-        if (globalContextGenerators)[tag] != nil {
-            return false
-        }
-        (globalContextGenerators)[tag] = generator
-        return true
+    /// Add or replace state machine in the state manager
+    func addOrReplace(stateMachine: StateMachineProtocol) {
+        stateManager.addOrReplaceStateMachine(stateMachine)
     }
-
-    /// Remove the global context associated with the string tag.
-    /// If the string tag exist it returns the global context generator associated with.
-    /// - Parameter tag: The tag associated to the global context.
-    /// - Returns: The global context associated with the tag or `nil` in case of any entry with that string tag.
-    func removeGlobalContext(_ tag: String) -> GlobalContext? {
-        let toDelete = (globalContextGenerators)[tag]
-        if toDelete != nil {
-            globalContextGenerators.removeValue(forKey: tag)
-        }
-        return toDelete
+    
+    /// Remove stata machine from the state manager
+    func remove(stateMachineIdentifier: String) {
+        _ = stateManager.removeStateMachine(stateMachineIdentifier)
     }
 
-    /// Pauses all event tracking, storage and session checking.
-
     // MARK: - Extra Functions
 
+    /// Pauses all event tracking, storage and session checking.
     func pauseEventTracking() {
         dataCollection = false
         emitter.pauseTimer()
@@ -417,11 +392,8 @@ class Tracker: NSObject {
         session?.startChecker()
     }
 
-    /// Returns whether the application is in the background or foreground.
-    /// - Returns: Whether application is suspended.
-
     // MARK: - Notifications management
-
+    
     @objc func receiveScreenViewNotification(_ notification: Notification) {
         guard let name = notification.userInfo?["name"] as? String else { return }
         
@@ -468,14 +440,11 @@ class Tracker: NSObject {
         }
     }
 
-    // MARK: - Events tracking methods
+    // MARK: - Event Tracking Functions
 
     /// Tracks an event despite its specific type.
     /// - Parameter event: The event to track
     /// - Returns: The event ID or nil in case tracking is paused
-
-    // MARK: - Event Tracking Functions
-
     func track(_ event: Event) -> UUID? {
         if !dataCollection {
             return nil
@@ -493,81 +462,69 @@ class Tracker: NSObject {
         let stateSnapshot = stateManager.trackerState(forProcessedEvent: event)
         objc_sync_exit(self)
         let trackerEvent = TrackerEvent(event: event, state: stateSnapshot)
-        transformEvent(trackerEvent)
         let payload = self.payload(with: trackerEvent)
         emitter.addPayload(toBuffer: payload)
+        stateManager.afterTrack(event: trackerEvent)
         return trackerEvent.eventId
     }
 
-    func transformEvent(_ event: TrackerEvent) {
-        // Application_install event needs the timestamp to the real installation event.
-        if (event.schema == kSPApplicationInstallSchema) {
-            if let trueTimestamp = event.trueTimestamp {
-                event.timestamp = Int64(trueTimestamp.timeIntervalSince1970 * 1000)
-                event.trueTimestamp = nil
-            }
-        }
-        // Payload can be optionally updated with values based on internal state
-        let _ = stateManager.addPayloadValues(to: event)
-    }
-
     func payload(with event: TrackerEvent) -> Payload {
         let payload = Payload()
         payload.allowDiagnostic = !event.isService
 
+        // Payload properties
+        setApplicationInstallEventTimestamp(event)
         addBasicProperties(to: payload, event: event)
-        if event.isPrimitive {
-            addPrimitiveProperties(to: payload, event: event)
-        } else {
-            addSelfDescribingProperties(to: payload, event: event)
-        }
-        var entities = event.entities
-        addBasicContexts(toContexts: &entities, event: event)
-        addGlobalContexts(toContexts: &entities, event: event)
-        addStateMachineEntities(toContexts: &entities, event: event)
-        wrapContexts(entities, to: payload)
+        addStateMachinePayloadValues(event: event)
+        event.wrapProperties(to: payload, base64Encoded: base64Encoded)
+
+        // Context entities
+        addBasicContexts(event: event)
+        addStateMachineEntities(event: event)
+        event.wrapContexts(to: payload, base64Encoded: base64Encoded)
+
+        // Workaround for campaign attribution
         if !event.isPrimitive {
             // TODO: To remove when Atomic table refactoring is finished
-            workaround(forCampaignAttributionEnrichment: payload, event: event, contexts: &entities)
+            workaround(forCampaignAttributionEnrichment: payload, event: event)
         }
         return payload
     }
+    
+    private func setApplicationInstallEventTimestamp(_ event: TrackerEvent) {
+        // Application_install event needs the timestamp to the real installation event.
+        if (event.schema == kSPApplicationInstallSchema) {
+            if let trueTimestamp = event.trueTimestamp {
+                event.timestamp = Int64(trueTimestamp.timeIntervalSince1970 * 1000)
+                event.trueTimestamp = nil
+            }
+        }
+    }
 
     func addBasicProperties(to payload: Payload, event: TrackerEvent) {
+        // Event ID
         payload.addValueToPayload(event.eventId.uuidString, forKey: kSPEid)
+        // Timestamps
         payload.addValueToPayload(String(format: "%lld", event.timestamp), forKey: kSPTimestamp)
         if let trueTimestamp = event.trueTimestamp {
             let ttInMilliSeconds = Int64(trueTimestamp.timeIntervalSince1970 * 1000)
             payload.addValueToPayload(String(format: "%lld", ttInMilliSeconds), forKey: kSPTrueTimestamp)
         }
+        // Tracker info (version, namespace, app ID)
         if let trackerData = trackerData {
             payload.addDictionaryToPayload(trackerData)
         }
+        // Subject
         if let subjectDict = subject?.standardDict(userAnonymisation: userAnonymisation) {
             payload.addDictionaryToPayload(subjectDict)
         }
+        // Platform
         payload.addValueToPayload(devicePlatformToString(devicePlatform), forKey: kSPPlatform)
-    }
-
-    func addPrimitiveProperties(to payload: Payload, event: TrackerEvent) {
-        payload.addValueToPayload(event.eventName, forKey: kSPEvent)
-        payload.addDictionaryToPayload(event.payload)
-    }
-
-    func addSelfDescribingProperties(to payload: Payload, event: TrackerEvent) {
-        payload.addValueToPayload(kSPEventUnstructured, forKey: kSPEvent)
-
-        if let schema = event.schema {
-            let eventPayload = event.payload
-            let data = SelfDescribingJson(schema: schema, andData: eventPayload)
-            let unstructuredEventPayload = SelfDescribingJson.dictionary(
-                schema: kSPUnstructSchema,
-                data: data.dictionary)
-            payload.addDictionaryToPayload(
-                unstructuredEventPayload,
-                base64Encoded: base64Encoded,
-                typeWhenEncoded: kSPUnstructuredEncoded,
-                typeWhenNotEncoded: kSPUnstructured)
+        // Event name
+        if event.isPrimitive {
+            payload.addValueToPayload(event.eventName, forKey: kSPEvent)
+        } else {
+            payload.addValueToPayload(kSPEventUnstructured, forKey: kSPEvent)
         }
     }
 
@@ -580,7 +537,7 @@ class Tracker: NSObject {
      This is a hack that should be removed once the atomic event table is dismissed and all the events
      will be SelfDescribing.
      */
-    func workaround(forCampaignAttributionEnrichment payload: Payload, event: TrackerEvent, contexts: inout [SelfDescribingJson]) {
+    func workaround(forCampaignAttributionEnrichment payload: Payload, event: TrackerEvent) {
         var url: String?
         var referrer: String?
 
@@ -588,7 +545,7 @@ class Tracker: NSObject {
             url = event.payload[DeepLinkReceived.paramUrl] as? String
             referrer = event.payload[DeepLinkReceived.paramReferrer] as? String
         } else if event.schema == kSPScreenViewSchema {
-            for entity in contexts {
+            for entity in event.entities {
                 if entity.schema == DeepLinkEntity.schema {
                     let data = entity.data
                     url = data[DeepLinkEntity.paramUrl] as? String
@@ -606,73 +563,54 @@ class Tracker: NSObject {
         }
     }
 
-    func addBasicContexts(toContexts contexts: inout [SelfDescribingJson], event: TrackerEvent) {
-        addBasicContexts(toContexts: &contexts, eventId: event.eventId.uuidString, eventTimestamp: event.timestamp, isService: event.isService)
-    }
-
-    func addBasicContexts(toContexts contexts: inout [SelfDescribingJson], eventId: String, eventTimestamp: Int64, isService: Bool) {
+    func addBasicContexts(event: TrackerEvent) {
         if subject != nil {
             if let platformDict = subject?.platformDict(
                 userAnonymisation: userAnonymisation,
                 advertisingIdentifierRetriever: advertisingIdentifierRetriever)?.dictionary {
-                contexts.append(SelfDescribingJson(schema: platformContextSchema, andDictionary: platformDict))
+                event.addContextEntity(SelfDescribingJson(schema: platformContextSchema, andDictionary: platformDict))
             }
             if let geoLocationDict = subject?.geoLocationDict {
-                contexts.append(SelfDescribingJson(schema: kSPGeoContextSchema, andDictionary: geoLocationDict))
+                event.addContextEntity(SelfDescribingJson(schema: kSPGeoContextSchema, andDictionary: geoLocationDict))
             }
         }
 
         if applicationContext {
             if let contextJson = Utilities.applicationContext {
-                contexts.append(contextJson)
+                event.addContextEntity(contextJson)
             }
         }
 
-        if isService {
+        if event.isService {
             return
         }
 
         // Add session
         if let session = session {
-            if let sessionDict = session.getDictWithEventId(eventId, eventTimestamp: eventTimestamp, userAnonymisation: userAnonymisation) {
-                contexts.append(SelfDescribingJson(schema: kSPSessionContextSchema, andDictionary: sessionDict))
+            if let sessionDict = session.getDictWithEventId(event.eventId.uuidString,
+                                                            eventTimestamp: event.timestamp,
+                                                            userAnonymisation: userAnonymisation) {
+                event.addContextEntity(SelfDescribingJson(schema: kSPSessionContextSchema, andDictionary: sessionDict))
             } else {
-                logDiagnostic(message: String(format: "Unable to get session context for eventId: %@", eventId))
+                logDiagnostic(message: String(format: "Unable to get session context for eventId: %@", event.eventId.uuidString))
             }
         }
 
         // Add GDPR context
         if let gdprContext = gdprContext?.context {
-            contexts.append(gdprContext)
+            event.addContextEntity(gdprContext)
         }
     }
 
-    func addGlobalContexts(toContexts contexts: inout [SelfDescribingJson], event: InspectableEvent) {
-        for (_, generator) in globalContextGenerators {
-            contexts.append(contentsOf: generator.contexts(from: event))
-        }
+    private func addStateMachinePayloadValues(event: TrackerEvent) {
+        _ = stateManager.addPayloadValues(to: event)
     }
 
-    func addStateMachineEntities(toContexts contexts: inout [SelfDescribingJson], event: InspectableEvent & StateMachineEvent) {
+    func addStateMachineEntities(event: TrackerEvent) {
         let stateManagerEntities = stateManager.entities(forProcessedEvent: event)
-        contexts.append(contentsOf: stateManagerEntities)
-    }
-
-    func wrapContexts(_ contexts: [SelfDescribingJson], to payload: Payload) {
-        if contexts.count == 0 {
-            return
+        for entity in stateManagerEntities {
+            event.addContextEntity(entity)
         }
-
-        let dict: [String : Any] = [
-            kSPSchema: kSPContextSchema,
-            kSPData: contexts.map { $0.dictionary }
-        ]
-
-        payload.addDictionaryToPayload(
-            dict,
-            base64Encoded: base64Encoded,
-            typeWhenEncoded: kSPContextEncoded,
-            typeWhenNotEncoded: kSPContext)
     }
 
     deinit {
diff --git a/Sources/Core/Tracker/TrackerControllerImpl.swift b/Sources/Core/Tracker/TrackerControllerImpl.swift
index 7a6c49bd9..f62918ad6 100644
--- a/Sources/Core/Tracker/TrackerControllerImpl.swift
+++ b/Sources/Core/Tracker/TrackerControllerImpl.swift
@@ -54,6 +54,10 @@ class TrackerControllerImpl: Controller, TrackerController {
         return sessionController.isEnabled ? sessionController : nil
     }
 
+    var plugins: PluginsController {
+        return serviceProvider.pluginsController
+    }
+
     // MARK: - Control methods
 
     func pause() {
diff --git a/Sources/Core/Tracker/TrackerEvent.swift b/Sources/Core/Tracker/TrackerEvent.swift
index 133eea6d0..a3c81a980 100644
--- a/Sources/Core/Tracker/TrackerEvent.swift
+++ b/Sources/Core/Tracker/TrackerEvent.swift
@@ -76,4 +76,46 @@ class TrackerEvent : InspectableEvent, StateMachineEvent {
         }
         return result
     }
+    
+    func addContextEntity(_ entity: SelfDescribingJson) {
+        entities.append(entity)
+    }
+    
+    func wrapContexts(to payload: Payload, base64Encoded: Bool) {
+        if entities.count == 0 {
+            return
+        }
+        
+        let dict = SelfDescribingJson.dictionary(
+            schema: kSPContextSchema,
+            data: entities.map { $0.dictionary })
+        
+        payload.addDictionaryToPayload(
+            dict,
+            base64Encoded: base64Encoded,
+            typeWhenEncoded: kSPContextEncoded,
+            typeWhenNotEncoded: kSPContext)
+    }
+    
+    func wrapProperties(to payload: Payload, base64Encoded: Bool) {
+        if isPrimitive {
+            payload.addDictionaryToPayload(self.payload)
+        } else {
+            wrapSelfDescribing(to: payload, base64Encoded: base64Encoded)
+        }
+    }
+    
+    private func wrapSelfDescribing(to payload: Payload, base64Encoded: Bool) {
+        guard let schema = schema else { return }
+        
+        let data = SelfDescribingJson(schema: schema, andData: self.payload)
+        let unstructuredEventPayload = SelfDescribingJson.dictionary(
+            schema: kSPUnstructSchema,
+            data: data.dictionary)
+        payload.addDictionaryToPayload(
+            unstructuredEventPayload,
+            base64Encoded: base64Encoded,
+            typeWhenEncoded: kSPUnstructuredEncoded,
+            typeWhenNotEncoded: kSPUnstructured)
+    }
 }
diff --git a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
index 4a9ca468b..edbafaeee 100644
--- a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
+++ b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
@@ -72,4 +72,13 @@ public class GlobalContextsConfiguration: Configuration, GlobalContextsConfigura
     }
 
     // MARK: - NSCoding (No coding possible as we can't encode and decode the contextGenerators)
+    
+    // MARK: - Internal functions
+    
+    func toPluginConfigurations() -> [GlobalContextPluginConfiguration] {
+        return contextGenerators.map { (identifier, globalContext) in
+            return GlobalContextPluginConfiguration(identifier: identifier,
+                                                    globalContext: globalContext)
+        }
+    }
 }
diff --git a/Sources/Snowplow/Configurations/PluginConfiguration.swift b/Sources/Snowplow/Configurations/PluginConfiguration.swift
new file mode 100644
index 000000000..d97780ba3
--- /dev/null
+++ b/Sources/Snowplow/Configurations/PluginConfiguration.swift
@@ -0,0 +1,163 @@
+//
+//  PluginConfiguration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Closure used in after track plugin callbacks.
+public typealias PluginAfterTrackClosure = (InspectableEvent) -> Void
+/// Closure used in plugin callbacks to generate entities.
+public typealias PluginEntitiesClosure = (InspectableEvent) -> [SelfDescribingJson]
+
+/// Provides a block closure to be called after events are tracked.
+/// Optionally, you can specify the event schemas for which the block should be called.
+@objc(SPPluginAfterTrackConfiguration)
+public class PluginAfterTrackConfiguration: NSObject {
+    var schemas: [String]?
+    var closure: PluginAfterTrackClosure
+
+    /// Create the after track closure configuration
+    /// - Parameters:
+    ///    - schemas: Optional list of event schemas to call the block for. If null, the block is called for all events.
+    ///    - closure: Block to call after events are tracked.
+    public init(schemas: [String]? = nil, closure: @escaping PluginAfterTrackClosure) {
+        self.schemas = schemas
+        self.closure = closure
+    }
+
+    func toTuple() -> (schemas: [String]?, closure: PluginAfterTrackClosure)? {
+        return (schemas: schemas, closure: closure)
+    }
+}
+
+/// Provides a block closure that returns a list of context entities and is called when events are tracked.
+/// Optionally, you can specify the event schemas for which the block should be called.
+@objc(SPPluginEntitiesConfiguration)
+public class PluginEntitiesConfiguration: NSObject {
+    var schemas: [String]?
+    var closure: PluginEntitiesClosure
+
+    /// Create the entities closure configuration
+    /// - Parameters:
+    ///    - schemas: Optional list of event schemas to call the block for. If null, the block is called for all events.
+    ///    - closure: Block that produces entities, called when events are tracked.
+    public init(schemas: [String]? = nil, closure: @escaping PluginEntitiesClosure) {
+        self.schemas = schemas
+        self.closure = closure
+    }
+
+    func toTuple() -> (schemas: [String]?, closure: PluginEntitiesClosure)? {
+        return (schemas: schemas, closure: closure)
+    }
+}
+
+/// Protocol for tracker plugin definition.
+/// Specifies configurations for the closures called when and after events are tracked.
+@objc(SPPluginConfigurationProtocol)
+public protocol PluginConfigurationProtocol {
+    /// Unique identifier of the plugin within the tracker.
+    var identifier: String { get }
+    /// Closure configuration that is called after events are tracked.
+    var afterTrackConfiguration: PluginAfterTrackConfiguration? { get }
+    /// Closure configuration that is called when events are tracked to generate context entities to enrich the events.
+    var entitiesConfiguration: PluginEntitiesConfiguration? { get }
+}
+
+extension PluginConfigurationProtocol {
+    func toStateMachine() -> StateMachineProtocol {
+        return PluginStateMachine(
+            identifier: identifier,
+            entitiesConfiguration: entitiesConfiguration?.toTuple(),
+            afterTrackConfiguration: afterTrackConfiguration?.toTuple())
+    }
+}
+
+/// Configuration for a custom tracker plugin.
+/// Enables you to add closures to be called when and after events are tracked in the tracker.
+@objc(SPPluginConfiguration)
+public class PluginConfiguration: Configuration, PluginConfigurationProtocol {
+    /// Unique identifier of the plugin within the tracker.
+    public private(set) var identifier: String
+    /// Closure configuration that is called after events are tracked.
+    /// Read-only, use `afterTrack(schemas:closure:)` to initialize.
+    public private(set) var afterTrackConfiguration: PluginAfterTrackConfiguration?
+    /// Closure configuration that is called when events are tracked to generate context entities to enrich the events.
+    /// Read-only, use `entities(schemas:closure:)` to initialize.
+    public private(set) var entitiesConfiguration: PluginEntitiesConfiguration?
+
+    /// Create a plugin configuration.
+    /// - Parameters:
+    ///    - identifier: Unique identifier of the plugin within the tracker.
+    public init(identifier: String) {
+        self.identifier = identifier
+    }
+
+    /// Add a closure that generates entities for a given tracked event.
+    /// - Parameters:
+    ///   - schemas: Optional list of event schemas to call the closure for. If null, the closure is called for all events.
+    ///   - closure: Closure that produces entities, called when events are tracked.
+    public func entities(schemas: [String]? = nil, closure: @escaping PluginEntitiesClosure) {
+        self.entitiesConfiguration = PluginEntitiesConfiguration(
+            schemas: schemas,
+            closure: closure
+        )
+    }
+
+    /// Add a closure that is called after the events are tracked.
+    /// The closure is called after the events are added to event queue in Emitter, not necessarily after they are sent to the Collector.
+    /// - Parameters:
+    ///   - schemas: Optional list of event schemas to call the closure for. If null, the closure is called for all events.
+    ///   - closure: Closure block to call after events are tracked.
+    public func afterTrack(schemas: [String]? = nil, closure: @escaping PluginAfterTrackClosure) {
+        self.afterTrackConfiguration = PluginAfterTrackConfiguration(
+            schemas: schemas,
+            closure: closure
+        )
+    }
+
+    // MARK: - NSCopying
+
+    @objc
+    public override func copy(with zone: NSZone? = nil) -> Any {
+        let copy = PluginConfiguration(identifier: identifier)
+        if let afterTrack = afterTrackConfiguration {
+            copy.afterTrack(schemas: afterTrack.schemas, closure: afterTrack.closure)
+        }
+        if let entities = entitiesConfiguration {
+            copy.entities(schemas: entities.schemas, closure: entities.closure)
+        }
+        return copy
+    }
+
+    // MARK: - NSSecureCoding
+    
+    @objc
+    public override class var supportsSecureCoding: Bool { return true }
+    
+    @objc
+    public override func encode(with coder: NSCoder) {
+        coder.encode(identifier, forKey: "identifier")
+    }
+
+    required init?(coder: NSCoder) {
+        identifier = coder.decodeObject(forKey: "identifier") as? String ?? ""
+        super.init()
+    }
+
+}
diff --git a/Sources/Snowplow/Controllers/PluginsController.swift b/Sources/Snowplow/Controllers/PluginsController.swift
new file mode 100644
index 000000000..50545df0d
--- /dev/null
+++ b/Sources/Snowplow/Controllers/PluginsController.swift
@@ -0,0 +1,32 @@
+//
+//  PluginsController.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+@objc(SPPluginsController)
+public protocol PluginsController {
+    @objc
+    var identifiers: [String] { get }
+    @objc
+    func add(plugin: PluginConfigurationProtocol)
+    @objc
+    func remove(identifier: String)
+}
diff --git a/Sources/Snowplow/Controllers/TrackerController.swift b/Sources/Snowplow/Controllers/TrackerController.swift
index 9ee816e9b..ccb7eef35 100644
--- a/Sources/Snowplow/Controllers/TrackerController.swift
+++ b/Sources/Snowplow/Controllers/TrackerController.swift
@@ -58,6 +58,9 @@ public protocol TrackerController: TrackerConfigurationProtocol {
     /// @apiNote Don't retain the reference. It may change on tracker reconfiguration.
     @objc
     var globalContexts: GlobalContextsController? { get }
+    /// 
+    @objc
+    var plugins: PluginsController { get }
     /// Track the event.
     /// The tracker will take care to process and send the event assigning `event_id` and `device_timestamp`.
     /// - Parameter event: The event to track.
diff --git a/Tests/Global Contexts/TestGlobalContexts.swift b/Tests/Global Contexts/TestGlobalContexts.swift
index 0f32c0a07..6e6667e96 100644
--- a/Tests/Global Contexts/TestGlobalContexts.swift	
+++ b/Tests/Global Contexts/TestGlobalContexts.swift	
@@ -56,41 +56,42 @@ class TestGlobalContexts: XCTestCase {
                 ])
             ]
         })
-        
+
         var generators = [
             "static": staticGC,
             "generator": generatorGC,
             "block": blockGC
         ]
-        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+        let serviceProvider = getServiceProviderWithGlobalContextGenerators(&generators)
+        let controller = serviceProvider.globalContextsController
 
-        var result = Set<String>(tracker.globalContextTags)
+        var result = Set<String>(controller.tags)
         var expected = Set<String>(["static", "generator", "block"])
         XCTAssertEqual(result, expected)
 
         // Can't remove a not existing tag
-        var removedGC = tracker.removeGlobalContext("notExistingTag")
+        var removedGC = controller.remove(tag: "notExistingTag")
         XCTAssertNil(removedGC)
-        result = Set<String>(tracker.globalContextTags)
+        result = Set<String>(controller.tags)
         expected = Set<String>(["static", "generator", "block"])
         XCTAssertTrue(result == expected)
 
         // Remove an existing tag
-        removedGC = tracker.removeGlobalContext("static")
+        removedGC = controller.remove(tag: "static")
         XCTAssertNotNil(removedGC)
-        result = Set<String>(tracker.globalContextTags)
+        result = Set<String>(controller.tags)
         expected = Set<String>(["generator", "block"])
         XCTAssertTrue(result == expected)
 
         // Add a not existing tag
-        XCTAssertTrue(tracker.add(staticGC, tag: "static"))
-        result = Set<String>(tracker.globalContextTags)
+        XCTAssertTrue(controller.add(tag: "static", contextGenerator: staticGC))
+        result = Set<String>(controller.tags)
         expected = Set<String>(["generator", "block", "static"])
         XCTAssertTrue(result == expected)
 
         // Can't add an existing tag
-        XCTAssertFalse(tracker.add(staticGC, tag: "static"))
-        result = Set<String>(tracker.globalContextTags)
+        XCTAssertFalse(controller.add(tag: "static", contextGenerator: staticGC))
+        result = Set<String>(controller.tags)
         expected = Set<String>(["generator", "block", "static"])
         XCTAssertTrue(result == expected)
     }
@@ -102,26 +103,27 @@ class TestGlobalContexts: XCTestCase {
             ])
         ])
         var generators: [String : GlobalContext] = [:]
-        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+        let serviceProvider = getServiceProviderWithGlobalContextGenerators(&generators)
+        let controller = serviceProvider.globalContextsController
 
-        var result = Set<String>(tracker.globalContextTags)
+        var result = Set<String>(controller.tags)
         var expected = Set<String>([])
         XCTAssertTrue(result == expected)
 
         // Can't remove a not existing tag
-        var removedGC = tracker.removeGlobalContext("notExistingTag")
+        var removedGC = controller.remove(tag: "notExistingTag")
         XCTAssertNil(removedGC)
 
         // Add a not existing tag
-        XCTAssertTrue(tracker.add(staticGC, tag: "static"))
-        result = Set<String>(tracker.globalContextTags)
+        XCTAssertTrue(controller.add(tag: "static", contextGenerator: staticGC))
+        result = Set<String>(controller.tags)
         expected = Set<String>(["static"])
         XCTAssertTrue(result == expected)
 
         // Remove an existing tag
-        removedGC = tracker.removeGlobalContext("static")
+        removedGC = controller.remove(tag: "static")
         XCTAssertNotNil(removedGC)
-        result = Set<String>(tracker.globalContextTags)
+        result = Set<String>(controller.tags)
         expected = Set<String>([])
         XCTAssertTrue(result == expected)
     }
@@ -135,15 +137,14 @@ class TestGlobalContexts: XCTestCase {
         var globalContexts = [
             "static": staticGC
         ]
-        let tracker = getTrackerWithGlobalContextGenerators(&globalContexts)
+        let serviceProvider = getServiceProviderWithGlobalContextGenerators(&globalContexts)
 
         let event = Structured(category: "Category", action: "Action")
         let trackerEvent = TrackerEvent(event: event, state: nil)
 
-        var contexts: [SelfDescribingJson] = []
-        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
-        XCTAssertTrue(contexts.count == 1)
-        XCTAssertEqual(contexts[0].schema, "schema")
+        serviceProvider.tracker.addStateMachineEntities(event: trackerEvent)
+        XCTAssertTrue(trackerEvent.entities.count == 1)
+        XCTAssertEqual(trackerEvent.entities[0].schema, "schema")
     }
 
     func testStaticGeneratortWithFilter() {
@@ -168,15 +169,14 @@ class TestGlobalContexts: XCTestCase {
             "matching": filterMatchingGC,
             "notMatching": filterNotMatchingGC
         ]
-        let tracker = getTrackerWithGlobalContextGenerators(&globalContexts)
+        let serviceProvider = getServiceProviderWithGlobalContextGenerators(&globalContexts)
 
         let event = Structured(category: stringToMatch, action: "Action")
         let trackerEvent = TrackerEvent(event: event, state: nil)
 
-        var contexts: [SelfDescribingJson] = []
-        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
-        XCTAssertTrue(contexts.count == 1)
-        XCTAssertEqual(contexts[0].schema, "schema")
+        serviceProvider.tracker.addStateMachineEntities(event: trackerEvent)
+        XCTAssertTrue(trackerEvent.entities.count == 1)
+        XCTAssertEqual(trackerEvent.entities[0].schema, "schema")
     }
 
     func testStaticGeneratorWithRuleset() {
@@ -192,30 +192,28 @@ class TestGlobalContexts: XCTestCase {
         var globalContexts = [
             "ruleset": rulesetGC
         ]
-        let tracker = getTrackerWithGlobalContextGenerators(&globalContexts)
-
-        var contexts: [SelfDescribingJson] = []
+        let serviceProvider = getServiceProviderWithGlobalContextGenerators(&globalContexts)
 
         // Not matching primitive event
         let primitiveEvent = Structured(category: "Category", action: "Action")
         var trackerEvent = TrackerEvent(event: primitiveEvent, state: nil)
-        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
-        XCTAssertTrue(contexts.count == 0)
+        serviceProvider.tracker.addStateMachineEntities(event: trackerEvent)
+        XCTAssertTrue(trackerEvent.entities.count == 0)
 
         // Not matching self-describing event with mobile schema
         let screenView = ScreenView(name: "Name", screenId: nil)
         screenView.type = "Type"
         trackerEvent = TrackerEvent(event: screenView, state: nil)
-        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
-        XCTAssertTrue(contexts.count == 0)
+        serviceProvider.tracker.addStateMachineEntities(event: trackerEvent)
+        XCTAssertTrue(trackerEvent.entities.count == 0)
 
         // Matching self-describing event with general schema
         let timing = Timing(category: "Category", variable: "Variable", timing: 123)
         timing.label = "Label"
         trackerEvent = TrackerEvent(event: timing, state: nil)
-        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
-        XCTAssertTrue(contexts.count == 1)
-        XCTAssertEqual(contexts[0].schema, "schema")
+        serviceProvider.tracker.addStateMachineEntities(event: trackerEvent)
+        XCTAssertTrue(trackerEvent.entities.count == 1)
+        XCTAssertEqual(trackerEvent.entities[0].schema, "schema")
     }
 
     func testBlockGenerator() {
@@ -228,15 +226,14 @@ class TestGlobalContexts: XCTestCase {
                 ]
             })
         ]
-        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+        let serviceProvider = getServiceProviderWithGlobalContextGenerators(&generators)
 
         let event = Structured(category: "Category", action: "Action")
         let trackerEvent = TrackerEvent(event: event, state: nil)
 
-        var contexts: [SelfDescribingJson] = []
-        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
-        XCTAssertTrue(contexts.count == 1)
-        XCTAssertEqual(contexts[0].schema, "schema")
+        serviceProvider.tracker.addStateMachineEntities(event: trackerEvent)
+        XCTAssertTrue(trackerEvent.entities.count == 1)
+        XCTAssertEqual(trackerEvent.entities[0].schema, "schema")
     }
 
     func testContextGenerator() {
@@ -244,21 +241,20 @@ class TestGlobalContexts: XCTestCase {
         var generators = [
             "contextGenerator": contextGeneratorGC
         ]
-        let tracker = getTrackerWithGlobalContextGenerators(&generators)
+        let serviceProvider = getServiceProviderWithGlobalContextGenerators(&generators)
 
         let event = Structured(category: "StringToMatch", action: "Action")
         let trackerEvent = TrackerEvent(event: event, state: nil)
 
-        var contexts: [SelfDescribingJson] = []
-        tracker.addGlobalContexts(toContexts: &contexts, event: trackerEvent)
-        XCTAssertTrue(contexts.count == 1)
-        XCTAssertEqual(contexts[0].schema, "schema")
+        serviceProvider.tracker.addStateMachineEntities(event: trackerEvent)
+        XCTAssertTrue(trackerEvent.entities.count == 1)
+        XCTAssertEqual(trackerEvent.entities[0].schema, "schema")
     }
 
     // MARK: - Utility function
 
 
-    func getTrackerWithGlobalContextGenerators(_ generators: inout [String : GlobalContext]) -> Tracker {
+    func getServiceProviderWithGlobalContextGenerators(_ generators: inout [String : GlobalContext]) -> ServiceProvider {
         let networkConfig = NetworkConfiguration(
             endpoint: "https://com.acme.fake",
             method: .post)
@@ -274,6 +270,6 @@ class TestGlobalContexts: XCTestCase {
             namespace: "aNamespace",
             network: networkConfig,
             configurations: [gcConfig])
-        return serviceProvider.tracker
+        return serviceProvider
     }
 }
diff --git a/Tests/Integration/TestTrackEventsToMicro.swift b/Tests/Integration/TestTrackEventsToMicro.swift
index 255857e6e..d41976d29 100644
--- a/Tests/Integration/TestTrackEventsToMicro.swift
+++ b/Tests/Integration/TestTrackEventsToMicro.swift
@@ -27,9 +27,10 @@ class TestTrackEventsToMicro: XCTestCase {
     
     override func setUp() {
         super.setUp()
-        
-        tracker = Snowplow.createTracker(namespace: "ns", network: NetworkConfiguration(endpoint: Micro.endpoint))!
-        
+
+        tracker = Snowplow.createTracker(namespace: "testMicro",
+                                         network: NetworkConfiguration(endpoint: Micro.endpoint))!
+
         Micro.setUpMockerIgnores()
         wait(for: [Micro.reset()], timeout: Micro.timeout)
     }
diff --git a/Tests/TestPlugins.swift b/Tests/TestPlugins.swift
new file mode 100644
index 000000000..cd619e1f9
--- /dev/null
+++ b/Tests/TestPlugins.swift
@@ -0,0 +1,197 @@
+//
+//  TestPlugins.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestPlugins: XCTestCase {
+    override func tearDown() {
+        Snowplow.removeAllTrackers()
+        super.tearDown()
+    }
+
+    func testAddsEntitiesToEvent() {
+        let plugin = PluginConfiguration(identifier: "plugin")
+        plugin.entities { [SelfDescribingJson(schema: "schema", andData: ["val": $0.payload["se_ca"]!])] }
+        
+        let testPlugin = PluginConfiguration(identifier: "test")
+        let expect = expectation(description: "Has context entity on event")
+        testPlugin.afterTrack { event in
+            XCTAssertTrue(
+                event.entities.filter({ entity in
+                    entity.schema == "schema" && entity.data["val"] as? String == "cat"
+                }).count == 1
+            )
+            expect.fulfill()
+        }
+        
+        let tracker = createTracker([plugin, testPlugin])
+        _ = tracker.track(Structured(category: "cat", action: "act"))
+        
+        wait(for: [expect], timeout: 10)
+    }
+
+    func testAddsEntitiesFromMultiplePlugins() {
+        let plugin1 = PluginConfiguration(identifier: "plugin1")
+        plugin1.entities { _ in [SelfDescribingJson(schema: "schema1", andData: [:])] }
+        
+        let plugin2 = PluginConfiguration(identifier: "plugin2")
+        plugin2.entities { _ in [SelfDescribingJson(schema: "schema2", andData: [:])] }
+        
+        let testPlugin = PluginConfiguration(identifier: "test")
+        let expect = expectation(description: "Has both context entities on event")
+        testPlugin.afterTrack { event in
+            XCTAssertTrue(
+                event.entities.filter({ $0.schema == "schema1" }).count == 1
+            )
+            XCTAssertTrue(
+                event.entities.filter({ $0.schema == "schema2" }).count == 1
+            )
+            expect.fulfill()
+        }
+        
+        let tracker = createTracker([plugin1, plugin2, testPlugin])
+        _ = tracker.track(ScreenView(name: "sv"))
+
+        wait(for: [expect], timeout: 1)
+    }
+
+    func testAddsEntitiesOnlyForEventsMatchingSchema() {
+        let plugin = PluginConfiguration(identifier: "plugin")
+        plugin.entities(schemas: ["schema1"]) { _ in [SelfDescribingJson(schema: "xx", andData: [:])] }
+        
+        var event1HasEntity: Bool? = nil
+        var event2HasEntity: Bool? = nil
+        
+        let testPlugin = PluginConfiguration(identifier: "test")
+        testPlugin.afterTrack { event in
+            if event.schema == "schema1" {
+                event1HasEntity = event.entities.contains(where: { $0.schema == "xx" })
+            }
+            if event.schema == "schema2" {
+                event2HasEntity = event.entities.contains(where: { $0.schema == "xx" })
+            }
+        }
+        
+        let tracker = createTracker([plugin, testPlugin])
+        _ = tracker.track(SelfDescribing(schema: "schema1", payload: [:]))
+        _ = tracker.track(SelfDescribing(schema: "schema2", payload: [:]))
+
+        waitForEventsToBeTracked()
+
+        XCTAssertTrue(event1HasEntity!)
+        XCTAssertFalse(event2HasEntity!)
+    }
+
+    func testCallsAfterTrackOnlyForEventsMatchingSchema() {
+        var event1Called: Bool = false
+        var event2Called: Bool = false
+        var event3Called: Bool = false
+
+        let plugin = PluginConfiguration(identifier: "plugin")
+        plugin.afterTrack(schemas: ["schema1"]) { event in
+            if event.schema == "schema1" { event1Called = true }
+            if event.schema == "schema2" { event2Called = true }
+            if event.schema == nil { event3Called = true }
+        }
+
+        let tracker = createTracker([plugin])
+        _ = tracker.track(SelfDescribing(schema: "schema1", payload: [:]))
+        _ = tracker.track(SelfDescribing(schema: "schema2", payload: [:]))
+        _ = tracker.track(Structured(category: "cat", action: "act"))
+
+        waitForEventsToBeTracked()
+
+        XCTAssertTrue(event1Called)
+        XCTAssertFalse(event2Called)
+        XCTAssertFalse(event3Called)
+    }
+
+    func testCallsAfterTrackOnlyForStructuredEvent() {
+        var selfDescribingCalled: Bool = false
+        var structuredCalled: Bool = false
+
+        let plugin = PluginConfiguration(identifier: "plugin")
+        plugin.afterTrack(schemas: ["se"]) { event in
+            if event.schema == "schema1" { selfDescribingCalled = true }
+            if event.schema == nil { structuredCalled = true }
+        }
+
+        let tracker = createTracker([plugin])
+        _ = tracker.track(SelfDescribing(schema: "schema1", payload: [:]))
+        _ = tracker.track(Structured(category: "cat", action: "act"))
+
+        waitForEventsToBeTracked()
+
+        XCTAssertTrue(structuredCalled)
+        XCTAssertFalse(selfDescribingCalled)
+    }
+    
+    func testAddsPluginToTracker() {
+        let tracker = createTracker([])
+        
+        let plugin = PluginConfiguration(identifier: "plugin")
+        let expect = expectation(description: "Plugin called")
+        plugin.afterTrack { _ -> Void in expect.fulfill() }
+        tracker.plugins.add(plugin: plugin)
+        
+        _ = tracker.track(ScreenView(name: "sv"))
+        
+        wait(for: [expect], timeout: 1)
+    }
+    
+    func testRemovesPluginFromTracker() {
+        var pluginCalled = false
+        let plugin = PluginConfiguration(identifier: "plugin")
+        plugin.afterTrack { _ in pluginCalled = true }
+
+        let tracker = createTracker([plugin])
+        XCTAssertEqual(["plugin"], tracker.plugins.identifiers)
+        tracker.plugins.remove(identifier: "plugin")
+
+        _ = tracker.track(ScreenView(name: "sv"))
+
+        let expect = expectation(description: "Wait for events to be tracked")
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { () -> Void in expect.fulfill() }
+        wait(for: [expect], timeout: 1)
+
+        XCTAssertFalse(pluginCalled)
+    }
+
+    private func createTracker(_ configurations: [Configuration]) -> TrackerController {
+        let networkConfig = NetworkConfiguration(networkConnection: MockNetworkConnection(requestOption: .post, statusCode: 200))
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.installAutotracking = false
+        trackerConfig.lifecycleAutotracking = false
+        let namespace = "testPlugins" + String(describing: Int.random(in: 0..<100))
+        return Snowplow.createTracker(namespace: namespace,
+                                      network: networkConfig,
+                                      configurations: configurations + [trackerConfig])!
+    }
+    
+    private func waitForEventsToBeTracked() {
+        let expect = expectation(description: "Wait for events to be tracked")
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { () -> Void in
+            expect.fulfill()
+        }
+        wait(for: [expect], timeout: 1)
+    }
+}
diff --git a/Tests/TestScreenViewModifier.swift b/Tests/TestScreenViewModifier.swift
index f9eab5e8c..38f122b30 100644
--- a/Tests/TestScreenViewModifier.swift
+++ b/Tests/TestScreenViewModifier.swift
@@ -27,24 +27,20 @@ import XCTest
 #if os(iOS) || os(tvOS) || os(macOS)
 
 class TestScreenViewModifier: XCTestCase {
-    var tracker: TrackerController?
-    
-    override func setUp() {
-        super.setUp()
-        
-        tracker = Snowplow.createTracker(namespace: "ns",
-                                         network: NetworkConfiguration(endpoint: Micro.endpoint))!
-        
-        Micro.setUpMockerIgnores()
-        wait(for: [Micro.reset()], timeout: Micro.timeout)
-    }
-    
-    override func tearDown() {
-        super.tearDown()
-    }
-    
     func testTracksScreenViewWithContextEntity() {
         if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, *) {
+            let expect = expectation(description: "Event received")
+            createTracker { event in
+                XCTAssertEqual("screen-1", event.payload["name"] as? String)
+                XCTAssertEqual(kSPScreenViewSchema, event.schema)
+                XCTAssertTrue(
+                    event.entities.filter({ entity in
+                        entity.schema == "iglu:com.snowplowanalytics.iglu/anything-a/jsonschema/1-0-0"
+                    }).count == 1
+                )
+                expect.fulfill()
+            }
+
             let modifier = ScreenViewModifier(
                 name: "screen-1",
                 entities: [
@@ -55,20 +51,28 @@ class TestScreenViewModifier: XCTestCase {
                         ]
                     )
                 ],
-                trackerNamespace: "ns"
+                trackerNamespace: "screenViewTracker"
             )
             modifier.trackScreenView()
-            
-            wait(for: [
-                Micro.expectSelfDescribingEvent() { (actual: ScreenViewExpected) in
-                    XCTAssertEqual("screen-1", actual.name)
-                },
-                Micro.expectEventContext(schema: "iglu:com.snowplowanalytics.iglu/anything-a/jsonschema/1-0-0") { (actual: AnythingEntityExpected) in
-                    XCTAssertTrue(actual.works)
-                }
-            ], timeout: Micro.timeout)
+
+            wait(for: [expect], timeout: 1)
         }
     }
+    
+    private func createTracker(afterTrack: @escaping (InspectableEvent) -> ()) {
+        let plugin = PluginConfiguration(identifier: "screenViewPlugin")
+        plugin.afterTrack(closure: afterTrack)
+        
+        let networkConfig = NetworkConfiguration(networkConnection: MockNetworkConnection(requestOption: .post, statusCode: 200))
+        
+        let trackerConfig = TrackerConfiguration()
+        trackerConfig.installAutotracking = false
+        trackerConfig.lifecycleAutotracking = false
+        
+        _ = Snowplow.createTracker(namespace: "screenViewTracker",
+                                      network: networkConfig,
+                                      configurations: [trackerConfig, plugin])!
+    }
 }
 
 private struct ScreenViewExpected: Codable {
diff --git a/Tests/TestServiceProvider.swift b/Tests/TestServiceProvider.swift
index 750d473ed..d1f58a86d 100644
--- a/Tests/TestServiceProvider.swift
+++ b/Tests/TestServiceProvider.swift
@@ -44,7 +44,9 @@ class TestServiceProvider: XCTestCase {
         trackerConfig.installAutotracking = false
         trackerConfig.screenViewAutotracking = false
         trackerConfig.lifecycleAutotracking = false
-        let serviceProvider = ServiceProvider(namespace: "ns", network: networkConfig, configurations: [emitterConfig, trackerConfig])
+        let serviceProvider = ServiceProvider(namespace: "serviceProviderTest",
+                                              network: networkConfig,
+                                              configurations: [emitterConfig, trackerConfig])
         XCTAssertNotNil(serviceProvider)
 
         // pause emitter
diff --git a/Tests/TestStateManager.swift b/Tests/TestStateManager.swift
index f9f57d5bf..d02454fce 100644
--- a/Tests/TestStateManager.swift
+++ b/Tests/TestStateManager.swift
@@ -80,6 +80,13 @@ class MockStateMachine: StateMachineProtocol {
             "newParam": "value"
         ]
     }
+
+    var subscribedEventSchemasForAfterTrackCallback: [String] {
+        return []
+    }
+
+    func afterTrack(event: SnowplowTracker.InspectableEvent) {
+    }
 }
 
 class MockStateMachine1: MockStateMachine {

From 3256f1be81df733cc878d5b7bf361e0ca0bc3e2b Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Wed, 1 Feb 2023 16:55:09 +0100
Subject: [PATCH 13/19] Remove requirement for all configurations to be
 serializable (close #753)

PR #753
---
 .../Core/GDPR/GDPRConfigurationUpdate.swift   |  4 --
 .../GlobalContextPluginConfiguration.swift    | 18 +------
 .../FetchedConfigurationBundle.swift          |  2 +-
 Sources/Core/Tracker/ServiceProvider.swift    |  6 +--
 .../Configurations/ConfigurationBundle.swift  |  9 ++--
 .../ConfigurationProtocol.swift               | 27 ++++++++++
 .../Configurations/ConfigurationState.swift   |  2 +-
 .../Configurations/EmitterConfiguration.swift | 51 ++-----------------
 .../FocalMeterConfiguration.swift             |  8 +++
 .../Configurations/GDPRConfiguration.swift    | 41 ++-------------
 .../GlobalContextsConfiguration.swift         | 16 ++----
 .../Configurations/NetworkConfiguration.swift |  5 +-
 .../Configurations/PluginConfiguration.swift  | 32 +-----------
 .../Configurations/RemoteConfiguration.swift  | 34 ++-----------
 ....swift => SerializableConfiguration.swift} |  9 ++--
 .../Configurations/SessionConfiguration.swift |  5 +-
 .../Configurations/SubjectConfiguration.swift |  4 +-
 .../Configurations/TrackerConfiguration.swift |  8 +--
 Sources/Snowplow/Snowplow.swift               |  2 +-
 Tests/TestPlugins.swift                       |  2 +-
 20 files changed, 78 insertions(+), 207 deletions(-)
 create mode 100644 Sources/Snowplow/Configurations/ConfigurationProtocol.swift
 create mode 100644 Sources/Snowplow/Configurations/FocalMeterConfiguration.swift
 rename Sources/Snowplow/Configurations/{Configuration.swift => SerializableConfiguration.swift} (86%)

diff --git a/Sources/Core/GDPR/GDPRConfigurationUpdate.swift b/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
index 7b264e988..e88178488 100644
--- a/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
+++ b/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
@@ -90,8 +90,4 @@ class GDPRConfigurationUpdate: GDPRConfiguration {
     public init() {
         super.init(basis: .consent, documentId: nil, documentVersion: nil, documentDescription: nil)
     }
-    
-    required init?(coder: NSCoder) {
-        super.init(coder: coder)
-    }
 }
diff --git a/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
index b52ad835b..e3c38a692 100644
--- a/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
+++ b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
@@ -21,7 +21,7 @@
 
 import Foundation
 
-class GlobalContextPluginConfiguration: Configuration, PluginConfigurationProtocol {
+class GlobalContextPluginConfiguration: NSObject, ConfigurationProtocol, PluginConfigurationProtocol {
     private(set) var identifier: String
     private(set) var globalContext: GlobalContext
     private(set) var afterTrackConfiguration: PluginAfterTrackConfiguration? = nil
@@ -32,20 +32,4 @@ class GlobalContextPluginConfiguration: Configuration, PluginConfigurationProtoc
         self.globalContext = globalContext
         self.entitiesConfiguration = PluginEntitiesConfiguration(closure: globalContext.contexts)
     }
-
-    // MARK: - NSCopying
-
-    override func copy(with zone: NSZone? = nil) -> Any {
-        let copy = GlobalContextPluginConfiguration(
-            identifier: identifier,
-            globalContext: globalContext
-        )
-        return copy
-    }
-
-    // MARK: - NSCoding (No coding possible as we can't encode and decode the contextGenerators)
-
-    required convenience public init?(coder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
 }
diff --git a/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
index 01718e48a..235f58b3c 100644
--- a/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
+++ b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
@@ -21,7 +21,7 @@
 
 import Foundation
 
-class FetchedConfigurationBundle: Configuration {
+class FetchedConfigurationBundle: SerializableConfiguration {
     var schema: String
     var configurationVersion: Int
     var configurationBundle: [ConfigurationBundle] = []
diff --git a/Sources/Core/Tracker/ServiceProvider.swift b/Sources/Core/Tracker/ServiceProvider.swift
index 5a7ab5678..97f722d28 100644
--- a/Sources/Core/Tracker/ServiceProvider.swift
+++ b/Sources/Core/Tracker/ServiceProvider.swift
@@ -122,7 +122,7 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
     
     // MARK: - Init
 
-    init(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [Configuration]) {
+    init(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [ConfigurationProtocol]) {
         self.namespace = namespace
         super.init()
         
@@ -134,7 +134,7 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
         let _ = tracker // Build tracker to initialize NotificationCenter receivers
     }
 
-    func reset(configurations: [Configuration]) {
+    func reset(configurations: [ConfigurationProtocol]) {
         stopServices()
         resetConfigurationUpdates()
         processConfigurations(configurations)
@@ -152,7 +152,7 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
 
     // MARK: - Private methods
 
-    func processConfigurations(_ configurations: [Configuration]) {
+    func processConfigurations(_ configurations: [ConfigurationProtocol]) {
         for configuration in configurations {
             if let configuration = configuration as? NetworkConfiguration {
                 networkConfigurationUpdate.sourceConfig = configuration
diff --git a/Sources/Snowplow/Configurations/ConfigurationBundle.swift b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
index 19154a9e8..8d2e6c776 100644
--- a/Sources/Snowplow/Configurations/ConfigurationBundle.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
@@ -1,4 +1,5 @@
-//  SPConfigurationBundle.swift
+//
+//  ConfigurationBundle.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -22,7 +23,7 @@ import Foundation
 
 /// This class represents the default configuration applied in place of the remote configuration.
 @objc(SPConfigurationBundle)
-public class ConfigurationBundle: Configuration {
+public class ConfigurationBundle: SerializableConfiguration {
     @objc
     private(set) public var namespace: String
     @objc
@@ -35,8 +36,8 @@ public class ConfigurationBundle: Configuration {
     public var sessionConfiguration: SessionConfiguration?
 
     @objc
-    public var configurations: [Configuration] {
-        var array: [Configuration] = []
+    public var configurations: [ConfigurationProtocol] {
+        var array: [ConfigurationProtocol] = []
         if let networkConfiguration = networkConfiguration {
             array.append(networkConfiguration)
         }
diff --git a/Sources/Snowplow/Configurations/ConfigurationProtocol.swift b/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
new file mode 100644
index 000000000..e434213a1
--- /dev/null
+++ b/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
@@ -0,0 +1,27 @@
+//
+//  Configuration.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Alex Benini
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+/// Common parent protocol for configuration classes.
+@objc(SPConfigurationProtocol)
+public protocol ConfigurationProtocol {
+}
diff --git a/Sources/Snowplow/Configurations/ConfigurationState.swift b/Sources/Snowplow/Configurations/ConfigurationState.swift
index 4a0c87517..63c303b16 100644
--- a/Sources/Snowplow/Configurations/ConfigurationState.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationState.swift
@@ -1,5 +1,5 @@
 //
-//  SPConfigurationState.h
+//  ConfigurationState.h
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
diff --git a/Sources/Snowplow/Configurations/EmitterConfiguration.swift b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
index e828ec392..70bc77d2d 100644
--- a/Sources/Snowplow/Configurations/EmitterConfiguration.swift
+++ b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
@@ -1,4 +1,5 @@
-//  SPEmitterConfiguration.swift
+//
+//  EmitterConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -54,7 +55,7 @@ public protocol EmitterConfigurationProtocol: AnyObject {
 /// The EmitterConfiguration can be used to setup details about how the tracker should treat the events
 /// to emit to the collector.
 @objc(SPEmitterConfiguration)
-public class EmitterConfiguration: Configuration, EmitterConfigurationProtocol {
+public class EmitterConfiguration: NSObject, EmitterConfigurationProtocol, ConfigurationProtocol {
     /// Sets whether the buffer should send events instantly or after the buffer
     /// has reached it's limit. By default, this is set to BufferOption Default.
     @objc
@@ -106,50 +107,4 @@ public class EmitterConfiguration: Configuration, EmitterConfigurationProtocol {
     public override init() {
         super.init()
     }
-
-    // MARK: - NSCopying
-
-    @objc
-    public override func copy(with zone: NSZone? = nil) -> Any {
-        let copy = EmitterConfiguration()
-        copy.bufferOption = bufferOption
-        copy.emitRange = emitRange
-        copy.threadPoolSize = threadPoolSize
-        copy.byteLimitGet = byteLimitGet
-        copy.byteLimitPost = byteLimitPost
-        copy.requestCallback = requestCallback
-        copy.eventStore = eventStore
-        copy.customRetryForStatusCodes = customRetryForStatusCodes
-        copy.serverAnonymisation = serverAnonymisation
-        return copy
-    }
-
-    // MARK: - NSSecureCoding
-    
-    @objc
-    public override class var supportsSecureCoding: Bool { return true }
-
-    @objc
-    public override func encode(with coder: NSCoder) {
-        coder.encode(bufferOption, forKey: "bufferOption")
-        coder.encode(emitRange, forKey: "emitRange")
-        coder.encode(threadPoolSize, forKey: "threadPoolSize")
-        coder.encode(byteLimitGet, forKey: "byteLimitGet")
-        coder.encode(byteLimitPost, forKey: "byteLimitPost")
-        coder.encode(customRetryForStatusCodes, forKey: "customRetryForStatusCodes")
-        coder.encode(serverAnonymisation, forKey: "serverAnonymisation")
-    }
-
-    required init?(coder: NSCoder) {
-        super.init()
-        if let bufferOption = BufferOption(rawValue: coder.decodeInteger(forKey: "bufferOption")) {
-            self.bufferOption = bufferOption
-        }
-        emitRange = coder.decodeInteger(forKey: "emitRange")
-        threadPoolSize = coder.decodeInteger(forKey: "threadPoolSize")
-        byteLimitGet = coder.decodeInteger(forKey: "byteLimitGet")
-        byteLimitPost = coder.decodeInteger(forKey: "byteLimitPost")
-        customRetryForStatusCodes = coder.decodeObject(forKey: "customRetryForStatusCodes") as? [Int : Bool]
-        serverAnonymisation = coder.decodeBool(forKey: "serverAnonymisation")
-    }
 }
diff --git a/Sources/Snowplow/Configurations/FocalMeterConfiguration.swift b/Sources/Snowplow/Configurations/FocalMeterConfiguration.swift
new file mode 100644
index 000000000..5d01cc347
--- /dev/null
+++ b/Sources/Snowplow/Configurations/FocalMeterConfiguration.swift
@@ -0,0 +1,8 @@
+//
+//  File.swift
+//  
+//
+//  Created by Matus Tomlein on 30/12/2022.
+//
+
+import Foundation
diff --git a/Sources/Snowplow/Configurations/GDPRConfiguration.swift b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
index c937b7ee5..02a5745bb 100644
--- a/Sources/Snowplow/Configurations/GDPRConfiguration.swift
+++ b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
@@ -1,4 +1,5 @@
-//  SPGDPRConfiguration.swift
+//
+//  GDPRConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -38,7 +39,7 @@ public protocol GDPRConfigurationProtocol: AnyObject {
 
 /// This class allows the GDPR configuration of the tracker.
 @objc(SPGDPRConfiguration)
-public class GDPRConfiguration: Configuration, GDPRConfigurationProtocol {
+public class GDPRConfiguration: NSObject, GDPRConfigurationProtocol, ConfigurationProtocol {
     /// Basis for processing.
     @objc
     public var basisForProcessing: GDPRProcessingBasis
@@ -71,40 +72,4 @@ public class GDPRConfiguration: Configuration, GDPRConfigurationProtocol {
         self.documentDescription = documentDescription ?? ""
         super.init()
     }
-
-    // MARK: - NSCopying
-
-    @objc
-    public override func copy(with zone: NSZone? = nil) -> Any {
-        let copy = GDPRConfiguration(basis: basisForProcessing,
-                                     documentId: documentId,
-                                     documentVersion: documentVersion,
-                                     documentDescription: documentDescription)
-        return copy
-    }
-
-    // MARK: - NSSecureCodin
-    
-    @objc
-    public override class var supportsSecureCoding: Bool { return true }
-
-    @objc
-    public override func encode(with coder: NSCoder) {
-        coder.encode(basisForProcessing.rawValue, forKey: "basisForProcessing")
-        coder.encode(documentId, forKey: "documentId")
-        coder.encode(documentVersion, forKey: "documentVersion")
-        coder.encode(documentDescription, forKey: "documentDescription")
-    }
-
-    required init?(coder: NSCoder) {
-        if let basisForProcessing = GDPRProcessingBasis(rawValue: coder.decodeInteger(forKey: "basisForProcessing")) {
-            self.basisForProcessing = basisForProcessing
-            self.documentId = coder.decodeObject(forKey: "documentId") as? String ?? ""
-            self.documentVersion = coder.decodeObject(forKey: "documentVersion") as? String ?? ""
-            self.documentDescription = coder.decodeObject(forKey: "documentDescription") as? String ?? ""
-            super.init()
-        } else {
-            return nil
-        }
-    }
 }
diff --git a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
index edbafaeee..6b20ec7ec 100644
--- a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
+++ b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
@@ -1,4 +1,5 @@
-//  SPGlobalContextsConfiguration.swift
+//
+//  GlobalContextsConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -40,7 +41,7 @@ public protocol GlobalContextsConfigurationProtocol: AnyObject {
 
 /// This class allows the setup of Global Contexts which are attached to selected events.
 @objc(SPGlobalContextsConfiguration)
-public class GlobalContextsConfiguration: Configuration, GlobalContextsConfigurationProtocol {
+public class GlobalContextsConfiguration: NSObject, GlobalContextsConfigurationProtocol, ConfigurationProtocol {
     @objc
     public var contextGenerators: [String : GlobalContext] = [:]
 
@@ -61,17 +62,6 @@ public class GlobalContextsConfiguration: Configuration, GlobalContextsConfigura
         }
         return toDelete
     }
-
-    // MARK: - NSCopying
-
-    @objc
-    public override func copy(with zone: NSZone? = nil) -> Any {
-        let copy = GlobalContextsConfiguration()
-        copy.contextGenerators = contextGenerators
-        return copy
-    }
-
-    // MARK: - NSCoding (No coding possible as we can't encode and decode the contextGenerators)
     
     // MARK: - Internal functions
     
diff --git a/Sources/Snowplow/Configurations/NetworkConfiguration.swift b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
index 99ddae2a0..13e54e728 100644
--- a/Sources/Snowplow/Configurations/NetworkConfiguration.swift
+++ b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
@@ -1,4 +1,5 @@
-//  SPNetworkConfiguration.swift
+//
+//  NetworkConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -22,7 +23,7 @@ import Foundation
 
 /// Represents the network communication configuration allowing the tracker to be able to send events to the Snowplow collector.
 @objc(SPNetworkConfiguration)
-public class NetworkConfiguration: Configuration {
+public class NetworkConfiguration: SerializableConfiguration, ConfigurationProtocol {
     /// URL (without schema/protocol) used to send events to the collector.
     @objc
     private(set) public var endpoint: String?
diff --git a/Sources/Snowplow/Configurations/PluginConfiguration.swift b/Sources/Snowplow/Configurations/PluginConfiguration.swift
index d97780ba3..5a919cf78 100644
--- a/Sources/Snowplow/Configurations/PluginConfiguration.swift
+++ b/Sources/Snowplow/Configurations/PluginConfiguration.swift
@@ -91,7 +91,7 @@ extension PluginConfigurationProtocol {
 /// Configuration for a custom tracker plugin.
 /// Enables you to add closures to be called when and after events are tracked in the tracker.
 @objc(SPPluginConfiguration)
-public class PluginConfiguration: Configuration, PluginConfigurationProtocol {
+public class PluginConfiguration: NSObject, PluginConfigurationProtocol, ConfigurationProtocol {
     /// Unique identifier of the plugin within the tracker.
     public private(set) var identifier: String
     /// Closure configuration that is called after events are tracked.
@@ -130,34 +130,4 @@ public class PluginConfiguration: Configuration, PluginConfigurationProtocol {
             closure: closure
         )
     }
-
-    // MARK: - NSCopying
-
-    @objc
-    public override func copy(with zone: NSZone? = nil) -> Any {
-        let copy = PluginConfiguration(identifier: identifier)
-        if let afterTrack = afterTrackConfiguration {
-            copy.afterTrack(schemas: afterTrack.schemas, closure: afterTrack.closure)
-        }
-        if let entities = entitiesConfiguration {
-            copy.entities(schemas: entities.schemas, closure: entities.closure)
-        }
-        return copy
-    }
-
-    // MARK: - NSSecureCoding
-    
-    @objc
-    public override class var supportsSecureCoding: Bool { return true }
-    
-    @objc
-    public override func encode(with coder: NSCoder) {
-        coder.encode(identifier, forKey: "identifier")
-    }
-
-    required init?(coder: NSCoder) {
-        identifier = coder.decodeObject(forKey: "identifier") as? String ?? ""
-        super.init()
-    }
-
 }
diff --git a/Sources/Snowplow/Configurations/RemoteConfiguration.swift b/Sources/Snowplow/Configurations/RemoteConfiguration.swift
index 5ea96af5b..a8ffeb36f 100644
--- a/Sources/Snowplow/Configurations/RemoteConfiguration.swift
+++ b/Sources/Snowplow/Configurations/RemoteConfiguration.swift
@@ -1,4 +1,5 @@
-//  SPRemoteConfiguration.swift
+//
+//  RemoteConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -23,7 +24,7 @@ import Foundation
 /// Represents the configuration for fetching configurations from a remote source.
 /// For details on the correct format of a remote configuration see the official documentation.
 @objc(SPRemoteConfiguration)
-public class RemoteConfiguration: Configuration {
+public class RemoteConfiguration: NSObject {
     /// URL of the remote configuration.
     @objc
     private(set) public var endpoint: String
@@ -56,33 +57,4 @@ public class RemoteConfiguration: Configuration {
         self.init(endpoint: endpoint)
         self.method = method
     }
-
-    // MARK: - NSCopying
-
-    @objc
-    override public func copy(with zone: NSZone? = nil) -> Any {
-        let copy = RemoteConfiguration(endpoint: endpoint)
-        copy.method = method
-        return copy
-    }
-
-    // MARK: - NSSecureCoding
-
-    @objc
-    override public func encode(with coder: NSCoder) {
-        coder.encode(endpoint, forKey: "endpoint")
-        coder.encode(method?.rawValue, forKey: "method")
-    }
-    
-    @objc
-    public override class var supportsSecureCoding: Bool { return true }
-    
-    required init?(coder: NSCoder) {
-        if let endpoint = coder.decodeObject(forKey: "endpoint") as? String {
-            self.endpoint = endpoint
-            method = HttpMethodOptions(rawValue: coder.decodeInteger(forKey: "method"))
-        } else {
-            return nil
-        }
-    }
 }
diff --git a/Sources/Snowplow/Configurations/Configuration.swift b/Sources/Snowplow/Configurations/SerializableConfiguration.swift
similarity index 86%
rename from Sources/Snowplow/Configurations/Configuration.swift
rename to Sources/Snowplow/Configurations/SerializableConfiguration.swift
index 822288179..1a54f1853 100644
--- a/Sources/Snowplow/Configurations/Configuration.swift
+++ b/Sources/Snowplow/Configurations/SerializableConfiguration.swift
@@ -1,4 +1,5 @@
-//  Configuration.swift
+//
+//  SerializableConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -21,8 +22,8 @@
 import Foundation
 
 /// Common parent class for configuration classes.
-@objc(SPConfiguration)
-public class Configuration: NSObject, NSCopying, NSSecureCoding {
+@objc(SPSerializableConfiguration)
+public class SerializableConfiguration: NSObject, NSCopying, NSSecureCoding {
     @objc
     public convenience init?(dictionary: [String : Any]) {
         self.init()
@@ -30,7 +31,7 @@ public class Configuration: NSObject, NSCopying, NSSecureCoding {
 
     @objc
     public func copy(with zone: NSZone? = nil) -> Any {
-        return Configuration()
+        return SerializableConfiguration()
     }
 
     @objc
diff --git a/Sources/Snowplow/Configurations/SessionConfiguration.swift b/Sources/Snowplow/Configurations/SessionConfiguration.swift
index baf2e0c65..92f1ff13e 100644
--- a/Sources/Snowplow/Configurations/SessionConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SessionConfiguration.swift
@@ -1,4 +1,5 @@
-//  SPSessionConfiguration.swift
+//
+//  SessionConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -59,7 +60,7 @@ public protocol SessionConfigurationProtocol: AnyObject {
 /// Session data is maintained for the life of the application being installed on a device.
 /// A new session will be created if the session information is not accessed within a configurable timeout.
 @objc(SPSessionConfiguration)
-public class SessionConfiguration: Configuration, SessionConfigurationProtocol {
+public class SessionConfiguration: SerializableConfiguration, SessionConfigurationProtocol, ConfigurationProtocol {
     @objc
     public var backgroundTimeoutInSeconds: Int
     @objc
diff --git a/Sources/Snowplow/Configurations/SubjectConfiguration.swift b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
index a04206760..c6f1c574d 100644
--- a/Sources/Snowplow/Configurations/SubjectConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
@@ -1,4 +1,4 @@
-//  SPSubjectConfiguration.swift
+//  SubjectConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -94,7 +94,7 @@ public protocol SubjectConfigurationProtocol: AnyObject {
 /// user and the app which will be attached on all the events as contexts.
 /// The contexts to track can be enabled in the `TrackerConfiguration` class.
 @objc(SPSubjectConfiguration)
-public class SubjectConfiguration: Configuration, SubjectConfigurationProtocol {
+public class SubjectConfiguration: SerializableConfiguration, SubjectConfigurationProtocol, ConfigurationProtocol {
     convenience init(dictionary: [String : Any]) {
         self.init()
         if let userId = dictionary["userId"] as? String { self.userId = userId }
diff --git a/Sources/Snowplow/Configurations/TrackerConfiguration.swift b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
index e93a33ba4..bfe2f03f2 100644
--- a/Sources/Snowplow/Configurations/TrackerConfiguration.swift
+++ b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
@@ -1,5 +1,5 @@
 //
-//  SPTrackerConfiguration.swift
+//  TrackerConfiguration.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
@@ -90,7 +90,7 @@ public protocol TrackerConfigurationProtocol: AnyObject {
 /// The TrackerConfiguration can be used to setup the tracker behaviour indicating what should be
 /// tracked in term of automatic tracking and contexts/entities to track with the events.
 @objc(SPTrackerConfiguration)
-public class TrackerConfiguration: Configuration, TrackerConfigurationProtocol {
+public class TrackerConfiguration: SerializableConfiguration, TrackerConfigurationProtocol, ConfigurationProtocol {
     /// Identifer of the app.
     @objc
     public var appId = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? ""
@@ -254,9 +254,9 @@ public class TrackerConfiguration: Configuration, TrackerConfigurationProtocol {
     @objc
     public override func encode(with coder: NSCoder) {
         coder.encode(appId, forKey: "appId")
-        coder.encode(devicePlatform, forKey: "devicePlatform")
+        coder.encode(devicePlatform.rawValue, forKey: "devicePlatform")
         coder.encode(base64Encoding, forKey: "base64Encoding")
-        coder.encode(logLevel, forKey: "logLevel")
+        coder.encode(logLevel.rawValue, forKey: "logLevel")
         coder.encode(loggerDelegate, forKey: "loggerDelegate")
         coder.encode(sessionContext, forKey: "sessionContext")
         coder.encode(applicationContext, forKey: "applicationContext")
diff --git a/Sources/Snowplow/Snowplow.swift b/Sources/Snowplow/Snowplow.swift
index 6796b33ad..98cacb23a 100644
--- a/Sources/Snowplow/Snowplow.swift
+++ b/Sources/Snowplow/Snowplow.swift
@@ -194,7 +194,7 @@ public class Snowplow: NSObject {
     ///                       the tracker.
     /// - Returns: The tracker instance created.
     @objc
-    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [Configuration]) -> TrackerController? {
+    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [ConfigurationProtocol]) -> TrackerController? {
         if let serviceProvider = serviceProviderInstances[namespace] {
             serviceProvider.reset(configurations: configurations + [networkConfiguration])
             return serviceProvider.trackerController
diff --git a/Tests/TestPlugins.swift b/Tests/TestPlugins.swift
index cd619e1f9..d455b5bb6 100644
--- a/Tests/TestPlugins.swift
+++ b/Tests/TestPlugins.swift
@@ -176,7 +176,7 @@ class TestPlugins: XCTestCase {
         XCTAssertFalse(pluginCalled)
     }
 
-    private func createTracker(_ configurations: [Configuration]) -> TrackerController {
+    private func createTracker(_ configurations: [ConfigurationProtocol]) -> TrackerController {
         let networkConfig = NetworkConfiguration(networkConnection: MockNetworkConnection(requestOption: .post, statusCode: 200))
         let trackerConfig = TrackerConfiguration()
         trackerConfig.installAutotracking = false

From b3e002c514854c43c8159dd27c3dceb8981abaa1 Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Wed, 1 Feb 2023 16:57:04 +0100
Subject: [PATCH 14/19] Use Swift DSL for building configurations and add
 builder functions for configurations and events (close #755)

PR #756
---
 Examples                                      |   2 +-
 .../GlobalContextPluginConfiguration.swift    |   2 +-
 .../Configurations/ConfigurationBuilder.swift |  76 ++++++++++
 .../ConfigurationProtocol.swift               |   2 +-
 .../Configurations/EmitterConfiguration.swift |  68 +++++++++
 .../Configurations/GDPRConfiguration.swift    |  30 ++++
 .../GlobalContextsConfiguration.swift         |   7 +
 .../Configurations/NetworkConfiguration.swift |  17 +++
 .../Configurations/PluginConfiguration.swift  |   8 +-
 .../Configurations/SessionConfiguration.swift |   5 +-
 .../Configurations/SubjectConfiguration.swift | 139 +++++++++++++++++-
 .../Configurations/TrackerConfiguration.swift | 138 +++++++++++++++++
 .../Snowplow/Entities/DeepLinkEntity.swift    |   9 ++
 .../Snowplow/Entities/LifecycleEntity.swift   |  11 ++
 Sources/Snowplow/Events/ConsentDocument.swift |  16 ++
 Sources/Snowplow/Events/ConsentGranted.swift  |  25 ++++
 .../Snowplow/Events/ConsentWithdrawn.swift    |  46 ++++++
 .../Snowplow/Events/DeepLinkReceived.swift    |   9 ++
 Sources/Snowplow/Events/Ecommerce.swift       |  52 ++++++-
 Sources/Snowplow/Events/EcommerceItem.swift   |  30 ++++
 Sources/Snowplow/Events/EventBase.swift       |  24 +++
 .../Snowplow/Events/MessageNotification.swift | 126 ++++++++++++++++
 Sources/Snowplow/Events/PageView.swift        |  16 ++
 .../Snowplow/Events/PushNotification.swift    |  37 +++++
 Sources/Snowplow/Events/SNOWError.swift       |  16 ++
 Sources/Snowplow/Events/ScreenView.swift      |  51 +++++++
 Sources/Snowplow/Events/Structured.swift      |  29 ++++
 Sources/Snowplow/Events/Timing.swift          |   9 ++
 Sources/Snowplow/Snowplow.swift               |  60 ++++++--
 .../TestConfigurationBuilder.swift            |  59 ++++++++
 Tests/TestPlugins.swift                       | 126 ++++++++--------
 Tests/TestScreenViewModifier.swift            |  20 +--
 32 files changed, 1169 insertions(+), 96 deletions(-)
 create mode 100644 Sources/Snowplow/Configurations/ConfigurationBuilder.swift
 create mode 100644 Tests/Configurations/TestConfigurationBuilder.swift

diff --git a/Examples b/Examples
index a5e796d9b..7057e14b7 160000
--- a/Examples
+++ b/Examples
@@ -1 +1 @@
-Subproject commit a5e796d9b37a2f8968deb9658c0cebd102b30bc4
+Subproject commit 7057e14b770ae33f0ff2d95464c61982d77010d1
diff --git a/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
index e3c38a692..4e66c0f54 100644
--- a/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
+++ b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
@@ -21,7 +21,7 @@
 
 import Foundation
 
-class GlobalContextPluginConfiguration: NSObject, ConfigurationProtocol, PluginConfigurationProtocol {
+class GlobalContextPluginConfiguration: ConfigurationProtocol, PluginConfigurationProtocol {
     private(set) var identifier: String
     private(set) var globalContext: GlobalContext
     private(set) var afterTrackConfiguration: PluginAfterTrackConfiguration? = nil
diff --git a/Sources/Snowplow/Configurations/ConfigurationBuilder.swift b/Sources/Snowplow/Configurations/ConfigurationBuilder.swift
new file mode 100644
index 000000000..6ce48d921
--- /dev/null
+++ b/Sources/Snowplow/Configurations/ConfigurationBuilder.swift
@@ -0,0 +1,76 @@
+//
+//  ConfigurationBuilder.swift
+//  Snowplow
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import Foundation
+
+#if swift(>=5.4)
+/// Result builder used to build a list of configuration objects when creating a new tracker
+@resultBuilder
+public struct ConfigurationBuilder {
+    public static func buildExpression(_ expression: [ConfigurationProtocol]) -> [ConfigurationProtocol] {
+        return expression
+    }
+    
+    public static func buildExpression(_ expression: ConfigurationProtocol) -> [ConfigurationProtocol] {
+        return [expression]
+    }
+    
+    public static func buildBlock() -> [ConfigurationProtocol] {
+        return []
+    }
+    
+    public static func buildBlock(_ configurations: ConfigurationProtocol...) -> [ConfigurationProtocol] {
+        return configurations.map { $0 }
+    }
+    
+    public static func buildBlock(_ configurations: [ConfigurationProtocol]...) -> [ConfigurationProtocol] {
+        return configurations.flatMap { $0 }
+    }
+    
+    public static func buildArray(_ configurations: [[ConfigurationProtocol]]) -> [ConfigurationProtocol] {
+        return configurations.flatMap { $0 }
+    }
+    
+    public static func buildArray(_ configurations: [ConfigurationProtocol]) -> [ConfigurationProtocol] {
+        return configurations
+    }
+    
+    public static func buildEither(first configurations: [ConfigurationProtocol]) -> [ConfigurationProtocol] {
+        return configurations
+    }
+    
+    public static func buildEither(first configuration: ConfigurationProtocol) -> [ConfigurationProtocol] {
+        return [configuration]
+    }
+    
+    public static func buildEither(second configuration: [ConfigurationProtocol]) -> [ConfigurationProtocol] {
+        return configuration
+    }
+    
+    public static func buildEither(second configuration: ConfigurationProtocol) -> [ConfigurationProtocol] {
+        return [configuration]
+    }
+    
+    public static func buildOptional(_ configurations: [ConfigurationProtocol]?) -> [ConfigurationProtocol] {
+        return configurations ?? []
+    }
+}
+#endif
diff --git a/Sources/Snowplow/Configurations/ConfigurationProtocol.swift b/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
index e434213a1..f479c9137 100644
--- a/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
@@ -1,5 +1,5 @@
 //
-//  Configuration.swift
+//  ConfigurationProtocol.swift
 //  Snowplow
 //
 //  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
diff --git a/Sources/Snowplow/Configurations/EmitterConfiguration.swift b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
index 70bc77d2d..a59f00217 100644
--- a/Sources/Snowplow/Configurations/EmitterConfiguration.swift
+++ b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
@@ -107,4 +107,72 @@ public class EmitterConfiguration: NSObject, EmitterConfigurationProtocol, Confi
     public override init() {
         super.init()
     }
+    
+    // MARK: - Builders
+    
+    /// Sets whether the buffer should send events instantly or after the buffer
+    /// has reached it's limit. By default, this is set to BufferOption Default.
+    @objc
+    public func bufferOption(_ option: BufferOption) -> Self {
+        self.bufferOption = option
+        return self
+    }
+
+    /// Maximum number of events collected from the EventStore to be sent in a request.
+    @objc
+    public func emitRange(_ range: Int) -> Self {
+        self.emitRange = range
+        return self
+    }
+
+    /// Maximum number of threads working in parallel in the tracker to send requests.
+    @objc
+    public func threadPoolSize(_ size: Int) -> Self {
+        self.threadPoolSize = size
+        return self
+    }
+
+    /// Maximum amount of bytes allowed to be sent in a payload in a GET request.
+    @objc
+    public func byteLimitGet(_ limit: Int) -> Self {
+        self.byteLimitGet = limit
+        return self
+    }
+
+    /// Maximum amount of bytes allowed to be sent in a payload in a POST request.
+    @objc
+    public func byteLimitPost(_ limit: Int) -> Self {
+        self.byteLimitPost = limit
+        return self
+    }
+
+    /// Callback called for each request performed by the tracker to the collector.
+    @objc
+    public func requestCallback(_ callback: RequestCallback?) -> Self {
+        self.requestCallback = callback
+        return self
+    }
+
+    /// Custom retry rules for HTTP status codes returned from the Collector.
+    /// The dictionary is a mapping of integers (status codes) to booleans (true for retry and false for not retry).
+    @objc
+    public func customRetryForStatusCodes(_ rules: [Int : Bool]?) -> Self {
+        self.customRetryForStatusCodes = rules
+        return self
+    }
+
+    /// Whether to anonymise server-side user identifiers including the `network_userid` and `user_ipaddress`
+    @objc
+    public func serverAnonymisation(_ serverAnonymisation: Bool) -> Self {
+        self.serverAnonymisation = serverAnonymisation
+        return self
+    }
+
+    /// Custom component with full ownership for persisting events before to be sent to the collector.
+    /// If it's not set the tracker will use a SQLite database as default EventStore.
+    @objc
+    public func eventStore(_ eventStore: EventStore?) -> Self {
+        self.eventStore = eventStore
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Configurations/GDPRConfiguration.swift b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
index 02a5745bb..e5ef5294a 100644
--- a/Sources/Snowplow/Configurations/GDPRConfiguration.swift
+++ b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
@@ -72,4 +72,34 @@ public class GDPRConfiguration: NSObject, GDPRConfigurationProtocol, Configurati
         self.documentDescription = documentDescription ?? ""
         super.init()
     }
+    
+    // MARK: - Builders
+    
+    /// Basis for processing.
+    @objc
+    public func basisForProcessing(_ basis: GDPRProcessingBasis) -> Self {
+        self.basisForProcessing = basis
+        return self
+    }
+    
+    /// ID of a GDPR basis document.
+    @objc
+    public func documentId(_ documentId: String?) -> Self {
+        self.documentId = documentId
+        return self
+    }
+    
+    /// Version of the document.
+    @objc
+    public func documentVersion(_ version: String?) -> Self {
+        self.documentVersion = version
+        return self
+    }
+    
+    /// Description of the document.
+    @objc
+    public func documentDescription(_ description: String?) -> Self {
+        self.documentDescription = description
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
index 6b20ec7ec..b7dd2e2d5 100644
--- a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
+++ b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
@@ -63,6 +63,13 @@ public class GlobalContextsConfiguration: NSObject, GlobalContextsConfigurationP
         return toDelete
     }
     
+    // MARK: - Builders
+    
+    public func contextGenerators(_ generators: [String : GlobalContext]) -> Self {
+        self.contextGenerators = generators
+        return self
+    }
+    
     // MARK: - Internal functions
     
     func toPluginConfigurations() -> [GlobalContextPluginConfiguration] {
diff --git a/Sources/Snowplow/Configurations/NetworkConfiguration.swift b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
index 13e54e728..bdeb00522 100644
--- a/Sources/Snowplow/Configurations/NetworkConfiguration.swift
+++ b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
@@ -93,6 +93,23 @@ public class NetworkConfiguration: SerializableConfiguration, ConfigurationProto
         self.networkConnection = networkConnection
         customPostPath = nil
     }
+    
+    // MARK: - Builders
+    
+    /// A custom path which will be added to the endpoint URL to specify the
+    /// complete URL of the collector when paired with the POST method.
+    @objc
+    public func customPostPath(_ path: String?) -> Self {
+        self.customPostPath = path
+        return self
+    }
+    
+    ///  Custom headers for http requests.
+    @objc
+    public func requestHeaders(_ headers: [String : String]?) -> Self {
+        self.requestHeaders = headers
+        return self
+    }
 
     // MARK: - NSCopying
 
diff --git a/Sources/Snowplow/Configurations/PluginConfiguration.swift b/Sources/Snowplow/Configurations/PluginConfiguration.swift
index 5a919cf78..8002054aa 100644
--- a/Sources/Snowplow/Configurations/PluginConfiguration.swift
+++ b/Sources/Snowplow/Configurations/PluginConfiguration.swift
@@ -107,16 +107,19 @@ public class PluginConfiguration: NSObject, PluginConfigurationProtocol, Configu
     public init(identifier: String) {
         self.identifier = identifier
     }
+    
+    // MARK: - Builders
 
     /// Add a closure that generates entities for a given tracked event.
     /// - Parameters:
     ///   - schemas: Optional list of event schemas to call the closure for. If null, the closure is called for all events.
     ///   - closure: Closure that produces entities, called when events are tracked.
-    public func entities(schemas: [String]? = nil, closure: @escaping PluginEntitiesClosure) {
+    public func entities(schemas: [String]? = nil, closure: @escaping PluginEntitiesClosure) -> Self {
         self.entitiesConfiguration = PluginEntitiesConfiguration(
             schemas: schemas,
             closure: closure
         )
+        return self
     }
 
     /// Add a closure that is called after the events are tracked.
@@ -124,10 +127,11 @@ public class PluginConfiguration: NSObject, PluginConfigurationProtocol, Configu
     /// - Parameters:
     ///   - schemas: Optional list of event schemas to call the closure for. If null, the closure is called for all events.
     ///   - closure: Closure block to call after events are tracked.
-    public func afterTrack(schemas: [String]? = nil, closure: @escaping PluginAfterTrackClosure) {
+    public func afterTrack(schemas: [String]? = nil, closure: @escaping PluginAfterTrackClosure) -> Self {
         self.afterTrackConfiguration = PluginAfterTrackConfiguration(
             schemas: schemas,
             closure: closure
         )
+        return self
     }
 }
diff --git a/Sources/Snowplow/Configurations/SessionConfiguration.swift b/Sources/Snowplow/Configurations/SessionConfiguration.swift
index 92f1ff13e..af4c33709 100644
--- a/Sources/Snowplow/Configurations/SessionConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SessionConfiguration.swift
@@ -125,11 +125,10 @@ public class SessionConfiguration: SerializableConfiguration, SessionConfigurati
         }
     }
 
-    /// The callback called everytime the session is updated.
-
     // MARK: - Builders
 
-    func onSessionStateUpdate(_ value: ((_ sessionState: SessionState) -> Void)?) -> Self {
+    /// The callback called everytime the session is updated.
+    public func onSessionStateUpdate(_ value: ((_ sessionState: SessionState) -> Void)?) -> Self {
         onSessionStateUpdate = value
         return self
     }
diff --git a/Sources/Snowplow/Configurations/SubjectConfiguration.swift b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
index c6f1c574d..4282467f9 100644
--- a/Sources/Snowplow/Configurations/SubjectConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
@@ -112,9 +112,6 @@ public class SubjectConfiguration: SerializableConfiguration, SubjectConfigurati
     }
 
     /// The custom UserID.
-
-    // MARK: - Builder
-
     @objc
     public var userId: String?
 
@@ -194,6 +191,142 @@ public class SubjectConfiguration: SerializableConfiguration, SubjectConfigurati
     @objc
     public var geoTimestamp: NSNumber?
 
+    // MARK: - Builder
+
+    /// The custom UserID.
+    @objc
+    public func userId(_ userId: String?) -> Self {
+        self.userId = userId
+        return self
+    }
+
+    /// The network UserID.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public func networkUserId(_ userId: String?) -> Self {
+        self.networkUserId = userId
+        return self
+    }
+
+    /// The domain UserID.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public func domainUserId(_ userId: String?) -> Self {
+        self.domainUserId = userId
+        return self
+    }
+
+    /// The user-agent.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public func useragent(_ useragent: String?) -> Self {
+        self.useragent = useragent
+        return self
+    }
+
+    /// The IP address.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public func ipAddress(_ ip: String?) -> Self {
+        self.ipAddress = ip
+        return self
+    }
+
+    /// The current timezone.
+    @objc
+    public func timezone(_ timezone: String?) -> Self {
+        self.timezone = timezone
+        return self
+    }
+
+    /// The language set in the device.
+    @objc
+    public func language(_ language: String?) -> Self {
+        self.language = language
+        return self
+    }
+
+    /// The screen resolution.
+    @objc
+    public func screenResolution(_ resolution: SPSize?) -> Self {
+        self.screenResolution = resolution
+        return self
+    }
+
+    /// The screen viewport.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public func screenViewPort(_ viewPort: SPSize?) -> Self {
+        self.screenViewPort = viewPort
+        return self
+    }
+
+    /// The color depth.
+    /// Note: It's not generated by the tracker, it needs to be filled by the developer when instrumenting the tracker.
+    @objc
+    public func colorDepth(_ colorDepth: NSNumber?) -> Self {
+        self.colorDepth = colorDepth
+        return self
+    }
+
+    // GeoLocation builders
+
+    /// Latitude value for the geolocation context.
+    @objc
+    public func geoLatitude(_ geoLatitude: NSNumber?) -> Self {
+        self.geoLatitude = geoLatitude
+        return self
+    }
+
+    /// Longitude value for the geo context.
+    @objc
+    public func geoLongitude(_ geoLongitude: NSNumber?) -> Self {
+        self.geoLongitude = geoLongitude
+        return self
+    }
+
+    /// LatitudeLongitudeAccuracy value for the geolocation context.
+    @objc
+    public func geoLatitudeLongitudeAccuracy(_ accuracy: NSNumber?) -> Self {
+        self.geoLatitudeLongitudeAccuracy = accuracy
+        return self
+    }
+
+    /// Altitude value for the geolocation context.
+    @objc
+    public func geoAltitude(_ geoAltitude: NSNumber?) -> Self {
+        self.geoAltitude = geoAltitude
+        return self
+    }
+
+    /// AltitudeAccuracy value for the geolocation context.
+    @objc
+    public func geoAltitudeAccuracy(_ accuracy: NSNumber?) -> Self {
+        self.geoAltitudeAccuracy = accuracy
+        return self
+    }
+
+    /// Bearing value for the geolocation context.
+    @objc
+    public func geoBearing(_ bearing: NSNumber?) -> Self {
+        self.geoBearing = bearing
+        return self
+    }
+
+    /// Speed value for the geolocation context.
+    @objc
+    public func geoSpeed(_ geoSpeed: NSNumber?) -> Self {
+        self.geoSpeed = geoSpeed
+        return self
+    }
+
+    /// Timestamp value for the geolocation context.
+    @objc
+    public func geoTimestamp(_ timestamp: NSNumber?) -> Self {
+        self.geoTimestamp = timestamp
+        return self
+    }
+
     // MARK: - NSCopying
 
     @objc
diff --git a/Sources/Snowplow/Configurations/TrackerConfiguration.swift b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
index bfe2f03f2..b01547b62 100644
--- a/Sources/Snowplow/Configurations/TrackerConfiguration.swift
+++ b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
@@ -219,6 +219,144 @@ public class TrackerConfiguration: SerializableConfiguration, TrackerConfigurati
         }
     }
 
+    // MARK: - Builders
+    
+    /// Identifer of the app.
+    @objc
+    public func appId(_ appId: String) -> Self {
+        self.appId = appId
+        return self
+    }
+    
+    /// It sets the device platform the tracker is running on.
+    @objc
+    public func devicePlatform(_ devicePlatform: DevicePlatform) -> Self {
+        self.devicePlatform = devicePlatform
+        return self
+    }
+    
+    /// It indicates whether the JSON data in the payload should be base64 encoded.
+    @objc
+    public func base64Encoding(_ base64Encoding: Bool) -> Self {
+        self.base64Encoding = base64Encoding
+        return self
+    }
+    
+    /// It sets the log level of tracker logs.
+    @objc
+    public func logLevel(_ logLevel: LogLevel) -> Self {
+        self.logLevel = logLevel
+        return self
+    }
+    
+    /// It sets the logger delegate that receive logs from the tracker.
+    @objc
+    public func loggerDelegate(_ loggerDelegate: LoggerDelegate?) -> Self {
+        self.loggerDelegate = loggerDelegate
+        return self
+    }
+    
+    /// Whether application context is sent with all the tracked events.
+    @objc
+    public func applicationContext(_ applicationContext: Bool) -> Self {
+        self.applicationContext = applicationContext
+        return self
+    }
+    
+    /// Whether mobile/platform context is sent with all the tracked events.
+    @objc
+    public func platformContext(_ platformContext: Bool) -> Self {
+        self.platformContext = platformContext
+        return self
+    }
+    
+    /// Whether geo-location context is sent with all the tracked events.
+    @objc
+    public func geoLocationContext(_ geoLocationContext: Bool) -> Self {
+        self.geoLocationContext = geoLocationContext
+        return self
+    }
+    
+    /// Whether session context is sent with all the tracked events.
+    @objc
+    public func sessionContext(_ sessionContext: Bool) -> Self {
+        self.sessionContext = sessionContext
+        return self
+    }
+    
+    /// Whether deepLink context is sent with all the ScreenView events.
+    @objc
+    public func deepLinkContext(_ deepLinkContext: Bool) -> Self {
+        self.deepLinkContext = deepLinkContext
+        return self
+    }
+    
+    /// Whether screen context is sent with all the tracked events.
+    @objc
+    public func screenContext(_ screenContext: Bool) -> Self {
+        self.screenContext = screenContext
+        return self
+    }
+    
+    /// Whether enable automatic tracking of ScreenView events.
+    @objc
+    public func screenViewAutotracking(_ screenViewAutotracking: Bool) -> Self {
+        self.screenViewAutotracking = screenViewAutotracking
+        return self
+    }
+    
+    /// Whether enable automatic tracking of background and foreground transitions.
+    @objc
+    public func lifecycleAutotracking(_ lifecycleAutotracking: Bool) -> Self {
+        self.lifecycleAutotracking = lifecycleAutotracking
+        return self
+    }
+    
+    /// Whether enable automatic tracking of install event.
+    @objc
+    public func installAutotracking(_ installAutotracking: Bool) -> Self {
+        self.installAutotracking = installAutotracking
+        return self
+    }
+    
+    /// Whether enable crash reporting.
+    @objc
+    public func exceptionAutotracking(_ exceptionAutotracking: Bool) -> Self {
+        self.exceptionAutotracking = exceptionAutotracking
+        return self
+    }
+    
+    /// Whether enable diagnostic reporting.
+    @objc
+    public func diagnosticAutotracking(_ diagnosticAutotracking: Bool) -> Self {
+        self.diagnosticAutotracking = diagnosticAutotracking
+        return self
+    }
+    
+    /// Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
+    /// Setting this property on a running tracker instance starts a new session (if sessions are tracked).
+    @objc
+    public func userAnonymisation(_ userAnonymisation: Bool) -> Self {
+        self.userAnonymisation = userAnonymisation
+        return self
+    }
+    
+    /// Decorate the v_tracker field in the tracker protocol.
+    /// @note Do not use. Internal use only.
+    @objc
+    public func trackerVersionSuffix(_ trackerVersionSuffix: String?) -> Self {
+        self.trackerVersionSuffix = trackerVersionSuffix
+        return self
+    }
+    
+    /// Closure called to retrieve the Identifier for Advertisers (IDFA) from AdSupport module
+    /// It is called repeatedly (on each tracked event) until a UUID is returned.
+    @objc
+    public func advertisingIdentifierRetriever(_ retriever: (() -> UUID?)?) -> Self {
+        self.advertisingIdentifierRetriever = retriever
+        return self
+    }
+
     // MARK: - NSCopying
 
     @objc
diff --git a/Sources/Snowplow/Entities/DeepLinkEntity.swift b/Sources/Snowplow/Entities/DeepLinkEntity.swift
index 866e3d6f5..bc15ce475 100644
--- a/Sources/Snowplow/Entities/DeepLinkEntity.swift
+++ b/Sources/Snowplow/Entities/DeepLinkEntity.swift
@@ -55,4 +55,13 @@ public class DeepLinkEntity: SelfDescribingJson {
         }
         set {}
     }
+    
+    // MARK: - Builders
+    
+    /// Referrer URL, source of this deep-link
+    @objc
+    public func referrer(_ referrer: String?) -> Self {
+        self.referrer = referrer
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Entities/LifecycleEntity.swift b/Sources/Snowplow/Entities/LifecycleEntity.swift
index 6815175d1..a86474749 100644
--- a/Sources/Snowplow/Entities/LifecycleEntity.swift
+++ b/Sources/Snowplow/Entities/LifecycleEntity.swift
@@ -30,6 +30,8 @@ let kSPLifecycleEntityParamIsVisible = "isVisible"
 @objc(SPLifecycleEntity)
 public class LifecycleEntity: SelfDescribingJson {
 
+    /// - Parameters:
+    ///    - isVisible: Indicates if the app is in foreground state (true) or background state (false)
     @objc
     public init(isVisible: Bool) {
         var parameters: [String : Any] = [:]
@@ -37,6 +39,7 @@ public class LifecycleEntity: SelfDescribingJson {
         super.init(schema: kSPLifecycleEntitySchema, andData: parameters)
     }
 
+    /// Represents the foreground index or background index (tracked with com.snowplowanalytics.snowplow application_foreground and application_background events.
     @objc
     public var index: NSNumber? {
         set {
@@ -49,4 +52,12 @@ public class LifecycleEntity: SelfDescribingJson {
             return nil
         }
     }
+    
+    // MARK: - Builders
+    
+    /// Represents the foreground index or background index (tracked with com.snowplowanalytics.snowplow application_foreground and application_background events.
+    public func index(_ index: NSNumber?) -> Self {
+        self.index = index
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/ConsentDocument.swift b/Sources/Snowplow/Events/ConsentDocument.swift
index 510861326..31a7a8b56 100644
--- a/Sources/Snowplow/Events/ConsentDocument.swift
+++ b/Sources/Snowplow/Events/ConsentDocument.swift
@@ -64,4 +64,20 @@ public class ConsentDocument: NSObject {
             schema: kSPConsentDocumentSchema,
             andDictionary: event)
     }
+    
+    // MARK: - Builders
+    
+    /// Name of the document.
+    @objc
+    public func name(_ name: String?) -> Self {
+        self.name = name
+        return self
+    }
+    
+    /// Description of the document.
+    @objc
+    public func documentDescription(_ description: String?) -> Self {
+        self.documentDescription = description
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/ConsentGranted.swift b/Sources/Snowplow/Events/ConsentGranted.swift
index 488a63c9a..369e9c44e 100644
--- a/Sources/Snowplow/Events/ConsentGranted.swift
+++ b/Sources/Snowplow/Events/ConsentGranted.swift
@@ -92,4 +92,29 @@ public class ConsentGranted: SelfDescribingAbstract {
     override func beginProcessing(withTracker tracker: Tracker) {
         entities.append(contentsOf: allDocuments) // TODO: Only the user should modify the public contexts property
     }
+    
+    // MARK: - Builders
+    
+    /// Name of the first document.
+    @objc
+    public func name(_ name: String?) -> Self {
+        self.name = name
+        return self
+    }
+    
+    /// Description of the first document.
+    @objc
+    public func documentDescription(_ description: String?) -> Self {
+        self.documentDescription = description
+        return self
+    }
+    
+    /// Other attached documents.
+    ///
+    /// Schema for the documents: `iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0`
+    @objc
+    public func documents(_ documents: [SelfDescribingJson]?) -> Self {
+        self.documents = documents
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/ConsentWithdrawn.swift b/Sources/Snowplow/Events/ConsentWithdrawn.swift
index 25bc7fa87..75bc4adc9 100644
--- a/Sources/Snowplow/Events/ConsentWithdrawn.swift
+++ b/Sources/Snowplow/Events/ConsentWithdrawn.swift
@@ -80,4 +80,50 @@ public class ConsentWithdrawn: SelfDescribingAbstract {
     override func beginProcessing(withTracker tracker: Tracker) {
         entities.append(contentsOf: allDocuments)
     }
+    
+    // MARK: - Builders
+    
+    /// Consent to all.
+    @objc
+    public func all(_ all: Bool) -> Self {
+        self.all = all
+        return self
+    }
+    
+    /// Identifier of the first document.
+    @objc
+    public func documentId(_ documentId: String?) -> Self {
+        self.documentId = documentId
+        return self
+    }
+    
+    /// Version of the first document.
+    @objc
+    public func version(_ version: String?) -> Self {
+        self.version = version
+        return self
+    }
+    
+    /// Name of the first document.
+    @objc
+    public func name(_ name: String?) -> Self {
+        self.name = name
+        return self
+    }
+    
+    /// Description of the first document.
+    @objc
+    public func documentDescription(_ description: String?) -> Self {
+        self.documentDescription = description
+        return self
+    }
+    
+    /// Other documents.
+    ///
+    /// Schema for the documents: `iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0`
+    @objc
+    public func documents(_ documents: [SelfDescribingJson]?) -> Self {
+        self.documents = documents
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/DeepLinkReceived.swift b/Sources/Snowplow/Events/DeepLinkReceived.swift
index 47788b1ba..a8b8a9174 100644
--- a/Sources/Snowplow/Events/DeepLinkReceived.swift
+++ b/Sources/Snowplow/Events/DeepLinkReceived.swift
@@ -66,4 +66,13 @@ public class DeepLinkReceived: SelfDescribingAbstract {
         payload[DeepLinkReceived.paramUrl] = url
         return payload
     }
+    
+    // MARK: - Builders
+    
+    /// Referrer URL, source of this deep-link.
+    @objc
+    public func referrer(_ referrer: String?) -> Self {
+        self.referrer = referrer
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/Ecommerce.swift b/Sources/Snowplow/Events/Ecommerce.swift
index fe89770a2..9d3525270 100644
--- a/Sources/Snowplow/Events/Ecommerce.swift
+++ b/Sources/Snowplow/Events/Ecommerce.swift
@@ -38,7 +38,7 @@ public class Ecommerce : PrimitiveAbstract {
     /// Taxes applied to the purchase.
     @objc
     public var taxValue: NSNumber?
-    /// Shipping number.
+    /// Shipping cost.
     @objc
     public var shipping: NSNumber?
     /// City for shipping.
@@ -89,4 +89,54 @@ public class Ecommerce : PrimitiveAbstract {
             _ = tracker?.track(item)
         }
     }
+    
+    // MARK: - Builders
+    
+    @objc
+    public func affiliation(_ affiliation: String?) -> Self {
+        self.affiliation = affiliation
+        return self
+    }
+    
+    /// Taxes applied to the purchase.
+    @objc
+    public func taxValue(_ taxValue: NSNumber?) -> Self {
+        self.taxValue = taxValue
+        return self
+    }
+    
+    /// Shipping cost.
+    @objc
+    public func shipping(_ shipping: NSNumber?) -> Self {
+        self.shipping = shipping
+        return self
+    }
+    
+    /// City for shipping.
+    @objc
+    public func city(_ city: String?) -> Self {
+        self.city = city
+        return self
+    }
+    
+    /// State for shipping.
+    @objc
+    public func state(_ state: String?) -> Self {
+        self.state = state
+        return self
+    }
+    
+    /// Country for shipping.
+    @objc
+    public func country(_ country: String?) -> Self {
+        self.country = country
+        return self
+    }
+    
+    /// Currency used for totalValue and taxValue.
+    @objc
+    public func currency(_ currency: String?) -> Self {
+        self.currency = currency
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/EcommerceItem.swift b/Sources/Snowplow/Events/EcommerceItem.swift
index 9f0346f28..5aa772191 100644
--- a/Sources/Snowplow/Events/EcommerceItem.swift
+++ b/Sources/Snowplow/Events/EcommerceItem.swift
@@ -67,4 +67,34 @@ public class EcommerceItem : PrimitiveAbstract {
         payload[kSPEcommItemQuantity] = String(format: "%ld", quantity)
         return payload
     }
+    
+    // MARK: - Builders
+    
+    /// Name of the item.
+    @objc
+    public func name(_ name: String?) -> Self {
+        self.name = name
+        return self
+    }
+
+    /// Category of the item.
+    @objc
+    public func category(_ category: String?) -> Self {
+        self.category = category
+        return self
+    }
+    
+    /// Currency used for the price of the item.
+    @objc
+    public func currency(_ currency: String?) -> Self {
+        self.currency = currency
+        return self
+    }
+    
+    /// OrderID of the order that contains this item.
+    @objc
+    public func orderId(_ orderId: String?) -> Self {
+        self.orderId = orderId
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/EventBase.swift b/Sources/Snowplow/Events/EventBase.swift
index 9627dbe27..d3c7fc15a 100644
--- a/Sources/Snowplow/Events/EventBase.swift
+++ b/Sources/Snowplow/Events/EventBase.swift
@@ -58,6 +58,30 @@ public class Event: NSObject {
     /// @note Internal use only - Don't use in production, it can change without notice.
     func endProcessing(withTracker tracker: Tracker) {
     }
+    
+    // MARK: - Builders
+    
+    /// The user event timestamp in milliseconds (epoch time).
+    @objc
+    public func trueTimestamp(_ timestamp: Date?) -> Self {
+        self.trueTimestamp = timestamp
+        return self
+    }
+    
+    /// The context entities attached to the event.
+    @objc
+    public func entities(_ entities: [SelfDescribingJson]) -> Self {
+        self.entities = entities
+        return self
+    }
+    
+    /// The context entities attached to the event.
+    @objc
+    @available(*, deprecated, renamed: "entities")
+    public func contexts(_ entities: [SelfDescribingJson]) -> Self {
+        self.entities = entities
+        return self
+    }
 }
 
 /// The properties for all the self-describing events.
diff --git a/Sources/Snowplow/Events/MessageNotification.swift b/Sources/Snowplow/Events/MessageNotification.swift
index e0b0f2af4..7d803dc69 100644
--- a/Sources/Snowplow/Events/MessageNotification.swift
+++ b/Sources/Snowplow/Events/MessageNotification.swift
@@ -196,4 +196,130 @@ public class MessageNotification : SelfDescribingAbstract {
         payload[kSPMessageNotificationParamTrigger] = triggerToString(trigger)
         return payload
     }
+    
+    // MARK: - Builders
+    
+    /// The action associated with the notification.
+    @objc
+    public func action(_ action: String?) -> Self {
+        self.action = action
+        return self
+    }
+    
+    /// Attachments added to the notification (they can be part of the data object).
+    @objc
+    public func attachments(_ attachments: [MessageNotificationAttachment]?) -> Self {
+        self.attachments = attachments
+        return self
+    }
+    
+    /// The notification's body.
+    @objc
+    public func body(_ body: String) -> Self {
+        self.body = body
+        return self
+    }
+    
+    /// Variable string values to be used in place of the format specifiers in bodyLocArgs to use to localize the body text to the user's current localization.
+    @objc
+    public func bodyLocArgs(_ args: [String]?) -> Self {
+        self.bodyLocArgs = args
+        return self
+    }
+    
+    /// The key to the body string in the app's string resources to use to localize the body text to the user's current localization.
+    @objc
+    public func bodyLocKey(_ key: String?) -> Self {
+        self.bodyLocKey = key
+        return self
+    }
+    
+    /// The category associated to the notification.
+    @objc
+    public func category(_ category: String?) -> Self {
+        self.category = category
+        return self
+    }
+    
+    /// The application is notified of the delivery of the notification if it's in the foreground or background, the app will be woken up (iOS only).
+    public func contentAvailable(_ available: Bool?) -> Self {
+        self.contentAvailable = available
+        return self
+    }
+    
+    /// The group which this notification is part of.
+    @objc
+    public func group(_ group: String?) -> Self {
+        self.group = group
+        return self
+    }
+    
+    /// The icon associated to the notification (Android only).
+    @objc
+    public func icon(_ icon: String?) -> Self {
+        self.icon = icon
+        return self
+    }
+    
+    /// The number of items this notification represents. It's the badge number on iOS.
+    public func notificationCount(_ count: Int?) -> Self {
+        self.notificationCount = count
+        return self
+    }
+    
+    /// The time when the event of the notification occurred.
+    @objc
+    public func notificationTimestamp(_ timestamp: String?) -> Self {
+        self.notificationTimestamp = timestamp
+        return self
+    }
+    
+    /// The sound played when the device receives the notification.
+    @objc
+    public func sound(_ sound: String?) -> Self {
+        self.sound = sound
+        return self
+    }
+    
+    /// The notification's subtitle. (iOS only)
+    @objc
+    public func subtitle(_ subtitle: String?) -> Self {
+        self.subtitle = subtitle
+        return self
+    }
+    
+    /// An identifier similar to 'group' but usable for different purposes (Android only).
+    @objc
+    public func tag(_ tag: String?) -> Self {
+        self.tag = tag
+        return self
+    }
+    
+    /// An identifier similar to 'group' but usable for different purposes (iOS only).
+    @objc
+    public func threadIdentifier(_ identifier: String?) -> Self {
+        self.threadIdentifier = identifier
+        return self
+    }
+    
+    /// The notification's title.
+    @objc
+    public func title(_ title: String) -> Self {
+        self.title = title
+        return self
+    }
+    
+    /// Variable string values to be used in place of the format specifiers in titleLocArgs to use to localize the title text to the user's current localization.
+    @objc
+    public func titleLocArgs(_ args: [String]?) -> Self {
+        self.titleLocArgs = args
+        return self
+    }
+    
+    /// The key to the title string in the app's string resources to use to localize the title text to the user's current localization.
+    @objc
+    public func titleLocKey(_ key: String?) -> Self {
+        self.titleLocKey = key
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/PageView.swift b/Sources/Snowplow/Events/PageView.swift
index e65c8145e..6bd625f2f 100644
--- a/Sources/Snowplow/Events/PageView.swift
+++ b/Sources/Snowplow/Events/PageView.swift
@@ -56,4 +56,20 @@ public class PageView : PrimitiveAbstract {
         payload[kSPPageRefr] = referrer
         return payload
     }
+    
+    // MARK: - Builders
+    
+    /// Page title.
+    @objc
+    public func pageTitle(_ title: String?) -> Self {
+        self.pageTitle = title
+        return self
+    }
+    
+    /// Page referrer url.
+    @objc
+    public func referrer(_ referrer: String?) -> Self {
+        self.referrer = referrer
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/PushNotification.swift b/Sources/Snowplow/Events/PushNotification.swift
index cb2e564d6..e5cae0641 100644
--- a/Sources/Snowplow/Events/PushNotification.swift
+++ b/Sources/Snowplow/Events/PushNotification.swift
@@ -208,4 +208,41 @@ public class NotificationContent : NSObject {
         }
         return event // copyItems: true
     }
+    
+    // MARK: - Builders
+    
+    /// The notification's subtitle.
+    @objc
+    public func subtitle(_ subtitle: String?) -> Self {
+        self.subtitle = subtitle
+        return self
+    }
+    
+    /// The sound played when the device receives the notification.
+    @objc
+    public func sound(_ sound: String?) -> Self {
+        self.sound = sound
+        return self
+    }
+    
+    /// The name of the image or storyboard to use when your app launches because of the notification.
+    @objc
+    public func launchImageName(_ name: String?) -> Self {
+        self.launchImageName = name
+        return self
+    }
+    
+    /// The custom data associated with the notification.
+    @objc
+    public func userInfo(_ userInfo: [String : Any]?) -> Self {
+        self.userInfo = userInfo
+        return self
+    }
+    
+    /// Attachments added to the notification (they can be part of the data object).
+    @objc
+    public func attachments(_ attachments: [NSObject]?) -> Self {
+        self.attachments = attachments
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/SNOWError.swift b/Sources/Snowplow/Events/SNOWError.swift
index 00e296fa1..cb6d976f8 100644
--- a/Sources/Snowplow/Events/SNOWError.swift
+++ b/Sources/Snowplow/Events/SNOWError.swift
@@ -55,4 +55,20 @@ public class SNOWError: SelfDescribingAbstract {
         payload[kSPErrorLanguage] = "SWIFT"
         return payload
     }
+    
+    // MARK: - Builders
+    
+    /// Error name
+    @objc
+    public func name(_ name: String?) -> Self {
+        self.name = name
+        return self
+    }
+    
+    /// Stacktrace for the error
+    @objc
+    public func stackTrace(_ stackTrace: String?) -> Self {
+        self.stackTrace = stackTrace
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/ScreenView.swift b/Sources/Snowplow/Events/ScreenView.swift
index 10cd2d1a0..4a8567af6 100644
--- a/Sources/Snowplow/Events/ScreenView.swift
+++ b/Sources/Snowplow/Events/ScreenView.swift
@@ -106,4 +106,55 @@ public class ScreenView: SelfDescribingAbstract {
         ];
         return arr[screenType.rawValue];
     }
+    
+    // MARK: - Builders
+    
+    /// Type of screen.
+    @objc
+    public func type(_ type: String?) -> Self {
+        self.type = type
+        return self
+    }
+    
+    /// Name of the previous screen.
+    @objc
+    public func previousName(_ previousName: String?) -> Self {
+        self.previousName = previousName
+        return self
+    }
+    
+    /// Identifier of the previous screen.
+    @objc
+    public func previousId(_ previousId: String?) -> Self {
+        self.previousId = previousId
+        return self
+    }
+    
+    /// Type of the previous screen.
+    @objc
+    public func previousType(_ previousType: String?) -> Self {
+        self.previousType = previousType
+        return self
+    }
+    
+    /// Type of transition between previous and current screen,
+    @objc
+    public func transitionType(_ transitionType: String?) -> Self {
+        self.transitionType = transitionType
+        return self
+    }
+    
+    /// Name of the ViewController subclass.
+    @objc
+    public func viewControllerClassName(_ viewControllerClassName: String?) -> Self {
+        self.viewControllerClassName = viewControllerClassName
+        return self
+    }
+    
+    /// Name of the top ViewController subclass.
+    @objc
+    public func topViewControllerClassName(_ topViewControllerClassName: String?) -> Self {
+        self.topViewControllerClassName = topViewControllerClassName
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/Structured.swift b/Sources/Snowplow/Events/Structured.swift
index 2ada8ff08..67ddc4d88 100644
--- a/Sources/Snowplow/Events/Structured.swift
+++ b/Sources/Snowplow/Events/Structured.swift
@@ -70,4 +70,33 @@ public class Structured: PrimitiveAbstract {
         }
         return payload
     }
+    
+    // MARK: - Builders
+    
+    /// Identifies the specific object being actioned.
+    ///
+    /// E.g., ID of the video being played, or the SKU or the product added-to-basket.
+    @objc
+    public func label(_ label: String?) -> Self {
+        self.label = label
+        return self
+    }
+    
+    /// Describes the object or the action performed on it.
+    ///
+    /// This might be the quantity of an item added to basket
+    @objc
+    public func property(_ property: String?) -> Self {
+        self.property = property
+        return self
+    }
+    
+    /// Quantifies or further describes the user action.
+    ///
+    /// This might be the price of an item added-to-basket, or the starting time of the video where play was just pressed.
+    @objc
+    public func value(_ value: NSNumber?) -> Self {
+        self.value = value
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Events/Timing.swift b/Sources/Snowplow/Events/Timing.swift
index 7e3fdc033..c1c0871c3 100644
--- a/Sources/Snowplow/Events/Timing.swift
+++ b/Sources/Snowplow/Events/Timing.swift
@@ -62,4 +62,13 @@ public class Timing: SelfDescribingAbstract {
         if let label = label { payload[kSPUtLabel] = label }
         return payload
     }
+    
+    // MARK: - Builders
+    
+    /// The timing label
+    @objc
+    public func label(_ label: String?) -> Self {
+        self.label = label
+        return self
+    }
 }
diff --git a/Sources/Snowplow/Snowplow.swift b/Sources/Snowplow/Snowplow.swift
index 98cacb23a..5d8bc028b 100644
--- a/Sources/Snowplow/Snowplow.swift
+++ b/Sources/Snowplow/Snowplow.swift
@@ -63,7 +63,7 @@ public class Snowplow: NSObject {
     /// Those events are attached to the namespace.
     /// If the tracker is removed or the app relaunched with a different namespace, those events can't
     /// be sent to the collector and they remain in a zombie state inside the EventStore.
-    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// To remove all the zombie events you can call an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
     /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
     ///
     /// - Parameters:
@@ -95,7 +95,7 @@ public class Snowplow: NSObject {
     /// Those events are attached to the namespace.
     /// If the tracker is removed or the app relaunched with a different namespace, those events can't
     /// be sent to the collector and they remain in a zombie state inside the EventStore.
-    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// To remove all the zombie events you can call an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
     /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
     ///
     /// - Parameter onSuccess: The callback called when a configuration (cached or downloaded) is set It passes the list of the namespaces associated
@@ -125,7 +125,7 @@ public class Snowplow: NSObject {
     /// Those events are attached to the namespace.
     /// If the tracker is removed or the app relaunched with a different namespace, those events can't
     /// be sent to the collector and they remain in a zombie state inside the EventStore.
-    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// To remove all the zombie events you can call an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
     /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
     ///
     /// - Parameters:
@@ -140,6 +140,7 @@ public class Snowplow: NSObject {
         return createTracker(namespace: namespace, network: networkConfiguration, configurations: [])
     }
 
+#if swift(>=5.4)
     /// Create a new tracker instance which will be used inside the app to track events.
     ///
     /// The app can run multiple tracker instances which will be identified by string `namespaces`.
@@ -154,19 +155,23 @@ public class Snowplow: NSObject {
     /// Those events are attached to the namespace.
     /// If the tracker is removed or the app relaunched with a different namespace, those events can't
     /// be sent to the collector and they remain in a zombie state inside the EventStore.
-    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// To remove all the zombie events you can call an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
     /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
     ///
     /// - Parameters:
     ///   - namespace: The namespace used to identify the current tracker among the possible
     ///                  multiple tracker instances.
-    ///   - networkConfiguration: The NetworkConfiguration object with settings for the communication with the
-    ///                collector.
+    ///   - endpoint: The URL of the collector.
+    ///   - method: The method for the requests to the collector (GET or POST).
+    ///   - configurationBuilder: Swift DSL builder for your configuration objects (e.g, `EmitterConfiguration`, `TrackerConfiguration`)
     /// - Returns: The tracker instance created.
-    @objc
-    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration) -> TrackerController? {
-        return createTracker(namespace: namespace, network: networkConfiguration, configurations: [])
+    public class func createTracker(namespace: String, endpoint: String, method: HttpMethodOptions = .post, @ConfigurationBuilder _ configurationBuilder: () -> [ConfigurationProtocol]) -> TrackerController? {
+        let configurations = configurationBuilder()
+        return createTracker(namespace: namespace,
+                             network: NetworkConfiguration(endpoint: endpoint, method: method),
+                             configurations: configurations)
     }
+#endif
 
     /// Create a new tracker instance which will be used inside the app to track events.
     ///
@@ -182,7 +187,7 @@ public class Snowplow: NSObject {
     /// Those events are attached to the namespace.
     /// If the tracker is removed or the app relaunched with a different namespace, those events can't
     /// be sent to the collector and they remain in a zombie state inside the EventStore.
-    /// To remove all the zombie events you can an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// To remove all the zombie events you can call an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
     /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
     ///
     /// - Parameters:
@@ -194,7 +199,7 @@ public class Snowplow: NSObject {
     ///                       the tracker.
     /// - Returns: The tracker instance created.
     @objc
-    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [ConfigurationProtocol]) -> TrackerController? {
+    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration, configurations: [ConfigurationProtocol] = []) -> TrackerController? {
         if let serviceProvider = serviceProviderInstances[namespace] {
             serviceProvider.reset(configurations: configurations + [networkConfiguration])
             return serviceProvider.trackerController
@@ -205,6 +210,39 @@ public class Snowplow: NSObject {
         }
     }
 
+#if swift(>=5.4)
+    /// Create a new tracker instance which will be used inside the app to track events.
+    ///
+    /// The app can run multiple tracker instances which will be identified by string `namespaces`.
+    /// The tracker will be configured with default setting and only the collector endpoint URL need
+    /// to be passed for the configuration.
+    /// For the default configuration of the tracker see `TrackerConfiguration(String)`.
+    ///
+    /// To configure tracker with more details see `createTracker(Context, String, NetworkConfiguration, Configuration...)`
+    /// To use the tracker as singleton see `getDefaultTracker()`
+    ///
+    /// IMPORTANT: The EventStore will persist all the events that have been tracked but not yet sent.
+    /// Those events are attached to the namespace.
+    /// If the tracker is removed or the app relaunched with a different namespace, those events can't
+    /// be sent to the collector and they remain in a zombie state inside the EventStore.
+    /// To remove all the zombie events you can call an internal method `removeUnsentEventsExceptForNamespaces` on `SPSQLEventStore`
+    /// which will delete all the EventStores instanced with namespaces not listed in the passed list.
+    ///
+    /// - Parameters:
+    ///   - namespace: The namespace used to identify the current tracker among the possible
+    ///                  multiple tracker instances.
+    ///   - networkConfiguration: The NetworkConfiguration object with settings for the communication with the
+    ///                collector.
+    ///   - configurationBuilder: Swift DSL builder for your configuration objects (e.g, `EmitterConfiguration`, `TrackerConfiguration`)
+    /// - Returns: The tracker instance created.
+    public class func createTracker(namespace: String, network networkConfiguration: NetworkConfiguration, @ConfigurationBuilder _ configurationBuilder: () -> [ConfigurationProtocol]) -> TrackerController? {
+        let configurations = configurationBuilder()
+        return createTracker(namespace: namespace,
+                             network: networkConfiguration,
+                             configurations: configurations)
+    }
+#endif
+    
     /// Get the default tracker instance.
     ///
     /// The default tracker instance is the first created in the app, but that can be overridden programmatically
diff --git a/Tests/Configurations/TestConfigurationBuilder.swift b/Tests/Configurations/TestConfigurationBuilder.swift
new file mode 100644
index 000000000..e320bd514
--- /dev/null
+++ b/Tests/Configurations/TestConfigurationBuilder.swift
@@ -0,0 +1,59 @@
+//
+//  TestConfigurationBuilder.swift
+//  Snowplow-iOSTests
+//
+//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//
+//  This program is licensed to you under the Apache License Version 2.0,
+//  and you may not use this file except in compliance with the Apache License
+//  Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
+//  http://www.apache.org/licenses/LICENSE-2.0.
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the Apache License Version 2.0 is distributed on
+//  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+//  express or implied. See the Apache License Version 2.0 for the specific
+//  language governing permissions and limitations there under.
+//
+//  Authors: Matus Tomlein
+//  License: Apache License Version 2.0
+//
+
+import XCTest
+@testable import SnowplowTracker
+
+class TestConfigurationBuilder: XCTestCase {
+#if swift(>=5.4)
+    func testCreateTrackerUsingBuilder() {
+        let pluginName: String? = "plugin"
+        let tracker = Snowplow.createTracker(namespace: "ns",
+                                             endpoint: "https://snowplow.io") {
+            TrackerConfiguration()
+                .installAutotracking(false)
+                .exceptionAutotracking(false)
+                .appId("app_id")
+            
+            SubjectConfiguration()
+                .domainUserId("xxx")
+            
+            EmitterConfiguration()
+                .threadPoolSize(33)
+            
+            if let pluginName = pluginName {
+                PluginConfiguration(identifier: pluginName)
+                    .afterTrack { event in }
+            }
+        }
+        
+        XCTAssertEqual("ns", tracker?.namespace)
+        XCTAssertEqual("app_id", tracker?.appId)
+        XCTAssertTrue(tracker?.network?.endpoint?.starts(with: "https://snowplow.io") ?? false)
+        XCTAssertEqual(.post, tracker?.network?.method)
+        XCTAssertFalse(tracker!.installAutotracking)
+        XCTAssertFalse(tracker!.exceptionAutotracking)
+        XCTAssertEqual("xxx", tracker?.subject?.domainUserId)
+        XCTAssertEqual(33, tracker?.emitter?.threadPoolSize)
+        XCTAssertEqual(["plugin"], tracker?.plugins.identifiers)
+    }
+#endif
+}
diff --git a/Tests/TestPlugins.swift b/Tests/TestPlugins.swift
index d455b5bb6..7c67cc5f4 100644
--- a/Tests/TestPlugins.swift
+++ b/Tests/TestPlugins.swift
@@ -27,120 +27,120 @@ class TestPlugins: XCTestCase {
         Snowplow.removeAllTrackers()
         super.tearDown()
     }
-
+    
     func testAddsEntitiesToEvent() {
         let plugin = PluginConfiguration(identifier: "plugin")
-        plugin.entities { [SelfDescribingJson(schema: "schema", andData: ["val": $0.payload["se_ca"]!])] }
+            .entities { [SelfDescribingJson(schema: "schema", andData: ["val": $0.payload["se_ca"]!])] }
         
-        let testPlugin = PluginConfiguration(identifier: "test")
         let expect = expectation(description: "Has context entity on event")
-        testPlugin.afterTrack { event in
-            XCTAssertTrue(
-                event.entities.filter({ entity in
-                    entity.schema == "schema" && entity.data["val"] as? String == "cat"
-                }).count == 1
-            )
-            expect.fulfill()
-        }
+        let testPlugin = PluginConfiguration(identifier: "test")
+            .afterTrack { event in
+                XCTAssertTrue(
+                    event.entities.filter({ entity in
+                        entity.schema == "schema" && entity.data["val"] as? String == "cat"
+                    }).count == 1
+                )
+                expect.fulfill()
+            }
         
         let tracker = createTracker([plugin, testPlugin])
         _ = tracker.track(Structured(category: "cat", action: "act"))
         
         wait(for: [expect], timeout: 10)
     }
-
+    
     func testAddsEntitiesFromMultiplePlugins() {
         let plugin1 = PluginConfiguration(identifier: "plugin1")
-        plugin1.entities { _ in [SelfDescribingJson(schema: "schema1", andData: [:])] }
+            .entities { _ in [SelfDescribingJson(schema: "schema1", andData: [:])] }
         
         let plugin2 = PluginConfiguration(identifier: "plugin2")
-        plugin2.entities { _ in [SelfDescribingJson(schema: "schema2", andData: [:])] }
+            .entities { _ in [SelfDescribingJson(schema: "schema2", andData: [:])] }
         
-        let testPlugin = PluginConfiguration(identifier: "test")
         let expect = expectation(description: "Has both context entities on event")
-        testPlugin.afterTrack { event in
-            XCTAssertTrue(
-                event.entities.filter({ $0.schema == "schema1" }).count == 1
-            )
-            XCTAssertTrue(
-                event.entities.filter({ $0.schema == "schema2" }).count == 1
-            )
-            expect.fulfill()
-        }
+        let testPlugin = PluginConfiguration(identifier: "test")
+            .afterTrack { event in
+                XCTAssertTrue(
+                    event.entities.filter({ $0.schema == "schema1" }).count == 1
+                )
+                XCTAssertTrue(
+                    event.entities.filter({ $0.schema == "schema2" }).count == 1
+                )
+                expect.fulfill()
+            }
         
         let tracker = createTracker([plugin1, plugin2, testPlugin])
         _ = tracker.track(ScreenView(name: "sv"))
-
+        
         wait(for: [expect], timeout: 1)
     }
-
+    
     func testAddsEntitiesOnlyForEventsMatchingSchema() {
         let plugin = PluginConfiguration(identifier: "plugin")
-        plugin.entities(schemas: ["schema1"]) { _ in [SelfDescribingJson(schema: "xx", andData: [:])] }
+            .entities(schemas: ["schema1"]) { _ in [SelfDescribingJson(schema: "xx", andData: [:])] }
         
         var event1HasEntity: Bool? = nil
         var event2HasEntity: Bool? = nil
         
         let testPlugin = PluginConfiguration(identifier: "test")
-        testPlugin.afterTrack { event in
-            if event.schema == "schema1" {
-                event1HasEntity = event.entities.contains(where: { $0.schema == "xx" })
-            }
-            if event.schema == "schema2" {
-                event2HasEntity = event.entities.contains(where: { $0.schema == "xx" })
+            .afterTrack { event in
+                if event.schema == "schema1" {
+                    event1HasEntity = event.entities.contains(where: { $0.schema == "xx" })
+                }
+                if event.schema == "schema2" {
+                    event2HasEntity = event.entities.contains(where: { $0.schema == "xx" })
+                }
             }
-        }
         
         let tracker = createTracker([plugin, testPlugin])
         _ = tracker.track(SelfDescribing(schema: "schema1", payload: [:]))
         _ = tracker.track(SelfDescribing(schema: "schema2", payload: [:]))
-
+        
         waitForEventsToBeTracked()
-
+        
         XCTAssertTrue(event1HasEntity!)
         XCTAssertFalse(event2HasEntity!)
     }
-
+    
     func testCallsAfterTrackOnlyForEventsMatchingSchema() {
         var event1Called: Bool = false
         var event2Called: Bool = false
         var event3Called: Bool = false
-
+        
         let plugin = PluginConfiguration(identifier: "plugin")
-        plugin.afterTrack(schemas: ["schema1"]) { event in
-            if event.schema == "schema1" { event1Called = true }
-            if event.schema == "schema2" { event2Called = true }
-            if event.schema == nil { event3Called = true }
-        }
-
+            .afterTrack(schemas: ["schema1"]) { event in
+                if event.schema == "schema1" { event1Called = true }
+                if event.schema == "schema2" { event2Called = true }
+                if event.schema == nil { event3Called = true }
+            }
+        
         let tracker = createTracker([plugin])
         _ = tracker.track(SelfDescribing(schema: "schema1", payload: [:]))
         _ = tracker.track(SelfDescribing(schema: "schema2", payload: [:]))
         _ = tracker.track(Structured(category: "cat", action: "act"))
-
+        
         waitForEventsToBeTracked()
-
+        
         XCTAssertTrue(event1Called)
         XCTAssertFalse(event2Called)
         XCTAssertFalse(event3Called)
     }
-
+    
     func testCallsAfterTrackOnlyForStructuredEvent() {
         var selfDescribingCalled: Bool = false
         var structuredCalled: Bool = false
-
+        
         let plugin = PluginConfiguration(identifier: "plugin")
-        plugin.afterTrack(schemas: ["se"]) { event in
-            if event.schema == "schema1" { selfDescribingCalled = true }
-            if event.schema == nil { structuredCalled = true }
-        }
-
+            .afterTrack(schemas: ["se"]) { event in
+                if event.schema == "schema1" { selfDescribingCalled = true }
+                if event.schema == nil { structuredCalled = true }
+            }
+        
         let tracker = createTracker([plugin])
         _ = tracker.track(SelfDescribing(schema: "schema1", payload: [:]))
         _ = tracker.track(Structured(category: "cat", action: "act"))
-
+        
         waitForEventsToBeTracked()
-
+        
         XCTAssertTrue(structuredCalled)
         XCTAssertFalse(selfDescribingCalled)
     }
@@ -148,9 +148,9 @@ class TestPlugins: XCTestCase {
     func testAddsPluginToTracker() {
         let tracker = createTracker([])
         
-        let plugin = PluginConfiguration(identifier: "plugin")
         let expect = expectation(description: "Plugin called")
-        plugin.afterTrack { _ -> Void in expect.fulfill() }
+        let plugin = PluginConfiguration(identifier: "plugin")
+            .afterTrack { _ -> Void in expect.fulfill() }
         tracker.plugins.add(plugin: plugin)
         
         _ = tracker.track(ScreenView(name: "sv"))
@@ -161,21 +161,21 @@ class TestPlugins: XCTestCase {
     func testRemovesPluginFromTracker() {
         var pluginCalled = false
         let plugin = PluginConfiguration(identifier: "plugin")
-        plugin.afterTrack { _ in pluginCalled = true }
-
+            .afterTrack { _ in pluginCalled = true }
+        
         let tracker = createTracker([plugin])
         XCTAssertEqual(["plugin"], tracker.plugins.identifiers)
         tracker.plugins.remove(identifier: "plugin")
-
+        
         _ = tracker.track(ScreenView(name: "sv"))
-
+        
         let expect = expectation(description: "Wait for events to be tracked")
         DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { () -> Void in expect.fulfill() }
         wait(for: [expect], timeout: 1)
-
+        
         XCTAssertFalse(pluginCalled)
     }
-
+    
     private func createTracker(_ configurations: [ConfigurationProtocol]) -> TrackerController {
         let networkConfig = NetworkConfiguration(networkConnection: MockNetworkConnection(requestOption: .post, statusCode: 200))
         let trackerConfig = TrackerConfiguration()
diff --git a/Tests/TestScreenViewModifier.swift b/Tests/TestScreenViewModifier.swift
index 38f122b30..525dd3c8e 100644
--- a/Tests/TestScreenViewModifier.swift
+++ b/Tests/TestScreenViewModifier.swift
@@ -60,18 +60,18 @@ class TestScreenViewModifier: XCTestCase {
     }
     
     private func createTracker(afterTrack: @escaping (InspectableEvent) -> ()) {
-        let plugin = PluginConfiguration(identifier: "screenViewPlugin")
-        plugin.afterTrack(closure: afterTrack)
-        
         let networkConfig = NetworkConfiguration(networkConnection: MockNetworkConnection(requestOption: .post, statusCode: 200))
         
-        let trackerConfig = TrackerConfiguration()
-        trackerConfig.installAutotracking = false
-        trackerConfig.lifecycleAutotracking = false
-        
-        _ = Snowplow.createTracker(namespace: "screenViewTracker",
-                                      network: networkConfig,
-                                      configurations: [trackerConfig, plugin])!
+        _ = Snowplow.createTracker(
+            namespace: "screenViewTracker",
+            network: networkConfig,
+            configurations: [
+                PluginConfiguration(identifier: "screenViewPlugin")
+                    .afterTrack(closure: afterTrack),
+                TrackerConfiguration()
+                    .installAutotracking(false)
+                    .lifecycleAutotracking(false)
+            ])
     }
 }
 

From 9ac9acf06df3f1b0e149d7854a37959682fcfa08 Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Thu, 2 Feb 2023 13:51:21 +0100
Subject: [PATCH 15/19] Update year in copyright headers and remove authors
 from headers in source files (#759)

---
 LICENSE                                               |  2 +-
 README.md                                             |  2 +-
 Sources/Core/Emitter/Emitter.swift                    | 10 +---------
 Sources/Core/Emitter/EmitterConfigurationUpdate.swift | 10 +---------
 Sources/Core/Emitter/EmitterControllerImpl.swift      | 10 +---------
 Sources/Core/Emitter/EmitterEventProcessing.swift     | 10 +---------
 Sources/Core/GDPR/GDPRConfigurationUpdate.swift       | 10 +---------
 Sources/Core/GDPR/GDPRContext.swift                   | 10 +---------
 Sources/Core/GDPR/GDPRControllerImpl.swift            |  9 +--------
 .../GlobalContextPluginConfiguration.swift            | 10 +---------
 .../GlobalContexts/GlobalContextsControllerImpl.swift | 10 +---------
 Sources/Core/GlobalContexts/SchemaRule.swift          | 10 +---------
 Sources/Core/Logger/Logger.swift                      | 10 +---------
 .../NetworkConfigurationUpdate.swift                  | 10 +---------
 .../NetworkConnection/NetworkControllerImpl.swift     | 10 +---------
 .../Core/RemoteConfiguration/ConfigurationCache.swift | 10 +---------
 .../RemoteConfiguration/ConfigurationFetcher.swift    | 10 +---------
 .../RemoteConfiguration/ConfigurationProvider.swift   | 10 +---------
 .../FetchedConfigurationBundle.swift                  | 10 +---------
 Sources/Core/ScreenViewTracking/ScreenState.swift     | 10 +---------
 .../Core/ScreenViewTracking/ScreenStateMachine.swift  | 10 +---------
 .../Core/ScreenViewTracking/ScreenViewModifier.swift  |  9 +--------
 .../ScreenViewTracking/UIKitScreenViewTracking.swift  | 10 +---------
 Sources/Core/Session/Session.swift                    | 10 +---------
 Sources/Core/Session/SessionConfigurationUpdate.swift | 10 +---------
 Sources/Core/Session/SessionControllerImpl.swift      | 10 +---------
 Sources/Core/StateMachine/DeepLinkState.swift         | 10 +---------
 Sources/Core/StateMachine/DeepLinkStateMachine.swift  | 10 +---------
 Sources/Core/StateMachine/LifecycleState.swift        |  9 +--------
 Sources/Core/StateMachine/LifecycleStateMachine.swift |  9 +--------
 Sources/Core/StateMachine/PluginStateMachine.swift    |  9 +--------
 Sources/Core/StateMachine/State.swift                 | 10 +---------
 Sources/Core/StateMachine/StateFuture.swift           | 10 +---------
 Sources/Core/StateMachine/StateMachineEvent.swift     | 10 +---------
 Sources/Core/StateMachine/StateMachineProtocol.swift  | 10 +---------
 Sources/Core/StateMachine/StateManager.swift          | 10 +---------
 Sources/Core/StateMachine/TrackerState.swift          | 10 +---------
 Sources/Core/StateMachine/TrackerStateSnapshot.swift  | 10 +---------
 Sources/Core/Storage/MemoryEventStore.swift           | 10 +---------
 Sources/Core/Storage/SQLiteEventStore.swift           | 10 +---------
 Sources/Core/Subject/PlatformContext.swift            | 10 +---------
 Sources/Core/Subject/Subject.swift                    | 10 +---------
 Sources/Core/Subject/SubjectConfigurationUpdate.swift |  9 +--------
 Sources/Core/Subject/SubjectControllerImpl.swift      | 10 +---------
 Sources/Core/Tracker/InstallTracker.swift             | 10 +---------
 Sources/Core/Tracker/PluginsControllerImpl.swift      | 10 +---------
 Sources/Core/Tracker/ServiceProvider.swift            | 10 +---------
 Sources/Core/Tracker/ServiceProviderProtocol.swift    | 10 +---------
 Sources/Core/Tracker/Tracker.swift                    | 10 +---------
 Sources/Core/Tracker/TrackerConfigurationUpdate.swift | 10 +---------
 Sources/Core/Tracker/TrackerControllerImpl.swift      | 10 +---------
 Sources/Core/Tracker/TrackerDefaults.swift            | 10 +---------
 Sources/Core/Tracker/TrackerEvent.swift               | 10 +---------
 Sources/Core/Tracker/WebViewMessageHandler.swift      | 10 +---------
 Sources/Core/TrackerConstants.swift                   | 10 +---------
 Sources/Core/Utils/DataPersistence.swift              | 10 +---------
 Sources/Core/Utils/DeviceInfoMonitor.swift            | 10 +---------
 Sources/Core/Utils/SNOWReachability.swift             | 10 +---------
 Sources/Core/Utils/Utilities.swift                    | 10 +---------
 .../Configurations/ConfigurationBuilder.swift         | 10 +---------
 .../Snowplow/Configurations/ConfigurationBundle.swift | 10 +---------
 .../Configurations/ConfigurationProtocol.swift        | 10 +---------
 .../Snowplow/Configurations/ConfigurationState.swift  | 10 +---------
 .../Configurations/EmitterConfiguration.swift         | 10 +---------
 .../Configurations/FocalMeterConfiguration.swift      |  8 --------
 .../Snowplow/Configurations/GDPRConfiguration.swift   | 10 +---------
 .../Configurations/GlobalContextsConfiguration.swift  | 10 +---------
 .../Configurations/NetworkConfiguration.swift         | 10 +---------
 .../Snowplow/Configurations/PluginConfiguration.swift |  9 +--------
 .../Snowplow/Configurations/RemoteConfiguration.swift | 10 +---------
 .../Configurations/SerializableConfiguration.swift    | 10 +---------
 .../Configurations/SessionConfiguration.swift         | 10 +---------
 .../Configurations/SubjectConfiguration.swift         |  9 +--------
 .../Configurations/TrackerConfiguration.swift         | 10 +---------
 Sources/Snowplow/Controllers/Controller.swift         |  9 +--------
 Sources/Snowplow/Controllers/EmitterController.swift  | 10 +---------
 Sources/Snowplow/Controllers/GDPRController.swift     | 10 +---------
 .../Controllers/GlobalContextsController.swift        | 10 +---------
 Sources/Snowplow/Controllers/NetworkController.swift  | 10 +---------
 Sources/Snowplow/Controllers/PluginsController.swift  | 10 +---------
 Sources/Snowplow/Controllers/SessionController.swift  | 10 +---------
 Sources/Snowplow/Controllers/SubjectController.swift  | 10 +---------
 Sources/Snowplow/Controllers/TrackerController.swift  | 10 +---------
 Sources/Snowplow/Emitter/BufferOption.swift           |  9 +--------
 Sources/Snowplow/Emitter/EmitterDefaults.swift        | 10 +---------
 Sources/Snowplow/Emitter/EmitterEvent.swift           | 10 +---------
 Sources/Snowplow/Emitter/EventStore.swift             | 10 +---------
 Sources/Snowplow/Entities/DeepLinkEntity.swift        |  9 +--------
 Sources/Snowplow/Entities/LifecycleEntity.swift       |  9 +--------
 Sources/Snowplow/Events/Background.swift              | 10 +---------
 Sources/Snowplow/Events/ConsentDocument.swift         | 10 +---------
 Sources/Snowplow/Events/ConsentGranted.swift          | 10 +---------
 Sources/Snowplow/Events/ConsentWithdrawn.swift        | 10 +---------
 Sources/Snowplow/Events/DeepLinkReceived.swift        |  9 +--------
 Sources/Snowplow/Events/Ecommerce.swift               | 10 +---------
 Sources/Snowplow/Events/EcommerceItem.swift           | 10 +---------
 Sources/Snowplow/Events/EventBase.swift               | 10 +---------
 Sources/Snowplow/Events/Foreground.swift              | 10 +---------
 Sources/Snowplow/Events/MessageNotification.swift     |  9 +--------
 .../Events/MessageNotificationAttachment.swift        |  9 +--------
 Sources/Snowplow/Events/PageView.swift                | 10 +---------
 Sources/Snowplow/Events/PushNotification.swift        |  9 +--------
 Sources/Snowplow/Events/SNOWError.swift               | 10 +---------
 Sources/Snowplow/Events/ScreenView.swift              | 10 +---------
 Sources/Snowplow/Events/SelfDescribing.swift          | 10 +---------
 Sources/Snowplow/Events/Structured.swift              | 10 +---------
 Sources/Snowplow/Events/Timing.swift                  | 10 +---------
 Sources/Snowplow/Events/TrackerError.swift            | 10 +---------
 .../Snowplow/GlobalContexts/ContextGenerator.swift    | 10 +---------
 Sources/Snowplow/GlobalContexts/GlobalContext.swift   | 10 +---------
 Sources/Snowplow/GlobalContexts/SchemaRuleset.swift   | 10 +---------
 .../Snowplow/Network/DefaultNetworkConnection.swift   | 10 +---------
 Sources/Snowplow/Network/HttpMethodOptions.swift      | 10 +---------
 Sources/Snowplow/Network/NetworkConnection.swift      | 10 +---------
 Sources/Snowplow/Network/ProtocolOptions.swift        | 10 +---------
 Sources/Snowplow/Network/Request.swift                | 10 +---------
 Sources/Snowplow/Network/RequestCallback.swift        | 10 +---------
 Sources/Snowplow/Network/RequestResult.swift          | 10 +---------
 Sources/Snowplow/Payload/Payload.swift                | 10 +---------
 Sources/Snowplow/Payload/SelfDescribingJson.swift     | 10 +---------
 Sources/Snowplow/Snowplow.swift                       | 10 +---------
 Sources/Snowplow/Tracker/DevicePlatform.swift         | 10 +---------
 Sources/Snowplow/Tracker/InspectableEvent.swift       | 10 +---------
 Sources/Snowplow/Tracker/LogLevel.swift               | 10 +---------
 Sources/Snowplow/Tracker/LoggerDelegate.swift         | 10 +---------
 Sources/Snowplow/Tracker/SessionState.swift           | 10 +---------
 Sources/Snowplow/Tracker/View.swift                   |  9 +--------
 Sources/Snowplow/Utils/GDPRProcessingBasis.swift      | 10 +---------
 Sources/Snowplow/Utils/SPSize.swift                   | 10 +---------
 Tests/Configurations/TestConfigurationBuilder.swift   | 10 +---------
 Tests/Configurations/TestEmitterConfiguration.swift   | 10 +---------
 Tests/Configurations/TestMultipleInstances.swift      | 10 +---------
 Tests/Configurations/TestRemoteConfiguration.swift    | 10 +---------
 Tests/Configurations/TestTrackerConfiguration.swift   | 10 +---------
 Tests/Configurations/TestTrackerController.swift      | 10 +---------
 Tests/Global Contexts/TestGlobalContexts.swift        | 11 +----------
 Tests/Global Contexts/TestSchemaRuleset.swift         | 11 +----------
 Tests/Integration/TestTrackEventsToMicro.swift        | 10 +---------
 Tests/Legacy Tests/LegacyTestEmitter.swift            | 11 +----------
 Tests/Legacy Tests/LegacyTestSubject.swift            | 10 +---------
 Tests/Legacy Tests/LegacyTestTracker.swift            | 10 +---------
 Tests/TestDataPersistence.swift                       | 10 +---------
 Tests/TestEvents.swift                                | 11 +----------
 Tests/TestLifecycleState.swift                        | 10 +---------
 Tests/TestLogger.swift                                | 10 +---------
 Tests/TestMemoryEventStore.swift                      | 10 +---------
 Tests/TestNetworkConnection.swift                     | 10 +---------
 Tests/TestPayload.swift                               | 10 +---------
 Tests/TestPlatformContext.swift                       | 10 +---------
 Tests/TestPlugins.swift                               | 10 +---------
 Tests/TestRequest.swift                               | 10 +---------
 Tests/TestRequestResult.swift                         | 11 +----------
 Tests/TestSQLiteEventStore.swift                      | 10 +---------
 Tests/TestScreenState.swift                           | 10 +---------
 Tests/TestScreenViewModifier.swift                    | 10 +---------
 Tests/TestSelfDescribingJson.swift                    | 10 +---------
 Tests/TestServiceProvider.swift                       | 10 +---------
 Tests/TestSession.swift                               | 11 +----------
 Tests/TestStateManager.swift                          | 10 +---------
 Tests/TestSubject.swift                               | 10 +---------
 Tests/TestUtils.swift                                 | 10 +---------
 Tests/TestWebViewMessageHandler.swift                 | 10 +---------
 Tests/Utils/Micro.swift                               | 10 +---------
 Tests/Utils/MockDeviceInfoMonitor.swift               | 10 +---------
 Tests/Utils/MockEventStore.swift                      | 10 +---------
 Tests/Utils/MockLoggerDelegate.swift                  | 10 +---------
 Tests/Utils/MockNetworkConnection.swift               | 10 +---------
 Tests/Utils/MockWKScriptMessage.swift                 | 10 +---------
 168 files changed, 167 insertions(+), 1484 deletions(-)
 delete mode 100644 Sources/Snowplow/Configurations/FocalMeterConfiguration.swift

diff --git a/LICENSE b/LICENSE
index a829d1d35..8e001cd71 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright 2022 Snowplow Analytics Ltd.
+   Copyright 2023 Snowplow Analytics Ltd.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index 2abfc9097..7eaaa3150 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ Some examples of demo apps instrumented with our iOS Tracker can be found in the
 
 ## Copyright and license
 
-The Snowplow iOS/macOS/tvOS/watchOS Tracker is copyright 2013-2022 Snowplow Analytics Ltd.
+The Snowplow iOS/macOS/tvOS/watchOS Tracker is copyright 2013-2023 Snowplow Analytics Ltd.
 
 Licensed under the **[Apache License, Version 2.0][license]** (the "License");
 you may not use this software except in compliance with the License.
diff --git a/Sources/Core/Emitter/Emitter.swift b/Sources/Core/Emitter/Emitter.swift
index 88986a54a..45197a03d 100644
--- a/Sources/Core/Emitter/Emitter.swift
+++ b/Sources/Core/Emitter/Emitter.swift
@@ -1,8 +1,4 @@
-//
-//  Emitter.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Emitter/EmitterConfigurationUpdate.swift b/Sources/Core/Emitter/EmitterConfigurationUpdate.swift
index 26464f75c..137298c1b 100644
--- a/Sources/Core/Emitter/EmitterConfigurationUpdate.swift
+++ b/Sources/Core/Emitter/EmitterConfigurationUpdate.swift
@@ -1,8 +1,4 @@
-//
-//  SPEmitterConfigurationUpdate.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Emitter/EmitterControllerImpl.swift b/Sources/Core/Emitter/EmitterControllerImpl.swift
index 090a71112..e6ab16c00 100644
--- a/Sources/Core/Emitter/EmitterControllerImpl.swift
+++ b/Sources/Core/Emitter/EmitterControllerImpl.swift
@@ -1,8 +1,4 @@
-//
-//  SPEmitterControllerImpl.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Emitter/EmitterEventProcessing.swift b/Sources/Core/Emitter/EmitterEventProcessing.swift
index d87419151..c980f696d 100644
--- a/Sources/Core/Emitter/EmitterEventProcessing.swift
+++ b/Sources/Core/Emitter/EmitterEventProcessing.swift
@@ -1,8 +1,4 @@
-//
-//  SPEmitterEventProcessing.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/GDPR/GDPRConfigurationUpdate.swift b/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
index e88178488..480be8573 100644
--- a/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
+++ b/Sources/Core/GDPR/GDPRConfigurationUpdate.swift
@@ -1,8 +1,4 @@
-//
-//  SPGDPRConfigurationUpdate.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/GDPR/GDPRContext.swift b/Sources/Core/GDPR/GDPRContext.swift
index 29dcfaf85..a65f5939b 100644
--- a/Sources/Core/GDPR/GDPRContext.swift
+++ b/Sources/Core/GDPR/GDPRContext.swift
@@ -1,8 +1,4 @@
-//
-//  SPGdprContext.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/GDPR/GDPRControllerImpl.swift b/Sources/Core/GDPR/GDPRControllerImpl.swift
index fa30531c8..044b96eca 100644
--- a/Sources/Core/GDPR/GDPRControllerImpl.swift
+++ b/Sources/Core/GDPR/GDPRControllerImpl.swift
@@ -1,7 +1,4 @@
-//  SPGDPRControllerImpl.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -13,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
index 4e66c0f54..1134a06d5 100644
--- a/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
+++ b/Sources/Core/GlobalContexts/GlobalContextPluginConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  GlobalContextPluginConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift b/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
index c6c5c4eb5..bf60a991f 100644
--- a/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
+++ b/Sources/Core/GlobalContexts/GlobalContextsControllerImpl.swift
@@ -1,8 +1,4 @@
-//
-//  SPGlobalContextsControllerImpl.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/GlobalContexts/SchemaRule.swift b/Sources/Core/GlobalContexts/SchemaRule.swift
index ee907482d..286520ddd 100644
--- a/Sources/Core/GlobalContexts/SchemaRule.swift
+++ b/Sources/Core/GlobalContexts/SchemaRule.swift
@@ -1,8 +1,4 @@
-//
-//  SchemaRule.swift
-//  Snowplow-iOS
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Logger/Logger.swift b/Sources/Core/Logger/Logger.swift
index 8b055d16c..c593efc8e 100644
--- a/Sources/Core/Logger/Logger.swift
+++ b/Sources/Core/Logger/Logger.swift
@@ -1,8 +1,4 @@
-//
-//  Logger.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift b/Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift
index 5c31aebc8..680ce8115 100644
--- a/Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift
+++ b/Sources/Core/NetworkConnection/NetworkConfigurationUpdate.swift
@@ -1,8 +1,4 @@
-//
-//  SPNetworkConfigurationUpdate.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/NetworkConnection/NetworkControllerImpl.swift b/Sources/Core/NetworkConnection/NetworkControllerImpl.swift
index d8107922d..93924514a 100644
--- a/Sources/Core/NetworkConnection/NetworkControllerImpl.swift
+++ b/Sources/Core/NetworkConnection/NetworkControllerImpl.swift
@@ -1,8 +1,4 @@
-//
-//  SPNetworkControllerImpl.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/RemoteConfiguration/ConfigurationCache.swift b/Sources/Core/RemoteConfiguration/ConfigurationCache.swift
index 4150b5b97..8a55e3271 100644
--- a/Sources/Core/RemoteConfiguration/ConfigurationCache.swift
+++ b/Sources/Core/RemoteConfiguration/ConfigurationCache.swift
@@ -1,8 +1,4 @@
-//
-//  SPConfigurationCache.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift b/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
index b0b6e072c..58b1cfcc4 100644
--- a/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
+++ b/Sources/Core/RemoteConfiguration/ConfigurationFetcher.swift
@@ -1,8 +1,4 @@
-//
-//  SPConfigurationFetcher.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/RemoteConfiguration/ConfigurationProvider.swift b/Sources/Core/RemoteConfiguration/ConfigurationProvider.swift
index 6d47b91a2..5c757c1a3 100644
--- a/Sources/Core/RemoteConfiguration/ConfigurationProvider.swift
+++ b/Sources/Core/RemoteConfiguration/ConfigurationProvider.swift
@@ -1,8 +1,4 @@
-//
-//  ConfigurationProvider.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
index 235f58b3c..fa5cfa9fa 100644
--- a/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
+++ b/Sources/Core/RemoteConfiguration/FetchedConfigurationBundle.swift
@@ -1,8 +1,4 @@
-//
-//  SPFetchedConfigurationBundle.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/ScreenViewTracking/ScreenState.swift b/Sources/Core/ScreenViewTracking/ScreenState.swift
index d155c00a9..93ae5b4f5 100644
--- a/Sources/Core/ScreenViewTracking/ScreenState.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenState.swift
@@ -1,8 +1,4 @@
-//
-//  ScreenState.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
index 0c4f21bae..def2d14f4 100644
--- a/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenStateMachine.swift
@@ -1,8 +1,4 @@
-//
-//  SPScreenStateMachine.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
index 7dcf3c50e..d721a1b95 100644
--- a/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
+++ b/Sources/Core/ScreenViewTracking/ScreenViewModifier.swift
@@ -1,8 +1,4 @@
-//
-// ScreenViewModifier.swift
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 #if canImport(SwiftUI)
 
diff --git a/Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift b/Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift
index 4652b4afb..389cec01f 100644
--- a/Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift
+++ b/Sources/Core/ScreenViewTracking/UIKitScreenViewTracking.swift
@@ -1,8 +1,4 @@
-//
-//  UIKitScreenViewTracking.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 class UIKitScreenViewTracking {
     private static var initialized = false
diff --git a/Sources/Core/Session/Session.swift b/Sources/Core/Session/Session.swift
index afd19fd9a..90bc97968 100644
--- a/Sources/Core/Session/Session.swift
+++ b/Sources/Core/Session/Session.swift
@@ -1,8 +1,4 @@
-//
-//  Session.swift
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 #if os(iOS) || os(tvOS)
diff --git a/Sources/Core/Session/SessionConfigurationUpdate.swift b/Sources/Core/Session/SessionConfigurationUpdate.swift
index e5d0f8d43..7f19e5edd 100644
--- a/Sources/Core/Session/SessionConfigurationUpdate.swift
+++ b/Sources/Core/Session/SessionConfigurationUpdate.swift
@@ -1,8 +1,4 @@
-//
-//  SPSessionConfigurationUpdate.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Session/SessionControllerImpl.swift b/Sources/Core/Session/SessionControllerImpl.swift
index 35c62647d..4d40cefb1 100644
--- a/Sources/Core/Session/SessionControllerImpl.swift
+++ b/Sources/Core/Session/SessionControllerImpl.swift
@@ -1,8 +1,4 @@
-//
-//  SPSessionControllerImpl.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/DeepLinkState.swift b/Sources/Core/StateMachine/DeepLinkState.swift
index 6273b96b8..e6540ffee 100644
--- a/Sources/Core/StateMachine/DeepLinkState.swift
+++ b/Sources/Core/StateMachine/DeepLinkState.swift
@@ -1,8 +1,4 @@
-//
-//  SPDeepLinkState.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/DeepLinkStateMachine.swift b/Sources/Core/StateMachine/DeepLinkStateMachine.swift
index c903c956a..f9a236bd7 100644
--- a/Sources/Core/StateMachine/DeepLinkStateMachine.swift
+++ b/Sources/Core/StateMachine/DeepLinkStateMachine.swift
@@ -1,8 +1,4 @@
-//
-//  DeepLinkStateMachine.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/LifecycleState.swift b/Sources/Core/StateMachine/LifecycleState.swift
index 72ec80a53..79f67d05c 100644
--- a/Sources/Core/StateMachine/LifecycleState.swift
+++ b/Sources/Core/StateMachine/LifecycleState.swift
@@ -1,8 +1,4 @@
-//
-//  SPLifecycleState.swift
-//  Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/LifecycleStateMachine.swift b/Sources/Core/StateMachine/LifecycleStateMachine.swift
index 616199a48..2cf62c9f3 100644
--- a/Sources/Core/StateMachine/LifecycleStateMachine.swift
+++ b/Sources/Core/StateMachine/LifecycleStateMachine.swift
@@ -1,8 +1,4 @@
-//
-//  SPLifecycleStateMachine.swift
-//  Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/PluginStateMachine.swift b/Sources/Core/StateMachine/PluginStateMachine.swift
index 07e966745..5e0532abb 100644
--- a/Sources/Core/StateMachine/PluginStateMachine.swift
+++ b/Sources/Core/StateMachine/PluginStateMachine.swift
@@ -1,8 +1,4 @@
-//
-//  PluginStateMachine.swift
-//  Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/State.swift b/Sources/Core/StateMachine/State.swift
index ddccbe452..e9ffbce97 100644
--- a/Sources/Core/StateMachine/State.swift
+++ b/Sources/Core/StateMachine/State.swift
@@ -1,8 +1,4 @@
-//
-//  State.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/StateFuture.swift b/Sources/Core/StateMachine/StateFuture.swift
index ab73f3db3..c6d9f73ff 100644
--- a/Sources/Core/StateMachine/StateFuture.swift
+++ b/Sources/Core/StateMachine/StateFuture.swift
@@ -1,8 +1,4 @@
-//
-//  StateFuture.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/StateMachineEvent.swift b/Sources/Core/StateMachine/StateMachineEvent.swift
index 65c1d979a..0ce2eceab 100644
--- a/Sources/Core/StateMachine/StateMachineEvent.swift
+++ b/Sources/Core/StateMachine/StateMachineEvent.swift
@@ -1,8 +1,4 @@
-//
-//  StateMachineEvent.swift
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/StateMachineProtocol.swift b/Sources/Core/StateMachine/StateMachineProtocol.swift
index dbd844f6a..73bad7de9 100644
--- a/Sources/Core/StateMachine/StateMachineProtocol.swift
+++ b/Sources/Core/StateMachine/StateMachineProtocol.swift
@@ -1,8 +1,4 @@
-//
-//  SPStateMachineProtocol.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/StateManager.swift b/Sources/Core/StateMachine/StateManager.swift
index a5f4c5bb0..a4ad93412 100644
--- a/Sources/Core/StateMachine/StateManager.swift
+++ b/Sources/Core/StateMachine/StateManager.swift
@@ -1,8 +1,4 @@
-//
-//  StateManager.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/TrackerState.swift b/Sources/Core/StateMachine/TrackerState.swift
index 0f11bed40..3157260d8 100644
--- a/Sources/Core/StateMachine/TrackerState.swift
+++ b/Sources/Core/StateMachine/TrackerState.swift
@@ -1,8 +1,4 @@
-//
-//  TrackerState.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/StateMachine/TrackerStateSnapshot.swift b/Sources/Core/StateMachine/TrackerStateSnapshot.swift
index a352f99a1..d178aef08 100644
--- a/Sources/Core/StateMachine/TrackerStateSnapshot.swift
+++ b/Sources/Core/StateMachine/TrackerStateSnapshot.swift
@@ -1,8 +1,4 @@
-//
-//  TrackerStateSnapshot.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Storage/MemoryEventStore.swift b/Sources/Core/Storage/MemoryEventStore.swift
index 215c5d935..1e0f569b7 100644
--- a/Sources/Core/Storage/MemoryEventStore.swift
+++ b/Sources/Core/Storage/MemoryEventStore.swift
@@ -1,8 +1,4 @@
-//
-//  SPMemoryEventStore.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Storage/SQLiteEventStore.swift b/Sources/Core/Storage/SQLiteEventStore.swift
index 062ac9a6c..22db26731 100644
--- a/Sources/Core/Storage/SQLiteEventStore.swift
+++ b/Sources/Core/Storage/SQLiteEventStore.swift
@@ -1,8 +1,4 @@
-//
-//  SQLiteEventStore.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 #if os(iOS) || os(macOS)
 
diff --git a/Sources/Core/Subject/PlatformContext.swift b/Sources/Core/Subject/PlatformContext.swift
index db5fb756f..05dd07bd9 100644
--- a/Sources/Core/Subject/PlatformContext.swift
+++ b/Sources/Core/Subject/PlatformContext.swift
@@ -1,8 +1,4 @@
-//
-//  PlatformContext.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 #if os(iOS)
diff --git a/Sources/Core/Subject/Subject.swift b/Sources/Core/Subject/Subject.swift
index 4384d0401..4db043429 100644
--- a/Sources/Core/Subject/Subject.swift
+++ b/Sources/Core/Subject/Subject.swift
@@ -1,8 +1,4 @@
-//
-//  Subject.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Subject/SubjectConfigurationUpdate.swift b/Sources/Core/Subject/SubjectConfigurationUpdate.swift
index 4fb8a362b..4c29841ae 100644
--- a/Sources/Core/Subject/SubjectConfigurationUpdate.swift
+++ b/Sources/Core/Subject/SubjectConfigurationUpdate.swift
@@ -1,7 +1,4 @@
-//  SPSubjectConfigurationUpdate.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -13,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Subject/SubjectControllerImpl.swift b/Sources/Core/Subject/SubjectControllerImpl.swift
index 773e35d98..dd498e964 100644
--- a/Sources/Core/Subject/SubjectControllerImpl.swift
+++ b/Sources/Core/Subject/SubjectControllerImpl.swift
@@ -1,8 +1,4 @@
-//
-//  SubjectControllerImpl.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/InstallTracker.swift b/Sources/Core/Tracker/InstallTracker.swift
index 1c596203d..2c3bc3660 100644
--- a/Sources/Core/Tracker/InstallTracker.swift
+++ b/Sources/Core/Tracker/InstallTracker.swift
@@ -1,8 +1,4 @@
-//
-//  InstallTracker.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/PluginsControllerImpl.swift b/Sources/Core/Tracker/PluginsControllerImpl.swift
index 0451fad44..fe3cb02f5 100644
--- a/Sources/Core/Tracker/PluginsControllerImpl.swift
+++ b/Sources/Core/Tracker/PluginsControllerImpl.swift
@@ -1,8 +1,4 @@
-//
-//  PluginsControllerImpl.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/ServiceProvider.swift b/Sources/Core/Tracker/ServiceProvider.swift
index 97f722d28..bbf2c7b46 100644
--- a/Sources/Core/Tracker/ServiceProvider.swift
+++ b/Sources/Core/Tracker/ServiceProvider.swift
@@ -1,8 +1,4 @@
-//
-//  SPServiceProvider.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/ServiceProviderProtocol.swift b/Sources/Core/Tracker/ServiceProviderProtocol.swift
index f97c2e573..d647966f5 100644
--- a/Sources/Core/Tracker/ServiceProviderProtocol.swift
+++ b/Sources/Core/Tracker/ServiceProviderProtocol.swift
@@ -1,8 +1,4 @@
-//
-//  SPServiceProviderProtocol.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/Tracker.swift b/Sources/Core/Tracker/Tracker.swift
index 06177b082..3817ff8ba 100644
--- a/Sources/Core/Tracker/Tracker.swift
+++ b/Sources/Core/Tracker/Tracker.swift
@@ -1,8 +1,4 @@
-//
-//  Tracker.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/TrackerConfigurationUpdate.swift b/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
index 4c1a85dbb..203cba1d1 100644
--- a/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
+++ b/Sources/Core/Tracker/TrackerConfigurationUpdate.swift
@@ -1,8 +1,4 @@
-//
-//  SPTrackerConfigurationUpdate.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/TrackerControllerImpl.swift b/Sources/Core/Tracker/TrackerControllerImpl.swift
index f62918ad6..5fef80134 100644
--- a/Sources/Core/Tracker/TrackerControllerImpl.swift
+++ b/Sources/Core/Tracker/TrackerControllerImpl.swift
@@ -1,8 +1,4 @@
-//
-//  TrackerController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/TrackerDefaults.swift b/Sources/Core/Tracker/TrackerDefaults.swift
index f43571855..30b0f9a90 100644
--- a/Sources/Core/Tracker/TrackerDefaults.swift
+++ b/Sources/Core/Tracker/TrackerDefaults.swift
@@ -1,8 +1,4 @@
-//
-//  TrackerDefaults.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/TrackerEvent.swift b/Sources/Core/Tracker/TrackerEvent.swift
index a3c81a980..eee18f3f4 100644
--- a/Sources/Core/Tracker/TrackerEvent.swift
+++ b/Sources/Core/Tracker/TrackerEvent.swift
@@ -1,8 +1,4 @@
-//
-//  SPTrackerEvent.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Tracker/WebViewMessageHandler.swift b/Sources/Core/Tracker/WebViewMessageHandler.swift
index a8e3281fb..04873c70b 100644
--- a/Sources/Core/Tracker/WebViewMessageHandler.swift
+++ b/Sources/Core/Tracker/WebViewMessageHandler.swift
@@ -1,8 +1,4 @@
-//
-//  WebViewMessageHandler.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/TrackerConstants.swift b/Sources/Core/TrackerConstants.swift
index b16cc2217..eb58d70d7 100644
--- a/Sources/Core/TrackerConstants.swift
+++ b/Sources/Core/TrackerConstants.swift
@@ -1,8 +1,4 @@
-//
-//  TrackerConstants.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Utils/DataPersistence.swift b/Sources/Core/Utils/DataPersistence.swift
index 96ff948d5..9d2d3b887 100644
--- a/Sources/Core/Utils/DataPersistence.swift
+++ b/Sources/Core/Utils/DataPersistence.swift
@@ -1,8 +1,4 @@
-//
-//  SPDataPersistence.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Core/Utils/DeviceInfoMonitor.swift b/Sources/Core/Utils/DeviceInfoMonitor.swift
index 69f57f3cb..7d585bde7 100644
--- a/Sources/Core/Utils/DeviceInfoMonitor.swift
+++ b/Sources/Core/Utils/DeviceInfoMonitor.swift
@@ -1,8 +1,4 @@
-//
-//  SPDeviceInfoMonitor.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 import os
diff --git a/Sources/Core/Utils/SNOWReachability.swift b/Sources/Core/Utils/SNOWReachability.swift
index 2ffcaf465..84d4d9ff1 100644
--- a/Sources/Core/Utils/SNOWReachability.swift
+++ b/Sources/Core/Utils/SNOWReachability.swift
@@ -1,8 +1,4 @@
-//
-//  SNOWReachability.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 #if os(iOS)
 import Foundation
diff --git a/Sources/Core/Utils/Utilities.swift b/Sources/Core/Utils/Utilities.swift
index 978c18bae..abe4cf4c9 100644
--- a/Sources/Core/Utils/Utilities.swift
+++ b/Sources/Core/Utils/Utilities.swift
@@ -1,8 +1,4 @@
-//
-//  Utilities.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 #if os(iOS)
 import UIKit
diff --git a/Sources/Snowplow/Configurations/ConfigurationBuilder.swift b/Sources/Snowplow/Configurations/ConfigurationBuilder.swift
index 6ce48d921..9bc845410 100644
--- a/Sources/Snowplow/Configurations/ConfigurationBuilder.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationBuilder.swift
@@ -1,8 +1,4 @@
-//
-//  ConfigurationBuilder.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/ConfigurationBundle.swift b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
index 8d2e6c776..962115b27 100644
--- a/Sources/Snowplow/Configurations/ConfigurationBundle.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationBundle.swift
@@ -1,8 +1,4 @@
-//
-//  ConfigurationBundle.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/ConfigurationProtocol.swift b/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
index f479c9137..809d93471 100644
--- a/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationProtocol.swift
@@ -1,8 +1,4 @@
-//
-//  ConfigurationProtocol.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/ConfigurationState.swift b/Sources/Snowplow/Configurations/ConfigurationState.swift
index 63c303b16..23c86449b 100644
--- a/Sources/Snowplow/Configurations/ConfigurationState.swift
+++ b/Sources/Snowplow/Configurations/ConfigurationState.swift
@@ -1,8 +1,4 @@
-//
-//  ConfigurationState.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/EmitterConfiguration.swift b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
index a59f00217..efc14e866 100644
--- a/Sources/Snowplow/Configurations/EmitterConfiguration.swift
+++ b/Sources/Snowplow/Configurations/EmitterConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  EmitterConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/FocalMeterConfiguration.swift b/Sources/Snowplow/Configurations/FocalMeterConfiguration.swift
deleted file mode 100644
index 5d01cc347..000000000
--- a/Sources/Snowplow/Configurations/FocalMeterConfiguration.swift
+++ /dev/null
@@ -1,8 +0,0 @@
-//
-//  File.swift
-//  
-//
-//  Created by Matus Tomlein on 30/12/2022.
-//
-
-import Foundation
diff --git a/Sources/Snowplow/Configurations/GDPRConfiguration.swift b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
index e5ef5294a..42206b564 100644
--- a/Sources/Snowplow/Configurations/GDPRConfiguration.swift
+++ b/Sources/Snowplow/Configurations/GDPRConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  GDPRConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
index b7dd2e2d5..32e6c38b9 100644
--- a/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
+++ b/Sources/Snowplow/Configurations/GlobalContextsConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  GlobalContextsConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/NetworkConfiguration.swift b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
index bdeb00522..07c3d6087 100644
--- a/Sources/Snowplow/Configurations/NetworkConfiguration.swift
+++ b/Sources/Snowplow/Configurations/NetworkConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  NetworkConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/PluginConfiguration.swift b/Sources/Snowplow/Configurations/PluginConfiguration.swift
index 8002054aa..65d17024c 100644
--- a/Sources/Snowplow/Configurations/PluginConfiguration.swift
+++ b/Sources/Snowplow/Configurations/PluginConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  PluginConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/RemoteConfiguration.swift b/Sources/Snowplow/Configurations/RemoteConfiguration.swift
index a8ffeb36f..4f61ecd9b 100644
--- a/Sources/Snowplow/Configurations/RemoteConfiguration.swift
+++ b/Sources/Snowplow/Configurations/RemoteConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  RemoteConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/SerializableConfiguration.swift b/Sources/Snowplow/Configurations/SerializableConfiguration.swift
index 1a54f1853..b11e8a496 100644
--- a/Sources/Snowplow/Configurations/SerializableConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SerializableConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  SerializableConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/SessionConfiguration.swift b/Sources/Snowplow/Configurations/SessionConfiguration.swift
index af4c33709..98f5799ec 100644
--- a/Sources/Snowplow/Configurations/SessionConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SessionConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  SessionConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Configurations/SubjectConfiguration.swift b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
index 4282467f9..d21b39b69 100644
--- a/Sources/Snowplow/Configurations/SubjectConfiguration.swift
+++ b/Sources/Snowplow/Configurations/SubjectConfiguration.swift
@@ -1,7 +1,4 @@
-//  SubjectConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -13,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import CoreGraphics
 import Foundation
diff --git a/Sources/Snowplow/Configurations/TrackerConfiguration.swift b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
index b01547b62..5445aacbe 100644
--- a/Sources/Snowplow/Configurations/TrackerConfiguration.swift
+++ b/Sources/Snowplow/Configurations/TrackerConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  TrackerConfiguration.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/Controller.swift b/Sources/Snowplow/Controllers/Controller.swift
index 8d05504b4..9340947c4 100644
--- a/Sources/Snowplow/Controllers/Controller.swift
+++ b/Sources/Snowplow/Controllers/Controller.swift
@@ -1,7 +1,4 @@
-//  SPController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -13,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/EmitterController.swift b/Sources/Snowplow/Controllers/EmitterController.swift
index 738fb4801..c0b016416 100644
--- a/Sources/Snowplow/Controllers/EmitterController.swift
+++ b/Sources/Snowplow/Controllers/EmitterController.swift
@@ -1,8 +1,4 @@
-//
-//  SPEmitterController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/GDPRController.swift b/Sources/Snowplow/Controllers/GDPRController.swift
index d075d9244..0bc5c253d 100644
--- a/Sources/Snowplow/Controllers/GDPRController.swift
+++ b/Sources/Snowplow/Controllers/GDPRController.swift
@@ -1,8 +1,4 @@
-//
-//  GDPRController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/GlobalContextsController.swift b/Sources/Snowplow/Controllers/GlobalContextsController.swift
index 3c94b60cf..ad326c6c8 100644
--- a/Sources/Snowplow/Controllers/GlobalContextsController.swift
+++ b/Sources/Snowplow/Controllers/GlobalContextsController.swift
@@ -1,8 +1,4 @@
-//
-//  SPGlobalContextsController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/NetworkController.swift b/Sources/Snowplow/Controllers/NetworkController.swift
index 9ed30a1bf..a0511c49e 100644
--- a/Sources/Snowplow/Controllers/NetworkController.swift
+++ b/Sources/Snowplow/Controllers/NetworkController.swift
@@ -1,8 +1,4 @@
-//
-//  SPNetworkController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/PluginsController.swift b/Sources/Snowplow/Controllers/PluginsController.swift
index 50545df0d..88b83fb26 100644
--- a/Sources/Snowplow/Controllers/PluginsController.swift
+++ b/Sources/Snowplow/Controllers/PluginsController.swift
@@ -1,8 +1,4 @@
-//
-//  PluginsController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/SessionController.swift b/Sources/Snowplow/Controllers/SessionController.swift
index 20eba6a00..a01a19790 100644
--- a/Sources/Snowplow/Controllers/SessionController.swift
+++ b/Sources/Snowplow/Controllers/SessionController.swift
@@ -1,8 +1,4 @@
-//
-//  SPSessionController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/SubjectController.swift b/Sources/Snowplow/Controllers/SubjectController.swift
index 094f1a13a..7912aaaf7 100644
--- a/Sources/Snowplow/Controllers/SubjectController.swift
+++ b/Sources/Snowplow/Controllers/SubjectController.swift
@@ -1,8 +1,4 @@
-//
-//  SPSubjectController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Controllers/TrackerController.swift b/Sources/Snowplow/Controllers/TrackerController.swift
index ccb7eef35..e666de332 100644
--- a/Sources/Snowplow/Controllers/TrackerController.swift
+++ b/Sources/Snowplow/Controllers/TrackerController.swift
@@ -1,8 +1,4 @@
-//
-//  SPTrackerController.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Emitter/BufferOption.swift b/Sources/Snowplow/Emitter/BufferOption.swift
index 209112a42..34aedcea1 100644
--- a/Sources/Snowplow/Emitter/BufferOption.swift
+++ b/Sources/Snowplow/Emitter/BufferOption.swift
@@ -1,7 +1,4 @@
-//  BufferOption.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -13,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Emitter/EmitterDefaults.swift b/Sources/Snowplow/Emitter/EmitterDefaults.swift
index 8043453be..9220f9a9c 100644
--- a/Sources/Snowplow/Emitter/EmitterDefaults.swift
+++ b/Sources/Snowplow/Emitter/EmitterDefaults.swift
@@ -1,8 +1,4 @@
-//
-//  EmitterDefaults.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Emitter/EmitterEvent.swift b/Sources/Snowplow/Emitter/EmitterEvent.swift
index 6fdbab83e..40c22c395 100644
--- a/Sources/Snowplow/Emitter/EmitterEvent.swift
+++ b/Sources/Snowplow/Emitter/EmitterEvent.swift
@@ -1,8 +1,4 @@
-//
-//  SPEmitterEvent.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Emitter/EventStore.swift b/Sources/Snowplow/Emitter/EventStore.swift
index 8991b11a6..745b12945 100644
--- a/Sources/Snowplow/Emitter/EventStore.swift
+++ b/Sources/Snowplow/Emitter/EventStore.swift
@@ -1,8 +1,4 @@
-//
-//  SPEventStore.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Entities/DeepLinkEntity.swift b/Sources/Snowplow/Entities/DeepLinkEntity.swift
index bc15ce475..2b0ea5788 100644
--- a/Sources/Snowplow/Entities/DeepLinkEntity.swift
+++ b/Sources/Snowplow/Entities/DeepLinkEntity.swift
@@ -1,8 +1,4 @@
-//
-// SPDeepLinkEntity.swift
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Entities/LifecycleEntity.swift b/Sources/Snowplow/Entities/LifecycleEntity.swift
index a86474749..fc0c9fa6f 100644
--- a/Sources/Snowplow/Entities/LifecycleEntity.swift
+++ b/Sources/Snowplow/Entities/LifecycleEntity.swift
@@ -1,8 +1,4 @@
-//
-// SPLifecycleEntity.swift
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/Background.swift b/Sources/Snowplow/Events/Background.swift
index bd927c2bc..ba24d4d46 100644
--- a/Sources/Snowplow/Events/Background.swift
+++ b/Sources/Snowplow/Events/Background.swift
@@ -1,8 +1,4 @@
-//
-//  Background.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/ConsentDocument.swift b/Sources/Snowplow/Events/ConsentDocument.swift
index 31a7a8b56..e1b1fe011 100644
--- a/Sources/Snowplow/Events/ConsentDocument.swift
+++ b/Sources/Snowplow/Events/ConsentDocument.swift
@@ -1,8 +1,4 @@
-//
-//  ConsentDocument.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/ConsentGranted.swift b/Sources/Snowplow/Events/ConsentGranted.swift
index 369e9c44e..1250659f8 100644
--- a/Sources/Snowplow/Events/ConsentGranted.swift
+++ b/Sources/Snowplow/Events/ConsentGranted.swift
@@ -1,8 +1,4 @@
-//
-//  ConsentGranted.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/ConsentWithdrawn.swift b/Sources/Snowplow/Events/ConsentWithdrawn.swift
index 75bc4adc9..b9fdf8376 100644
--- a/Sources/Snowplow/Events/ConsentWithdrawn.swift
+++ b/Sources/Snowplow/Events/ConsentWithdrawn.swift
@@ -1,8 +1,4 @@
-//
-//  ConsentWithdrawn.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/DeepLinkReceived.swift b/Sources/Snowplow/Events/DeepLinkReceived.swift
index a8b8a9174..6fbf1cb1b 100644
--- a/Sources/Snowplow/Events/DeepLinkReceived.swift
+++ b/Sources/Snowplow/Events/DeepLinkReceived.swift
@@ -1,8 +1,4 @@
-//
-// DeepLinkReceived.swift
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/Ecommerce.swift b/Sources/Snowplow/Events/Ecommerce.swift
index 9d3525270..54e2724a7 100644
--- a/Sources/Snowplow/Events/Ecommerce.swift
+++ b/Sources/Snowplow/Events/Ecommerce.swift
@@ -1,8 +1,4 @@
-//
-//  Ecommerce.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/EcommerceItem.swift b/Sources/Snowplow/Events/EcommerceItem.swift
index 5aa772191..2b1fb68b8 100644
--- a/Sources/Snowplow/Events/EcommerceItem.swift
+++ b/Sources/Snowplow/Events/EcommerceItem.swift
@@ -1,8 +1,4 @@
-//
-//  EcommerceItem.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/EventBase.swift b/Sources/Snowplow/Events/EventBase.swift
index d3c7fc15a..b18f64c36 100644
--- a/Sources/Snowplow/Events/EventBase.swift
+++ b/Sources/Snowplow/Events/EventBase.swift
@@ -1,8 +1,4 @@
-//
-//  EventBase.swift
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/Foreground.swift b/Sources/Snowplow/Events/Foreground.swift
index 019de493c..f5bda7ce3 100644
--- a/Sources/Snowplow/Events/Foreground.swift
+++ b/Sources/Snowplow/Events/Foreground.swift
@@ -1,8 +1,4 @@
-//
-//  Foreground.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/MessageNotification.swift b/Sources/Snowplow/Events/MessageNotification.swift
index 7d803dc69..411ddeaaa 100644
--- a/Sources/Snowplow/Events/MessageNotification.swift
+++ b/Sources/Snowplow/Events/MessageNotification.swift
@@ -1,8 +1,4 @@
-//
-// MessageNotification.m
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/MessageNotificationAttachment.swift b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
index f814e152f..cc38413c6 100644
--- a/Sources/Snowplow/Events/MessageNotificationAttachment.swift
+++ b/Sources/Snowplow/Events/MessageNotificationAttachment.swift
@@ -1,8 +1,4 @@
-//
-// MessageNotificationAttachment.swift
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/PageView.swift b/Sources/Snowplow/Events/PageView.swift
index 6bd625f2f..2db3292be 100644
--- a/Sources/Snowplow/Events/PageView.swift
+++ b/Sources/Snowplow/Events/PageView.swift
@@ -1,8 +1,4 @@
-//
-//  PageView.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/PushNotification.swift b/Sources/Snowplow/Events/PushNotification.swift
index e5cae0641..16e97df73 100644
--- a/Sources/Snowplow/Events/PushNotification.swift
+++ b/Sources/Snowplow/Events/PushNotification.swift
@@ -1,7 +1,4 @@
-//  PushNotification.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -13,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 #if os(iOS)
diff --git a/Sources/Snowplow/Events/SNOWError.swift b/Sources/Snowplow/Events/SNOWError.swift
index cb6d976f8..89e3cea61 100644
--- a/Sources/Snowplow/Events/SNOWError.swift
+++ b/Sources/Snowplow/Events/SNOWError.swift
@@ -1,8 +1,4 @@
-//
-//  SNOWError.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/ScreenView.swift b/Sources/Snowplow/Events/ScreenView.swift
index 4a8567af6..ed4b16f3f 100644
--- a/Sources/Snowplow/Events/ScreenView.swift
+++ b/Sources/Snowplow/Events/ScreenView.swift
@@ -1,8 +1,4 @@
-//
-//  ScreenView.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/SelfDescribing.swift b/Sources/Snowplow/Events/SelfDescribing.swift
index 535459b4d..f3b762c2b 100644
--- a/Sources/Snowplow/Events/SelfDescribing.swift
+++ b/Sources/Snowplow/Events/SelfDescribing.swift
@@ -1,8 +1,4 @@
-//
-//  SelfDescribing.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/Structured.swift b/Sources/Snowplow/Events/Structured.swift
index 67ddc4d88..448d6980f 100644
--- a/Sources/Snowplow/Events/Structured.swift
+++ b/Sources/Snowplow/Events/Structured.swift
@@ -1,8 +1,4 @@
-//
-//  Structured.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/Timing.swift b/Sources/Snowplow/Events/Timing.swift
index c1c0871c3..c958f5945 100644
--- a/Sources/Snowplow/Events/Timing.swift
+++ b/Sources/Snowplow/Events/Timing.swift
@@ -1,8 +1,4 @@
-//
-//  Timing.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Events/TrackerError.swift b/Sources/Snowplow/Events/TrackerError.swift
index 75e3a3b49..6c575d100 100644
--- a/Sources/Snowplow/Events/TrackerError.swift
+++ b/Sources/Snowplow/Events/TrackerError.swift
@@ -1,8 +1,4 @@
-//
-//  TrackerError.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 let kMaxMessageLength = 2048
 let kMaxStackLength = 8192
diff --git a/Sources/Snowplow/GlobalContexts/ContextGenerator.swift b/Sources/Snowplow/GlobalContexts/ContextGenerator.swift
index fad7be0c1..7bbb706ec 100644
--- a/Sources/Snowplow/GlobalContexts/ContextGenerator.swift
+++ b/Sources/Snowplow/GlobalContexts/ContextGenerator.swift
@@ -1,8 +1,4 @@
-//
-//  ContextGenerator.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/GlobalContexts/GlobalContext.swift b/Sources/Snowplow/GlobalContexts/GlobalContext.swift
index b11739117..dcaaddea1 100644
--- a/Sources/Snowplow/GlobalContexts/GlobalContext.swift
+++ b/Sources/Snowplow/GlobalContexts/GlobalContext.swift
@@ -1,8 +1,4 @@
-//
-//  GlobalContext.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/GlobalContexts/SchemaRuleset.swift b/Sources/Snowplow/GlobalContexts/SchemaRuleset.swift
index 43e8ca5d8..c5a8f398a 100644
--- a/Sources/Snowplow/GlobalContexts/SchemaRuleset.swift
+++ b/Sources/Snowplow/GlobalContexts/SchemaRuleset.swift
@@ -1,8 +1,4 @@
-//
-//  SchemaRuleset.swift
-//  Snowplow-iOS
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Network/DefaultNetworkConnection.swift b/Sources/Snowplow/Network/DefaultNetworkConnection.swift
index 63b49de69..900604d9d 100644
--- a/Sources/Snowplow/Network/DefaultNetworkConnection.swift
+++ b/Sources/Snowplow/Network/DefaultNetworkConnection.swift
@@ -1,8 +1,4 @@
-//
-//  SPDefaultNetworkConnection.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Network/HttpMethodOptions.swift b/Sources/Snowplow/Network/HttpMethodOptions.swift
index 4aedf0336..1c9f9ba08 100644
--- a/Sources/Snowplow/Network/HttpMethodOptions.swift
+++ b/Sources/Snowplow/Network/HttpMethodOptions.swift
@@ -1,8 +1,4 @@
-//
-//  HttpMethodOptions.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Network/NetworkConnection.swift b/Sources/Snowplow/Network/NetworkConnection.swift
index 9c9e1263e..fa7e68ab9 100644
--- a/Sources/Snowplow/Network/NetworkConnection.swift
+++ b/Sources/Snowplow/Network/NetworkConnection.swift
@@ -1,8 +1,4 @@
-//
-//  SPNetworkConnection.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Network/ProtocolOptions.swift b/Sources/Snowplow/Network/ProtocolOptions.swift
index 17ba49c2b..4f884ddea 100644
--- a/Sources/Snowplow/Network/ProtocolOptions.swift
+++ b/Sources/Snowplow/Network/ProtocolOptions.swift
@@ -1,8 +1,4 @@
-//
-//  ProtocolOptions.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Network/Request.swift b/Sources/Snowplow/Network/Request.swift
index 4c42dbd76..21c77eee4 100644
--- a/Sources/Snowplow/Network/Request.swift
+++ b/Sources/Snowplow/Network/Request.swift
@@ -1,8 +1,4 @@
-//
-//  SPRequest.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Network/RequestCallback.swift b/Sources/Snowplow/Network/RequestCallback.swift
index f25e5a7dd..d02783da0 100644
--- a/Sources/Snowplow/Network/RequestCallback.swift
+++ b/Sources/Snowplow/Network/RequestCallback.swift
@@ -1,8 +1,4 @@
-//
-//  SPRequestCallback.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Network/RequestResult.swift b/Sources/Snowplow/Network/RequestResult.swift
index e78de9b03..5b58c3a96 100644
--- a/Sources/Snowplow/Network/RequestResult.swift
+++ b/Sources/Snowplow/Network/RequestResult.swift
@@ -1,8 +1,4 @@
-//
-//  SPRequestResult.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Payload/Payload.swift b/Sources/Snowplow/Payload/Payload.swift
index 3cd21d7e1..f6aa86e49 100644
--- a/Sources/Snowplow/Payload/Payload.swift
+++ b/Sources/Snowplow/Payload/Payload.swift
@@ -1,8 +1,4 @@
-//
-//  SPPayload.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Payload/SelfDescribingJson.swift b/Sources/Snowplow/Payload/SelfDescribingJson.swift
index e5929a399..64cc12aef 100644
--- a/Sources/Snowplow/Payload/SelfDescribingJson.swift
+++ b/Sources/Snowplow/Payload/SelfDescribingJson.swift
@@ -1,8 +1,4 @@
-//
-//  SPSelfDescribingJson.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Snowplow.swift b/Sources/Snowplow/Snowplow.swift
index 5d8bc028b..9ef1c0723 100644
--- a/Sources/Snowplow/Snowplow.swift
+++ b/Sources/Snowplow/Snowplow.swift
@@ -1,8 +1,4 @@
-//
-//  SPSnowplow.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 #if os(iOS) || os(macOS)
diff --git a/Sources/Snowplow/Tracker/DevicePlatform.swift b/Sources/Snowplow/Tracker/DevicePlatform.swift
index 2db8a3817..cbe7d6e2b 100644
--- a/Sources/Snowplow/Tracker/DevicePlatform.swift
+++ b/Sources/Snowplow/Tracker/DevicePlatform.swift
@@ -1,8 +1,4 @@
-//
-//  SPDevicePlatform.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Tracker/InspectableEvent.swift b/Sources/Snowplow/Tracker/InspectableEvent.swift
index e4f5f50c4..e0ab6c1d2 100644
--- a/Sources/Snowplow/Tracker/InspectableEvent.swift
+++ b/Sources/Snowplow/Tracker/InspectableEvent.swift
@@ -1,8 +1,4 @@
-//
-//  InspectableEvent.swift
-//  Snowplow
-//
-//  Copyright (c) 2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Tracker/LogLevel.swift b/Sources/Snowplow/Tracker/LogLevel.swift
index 61e3e03b0..207c19b60 100644
--- a/Sources/Snowplow/Tracker/LogLevel.swift
+++ b/Sources/Snowplow/Tracker/LogLevel.swift
@@ -1,8 +1,4 @@
-//
-//  LogLevel.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Tracker/LoggerDelegate.swift b/Sources/Snowplow/Tracker/LoggerDelegate.swift
index 438ea6d9d..b8b937314 100644
--- a/Sources/Snowplow/Tracker/LoggerDelegate.swift
+++ b/Sources/Snowplow/Tracker/LoggerDelegate.swift
@@ -1,8 +1,4 @@
-//
-//  LoggerDelegate.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Tracker/SessionState.swift b/Sources/Snowplow/Tracker/SessionState.swift
index 9581bf7e1..cf8f0c90b 100644
--- a/Sources/Snowplow/Tracker/SessionState.swift
+++ b/Sources/Snowplow/Tracker/SessionState.swift
@@ -1,8 +1,4 @@
-//
-//  SPSessionState.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Tracker/View.swift b/Sources/Snowplow/Tracker/View.swift
index c9c7b6dc4..4773ffed0 100644
--- a/Sources/Snowplow/Tracker/View.swift
+++ b/Sources/Snowplow/Tracker/View.swift
@@ -1,8 +1,4 @@
-//
-// View.swift
-// Snowplow
-//
-// Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 // This program is licensed to you under the Apache License Version 2.0,
 // and you may not use this file except in compliance with the Apache License
@@ -14,9 +10,6 @@
 // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 // express or implied. See the Apache License Version 2.0 for the specific
 // language governing permissions and limitations there under.
-//
-// License: Apache License Version 2.0
-//
 
 #if canImport(SwiftUI)
 import SwiftUI
diff --git a/Sources/Snowplow/Utils/GDPRProcessingBasis.swift b/Sources/Snowplow/Utils/GDPRProcessingBasis.swift
index b90c325b3..13d6f472f 100644
--- a/Sources/Snowplow/Utils/GDPRProcessingBasis.swift
+++ b/Sources/Snowplow/Utils/GDPRProcessingBasis.swift
@@ -1,8 +1,4 @@
-//
-//  GDPRProcessingBasis.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Sources/Snowplow/Utils/SPSize.swift b/Sources/Snowplow/Utils/SPSize.swift
index d85972303..94c170de1 100644
--- a/Sources/Snowplow/Utils/SPSize.swift
+++ b/Sources/Snowplow/Utils/SPSize.swift
@@ -1,8 +1,4 @@
-//
-//  SPSubjectConfiguration.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 
diff --git a/Tests/Configurations/TestConfigurationBuilder.swift b/Tests/Configurations/TestConfigurationBuilder.swift
index e320bd514..d3ba1eec4 100644
--- a/Tests/Configurations/TestConfigurationBuilder.swift
+++ b/Tests/Configurations/TestConfigurationBuilder.swift
@@ -1,8 +1,4 @@
-//
-//  TestConfigurationBuilder.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Configurations/TestEmitterConfiguration.swift b/Tests/Configurations/TestEmitterConfiguration.swift
index 257c7584d..24ec5eaf2 100644
--- a/Tests/Configurations/TestEmitterConfiguration.swift
+++ b/Tests/Configurations/TestEmitterConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  TestEmitterConfiguration.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Configurations/TestMultipleInstances.swift b/Tests/Configurations/TestMultipleInstances.swift
index 98dcdf583..7cf3fa36b 100644
--- a/Tests/Configurations/TestMultipleInstances.swift
+++ b/Tests/Configurations/TestMultipleInstances.swift
@@ -1,8 +1,4 @@
-//
-//  TestMultipleInstances.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Configurations/TestRemoteConfiguration.swift b/Tests/Configurations/TestRemoteConfiguration.swift
index e3140f7ed..7468a4299 100644
--- a/Tests/Configurations/TestRemoteConfiguration.swift
+++ b/Tests/Configurations/TestRemoteConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  TestRemoteConfiguration.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 import Mocker
diff --git a/Tests/Configurations/TestTrackerConfiguration.swift b/Tests/Configurations/TestTrackerConfiguration.swift
index a3c13d5e6..f43661be9 100644
--- a/Tests/Configurations/TestTrackerConfiguration.swift
+++ b/Tests/Configurations/TestTrackerConfiguration.swift
@@ -1,8 +1,4 @@
-//
-//  TestTrackerConfiguration.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Configurations/TestTrackerController.swift b/Tests/Configurations/TestTrackerController.swift
index 203715c18..185789481 100644
--- a/Tests/Configurations/TestTrackerController.swift
+++ b/Tests/Configurations/TestTrackerController.swift
@@ -1,8 +1,4 @@
-//
-//  TestTrackerController.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Global Contexts/TestGlobalContexts.swift b/Tests/Global Contexts/TestGlobalContexts.swift
index 6e6667e96..f50a124c2 100644
--- a/Tests/Global Contexts/TestGlobalContexts.swift	
+++ b/Tests/Global Contexts/TestGlobalContexts.swift	
@@ -1,8 +1,4 @@
-//
-//  TestGlobalContexts.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,11 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  Copyright: Copyright © 2020 Snowplow Analytics.
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Global Contexts/TestSchemaRuleset.swift b/Tests/Global Contexts/TestSchemaRuleset.swift
index 9cb0813e6..ac2839679 100644
--- a/Tests/Global Contexts/TestSchemaRuleset.swift	
+++ b/Tests/Global Contexts/TestSchemaRuleset.swift	
@@ -1,8 +1,4 @@
-//
-//  TestSchemaRuleset.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,11 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  Copyright: Copyright © 2020 Snowplow Analytics.
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Integration/TestTrackEventsToMicro.swift b/Tests/Integration/TestTrackEventsToMicro.swift
index d41976d29..07667eb1c 100644
--- a/Tests/Integration/TestTrackEventsToMicro.swift
+++ b/Tests/Integration/TestTrackEventsToMicro.swift
@@ -1,8 +1,4 @@
-//
-//  TestWithMicro.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 import SnowplowTracker
diff --git a/Tests/Legacy Tests/LegacyTestEmitter.swift b/Tests/Legacy Tests/LegacyTestEmitter.swift
index 1c319e051..bcdeda63e 100644
--- a/Tests/Legacy Tests/LegacyTestEmitter.swift	
+++ b/Tests/Legacy Tests/LegacyTestEmitter.swift	
@@ -1,8 +1,4 @@
-//
-//  LegacyTestEmitter.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,11 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Legacy Tests/LegacyTestSubject.swift b/Tests/Legacy Tests/LegacyTestSubject.swift
index 3f76603e2..f8ab4ee78 100644
--- a/Tests/Legacy Tests/LegacyTestSubject.swift	
+++ b/Tests/Legacy Tests/LegacyTestSubject.swift	
@@ -1,8 +1,4 @@
-//
-//  TestLifecycleState.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/Legacy Tests/LegacyTestTracker.swift b/Tests/Legacy Tests/LegacyTestTracker.swift
index 776aea868..bc86535b4 100644
--- a/Tests/Legacy Tests/LegacyTestTracker.swift	
+++ b/Tests/Legacy Tests/LegacyTestTracker.swift	
@@ -1,8 +1,4 @@
-//
-//  LegacyTestTracker.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 //#pragma clang diagnostic push
 //#pragma clang diagnostic ignored "-Wdeprecated-declarations"
diff --git a/Tests/TestDataPersistence.swift b/Tests/TestDataPersistence.swift
index 55d4ada53..81e088f77 100644
--- a/Tests/TestDataPersistence.swift
+++ b/Tests/TestDataPersistence.swift
@@ -1,8 +1,4 @@
-//
-//  TestLifecycleState.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestEvents.swift b/Tests/TestEvents.swift
index 75d9e2f4e..2df8172c8 100644
--- a/Tests/TestEvents.swift
+++ b/Tests/TestEvents.swift
@@ -1,9 +1,4 @@
-
-//
-//  TestEvents.swift
-//  SnowplowTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -15,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestLifecycleState.swift b/Tests/TestLifecycleState.swift
index 9d36a9ac7..024e7e3c0 100644
--- a/Tests/TestLifecycleState.swift
+++ b/Tests/TestLifecycleState.swift
@@ -1,8 +1,4 @@
-//
-//  TestLifecycleState.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestLogger.swift b/Tests/TestLogger.swift
index 997d31836..94095ca32 100644
--- a/Tests/TestLogger.swift
+++ b/Tests/TestLogger.swift
@@ -1,8 +1,4 @@
-//
-//  TestLogger.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestMemoryEventStore.swift b/Tests/TestMemoryEventStore.swift
index 9007627b4..cd6868649 100644
--- a/Tests/TestMemoryEventStore.swift
+++ b/Tests/TestMemoryEventStore.swift
@@ -1,8 +1,4 @@
-//
-//  TestMemoryEventStore.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestNetworkConnection.swift b/Tests/TestNetworkConnection.swift
index 78f037a67..2ba85ba44 100644
--- a/Tests/TestNetworkConnection.swift
+++ b/Tests/TestNetworkConnection.swift
@@ -1,8 +1,4 @@
-//
-//  TestNetworkConnection.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Mocker
 import XCTest
diff --git a/Tests/TestPayload.swift b/Tests/TestPayload.swift
index 5d386e35e..d29ae7ea2 100644
--- a/Tests/TestPayload.swift
+++ b/Tests/TestPayload.swift
@@ -1,8 +1,4 @@
-//
-//  TestPayload.swift
-//  SnowplowTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestPlatformContext.swift b/Tests/TestPlatformContext.swift
index dec0e1c25..ce24352e9 100644
--- a/Tests/TestPlatformContext.swift
+++ b/Tests/TestPlatformContext.swift
@@ -1,8 +1,4 @@
-//
-//  TestPlatformContext.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestPlugins.swift b/Tests/TestPlugins.swift
index 7c67cc5f4..969178fcc 100644
--- a/Tests/TestPlugins.swift
+++ b/Tests/TestPlugins.swift
@@ -1,8 +1,4 @@
-//
-//  TestPlugins.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestRequest.swift b/Tests/TestRequest.swift
index b44c9b914..0d3e9c225 100644
--- a/Tests/TestRequest.swift
+++ b/Tests/TestRequest.swift
@@ -1,8 +1,4 @@
-//
-//  TestRequest.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida, Joshua Beemster
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestRequestResult.swift b/Tests/TestRequestResult.swift
index 02545d33f..3bf19ea57 100644
--- a/Tests/TestRequestResult.swift
+++ b/Tests/TestRequestResult.swift
@@ -1,8 +1,4 @@
-//
-//  TestRequestResult.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,11 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestSQLiteEventStore.swift b/Tests/TestSQLiteEventStore.swift
index b3c36b674..f152b7d3f 100644
--- a/Tests/TestSQLiteEventStore.swift
+++ b/Tests/TestSQLiteEventStore.swift
@@ -1,8 +1,4 @@
-//
-//  TestSQLiteEventStore.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
 
 #if os(iOS) || os(macOS)
 
diff --git a/Tests/TestScreenState.swift b/Tests/TestScreenState.swift
index e73bf025c..30ceeb8bd 100644
--- a/Tests/TestScreenState.swift
+++ b/Tests/TestScreenState.swift
@@ -1,8 +1,4 @@
-//
-//  TestScreenState.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestScreenViewModifier.swift b/Tests/TestScreenViewModifier.swift
index 525dd3c8e..82743a788 100644
--- a/Tests/TestScreenViewModifier.swift
+++ b/Tests/TestScreenViewModifier.swift
@@ -1,8 +1,4 @@
-//
-//  TestScreenViewModifier.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 import XCTest
diff --git a/Tests/TestSelfDescribingJson.swift b/Tests/TestSelfDescribingJson.swift
index 8b76920ab..178084861 100644
--- a/Tests/TestSelfDescribingJson.swift
+++ b/Tests/TestSelfDescribingJson.swift
@@ -1,8 +1,4 @@
-//
-//  TestSelfDescribingJson.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestServiceProvider.swift b/Tests/TestServiceProvider.swift
index d1f58a86d..f29d13689 100644
--- a/Tests/TestServiceProvider.swift
+++ b/Tests/TestServiceProvider.swift
@@ -1,8 +1,4 @@
-//
-//  TestServiceProvider.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestSession.swift b/Tests/TestSession.swift
index 0907df729..b08a1044f 100644
--- a/Tests/TestSession.swift
+++ b/Tests/TestSession.swift
@@ -1,8 +1,4 @@
-//
-//  TestSession.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,11 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Joshua Beemster
-//  Copyright: Copyright (c) 2020 Snowplow Analytics Ltd
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestStateManager.swift b/Tests/TestStateManager.swift
index d02454fce..3a90def1e 100644
--- a/Tests/TestStateManager.swift
+++ b/Tests/TestStateManager.swift
@@ -1,8 +1,4 @@
-//
-//  TestStateManager.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 
 import XCTest
diff --git a/Tests/TestSubject.swift b/Tests/TestSubject.swift
index 8f6a8e8a5..9314b9e4f 100644
--- a/Tests/TestSubject.swift
+++ b/Tests/TestSubject.swift
@@ -1,8 +1,4 @@
-//
-//  TestSubject.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestUtils.swift b/Tests/TestUtils.swift
index e27242837..57c179580 100644
--- a/Tests/TestUtils.swift
+++ b/Tests/TestUtils.swift
@@ -1,8 +1,4 @@
-//
-//  TestUtils.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Jonathan Almeida
-//  License: Apache License Version 2.0
-//
 
 import XCTest
 @testable import SnowplowTracker
diff --git a/Tests/TestWebViewMessageHandler.swift b/Tests/TestWebViewMessageHandler.swift
index 9915bbfca..a71aa39d6 100644
--- a/Tests/TestWebViewMessageHandler.swift
+++ b/Tests/TestWebViewMessageHandler.swift
@@ -1,8 +1,4 @@
-//
-//  TestWebViewMessageHandler.h
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 #if os(iOS) || os(macOS)
 import XCTest
diff --git a/Tests/Utils/Micro.swift b/Tests/Utils/Micro.swift
index 4c1d4e10e..a6a6455ec 100644
--- a/Tests/Utils/Micro.swift
+++ b/Tests/Utils/Micro.swift
@@ -1,8 +1,4 @@
-//
-//  Micro.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Michael Hadam
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 import XCTest
diff --git a/Tests/Utils/MockDeviceInfoMonitor.swift b/Tests/Utils/MockDeviceInfoMonitor.swift
index 64edad210..4576402cf 100644
--- a/Tests/Utils/MockDeviceInfoMonitor.swift
+++ b/Tests/Utils/MockDeviceInfoMonitor.swift
@@ -1,8 +1,4 @@
-//
-//  MockDeviceInfoMonitor.swift
-//  Snowplow-iOSTests
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 @testable import SnowplowTracker
diff --git a/Tests/Utils/MockEventStore.swift b/Tests/Utils/MockEventStore.swift
index 361389f91..9c5053e60 100644
--- a/Tests/Utils/MockEventStore.swift
+++ b/Tests/Utils/MockEventStore.swift
@@ -1,8 +1,4 @@
-//
-//  MockEventStore.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 @testable import SnowplowTracker
diff --git a/Tests/Utils/MockLoggerDelegate.swift b/Tests/Utils/MockLoggerDelegate.swift
index 7a93ec720..5e7463dc1 100644
--- a/Tests/Utils/MockLoggerDelegate.swift
+++ b/Tests/Utils/MockLoggerDelegate.swift
@@ -1,8 +1,4 @@
-//
-//  SPMockLoggerDelegate.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 @testable import SnowplowTracker
diff --git a/Tests/Utils/MockNetworkConnection.swift b/Tests/Utils/MockNetworkConnection.swift
index 9e8c5ec66..220cd5608 100644
--- a/Tests/Utils/MockNetworkConnection.swift
+++ b/Tests/Utils/MockNetworkConnection.swift
@@ -1,8 +1,4 @@
-//
-//  SPMockNetworkConnection.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Alex Benini, Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 import Foundation
 @testable import SnowplowTracker
diff --git a/Tests/Utils/MockWKScriptMessage.swift b/Tests/Utils/MockWKScriptMessage.swift
index 71bd85831..9ba06d742 100644
--- a/Tests/Utils/MockWKScriptMessage.swift
+++ b/Tests/Utils/MockWKScriptMessage.swift
@@ -1,8 +1,4 @@
-//
-//  SPMockWKScriptMessage.swift
-//  Snowplow
-//
-//  Copyright (c) 2013-2022 Snowplow Analytics Ltd. All rights reserved.
+//  Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
 //
 //  This program is licensed to you under the Apache License Version 2.0,
 //  and you may not use this file except in compliance with the Apache License
@@ -14,10 +10,6 @@
 //  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 //  express or implied. See the Apache License Version 2.0 for the specific
 //  language governing permissions and limitations there under.
-//
-//  Authors: Matus Tomlein
-//  License: Apache License Version 2.0
-//
 
 #if os(iOS) || os(macOS)
 import WebKit

From 838ab14366679f5464a3d8dcaddab1df40a04f66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matu=CC=81s=CC=8C=20Tomlein?= <matus.tomlein@gmail.com>
Date: Thu, 2 Feb 2023 14:00:52 +0100
Subject: [PATCH 16/19] Prepare for 5.0.0-beta.1 release

---
 CHANGELOG                           | 9 +++++++++
 Examples                            | 2 +-
 SnowplowTracker.podspec             | 2 +-
 Sources/Core/TrackerConstants.swift | 8 ++++----
 VERSION                             | 2 +-
 5 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index aa1c3d188..92412afd2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,12 @@
+Version 5.0.0-beta.1 (2022-02-02)
+----------------------------------
+Add ability to provide custom tracker plugins to inspect and enrich tracked events (#750)
+Use Swift DSL for building configurations and add builder functions for configurations and events (#755)
+Remove requirement for all configurations to be serializable (#753)
+Refactor event interface and rename contexts to entities (#757)
+Refactor APIs to replace usage of NSObject in dictionaries with the Any type (#748)
+Update year in copyright headers and remove authors from headers in source files (#759)
+
 Version 5.0.0-alpha.2 (2022-12-21)
 ----------------------------------
 Add screen view tracking for SwiftUI (#705)
diff --git a/Examples b/Examples
index 7057e14b7..826edfdfd 160000
--- a/Examples
+++ b/Examples
@@ -1 +1 @@
-Subproject commit 7057e14b770ae33f0ff2d95464c61982d77010d1
+Subproject commit 826edfdfdf57b51578c2006c07b15df78883ee6d
diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec
index 9eb238028..9c5b78a40 100644
--- a/SnowplowTracker.podspec
+++ b/SnowplowTracker.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = "SnowplowTracker"
-    s.version          = "5.0.0-alpha.2"
+    s.version          = "5.0.0-beta.1"
     s.summary          = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
     s.description      = <<-DESC
     Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
diff --git a/Sources/Core/TrackerConstants.swift b/Sources/Core/TrackerConstants.swift
index eb58d70d7..c0d4589a8 100644
--- a/Sources/Core/TrackerConstants.swift
+++ b/Sources/Core/TrackerConstants.swift
@@ -15,13 +15,13 @@ import Foundation
 
 // --- Version
 #if os(iOS)
-let kSPVersion = "ios-5.0.0-alpha.2"
+let kSPVersion = "ios-5.0.0-beta.1"
 #elseif os(tvOS)
-let kSPVersion = "tvos-5.0.0-alpha.2"
+let kSPVersion = "tvos-5.0.0-beta.1"
 #elseif os(watchOS)
-let kSPVersion = "watchos-5.0.0-alpha.2"
+let kSPVersion = "watchos-5.0.0-beta.1"
 #else
-let kSPVersion = "osx-5.0.0-alpha.2"
+let kSPVersion = "osx-5.0.0-beta.1"
 #endif
 
 // --- Session Dictionary keys
diff --git a/VERSION b/VERSION
index 42d68e545..9e25fc0ae 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.0.0-alpha.2
+5.0.0-beta.1

From 3926e945c7415706dc534bcca195a48b04bc79a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matu=CC=81s=CC=8C=20Tomlein?= <matus.tomlein@gmail.com>
Date: Wed, 15 Feb 2023 12:31:55 +0700
Subject: [PATCH 17/19] Add redirect from root documentation page

---
 .github/workflows/doc.yml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index 724fe321f..4cd67f52a 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -17,6 +17,10 @@ jobs:
       - name: Generate documentation
         run: |
           swift package --allow-writing-to-directory docs generate-documentation --target SnowplowTracker --disable-indexing --output-path docs --transform-for-static-hosting --hosting-base-path snowplow-objc-tracker
+
+      - name: Redirect from the index page
+        run: |
+          echo '<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url='https://snowplow.github.io/snowplow-objc-tracker/documentation/snowplowtracker/'" /></head><body></body></html>' > docs/index.html
     
       - name: Deploy to GitHub Pages
         if: success()

From 1d4e37c3d08c532772a1f3ec5f1e8078ab41cfd0 Mon Sep 17 00:00:00 2001
From: Matus Tomlein <matus.tomlein@gmail.com>
Date: Tue, 4 Apr 2023 11:00:12 +0200
Subject: [PATCH 18/19] Rename the repository from snowplow-objc-tracker to
 snowplow-ios-tracker

PR #761
---
 .gitmodules             |  2 +-
 CONTRIBUTING.md         |  2 +-
 README.md               | 14 +++++++-------
 SnowplowTracker.podspec |  2 +-
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/.gitmodules b/.gitmodules
index 7a2d11715..b879b777a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
 [submodule "Examples"]
 	path = Examples
-	url = https://github.com/snowplow-incubator/snowplow-objc-tracker-examples
+	url = https://github.com/snowplow-incubator/snowplow-ios-tracker-examples
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 635ea528c..3a792c4d6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -26,7 +26,7 @@ If you see an issue you would like to work on, please let us know in the issue!
 not doubling the amount of work.
 
 If you don't know where to start contributing, you can look at
-[the issues labeled `good first issue`](https://github.com/snowplow/snowplow-objc-tracker/labels/category%3Agood_first_issue).
+[the issues labeled `good first issue`](https://github.com/snowplow/snowplow-ios-tracker/labels/category%3Agood_first_issue).
 
 ## Pull requests
 
diff --git a/README.md b/README.md
index 7eaaa3150..5b5d507d0 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ With this tracker you can collect event data from your applications, games or fr
 
 ### Demo apps using the Snowplow iOS Tracker
 
-Some examples of demo apps instrumented with our iOS Tracker can be found in the [snowplow-objc-tracker-examples](https://github.com/snowplow-incubator/snowplow-objc-tracker-examples) repository.
+Some examples of demo apps instrumented with our iOS Tracker can be found in the [snowplow-ios-tracker-examples](https://github.com/snowplow-incubator/snowplow-ios-tracker-examples) repository.
 
 ### Instrument the iOS Tracker
 
@@ -58,14 +58,14 @@ limitations under the License.
 [docs]: https://docs.snowplow.io/
 [mobile-docs]: https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/mobile-trackers/
 
-[gh-actions]: https://github.com/snowplow/snowplow-objc-tracker/actions
-[gh-actions-image]: https://github.com/snowplow/snowplow-objc-tracker/workflows/Build/badge.svg
+[gh-actions]: https://github.com/snowplow/snowplow-ios-tracker/actions
+[gh-actions-image]: https://github.com/snowplow/snowplow-ios-tracker/workflows/Build/badge.svg
 
-[coveralls]: https://coveralls.io/github/snowplow/snowplow-objc-tracker?branch=master
-[coveralls-image]: https://coveralls.io/repos/github/snowplow/snowplow-objc-tracker/badge.svg?branch=master
+[coveralls]: https://coveralls.io/github/snowplow/snowplow-ios-tracker?branch=master
+[coveralls-image]: https://coveralls.io/repos/github/snowplow/snowplow-ios-tracker/badge.svg?branch=master
 
 [license]: https://www.apache.org/licenses/LICENSE-2.0
-[license-image]: https://img.shields.io/github/license/snowplow/snowplow-objc-tracker
+[license-image]: https://img.shields.io/github/license/snowplow/snowplow-ios-tracker
 
 [cocoadocs]: https://cocoadocs.org/docsets/SnowplowTracker
 [cocoa-version]: https://cocoapod-badges.herokuapp.com/v/SnowplowTracker/badge.png
@@ -80,7 +80,7 @@ limitations under the License.
 [tech-docs]: https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/mobile-trackers/
 [tech-docs-image]: https://d3i6fms1cm1j0i.cloudfront.net/github/images/techdocs.png
 
-[api-docs]: https://snowplow.github.io/snowplow-objc-tracker/documentation/snowplowtracker/snowplow/
+[api-docs]: https://snowplow.github.io/snowplow-ios-tracker/documentation/snowplowtracker/snowplow/
 
 [contributing-image]: https://d3i6fms1cm1j0i.cloudfront.net/github/images/contributing.png
 
diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec
index 9c5b78a40..224cf78b3 100644
--- a/SnowplowTracker.podspec
+++ b/SnowplowTracker.podspec
@@ -9,7 +9,7 @@ Pod::Spec.new do |s|
     s.screenshots      = "https://d3i6fms1cm1j0i.cloudfront.net/github-wiki/images/snowplow-logo-large.png"
     s.license          = 'Apache License, Version 2.0'
     s.author           = { "Snowplow Analytics Ltd" => "support@snowplow.io" }
-    s.source           = { :git => "https://github.com/snowplow/snowplow-objc-tracker.git", :tag => s.version.to_s }
+    s.source           = { :git => "https://github.com/snowplow/snowplow-ios-tracker.git", :tag => s.version.to_s }
     s.social_media_url = 'https://twitter.com/SnowPlowData'
     s.documentation_url	= 'https://github.com/snowplow/snowplow/wiki/iOS-Tracker'
   

From 4d4e1a81582d3b86e7102407a3684d56bc8d0331 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matu=CC=81s=CC=8C=20Tomlein?= <matus.tomlein@gmail.com>
Date: Tue, 4 Apr 2023 12:21:09 +0200
Subject: [PATCH 19/19] Prepare for 5.0.0 release

---
 CHANGELOG                           | 7 ++++++-
 Examples                            | 2 +-
 SnowplowTracker.podspec             | 2 +-
 Sources/Core/TrackerConstants.swift | 8 ++++----
 VERSION                             | 2 +-
 5 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 92412afd2..5b53f26a0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,9 @@
-Version 5.0.0-beta.1 (2022-02-02)
+Version 5.0.0 (2023-04-04)
+--------------------------
+Rename the repository from snowplow-objc-tracker to snowplow-ios-tracker
+Add redirect from root documentation page
+
+Version 5.0.0-beta.1 (2023-02-02)
 ----------------------------------
 Add ability to provide custom tracker plugins to inspect and enrich tracked events (#750)
 Use Swift DSL for building configurations and add builder functions for configurations and events (#755)
diff --git a/Examples b/Examples
index 826edfdfd..d05cd226a 160000
--- a/Examples
+++ b/Examples
@@ -1 +1 @@
-Subproject commit 826edfdfdf57b51578c2006c07b15df78883ee6d
+Subproject commit d05cd226a6235d6949ef707f9be3fabb43b478ad
diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec
index 224cf78b3..86d2766c5 100644
--- a/SnowplowTracker.podspec
+++ b/SnowplowTracker.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = "SnowplowTracker"
-    s.version          = "5.0.0-beta.1"
+    s.version          = "5.0.0"
     s.summary          = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
     s.description      = <<-DESC
     Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
diff --git a/Sources/Core/TrackerConstants.swift b/Sources/Core/TrackerConstants.swift
index c0d4589a8..fb2ccb69b 100644
--- a/Sources/Core/TrackerConstants.swift
+++ b/Sources/Core/TrackerConstants.swift
@@ -15,13 +15,13 @@ import Foundation
 
 // --- Version
 #if os(iOS)
-let kSPVersion = "ios-5.0.0-beta.1"
+let kSPVersion = "ios-5.0.0"
 #elseif os(tvOS)
-let kSPVersion = "tvos-5.0.0-beta.1"
+let kSPVersion = "tvos-5.0.0"
 #elseif os(watchOS)
-let kSPVersion = "watchos-5.0.0-beta.1"
+let kSPVersion = "watchos-5.0.0"
 #else
-let kSPVersion = "osx-5.0.0-beta.1"
+let kSPVersion = "osx-5.0.0"
 #endif
 
 // --- Session Dictionary keys
diff --git a/VERSION b/VERSION
index 9e25fc0ae..0062ac971 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.0.0-beta.1
+5.0.0