Skip to content

Commit

Permalink
feat: default tracking options, auto-track application lifecycle events
Browse files Browse the repository at this point in the history
  • Loading branch information
falconandy committed Aug 28, 2023
1 parent 8293ca0 commit dd9f3da
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 14 deletions.
4 changes: 4 additions & 0 deletions Amplitude-Swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
/* Begin PBXBuildFile section */
8EDEC14255F82E24CEE00B36 /* AmplitudeSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC0630C3B587334275D9B /* AmplitudeSessionTests.swift */; };
8EDEC2E0CC80DF79F5463ACC /* RemnantDataMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC6A9899998F823C278F7 /* RemnantDataMigrationTests.swift */; };
8EDEC4D0C0CE07BF211804CC /* DefaultTrackingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC17A6A0F8112A5DC8CEB /* DefaultTrackingOptions.swift */; };
8EDEC4EE0DE1C89889F451B5 /* QueueTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC4F83BFAA664749FAEF0 /* QueueTimeTests.swift */; };
8EDEC8F8DD2CDCD6568512F8 /* RemnantDataMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC19F9FBC98A0D4E5A513 /* RemnantDataMigration.swift */; };
8EDEC972AEB33E4528F7FEEB /* StoragePrefixMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDEC9B98272069D70D08EA4 /* StoragePrefixMigrationTests.swift */; };
Expand Down Expand Up @@ -111,6 +112,7 @@

