diff --git a/Examples/apps/BasicExample/BasicExample/AppDelegate.swift b/Examples/apps/BasicExample/BasicExample/AppDelegate.swift index b165a176..171868a1 100644 --- a/Examples/apps/BasicExample/BasicExample/AppDelegate.swift +++ b/Examples/apps/BasicExample/BasicExample/AppDelegate.swift @@ -17,7 +17,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Override point for customization after application launch. let configuration = Configuration(writeKey: "WRITE KEY") - .trackApplicationLifecycleEvents(true) + .setTrackedApplicationLifecycleEvents(.all) .flushInterval(10) .flushAt(2) diff --git a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift index 3e96c097..6c5ff71c 100644 --- a/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift +++ b/Examples/apps/DestinationsExample/DestinationsExample/AppDelegate.swift @@ -23,7 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Override point for customization after application launch. let configuration = Configuration(writeKey: "EioRQCqLHUECnoSseEguI8GnxOlZTOyX") - .trackApplicationLifecycleEvents(true) + .setTrackedApplicationLifecycleEvents(.all) .flushInterval(1) analytics = Analytics(configuration: configuration) diff --git a/Examples/apps/MacExample/MacExample/AppDelegate.swift b/Examples/apps/MacExample/MacExample/AppDelegate.swift index 6ddb6d85..6d6f02e1 100644 --- a/Examples/apps/MacExample/MacExample/AppDelegate.swift +++ b/Examples/apps/MacExample/MacExample/AppDelegate.swift @@ -18,7 +18,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { // Insert code here to initialize your application let configuration = Configuration(writeKey: "") - .trackApplicationLifecycleEvents(true) + .setTrackedApplicationLifecycleEvents(.all) .flushInterval(10) .flushAt(1) .errorHandler { error in diff --git a/Examples/apps/ObjCExample/ObjCExample/AppDelegate.m b/Examples/apps/ObjCExample/ObjCExample/AppDelegate.m index 8566eead..aca975f7 100644 --- a/Examples/apps/ObjCExample/ObjCExample/AppDelegate.m +++ b/Examples/apps/ObjCExample/ObjCExample/AppDelegate.m @@ -21,7 +21,7 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. SEGConfiguration *config = [[SEGConfiguration alloc] initWithWriteKey:@""]; - config.trackApplicationLifecycleEvents = YES; + config.trackApplicationLifecycleEvents = SEGTrackedLifecycleEvent.all; config.flushAt = 1; _analytics = [[SEGAnalytics alloc] initWithConfiguration: config]; diff --git a/Examples/apps/SegmentExtensionsExample/ArticleWidget/ArticleWidget.swift b/Examples/apps/SegmentExtensionsExample/ArticleWidget/ArticleWidget.swift index 6bbba8ba..36c902e4 100644 --- a/Examples/apps/SegmentExtensionsExample/ArticleWidget/ArticleWidget.swift +++ b/Examples/apps/SegmentExtensionsExample/ArticleWidget/ArticleWidget.swift @@ -101,5 +101,5 @@ extension Analytics { static var main = Analytics(configuration: Configuration(writeKey: "ABCD") .flushAt(3) - .trackApplicationLifecycleEvents(true)) + .setTrackedApplicationLifecycleEvents(.all)) } diff --git a/Examples/apps/SegmentSwiftUIExample/SegmentSwiftUIExample/SegmentSwiftUIExampleApp.swift b/Examples/apps/SegmentSwiftUIExample/SegmentSwiftUIExample/SegmentSwiftUIExampleApp.swift index 33fd077b..d161cf09 100644 --- a/Examples/apps/SegmentSwiftUIExample/SegmentSwiftUIExample/SegmentSwiftUIExampleApp.swift +++ b/Examples/apps/SegmentSwiftUIExample/SegmentSwiftUIExample/SegmentSwiftUIExampleApp.swift @@ -21,5 +21,5 @@ extension Analytics { static var main = Analytics(configuration: Configuration(writeKey: "ABCD") .flushAt(3) - .trackApplicationLifecycleEvents(true)) + .setTrackedApplicationLifecycleEvents(.all)) } diff --git a/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift b/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift index 63341a66..d12c4cff 100644 --- a/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift +++ b/Examples/apps/watchOSExample/watchOSExample WatchKit Extension/ExtensionDelegate.swift @@ -14,7 +14,7 @@ class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { // Perform any final initialization of your application. let configuration = Configuration(writeKey: "WRITE KEY") - .trackApplicationLifecycleEvents(true) + .setTrackedApplicationLifecycleEvents(.all) .flushInterval(10) analytics = Analytics(configuration: configuration) diff --git a/Examples/tasks/MultiInstance.swift b/Examples/tasks/MultiInstance.swift index e762ccb7..65944657 100644 --- a/Examples/tasks/MultiInstance.swift +++ b/Examples/tasks/MultiInstance.swift @@ -38,9 +38,9 @@ import Segment extension Analytics { static var main = Analytics(configuration: Configuration(writeKey: "1234") .flushAt(3) - .trackApplicationLifecycleEvents(true)) - + .setTrackedApplicationLifecycleEvents(.all)) + static var support = Analytics(configuration: Configuration(writeKey: "5678") .flushAt(10) - .trackApplicationLifecycleEvents(false)) + .setTrackedApplicationLifecycleEvents(.none)) } diff --git a/Sources/Segment/Configuration.swift b/Sources/Segment/Configuration.swift index b061c680..4a9f463a 100644 --- a/Sources/Segment/Configuration.swift +++ b/Sources/Segment/Configuration.swift @@ -47,11 +47,61 @@ public enum StorageMode { // MARK: - Internal Configuration +@objc(SEGTrackedLifecycleEvent) +public final class TrackedLifecycleEvent: NSObject, OptionSet { + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + public override func isEqual(_ object: Any?) -> Bool { + (object as? Self)?.rawValue == rawValue + } + + public override var hash: Int { + rawValue.hashValue + } + + @objc public static let none: TrackedLifecycleEvent = [] + @objc public static let applicationInstalled = TrackedLifecycleEvent(rawValue: 1 << 0) + @objc public static let applicationUpdated = TrackedLifecycleEvent(rawValue: 1 << 1) + @objc public static let applicationOpened = TrackedLifecycleEvent(rawValue: 1 << 2) + @objc public static let applicationBackgrounded = TrackedLifecycleEvent(rawValue: 1 << 3) + @objc public static let applicationForegrounded = TrackedLifecycleEvent(rawValue: 1 << 4) + #if os(macOS) + @objc public static let applicationUnhidden = TrackedLifecycleEvent(rawValue: 1 << 5) + @objc public static let applicationHidden = TrackedLifecycleEvent(rawValue: 1 << 6) + @objc public static let applicationTerminated = TrackedLifecycleEvent(rawValue: 1 << 7) + + @objc public static let all: TrackedLifecycleEvent = [ + .applicationInstalled, + .applicationUpdated, + .applicationOpened, + .applicationBackgrounded, + .applicationForegrounded, + .applicationUnhidden, + .applicationHidden, + .applicationTerminated, + ] + #elseif os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst) + @objc public static let all: TrackedLifecycleEvent = [ + .applicationInstalled, + .applicationUpdated, + .applicationOpened, + .applicationBackgrounded, + .applicationForegrounded, + ] + #else + @objc public static let all = TrackedLifecycleEvent.none + #endif +} + public class Configuration { internal struct Values { var writeKey: String var application: Any? = nil - var trackApplicationLifecycleEvents: Bool = true + var trackedApplicationLifecycleEvents = TrackedLifecycleEvent.all var flushAt: Int = 20 var flushInterval: TimeInterval = 30 var defaultSettings: Settings? = nil @@ -110,8 +160,19 @@ public extension Configuration { /// - Parameter enabled: A bool value /// - Returns: The current Configuration. @discardableResult + @available(*, deprecated, message: "Use `setTrackedApplicationLifecycleEvents(_:)` for more granular control") func trackApplicationLifecycleEvents(_ enabled: Bool) -> Configuration { - values.trackApplicationLifecycleEvents = enabled + values.trackedApplicationLifecycleEvents = enabled ? .all : .none + return self + } + + /// Opt-in/out of tracking lifecycle events. The default value is `.none`. + /// + /// - Parameter events: An option set of the events to track. + /// - Returns: The current Configuration. + @discardableResult + func setTrackedApplicationLifecycleEvents(_ events: TrackedLifecycleEvent) -> Configuration { + values.trackedApplicationLifecycleEvents = events return self } diff --git a/Sources/Segment/ObjC/ObjCConfiguration.swift b/Sources/Segment/ObjC/ObjCConfiguration.swift index af1abcd8..a6c0a673 100644 --- a/Sources/Segment/ObjC/ObjCConfiguration.swift +++ b/Sources/Segment/ObjC/ObjCConfiguration.swift @@ -29,12 +29,12 @@ public class ObjCConfiguration: NSObject { /// Opt-in/out of tracking lifecycle events. The default value is `false`. @objc - public var trackApplicationLifecycleEvents: Bool { + public var trackApplicationLifecycleEvents: TrackedLifecycleEvent { get { - return configuration.values.trackApplicationLifecycleEvents + return configuration.values.trackedApplicationLifecycleEvents } set(value) { - configuration.trackApplicationLifecycleEvents(value) + configuration.setTrackedApplicationLifecycleEvents(value) } } diff --git a/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleEvents.swift b/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleEvents.swift index 1664c770..0640fe9c 100644 --- a/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleEvents.swift +++ b/Sources/Segment/Plugins/Platforms/Mac/macOSLifecycleEvents.swift @@ -28,10 +28,6 @@ class macOSLifecycleEvents: PlatformPlugin, macOSLifecycle { // Make sure we aren't double calling application:didFinishLaunchingWithOptions // by resetting the check at the start _didFinishLaunching.set(true) - - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return - } let previousVersion = UserDefaults.standard.string(forKey: Self.versionKey) let previousBuild = UserDefaults.standard.string(forKey: Self.buildKey) @@ -40,64 +36,63 @@ class macOSLifecycleEvents: PlatformPlugin, macOSLifecycle { let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String if previousBuild == nil { - analytics?.track(name: "Application Installed", properties: [ - "version": currentVersion ?? "", - "build": currentBuild ?? "" - ]) + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationInstalled) == true { + analytics?.track(name: "Application Installed", properties: [ + "version": currentVersion ?? "", + "build": currentBuild ?? "" + ]) + } } else if currentBuild != previousBuild { - analytics?.track(name: "Application Updated", properties: [ - "previous_version": previousVersion ?? "", - "previous_build": previousBuild ?? "", + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationUpdated) == true { + analytics?.track(name: "Application Updated", properties: [ + "previous_version": previousVersion ?? "", + "previous_build": previousBuild ?? "", + "version": currentVersion ?? "", + "build": currentBuild ?? "" + ]) + } + } + + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationOpened) == true { + analytics?.track(name: "Application Opened", properties: [ + "from_background": false, "version": currentVersion ?? "", "build": currentBuild ?? "" ]) } - - analytics?.track(name: "Application Opened", properties: [ - "from_background": false, - "version": currentVersion ?? "", - "build": currentBuild ?? "" - ]) - + UserDefaults.standard.setValue(currentVersion, forKey: Self.versionKey) UserDefaults.standard.setValue(currentBuild, forKey: Self.buildKey) } func applicationDidUnhide() { - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationUnhidden) == true { + let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String + + analytics?.track(name: "Application Unhidden", properties: [ + "from_background": true, + "version": currentVersion ?? "", + "build": currentBuild ?? "" + ]) } - - let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String - let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String - - analytics?.track(name: "Application Unhidden", properties: [ - "from_background": true, - "version": currentVersion ?? "", - "build": currentBuild ?? "" - ]) } func applicationDidHide() { - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationHidden) == true { + analytics?.track(name: "Application Hidden") } - - analytics?.track(name: "Application Hidden") } func applicationDidResignActive() { - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationBackgrounded) == true { + analytics?.track(name: "Application Backgrounded") } - - analytics?.track(name: "Application Backgrounded") } func applicationDidBecomeActive() { - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationForegrounded) == false { return } - analytics?.track(name: "Application Foregrounded") // Lets check if we skipped application:didFinishLaunchingWithOptions, @@ -109,11 +104,9 @@ class macOSLifecycleEvents: PlatformPlugin, macOSLifecycle { } func applicationWillTerminate() { - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationTerminated) == true { + analytics?.track(name: "Application Terminated") } - - analytics?.track(name: "Application Terminated") } } diff --git a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift index 5783c657..ba658234 100644 --- a/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift +++ b/Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift @@ -25,15 +25,10 @@ class iOSLifecycleEvents: PlatformPlugin, iOSLifecycle { private var didFinishLaunching = false func application(_ application: UIApplication?, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { - // Make sure we aren't double calling application:didFinishLaunchingWithOptions // by resetting the check at the start _didFinishLaunching.set(true) - - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return - } - + let previousVersion: String? = UserDefaults.standard.string(forKey: Self.versionKey) let previousBuild: String? = UserDefaults.standard.string(forKey: Self.buildKey) @@ -41,65 +36,66 @@ class iOSLifecycleEvents: PlatformPlugin, iOSLifecycle { let currentBuild: String = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" if previousBuild == nil { - analytics?.track(name: "Application Installed", properties: [ + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationInstalled) == true { + analytics?.track(name: "Application Installed", properties: [ + "version": currentVersion, + "build": currentBuild + ]) + } + } else if let previousBuild, currentBuild != previousBuild { + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationUpdated) == true { + analytics?.track(name: "Application Updated", properties: [ + "previous_version": previousVersion ?? "", + "previous_build": previousBuild, + "version": currentVersion, + "build": currentBuild + ]) + } + } + + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationOpened) == true { + let sourceApp: String = launchOptions?[UIApplication.LaunchOptionsKey.sourceApplication] as? String ?? "" + let url: String = launchOptions?[UIApplication.LaunchOptionsKey.url] as? String ?? "" + + analytics?.track(name: "Application Opened", properties: [ + "from_background": false, "version": currentVersion, - "build": currentBuild + "build": currentBuild, + "referring_application": sourceApp, + "url": url ]) - } else if let previousBuild, - currentBuild != previousBuild { - analytics?.track(name: "Application Updated", properties: [ - "previous_version": previousVersion ?? "", - "previous_build": previousBuild, - "version": currentVersion, - "build": currentBuild - ]) } - let sourceApp: String = launchOptions?[UIApplication.LaunchOptionsKey.sourceApplication] as? String ?? "" - let url: String = launchOptions?[UIApplication.LaunchOptionsKey.url] as? String ?? "" - - analytics?.track(name: "Application Opened", properties: [ - "from_background": false, - "version": currentVersion, - "build": currentBuild, - "referring_application": sourceApp, - "url": url - ]) - UserDefaults.standard.setValue(currentVersion, forKey: Self.versionKey) UserDefaults.standard.setValue(currentBuild, forKey: Self.buildKey) } func applicationWillEnterForeground(application: UIApplication?) { - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return - } - - let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String - let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String - - if didFinishLaunching == false { - analytics?.track(name: "Application Opened", properties: [ - "from_background": true, - "version": currentVersion ?? "", - "build": currentBuild ?? "" - ]) + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationOpened) == true { + let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + let currentBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String + + if didFinishLaunching == false { + analytics?.track(name: "Application Opened", properties: [ + "from_background": true, + "version": currentVersion ?? "", + "build": currentBuild ?? "" + ]) + } } } func applicationDidEnterBackground(application: UIApplication?) { _didFinishLaunching.set(false) - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationBackgrounded) == true { + analytics?.track(name: "Application Backgrounded") } - analytics?.track(name: "Application Backgrounded") } func applicationDidBecomeActive(application: UIApplication?) { - if analytics?.configuration.values.trackApplicationLifecycleEvents == false { - return + if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationForegrounded) == true { + analytics?.track(name: "Application Foregrounded") } - analytics?.track(name: "Application Foregrounded") } } diff --git a/Sources/Segment/Startup.swift b/Sources/Segment/Startup.swift index 67eb767e..081e75f0 100644 --- a/Sources/Segment/Startup.swift +++ b/Sources/Segment/Startup.swift @@ -48,7 +48,7 @@ extension Analytics: Subscriber { plugins += VendorSystem.current.requiredPlugins // setup lifecycle if desired - if configuration.values.trackApplicationLifecycleEvents, operatingMode != .synchronous { + if configuration.values.trackedApplicationLifecycleEvents != .none, operatingMode != .synchronous { #if os(iOS) || os(tvOS) || os(visionOS) || os(visionOS) plugins.append(iOSLifecycleEvents()) #endif