From a0e8a9531156ab7837a53964158b7e55e7609af2 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 10 Jun 2021 11:56:12 -0700 Subject: [PATCH 01/11] Add StartupQueue automatically; update tests. --- Sources/Segment/Startup.swift | 2 ++ Tests/Segment-Tests/Analytics_Tests.swift | 21 +++++++++++++++++++ .../Segment-Tests/Support/TestUtilities.swift | 15 ++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Sources/Segment/Startup.swift b/Sources/Segment/Startup.swift index f0e6c068..f9334e1e 100644 --- a/Sources/Segment/Startup.swift +++ b/Sources/Segment/Startup.swift @@ -11,6 +11,8 @@ import Sovran extension Analytics: Subscriber { internal func platformStartup() { + add(plugin: StartupQueue(name: StartupQueue.specificName)) + // add segment destination plugin unless // asked not to via configuration. if configuration.values.autoAddSegmentDestination { diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index a362f4f9..a251e954 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -62,6 +62,8 @@ final class Analytics_Tests: XCTestCase { let analytics = Analytics(configuration: Configuration(writeKey: "test")) let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + + waitUntilStarted(analytics: analytics) analytics.setDeviceToken("1234") analytics.track(name: "token check") @@ -78,6 +80,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + let dataToken = UUID().asData() analytics.registeredForRemoteNotifications(deviceToken: dataToken) analytics.track(name: "token check") @@ -94,6 +98,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + analytics.track(name: "test track") let trackEvent: TrackEvent? = outputReader.lastEvent as? TrackEvent @@ -106,6 +112,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + analytics.identify(userId: "brandon", traits: MyTraits(email: "blah@blah.com")) let identifyEvent: IdentifyEvent? = outputReader.lastEvent as? IdentifyEvent @@ -119,6 +127,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + analytics.identify(userId: "brandon", traits: MyTraits(email: "blah@blah.com")) let identifyEvent: IdentifyEvent? = outputReader.lastEvent as? IdentifyEvent @@ -143,6 +153,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + analytics.screen(screenTitle: "screen1", category: "category1") let screen1Event: ScreenEvent? = outputReader.lastEvent as? ScreenEvent @@ -163,6 +175,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + analytics.group(groupId: "1234") let group1Event: GroupEvent? = outputReader.lastEvent as? GroupEvent @@ -181,6 +195,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + analytics.identify(userId: "brandon", traits: MyTraits(email: "blah@blah.com")) let identifyEvent: IdentifyEvent? = outputReader.lastEvent as? IdentifyEvent @@ -203,6 +219,9 @@ final class Analytics_Tests: XCTestCase { func testFlush() { let analytics = Analytics(configuration: Configuration(writeKey: "test")) + + waitUntilStarted(analytics: analytics) + analytics.identify(userId: "brandon", traits: MyTraits(email: "blah@blah.com")) let currentBatchCount = analytics.storage.eventFiles(includeUnfinished: true).count @@ -220,6 +239,8 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin(name: "outputReader") analytics.add(plugin: outputReader) + waitUntilStarted(analytics: analytics) + analytics.track(name: "whataversion") let trackEvent: TrackEvent? = outputReader.lastEvent as? TrackEvent diff --git a/Tests/Segment-Tests/Support/TestUtilities.swift b/Tests/Segment-Tests/Support/TestUtilities.swift index 28600a15..af2480b4 100644 --- a/Tests/Segment-Tests/Support/TestUtilities.swift +++ b/Tests/Segment-Tests/Support/TestUtilities.swift @@ -6,7 +6,7 @@ // import Foundation -import Segment +@testable import Segment extension UUID{ public func asUInt8Array() -> [UInt8]{ @@ -107,3 +107,16 @@ class OutputReaderPlugin: Plugin { return event } } + +func waitUntilStarted(analytics: Analytics?) { + guard let analytics = analytics else { return } + var started = false + while started == false { + if let system: System = analytics.store.currentState() { + if system.started { + started = true + } + } + RunLoop.main.run(until: Date.distantPast) + } +} From e0187c7913516fd4e76b0ff2f785f78a36abab94 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Mon, 14 Jun 2021 14:10:40 -0700 Subject: [PATCH 02/11] Refactored startup sequence. --- .../DestinationsExample/AppDelegate.swift | 6 ++-- .../DestinationsExample/ViewController.swift | 2 ++ Sources/Segment/Plugins.swift | 16 --------- Sources/Segment/Plugins/StartupQueue.swift | 17 +++++---- Sources/Segment/Settings.swift | 35 +++++++++++++++++-- Sources/Segment/Startup.swift | 25 ------------- Sources/Segment/State.swift | 24 ++++++------- 7 files changed, 60 insertions(+), 65 deletions(-) diff --git a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift index 0748fce4..fc954403 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift +++ b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift @@ -16,7 +16,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. - let configuration = Configuration(writeKey: "") + let configuration = Configuration(writeKey: "1234") .trackApplicationLifecycleEvents(true) .flushInterval(10) @@ -25,14 +25,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Add Adjust destination plugin analytics?.add(plugin: AdjustDestination(name: "Adjust")) - // Add Amplitude session plugin + /*// Add Amplitude session plugin analytics?.add(plugin: AmplitudeSession(name: "Amplitude")) // Add Mixpanel destination plugin analytics?.add(plugin: MixpanelDestination(name: "Mixpanel")) // Add Flurry destination plugin - analytics?.add(plugin: FlurryDestination(name: "Flurry")) + analytics?.add(plugin: FlurryDestination(name: "Flurry"))*/ return true } diff --git a/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift b/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift index 3e3f95c0..a4293106 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift +++ b/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift @@ -19,6 +19,8 @@ class ViewController: UIViewController { @IBAction func trackAction(_ sender: Any) { analytics?.track(name: "test event", properties: ["testValue": 42]) + + analytics?.add(plugin: MixpanelDestination(name: "Mixpanel")) } } diff --git a/Sources/Segment/Plugins.swift b/Sources/Segment/Plugins.swift index 5582769a..b8979bd0 100644 --- a/Sources/Segment/Plugins.swift +++ b/Sources/Segment/Plugins.swift @@ -141,16 +141,6 @@ extension Analytics { */ @discardableResult public func add(plugin: Plugin) -> String { - // we need to know if the system is already started. - var wasStarted = false - if let system: System = store.currentState(), system.started { - wasStarted = system.started - // if it was, we need to stop it temporarily. - store.dispatch(action: System.SetStartedAction(started: false)) - // adding the plugin to the timeline below will eventually call - // update(settings:) at which point, we can start it up again. - } - plugin.configure(analytics: self) timeline.add(plugin: plugin) if plugin is DestinationPlugin && !(plugin is SegmentDestination) { @@ -158,12 +148,6 @@ extension Analytics { store.dispatch(action: System.AddIntegrationAction(pluginName: plugin.name)) } - // if the timeline had started before, set it back to started since - // update(settings:) will have been called by now. - if wasStarted { - store.dispatch(action: System.SetStartedAction(started: true)) - } - return plugin.name } diff --git a/Sources/Segment/Plugins/StartupQueue.swift b/Sources/Segment/Plugins/StartupQueue.swift index 0ade41e6..55b66b7b 100644 --- a/Sources/Segment/Plugins/StartupQueue.swift +++ b/Sources/Segment/Plugins/StartupQueue.swift @@ -12,13 +12,17 @@ class StartupQueue: Plugin, Subscriber { static var specificName = "Segment_StartupQueue" static let maxSize = 1000 - @Atomic var started: Bool = false + @Atomic var running: Bool = false { + didSet { + print("queuing = \(!running)") + } + } let type: PluginType = .before let name: String = specificName var analytics: Analytics? = nil { didSet { - analytics?.store.subscribe(self, handler: systemUpdate) + analytics?.store.subscribe(self, handler: runningUpdate) } } @@ -29,13 +33,14 @@ class StartupQueue: Plugin, Subscriber { } func execute(event: T?) -> T? { - if started == false, let e = event { + if running == false, let e = event { // timeline hasn't started, so queue it up. if queuedEvents.count >= Self.maxSize { // if we've exceeded the max queue size start dropping events queuedEvents.removeFirst() } queuedEvents.append(e) + print("queued event: \(e.type)") return nil } // the timeline has started, so let the event pass. @@ -44,9 +49,9 @@ class StartupQueue: Plugin, Subscriber { } extension StartupQueue { - internal func systemUpdate(state: System) { - started = state.started - if started { + internal func runningUpdate(state: System) { + running = state.running + if running { replayEvents() } } diff --git a/Sources/Segment/Settings.swift b/Sources/Segment/Settings.swift index 01e95af3..81367265 100644 --- a/Sources/Segment/Settings.swift +++ b/Sources/Segment/Settings.swift @@ -23,7 +23,7 @@ public struct Settings: Codable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) - integrations = try? values.decode(JSON.self, forKey: CodingKeys.integrations) + self.integrations = try? values.decode(JSON.self, forKey: CodingKeys.integrations) self.plan = try? values.decode(JSON.self, forKey: CodingKeys.plan) self.edgeFunctions = try? values.decode(JSON.self, forKey: CodingKeys.edgeFunctions) } @@ -52,18 +52,49 @@ public struct Settings: Codable { } +extension Settings: Equatable { + public static func == (lhs: Settings, rhs: Settings) -> Bool { + let l = lhs.prettyPrint() + let r = rhs.prettyPrint() + return l == r + } +} + extension Analytics { - func checkSettings() { + internal func update(settings: Settings) { + apply { (plugin) in + // tell all top level plugins to update. + update(plugin: plugin, settings: settings) + } + } + + internal func update(plugin: Plugin, settings: Settings) { + plugin.update(settings: settings) + // if it's a destination, tell it's plugins to update as well. + if let dest = plugin as? DestinationPlugin { + dest.apply { (subPlugin) in + subPlugin.update(settings: settings) + } + } + } + + internal func checkSettings() { let writeKey = self.configuration.values.writeKey let httpClient = HTTPClient(analytics: self, cdnHost: configuration.values.cdnHost) + // stop things; queue in case our settings have changed. + self.store.dispatch(action: System.ToggleRunningAction(running: false)) httpClient.settingsFor(writeKey: writeKey) { (success, settings) in if success { if let s = settings { // put the new settings in the state store. // this will cause them to be cached. self.store.dispatch(action: System.UpdateSettingsAction(settings: s)) + // let plugins know we just received some settings.. + self.update(settings: s) } } + // we're good to go back to a running state. + self.store.dispatch(action: System.ToggleRunningAction(running: true)) } } } diff --git a/Sources/Segment/Startup.swift b/Sources/Segment/Startup.swift index f9334e1e..7c5c0a0d 100644 --- a/Sources/Segment/Startup.swift +++ b/Sources/Segment/Startup.swift @@ -29,37 +29,12 @@ extension Analytics: Subscriber { } } - // prepare our subscription for settings updates from segment.com - store.subscribe(self, initialState: true) { (state: System) in - if let settings = state.settings { - self.update(settings: settings) - } - self.store.dispatch(action: System.SetStartedAction(started: true)) - } - // plugins will receive any settings we currently have as they are added. // ... but lets go check if we have new stuff .... // start checking periodically for settings changes from segment.com setupSettingsCheck() } - internal func update(settings: Settings) { - apply { (plugin) in - // tell all top level plugins to update. - update(plugin: plugin, settings: settings) - } - } - - internal func update(plugin: Plugin, settings: Settings) { - plugin.update(settings: settings) - // if it's a destination, tell it's plugins to update as well. - if let dest = plugin as? DestinationPlugin { - dest.apply { (subPlugin) in - subPlugin.update(settings: settings) - } - } - } - internal func platformPlugins() -> [PlatformPlugin.Type]? { var plugins = [PlatformPlugin.Type]() diff --git a/Sources/Segment/State.swift b/Sources/Segment/State.swift index 665ffd78..40e39890 100644 --- a/Sources/Segment/State.swift +++ b/Sources/Segment/State.swift @@ -8,14 +8,13 @@ import Foundation import Sovran - // MARK: - System (Overall) struct System: State { let configuration: Configuration let integrations: JSON? let settings: Settings? - let started: Bool + let running: Bool struct UpdateSettingsAction: Action { let settings: Settings @@ -24,7 +23,7 @@ struct System: State { let result = System(configuration: state.configuration, integrations: state.integrations, settings: settings, - started: state.started) + running: state.running) return result } } @@ -42,7 +41,7 @@ struct System: State { let result = System(configuration: state.configuration, integrations: jsonIntegrations, settings: state.settings, - started: state.started) + running: state.running) return result } } @@ -60,7 +59,7 @@ struct System: State { let result = System(configuration: state.configuration, integrations: jsonIntegrations, settings: state.settings, - started: state.started) + running: state.running) return result } } @@ -68,15 +67,14 @@ struct System: State { } } - struct SetStartedAction: Action { - let started: Bool + struct ToggleRunningAction: Action { + let running: Bool func reduce(state: System) -> System { - let result = System(configuration: state.configuration, - integrations: state.integrations, - settings: state.settings, - started: started) - return result + return System(configuration: state.configuration, + integrations: state.integrations, + settings: state.settings, + running: running) } } } @@ -142,7 +140,7 @@ extension System { } } let integrationDictionary = try! JSON([String: Any]()) - return System(configuration: configuration, integrations: integrationDictionary, settings: settings, started: false) + return System(configuration: configuration, integrations: integrationDictionary, settings: settings, running: false) } } From 523c7031c212c4529ac22ba22b60fb935059fd57 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Mon, 14 Jun 2021 14:30:57 -0700 Subject: [PATCH 03/11] removed extraneous code --- .../DestinationsExample/AppDelegate.swift | 4 ++-- .../DestinationsExample/ViewController.swift | 2 -- Sources/Segment/Plugins/StartupQueue.swift | 7 +------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift index fc954403..df4f0e5a 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift +++ b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift @@ -25,14 +25,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Add Adjust destination plugin analytics?.add(plugin: AdjustDestination(name: "Adjust")) - /*// Add Amplitude session plugin + // Add Amplitude session plugin analytics?.add(plugin: AmplitudeSession(name: "Amplitude")) // Add Mixpanel destination plugin analytics?.add(plugin: MixpanelDestination(name: "Mixpanel")) // Add Flurry destination plugin - analytics?.add(plugin: FlurryDestination(name: "Flurry"))*/ + analytics?.add(plugin: FlurryDestination(name: "Flurry")) return true } diff --git a/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift b/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift index a4293106..3e3f95c0 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift +++ b/Examples/apps/DestinationsExample/DestinationsExample/ViewController.swift @@ -19,8 +19,6 @@ class ViewController: UIViewController { @IBAction func trackAction(_ sender: Any) { analytics?.track(name: "test event", properties: ["testValue": 42]) - - analytics?.add(plugin: MixpanelDestination(name: "Mixpanel")) } } diff --git a/Sources/Segment/Plugins/StartupQueue.swift b/Sources/Segment/Plugins/StartupQueue.swift index 55b66b7b..88f7a27d 100644 --- a/Sources/Segment/Plugins/StartupQueue.swift +++ b/Sources/Segment/Plugins/StartupQueue.swift @@ -12,11 +12,7 @@ class StartupQueue: Plugin, Subscriber { static var specificName = "Segment_StartupQueue" static let maxSize = 1000 - @Atomic var running: Bool = false { - didSet { - print("queuing = \(!running)") - } - } + @Atomic var running: Bool = false let type: PluginType = .before let name: String = specificName @@ -40,7 +36,6 @@ class StartupQueue: Plugin, Subscriber { queuedEvents.removeFirst() } queuedEvents.append(e) - print("queued event: \(e.type)") return nil } // the timeline has started, so let the event pass. From 89261442cdf0e43a970b11e6cc8589cd7abbb4fe Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Wed, 16 Jun 2021 15:06:39 -0700 Subject: [PATCH 04/11] More Startup changes for the queue + mac example --- .../BasicExample.xcodeproj/project.pbxproj | 14 +- .../BasicExample/BasicExample.entitlements | 10 + .../Base.lproj/Main.storyboard | 4 +- .../MacExample.xcodeproj/project.pbxproj | 351 +++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../MacExample/MacExample/AppDelegate.swift | 43 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 58 ++ .../MacExample/Assets.xcassets/Contents.json | 6 + .../MacExample/Base.lproj/Main.storyboard | 731 ++++++++++++++++++ .../apps/MacExample/MacExample/Info.plist | 30 + .../MacExample/MacExample.entitlements | 10 + .../MacExample/ViewController.swift | 30 + Segment.xcodeproj/project.pbxproj | 4 + Sources/Segment/Settings.swift | 15 +- Sources/Segment/Startup.swift | 20 +- Sources/Segment/Utilities/QueueTimer.swift | 90 +++ .../Segment-Tests/Support/TestUtilities.swift | 9 +- 21 files changed, 1456 insertions(+), 13 deletions(-) create mode 100644 Examples/apps/BasicExample/BasicExample/BasicExample.entitlements create mode 100644 Examples/apps/MacExample/MacExample.xcodeproj/project.pbxproj create mode 100644 Examples/apps/MacExample/MacExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Examples/apps/MacExample/MacExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Examples/apps/MacExample/MacExample.xcworkspace/contents.xcworkspacedata create mode 100644 Examples/apps/MacExample/MacExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Examples/apps/MacExample/MacExample/AppDelegate.swift create mode 100644 Examples/apps/MacExample/MacExample/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Examples/apps/MacExample/MacExample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Examples/apps/MacExample/MacExample/Assets.xcassets/Contents.json create mode 100644 Examples/apps/MacExample/MacExample/Base.lproj/Main.storyboard create mode 100644 Examples/apps/MacExample/MacExample/Info.plist create mode 100644 Examples/apps/MacExample/MacExample/MacExample.entitlements create mode 100644 Examples/apps/MacExample/MacExample/ViewController.swift create mode 100644 Sources/Segment/Utilities/QueueTimer.swift diff --git a/Examples/apps/BasicExample/BasicExample.xcodeproj/project.pbxproj b/Examples/apps/BasicExample/BasicExample.xcodeproj/project.pbxproj index 87e3f1f7..b0d77e27 100644 --- a/Examples/apps/BasicExample/BasicExample.xcodeproj/project.pbxproj +++ b/Examples/apps/BasicExample/BasicExample.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 4663C72A267A8D6B00ADDD1A /* BasicExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BasicExample.entitlements; sourceTree = ""; }; 469F7AF8265C25890038E773 /* EventData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventData.swift; sourceTree = ""; }; 46E38361265837EA00BA2502 /* BasicExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BasicExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46E38364265837EA00BA2502 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -63,6 +64,7 @@ 46E38363265837EA00BA2502 /* BasicExample */ = { isa = PBXGroup; children = ( + 4663C72A267A8D6B00ADDD1A /* BasicExample.entitlements */, 46E38364265837EA00BA2502 /* AppDelegate.swift */, 46E38366265837EA00BA2502 /* SceneDelegate.swift */, 469F7AF8265C25890038E773 /* EventData.swift */, @@ -309,7 +311,9 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_ENTITLEMENTS = BasicExample/BasicExample.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; INFOPLIST_FILE = BasicExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -317,6 +321,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.segment.BasicExample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -327,7 +333,9 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_ENTITLEMENTS = BasicExample/BasicExample.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; INFOPLIST_FILE = BasicExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -335,6 +343,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.segment.BasicExample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/Examples/apps/BasicExample/BasicExample/BasicExample.entitlements b/Examples/apps/BasicExample/BasicExample/BasicExample.entitlements new file mode 100644 index 00000000..ee95ab7e --- /dev/null +++ b/Examples/apps/BasicExample/BasicExample/BasicExample.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/Examples/apps/DestinationsExample/DestinationsExample/Base.lproj/Main.storyboard b/Examples/apps/DestinationsExample/DestinationsExample/Base.lproj/Main.storyboard index 55b776ab..101cad41 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample/Base.lproj/Main.storyboard +++ b/Examples/apps/DestinationsExample/DestinationsExample/Base.lproj/Main.storyboard @@ -1,6 +1,6 @@ - + @@ -13,7 +13,7 @@ - + + + + + + + + + + diff --git a/Examples/apps/MacExample/MacExample/Info.plist b/Examples/apps/MacExample/MacExample/Info.plist new file mode 100644 index 00000000..cfbbdb70 --- /dev/null +++ b/Examples/apps/MacExample/MacExample/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/Examples/apps/MacExample/MacExample/MacExample.entitlements b/Examples/apps/MacExample/MacExample/MacExample.entitlements new file mode 100644 index 00000000..f2ef3ae0 --- /dev/null +++ b/Examples/apps/MacExample/MacExample/MacExample.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/Examples/apps/MacExample/MacExample/ViewController.swift b/Examples/apps/MacExample/MacExample/ViewController.swift new file mode 100644 index 00000000..9ccc81d7 --- /dev/null +++ b/Examples/apps/MacExample/MacExample/ViewController.swift @@ -0,0 +1,30 @@ +// +// ViewController.swift +// MacExample +// +// Created by Brandon Sneed on 6/16/21. +// + +import Cocoa + +class ViewController: NSViewController { + var analytics = NSApplication.shared.delegate?.analytics + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override var representedObject: Any? { + didSet { + // Update the view, if already loaded. + } + } + + + @IBAction func trackButton(_ sender: Any) { + analytics?.track(name: "test event") + } +} + diff --git a/Segment.xcodeproj/project.pbxproj b/Segment.xcodeproj/project.pbxproj index 4d2a326c..2d05a971 100644 --- a/Segment.xcodeproj/project.pbxproj +++ b/Segment.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 46210811260538BE00EBC4A8 /* KeyPath_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46210810260538BE00EBC4A8 /* KeyPath_Tests.swift */; }; 46210836260BBEE400EBC4A8 /* DeviceToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46210835260BBEE400EBC4A8 /* DeviceToken.swift */; }; 4658175425BA4C20006B2809 /* HTTPClient_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4658175325BA4C20006B2809 /* HTTPClient_Tests.swift */; }; + 4663C729267A799100ADDD1A /* QueueTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4663C728267A799100ADDD1A /* QueueTimer.swift */; }; 46A018C225E5857D00F9CCD8 /* Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46A018C125E5857D00F9CCD8 /* Context.swift */; }; 46A018D425E6C9C200F9CCD8 /* LinuxUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46A018D325E6C9C200F9CCD8 /* LinuxUtils.swift */; }; 46A018DA25E97FDF00F9CCD8 /* AppleUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46A018D925E97FDF00F9CCD8 /* AppleUtils.swift */; }; @@ -109,6 +110,7 @@ 46210810260538BE00EBC4A8 /* KeyPath_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPath_Tests.swift; sourceTree = ""; }; 46210835260BBEE400EBC4A8 /* DeviceToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceToken.swift; sourceTree = ""; }; 4658175325BA4C20006B2809 /* HTTPClient_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient_Tests.swift; sourceTree = ""; }; + 4663C728267A799100ADDD1A /* QueueTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueTimer.swift; sourceTree = ""; }; 46A018C125E5857D00F9CCD8 /* Context.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Context.swift; sourceTree = ""; }; 46A018D325E6C9C200F9CCD8 /* LinuxUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxUtils.swift; sourceTree = ""; }; 46A018D925E97FDF00F9CCD8 /* AppleUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleUtils.swift; sourceTree = ""; }; @@ -267,6 +269,7 @@ 4621080B2605332D00EBC4A8 /* KeyPath.swift */, 46022770261F7A4800A9E913 /* Atomic.swift */, 46E382E62654429A00BA2502 /* Utils.swift */, + 4663C728267A799100ADDD1A /* QueueTimer.swift */, ); path = Utilities; sourceTree = ""; @@ -457,6 +460,7 @@ A31A16E12579779600C9CDDF /* Version.swift in Sources */, 46210836260BBEE400EBC4A8 /* DeviceToken.swift in Sources */, 9692724E25A4E5B7009B5298 /* Startup.swift in Sources */, + 4663C729267A799100ADDD1A /* QueueTimer.swift in Sources */, 46FE4C9C25A3F41C003A7362 /* LinuxLifecycleMonitor.swift in Sources */, 460227422612987300A9E913 /* watchOSLifecycleEvents.swift in Sources */, A31A162F2576B73F00C9CDDF /* State.swift in Sources */, diff --git a/Sources/Segment/Settings.swift b/Sources/Segment/Settings.swift index 81367265..70582022 100644 --- a/Sources/Segment/Settings.swift +++ b/Sources/Segment/Settings.swift @@ -79,10 +79,23 @@ extension Analytics { } internal func checkSettings() { + if isUnitTesting { + // we don't really wanna wait for this network call during tests... + // but we should make it work similarly. + store.dispatch(action: System.ToggleRunningAction(running: false)) + DispatchQueue.main.async { + if let state: System = self.store.currentState(), let settings = state.settings { + self.store.dispatch(action: System.UpdateSettingsAction(settings: settings)) + } + self.store.dispatch(action: System.ToggleRunningAction(running: true)) + } + return + } + let writeKey = self.configuration.values.writeKey let httpClient = HTTPClient(analytics: self, cdnHost: configuration.values.cdnHost) // stop things; queue in case our settings have changed. - self.store.dispatch(action: System.ToggleRunningAction(running: false)) + store.dispatch(action: System.ToggleRunningAction(running: false)) httpClient.settingsFor(writeKey: writeKey) { (success, settings) in if success { if let s = settings { diff --git a/Sources/Segment/Startup.swift b/Sources/Segment/Startup.swift index 7c5c0a0d..2f43cfc5 100644 --- a/Sources/Segment/Startup.swift +++ b/Sources/Segment/Startup.swift @@ -66,12 +66,18 @@ extension Analytics: Subscriber { } } -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) import UIKit extension Analytics { internal func setupSettingsCheck() { + // do the first one + checkSettings() + // set up return-from-background to do it again. NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { (notification) in - self.checkSettings() + guard let app = notification.object as? UIApplication else { return } + if app.applicationState == .background { + self.checkSettings() + } } } } @@ -79,13 +85,19 @@ extension Analytics { extension Analytics { internal func setupSettingsCheck() { // TBD: we don't know what to do here yet. + checkSettings() } } #elseif os(macOS) import Cocoa extension Analytics { internal func setupSettingsCheck() { - NotificationCenter.default.addObserver(forName: NSApplication.willBecomeActiveNotification, object: nil, queue: OperationQueue.main) { (notification) in + // do the first one + checkSettings() + // now set up a timer to do it every 24 hrs. + // mac apps change focus a lot more than iOS apps, so this + // seems more appropriate here. + QueueTimer.schedule(interval: 5, queue: .main) { self.checkSettings() } } @@ -93,7 +105,7 @@ extension Analytics { #elseif os(Linux) extension Analytics { internal func setupSettingsCheck() { - // TBD: we don't know what to do here. + checkSettings() } } #endif diff --git a/Sources/Segment/Utilities/QueueTimer.swift b/Sources/Segment/Utilities/QueueTimer.swift new file mode 100644 index 00000000..1f14ec10 --- /dev/null +++ b/Sources/Segment/Utilities/QueueTimer.swift @@ -0,0 +1,90 @@ +// +// QueueTimer.swift +// Segment +// +// Created by Brandon Sneed on 6/16/21. +// + +import Foundation + +internal class QueueTimer { + enum State { + case suspended + case resumed + } + + let interval: TimeInterval + let timer: DispatchSourceTimer + let queue: DispatchQueue + let handler: () -> Void + + @Atomic var state: State = .suspended + + static var timers = [QueueTimer]() + + static func schedule(interval: TimeInterval, queue: DispatchQueue = .main, handler: @escaping () -> Void) { + let timer = QueueTimer(interval: interval, queue: queue, handler: handler) + Self.timers.append(timer) + } + + init(interval: TimeInterval, queue: DispatchQueue = .main, handler: @escaping () -> Void) { + self.interval = interval + self.queue = queue + self.handler = handler + + timer = DispatchSource.makeTimerSource(flags: [], queue: queue) + timer.schedule(deadline: .now() + self.interval, repeating: self.interval) + timer.setEventHandler { [weak self] in + self?.handler() + } + resume() + } + + deinit { + timer.setEventHandler { + // do nothing ... + } + // if timer is suspended, we must resume if we're going to cancel. + timer.cancel() + resume() + } + + func suspend() { + if state == .suspended { + return + } + state = .suspended + timer.suspend() + } + + func resume() { + if state == .resumed { + return + } + state = .resumed + timer.resume() + } +} + + +extension TimeInterval { + static func milligarvins(_ value: Int) -> TimeInterval { + return TimeInterval(other: ((value / 60) * 1000) / 42) + } + + static func milliseconds(_ value: Int) -> TimeInterval { + return TimeInterval(other: value / 1000) + } + + static func seconds(_ value: Int) -> TimeInterval { + return TimeInterval(value) + } + + static func hours(_ value: Int) -> TimeInterval { + return TimeInterval(other: 60 * value) + } + + static func days(_ value: Int) -> TimeInterval { + return TimeInterval(other: (60 * value) * 24) + } +} diff --git a/Tests/Segment-Tests/Support/TestUtilities.swift b/Tests/Segment-Tests/Support/TestUtilities.swift index af2480b4..1a2bdf32 100644 --- a/Tests/Segment-Tests/Support/TestUtilities.swift +++ b/Tests/Segment-Tests/Support/TestUtilities.swift @@ -110,13 +110,14 @@ class OutputReaderPlugin: Plugin { func waitUntilStarted(analytics: Analytics?) { guard let analytics = analytics else { return } - var started = false - while started == false { + var running = false + while running == false { if let system: System = analytics.store.currentState() { - if system.started { - started = true + if system.running { + running = true } } RunLoop.main.run(until: Date.distantPast) } + //RunLoop.main.run(until: Date(timeIntervalSinceNow: 2)) } From d9f3f2fca2108e4d73a138e5a8c41a020d8870f0 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 17 Jun 2021 12:04:22 -0700 Subject: [PATCH 05/11] catch mac catalyst in platform bits --- Sources/Segment/Plugins/Platforms/iOS/iOSAppBackground.swift | 2 +- Sources/Segment/Plugins/Platforms/iOS/iOSDelegation.swift | 2 +- Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Segment/Plugins/Platforms/iOS/iOSAppBackground.swift b/Sources/Segment/Plugins/Platforms/iOS/iOSAppBackground.swift index f9fb1014..e3e492aa 100644 --- a/Sources/Segment/Plugins/Platforms/iOS/iOSAppBackground.swift +++ b/Sources/Segment/Plugins/Platforms/iOS/iOSAppBackground.swift @@ -5,7 +5,7 @@ // Created by Cody Garvin on 1/14/21. // -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) import UIKit class iOSAppBackground: PlatformPlugin { diff --git a/Sources/Segment/Plugins/Platforms/iOS/iOSDelegation.swift b/Sources/Segment/Plugins/Platforms/iOS/iOSDelegation.swift index 6439b235..28df4f38 100644 --- a/Sources/Segment/Plugins/Platforms/iOS/iOSDelegation.swift +++ b/Sources/Segment/Plugins/Platforms/iOS/iOSDelegation.swift @@ -7,7 +7,7 @@ import Foundation -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) import UIKit diff --git a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift index 503311f9..e111aef7 100644 --- a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift +++ b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift @@ -7,7 +7,7 @@ import Foundation -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) import UIKit From 6ed7e1036177b80dc882d780093b407e10b627de Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 17 Jun 2021 12:04:42 -0700 Subject: [PATCH 06/11] stop the flush timer when going to background --- .../Plugins/Platforms/Mac/macOSLifecycleMonitor.swift | 9 ++++++--- .../Plugins/Platforms/iOS/iOSLifecycleMonitor.swift | 8 +++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleMonitor.swift b/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleMonitor.swift index 6ad8ad4d..f752ddf1 100644 --- a/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleMonitor.swift +++ b/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleMonitor.swift @@ -230,11 +230,14 @@ class macOSLifecycleMonitor: PlatformPlugin { } extension SegmentDestination: macOSLifecycle { + public func applicationDidBecomeActive() { + flushTimer?.resume() + } - public func applicationDidEnterBackground() { - // TODO: Look into mac background tasks - //analytics.beginBackgroundTask() + public func applicationWillResignActive() { + flushTimer?.suspend() flush() } } + #endif diff --git a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift index 51f3d046..82bfddad 100644 --- a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift +++ b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleMonitor.swift @@ -5,7 +5,7 @@ // Created by Cody Garvin on 12/4/20. // -#if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) import Foundation import UIKit @@ -169,9 +169,15 @@ class iOSLifecycleMonitor: PlatformPlugin { } } +// MARK: - Segment Destination Extension + extension SegmentDestination: iOSLifecycle { + public func applicationWillEnterForeground(application: UIApplication) { + flushTimer?.resume() + } public func applicationDidEnterBackground() { + flushTimer?.suspend() analytics?.beginBackgroundTask() flush() } From 2b57e99282508df9c4531d6bb52a171834c67e3d Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 17 Jun 2021 12:05:04 -0700 Subject: [PATCH 07/11] fixed time interval computation --- Sources/Segment/Utilities/QueueTimer.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Sources/Segment/Utilities/QueueTimer.swift b/Sources/Segment/Utilities/QueueTimer.swift index 1f14ec10..18324c71 100644 --- a/Sources/Segment/Utilities/QueueTimer.swift +++ b/Sources/Segment/Utilities/QueueTimer.swift @@ -68,12 +68,8 @@ internal class QueueTimer { extension TimeInterval { - static func milligarvins(_ value: Int) -> TimeInterval { - return TimeInterval(other: ((value / 60) * 1000) / 42) - } - static func milliseconds(_ value: Int) -> TimeInterval { - return TimeInterval(other: value / 1000) + return TimeInterval(value / 1000) } static func seconds(_ value: Int) -> TimeInterval { @@ -81,10 +77,10 @@ extension TimeInterval { } static func hours(_ value: Int) -> TimeInterval { - return TimeInterval(other: 60 * value) + return TimeInterval(60 * value) } static func days(_ value: Int) -> TimeInterval { - return TimeInterval(other: (60 * value) * 24) + return TimeInterval((60 * value) * 24) } } From f72eb14899da27b9502c9f40a76bd2422132817d Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 17 Jun 2021 12:05:25 -0700 Subject: [PATCH 08/11] Use a queue timer instead of NSTimer. --- Sources/Segment/Plugins/SegmentDestination.swift | 7 ++++--- Sources/Segment/Startup.swift | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/Segment/Plugins/SegmentDestination.swift b/Sources/Segment/Plugins/SegmentDestination.swift index 8de9b242..873cdb1a 100644 --- a/Sources/Segment/Plugins/SegmentDestination.swift +++ b/Sources/Segment/Plugins/SegmentDestination.swift @@ -27,7 +27,7 @@ public class SegmentDestination: DestinationPlugin { private var apiHost: String? = nil @Atomic private var eventCount: Int = 0 - private var flushTimer: Timer? = nil + internal var flushTimer: QueueTimer? = nil internal enum Constants: String { case integrationName = "Segment.io" @@ -43,9 +43,9 @@ public class SegmentDestination: DestinationPlugin { guard let analytics = self.analytics else { return } storage = analytics.storage httpClient = HTTPClient(analytics: analytics) - flushTimer = Timer.scheduledTimer(withTimeInterval: analytics.configuration.values.flushInterval, repeats: true, block: { _ in + flushTimer = QueueTimer(interval: analytics.configuration.values.flushInterval) { self.flush() - }) + } } public func update(settings: Settings) { @@ -149,3 +149,4 @@ public class SegmentDestination: DestinationPlugin { } } } + diff --git a/Sources/Segment/Startup.swift b/Sources/Segment/Startup.swift index 2f43cfc5..2293e79d 100644 --- a/Sources/Segment/Startup.swift +++ b/Sources/Segment/Startup.swift @@ -97,7 +97,7 @@ extension Analytics { // now set up a timer to do it every 24 hrs. // mac apps change focus a lot more than iOS apps, so this // seems more appropriate here. - QueueTimer.schedule(interval: 5, queue: .main) { + QueueTimer.schedule(interval: .days(1), queue: .main) { self.checkSettings() } } From 68772d6b6bb13fb0387e2c9a85079624ac724d04 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 17 Jun 2021 12:24:21 -0700 Subject: [PATCH 09/11] fixed startup wait routine --- Tests/Segment-Tests/Support/TestUtilities.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Segment-Tests/Support/TestUtilities.swift b/Tests/Segment-Tests/Support/TestUtilities.swift index 1a2bdf32..5289b471 100644 --- a/Tests/Segment-Tests/Support/TestUtilities.swift +++ b/Tests/Segment-Tests/Support/TestUtilities.swift @@ -119,5 +119,6 @@ func waitUntilStarted(analytics: Analytics?) { } RunLoop.main.run(until: Date.distantPast) } - //RunLoop.main.run(until: Date(timeIntervalSinceNow: 2)) + // make sure stuff in the startup queue has time to process + RunLoop.main.run(until: Date.distantPast) } From 4add5008c7bdae09a03f3a2d40da2c2ff04c947e Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 17 Jun 2021 13:17:00 -0700 Subject: [PATCH 10/11] =?UTF-8?q?testing=20a=20change=20w/=20CI=20?= =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tests/Segment-Tests/Support/TestUtilities.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tests/Segment-Tests/Support/TestUtilities.swift b/Tests/Segment-Tests/Support/TestUtilities.swift index 5289b471..5fc33cb3 100644 --- a/Tests/Segment-Tests/Support/TestUtilities.swift +++ b/Tests/Segment-Tests/Support/TestUtilities.swift @@ -104,6 +104,9 @@ class OutputReaderPlugin: Plugin { func execute(event: T?) -> T? where T : RawEvent { lastEvent = event + if let t = lastEvent as? TrackEvent { + print("EVENT: \(t.event)") + } return event } } @@ -120,5 +123,5 @@ func waitUntilStarted(analytics: Analytics?) { RunLoop.main.run(until: Date.distantPast) } // make sure stuff in the startup queue has time to process - RunLoop.main.run(until: Date.distantPast) + RunLoop.main.run(until: Date(timeIntervalSinceNow: 2)) } From 6a1021d74f266c19a44f96b079e97637bf8740a4 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 17 Jun 2021 15:34:03 -0700 Subject: [PATCH 11/11] Watch the startupqueue plugin to decide when to continue w/ tests --- Sources/Segment/Plugins/StartupQueue.swift | 6 +++--- Tests/Segment-Tests/Support/TestUtilities.swift | 13 ++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Sources/Segment/Plugins/StartupQueue.swift b/Sources/Segment/Plugins/StartupQueue.swift index 88f7a27d..efb9b107 100644 --- a/Sources/Segment/Plugins/StartupQueue.swift +++ b/Sources/Segment/Plugins/StartupQueue.swift @@ -8,7 +8,7 @@ import Foundation import Sovran -class StartupQueue: Plugin, Subscriber { +internal class StartupQueue: Plugin, Subscriber { static var specificName = "Segment_StartupQueue" static let maxSize = 1000 @@ -45,10 +45,10 @@ class StartupQueue: Plugin, Subscriber { extension StartupQueue { internal func runningUpdate(state: System) { - running = state.running - if running { + if state.running { replayEvents() } + running = state.running } internal func replayEvents() { diff --git a/Tests/Segment-Tests/Support/TestUtilities.swift b/Tests/Segment-Tests/Support/TestUtilities.swift index 5fc33cb3..ed6c4aa5 100644 --- a/Tests/Segment-Tests/Support/TestUtilities.swift +++ b/Tests/Segment-Tests/Support/TestUtilities.swift @@ -113,15 +113,10 @@ class OutputReaderPlugin: Plugin { func waitUntilStarted(analytics: Analytics?) { guard let analytics = analytics else { return } - var running = false - while running == false { - if let system: System = analytics.store.currentState() { - if system.running { - running = true - } + // wait until the startup queue has emptied it's events. + if let startupQueue = analytics.find(pluginName: StartupQueue.specificName) as? StartupQueue { + while startupQueue.running != true { + RunLoop.main.run(until: Date.distantPast) } - RunLoop.main.run(until: Date.distantPast) } - // make sure stuff in the startup queue has time to process - RunLoop.main.run(until: Date(timeIntervalSinceNow: 2)) }