/* Begin PBXFileReference section */
8EDEC0630C3B587334275D9B /* AmplitudeSessionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplitudeSessionTests.swift; sourceTree = "<group>"; };
8EDEC17A6A0F8112A5DC8CEB /* DefaultTrackingOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultTrackingOptions.swift; sourceTree = "<group>"; };
8EDEC19F9FBC98A0D4E5A513 /* RemnantDataMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemnantDataMigration.swift; sourceTree = "<group>"; };
8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sessions.swift; sourceTree = "<group>"; };
8EDEC448C42C8C0A464FAA15 /* BasePlugins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePlugins.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -399,6 +401,7 @@
OBJ_43 /* Utilities */,
8EDEC2B8B38E04CDB51F0E83 /* Sessions.swift */,
8EDEC33A32439724A363C433 /* Migration */,
8EDEC17A6A0F8112A5DC8CEB /* DefaultTrackingOptions.swift */,
);
name = Sources;
path = Sources/Amplitude;
Expand Down Expand Up @@ -603,6 +606,7 @@
8EDECD602E181B3E2E85D4DF /* StoragePrefixMigration.swift in Sources */,
8EDEC8F8DD2CDCD6568512F8 /* RemnantDataMigration.swift in Sources */,
8EDEC977C03AA2676724F436 /* BasePlugins.swift in Sources */,
8EDEC4D0C0CE07BF211804CC /* DefaultTrackingOptions.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
11 changes: 8 additions & 3 deletions Sources/Amplitude/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public class Configuration {
public var enableCoppaControl: Bool?
public var flushEventsOnClose: Bool?
public var minTimeBetweenSessionsMillis: Int
public var trackingSessionEvents: Bool?
public var identifyBatchIntervalMillis: Int
public let migrateLegacyData: Bool
public var defaultTrackingOptions: DefaultTrackingOptions

public init(
apiKey: String,
Expand All @@ -59,7 +59,8 @@ public class Configuration {
minTimeBetweenSessionsMillis: Int = Constants.Configuration.MIN_TIME_BETWEEN_SESSIONS_MILLIS,
trackingSessionEvents: Bool = true,
identifyBatchIntervalMillis: Int = Constants.Configuration.IDENTIFY_BATCH_INTERVAL_MILLIS,
migrateLegacyData: Bool = true
migrateLegacyData: Bool = true,
defaultTrackingOptions: DefaultTrackingOptions = DefaultTrackingOptions()
) {
let normalizedInstanceName = instanceName == "" ? Constants.Configuration.DEFAULT_INSTANCE : instanceName

Expand Down Expand Up @@ -87,11 +88,15 @@ public class Configuration {
self.enableCoppaControl = enableCoppaControl
self.flushEventsOnClose = flushEventsOnClose
self.minTimeBetweenSessionsMillis = minTimeBetweenSessionsMillis
self.trackingSessionEvents = trackingSessionEvents
self.identifyBatchIntervalMillis = identifyBatchIntervalMillis
self.migrateLegacyData = migrateLegacyData
// Logging is OFF by default
self.loggerProvider.logLevel = logLevel.rawValue

self.defaultTrackingOptions = defaultTrackingOptions
if !trackingSessionEvents {
self.defaultTrackingOptions.sessions = false
}
}

func isValid() -> Bool {
Expand Down
20 changes: 19 additions & 1 deletion Sources/Amplitude/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public struct Constants {
static let EU_BATCH_API_HOST = "https://api.eu.amplitude.com/batch"
static let IDENTIFY_EVENT = "$identify"
static let GROUP_IDENTIFY_EVENT = "$groupidentify"
static let AMP_REVENUE_EVENT = "revenue_amount"
static let MAX_PROPERTY_KEYS = 1024
static let MAX_STRING_LENGTH = 1024
public static let MIN_IDENTIFY_BATCH_INTERVAL_MILLIS = 30 * 1000 // 30s
Expand All @@ -49,8 +48,27 @@ public struct Constants {
static let AMP_TRACKING_OPTION_PLATFORM = "platform"
static let AMP_TRACKING_OPTION_REGION = "region"
static let AMP_TRACKING_OPTION_VERSION_NAME = "version_name"

static let AMP_AMPLITUDE_PREFIX = "[Amplitude] "

static let AMP_SESSION_END_EVENT = "session_end"
static let AMP_SESSION_START_EVENT = "session_start"
static let AMP_APPLICATION_INSTALLED_EVENT = "\(AMP_AMPLITUDE_PREFIX)Application Installed"
static let AMP_APPLICATION_UPDATED_EVENT = "\(AMP_AMPLITUDE_PREFIX)Application Updated"
static let AMP_APPLICATION_OPENED_EVENT = "\(AMP_AMPLITUDE_PREFIX)Application Opened"
static let AMP_APPLICATION_BACKGROUNDED_EVENT = "\(AMP_AMPLITUDE_PREFIX)Application Backgrounded"
static let AMP_DEEP_LINK_OPENED_EVENT = "\(AMP_AMPLITUDE_PREFIX)Deep Link Opened"
static let AMP_SCREEN_VIEWED_EVENT = "\(AMP_AMPLITUDE_PREFIX)Screen Viewed"
static let AMP_REVENUE_EVENT = "revenue_amount"

static let AMP_APP_VERSION_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)Version"
static let AMP_APP_BUILD_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)Build"
static let AMP_APP_PREVIOUS_VERSION_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)Previous Version"
static let AMP_APP_PREVIOUS_BUILD_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)Previous Build"
static let AMP_APP_FROM_BACKGROUND_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)From Background"
static let AMP_APP_LINK_URL_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)Link URL"
static let AMP_APP_LINK_REFERRER_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)Link Referrer"
static let AMP_APP_SCREEN_NAME_PROPERTY = "\(AMP_AMPLITUDE_PREFIX)Screen Name"

public struct Configuration {
public static let FLUSH_QUEUE_SIZE = 30
Expand Down
27 changes: 27 additions & 0 deletions Sources/Amplitude/DefaultTrackingOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

public class DefaultTrackingOptions {
public static var ALL: DefaultTrackingOptions {
DefaultTrackingOptions(sessions: true, appLifecycles: true, deepLinks: true, screenViews: true)
}
public static var NONE: DefaultTrackingOptions {
DefaultTrackingOptions(sessions: false, appLifecycles: false, deepLinks: false, screenViews: false)
}

public var sessions: Bool = true
public var appLifecycles: Bool
public var deepLinks: Bool
public var screenViews: Bool

public init(
sessions: Bool = true,
appLifecycles: Bool = false,
deepLinks: Bool = false,
screenViews: Bool = false
) {
self.sessions = sessions
self.appLifecycles = appLifecycles
self.deepLinks = deepLinks
self.screenViews = screenViews
}
}
72 changes: 63 additions & 9 deletions Sources/Amplitude/Plugins/iOS/IOSLifecycleMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,21 @@
public protocol IOSLifecycle {
func applicationDidEnterBackground(application: UIApplication?)
func applicationWillEnterForeground(application: UIApplication?)
func application(
_ application: UIApplication?,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
)
func applicationDidFinishLaunchingNotification(application: UIApplication?)
}

extension IOSLifecycle {
public func applicationDidEnterBackground(application: UIApplication?) {}
public func applicationWillEnterForeground(application: UIApplication?) {}
public func application(
_ application: UIApplication?,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) {}
public func applicationDidFinishLaunchingNotification(application: UIApplication?) {}
}

class IOSLifecycleMonitor: UtilityPlugin {
private var application: UIApplication?
private var appNotifications: [NSNotification.Name] = [
UIApplication.didEnterBackgroundNotification,
UIApplication.willEnterForegroundNotification,
UIApplication.didFinishLaunchingNotification,
]

override init() {
Expand All @@ -51,8 +46,9 @@
self.didEnterBackground(notification: notification)
case UIApplication.willEnterForegroundNotification:
self.applicationWillEnterForeground(notification: notification)
case UIApplication.didFinishLaunchingNotification:
self.applicationDidFinishLaunchingNotification(notification: notification)
default:

break
}
}
Expand Down Expand Up @@ -87,17 +83,75 @@
}
}

func applicationDidFinishLaunchingNotification(notification: NSNotification) {
amplitude?.apply { (ext) in
if let validExt = ext as? IOSLifecycle {
validExt.applicationDidFinishLaunchingNotification(application: application)
}
}
}
}

extension AmplitudeDestinationPlugin: IOSLifecycle {
public func applicationWillEnterForeground(application: UIApplication?) {
let timestamp = Int64(NSDate().timeIntervalSince1970 * 1000)
self.amplitude?.onEnterForeground(timestamp: timestamp)
if (self.amplitude?.configuration.defaultTrackingOptions.appLifecycles == true) {
let info = Bundle.main.infoDictionary
let currentBuild = info?["CFBundleVersion"] as? String
let currentVersion = info?["CFBundleShortVersionString"] as? String
self.amplitude?.track(eventType: Constants.AMP_APPLICATION_OPENED_EVENT, eventProperties: [
Constants.AMP_APP_BUILD_PROPERTY: currentBuild ?? "",
Constants.AMP_APP_VERSION_PROPERTY: currentVersion ?? "",
Constants.AMP_APP_FROM_BACKGROUND_PROPERTY: true,
])
}
}

public func applicationDidEnterBackground(application: UIApplication?) {
let timestamp = Int64(NSDate().timeIntervalSince1970 * 1000)
self.amplitude?.onExitForeground(timestamp: timestamp)
if (self.amplitude?.configuration.defaultTrackingOptions.appLifecycles == true) {
self.amplitude?.track(eventType: Constants.AMP_APPLICATION_BACKGROUNDED_EVENT)
}
}

public func applicationDidFinishLaunchingNotification(application: UIApplication?) {
if (self.amplitude?.configuration.defaultTrackingOptions.appLifecycles != true) {
return;
}

let info = Bundle.main.infoDictionary
let currentBuild = info?["CFBundleVersion"] as? String
let currentVersion = info?["CFBundleShortVersionString"] as? String
let previousBuild: String? = amplitude?.storage.read(key: StorageKey.APP_BUILD)
let previousVersion: String? = amplitude?.storage.read(key: StorageKey.APP_VERSION)
let lastEventTime: Int64? = amplitude?.storage.read(key: StorageKey.LAST_EVENT_TIME)
if lastEventTime == nil {
self.amplitude?.track(eventType: Constants.AMP_APPLICATION_INSTALLED_EVENT, eventProperties: [
Constants.AMP_APP_BUILD_PROPERTY: currentBuild ?? "",
Constants.AMP_APP_VERSION_PROPERTY: currentVersion ?? "",
])
} else if currentBuild != previousBuild {
self.amplitude?.track(eventType: Constants.AMP_APPLICATION_UPDATED_EVENT, eventProperties: [
Constants.AMP_APP_BUILD_PROPERTY: currentBuild ?? "",
Constants.AMP_APP_VERSION_PROPERTY: currentVersion ?? "",
Constants.AMP_APP_PREVIOUS_BUILD_PROPERTY: previousBuild ?? "",
Constants.AMP_APP_PREVIOUS_VERSION_PROPERTY: previousVersion ?? "",
])
}
self.amplitude?.track(eventType: Constants.AMP_APPLICATION_OPENED_EVENT, eventProperties: [
Constants.AMP_APP_BUILD_PROPERTY: currentBuild ?? "",
Constants.AMP_APP_VERSION_PROPERTY: currentVersion ?? "",
Constants.AMP_APP_FROM_BACKGROUND_PROPERTY: false,
])

if currentBuild != previousBuild {
try? amplitude?.storage.write(key: StorageKey.APP_BUILD, value: currentBuild)
}
if currentVersion != previousVersion {
try? amplitude?.storage.write(key: StorageKey.APP_VERSION, value: currentVersion)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Amplitude/Sessions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public class Sessions {

public func startNewSession(timestamp: Int64) -> [BaseEvent] {
var sessionEvents: [BaseEvent] = Array()
let trackingSessionEvents = amplitude.configuration.trackingSessionEvents
let trackingSessionEvents = amplitude.configuration.defaultTrackingOptions.sessions

// end previous session
if trackingSessionEvents == true && self.sessionId >= 0 {
Expand Down
2 changes: 2 additions & 0 deletions Sources/Amplitude/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public enum StorageKey: String, CaseIterable {
case EVENTS = "events"
case USER_ID = "user_id"
case DEVICE_ID = "device_id"
case APP_BUILD = "app_build"
case APP_VERSION = "app_version"
}

public protocol Logger {
Expand Down

0 comments on commit dd9f3da

Please sign in to comment.