Skip to content

Commit

Permalink
fix: Send application opened for apps not yet using scene delegates (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
crleona authored May 21, 2024
1 parent 55838b0 commit dd43026
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 18 deletions.
63 changes: 45 additions & 18 deletions Sources/Amplitude/Plugins/iOS/IOSLifecycleMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
UIApplication.didEnterBackgroundNotification,
UIApplication.willEnterForegroundNotification,
UIApplication.didFinishLaunchingNotification,
UIApplication.didBecomeActiveNotification,
]
private var utils: DefaultEventUtils?
private var sendApplicationOpenedOnDidBecomeActive = false

override init() {
// TODO: Check if lifecycle plugin works for app extension
Expand All @@ -37,14 +39,16 @@
}

@objc
func notificationResponse(notification: NSNotification) {
func notificationResponse(notification: Notification) {
switch notification.name {
case UIApplication.didEnterBackgroundNotification:
self.didEnterBackground(notification: notification)
didEnterBackground(notification: notification)
case UIApplication.willEnterForegroundNotification:
self.applicationWillEnterForeground(notification: notification)
applicationWillEnterForeground(notification: notification)
case UIApplication.didFinishLaunchingNotification:
self.applicationDidFinishLaunchingNotification(notification: notification)
applicationDidFinishLaunchingNotification(notification: notification)
case UIApplication.didBecomeActiveNotification:
applicationDidBecomeActive(notification: notification)
default:
break
}
Expand All @@ -64,7 +68,7 @@

}

func applicationWillEnterForeground(notification: NSNotification) {
func applicationWillEnterForeground(notification: Notification) {
let timestamp = Int64(NSDate().timeIntervalSince1970 * 1000)

let fromBackground: Bool
Expand All @@ -81,29 +85,52 @@
fromBackground = false
}

self.amplitude?.onEnterForeground(timestamp: timestamp)
if self.amplitude?.configuration.defaultTracking.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: fromBackground,
])
}
amplitude?.onEnterForeground(timestamp: timestamp)
sendApplicationOpened(fromBackground: fromBackground)
}

func didEnterBackground(notification: NSNotification) {
func didEnterBackground(notification: Notification) {
let timestamp = Int64(NSDate().timeIntervalSince1970 * 1000)
self.amplitude?.onExitForeground(timestamp: timestamp)
if self.amplitude?.configuration.defaultTracking.appLifecycles == true {
self.amplitude?.track(eventType: Constants.AMP_APPLICATION_BACKGROUNDED_EVENT)
}
}

func applicationDidFinishLaunchingNotification(notification: NSNotification) {
func applicationDidFinishLaunchingNotification(notification: Notification) {
utils?.trackAppUpdatedInstalledEvent()

// Pre SceneDelegate apps wil not fire a willEnterForeground notification on app launch.
// Instead, use the initial applicationDidBecomeActive
let usesSceneDelegate = application?.delegate?.responds(to: #selector(UIApplicationDelegate.application(_:configurationForConnecting:options:))) ?? false
if !usesSceneDelegate {
sendApplicationOpenedOnDidBecomeActive = true
}
}

func applicationDidBecomeActive(notification: Notification) {
guard sendApplicationOpenedOnDidBecomeActive else {
return
}
sendApplicationOpenedOnDidBecomeActive = false

let timestamp = Int64(NSDate().timeIntervalSince1970 * 1000)
amplitude?.onEnterForeground(timestamp: timestamp)
sendApplicationOpened(fromBackground: false)
}

private func sendApplicationOpened(fromBackground: Bool) {
guard amplitude?.configuration.defaultTracking.appLifecycles ?? false else {
return
}
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: fromBackground,
])
}
}

Expand Down
57 changes: 57 additions & 0 deletions Tests/AmplitudeTests/AmplitudeIOSTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,63 @@ final class AmplitudeIOSTests: XCTestCase {
method_setImplementation(originalMethod, originalImplementation)
}

func testDidBecomeActivePreSceneDelegate() {

class TestApplication: NSObject, UIApplicationDelegate {

// Override UIApplicationDelegate to self, which does not implement application(_:configurationForConnecting:connectingSceneSession:)
weak var delegate: UIApplicationDelegate? {
return self
}

static let sharedTest = TestApplication()

@objc class var shared: AnyObject {
return sharedTest
}
}

guard let originalMethod = class_getClassMethod(UIApplication.self, #selector(getter: UIApplication.shared)),
let testMethod = class_getClassMethod(TestApplication.self, #selector(getter: TestApplication.shared)) else {
XCTFail("Unable to find methods to swizzle")
return
}
let originalImplementation = method_getImplementation(originalMethod)
let testImplementation = method_getImplementation(testMethod)
method_setImplementation(originalMethod, testImplementation)

let configuration = Configuration(
apiKey: "api-key",
storageProvider: storageMem,
identifyStorageProvider: interceptStorageMem,
defaultTracking: DefaultTrackingOptions(sessions: false, appLifecycles: true)
)

let info = Bundle.main.infoDictionary
let currentBuild = info?["CFBundleVersion"] ?? ""
let currentVersion = info?["CFBundleShortVersionString"] ?? ""

let amplitude = Amplitude(configuration: configuration)

NotificationCenter.default.post(name: UIApplication.didFinishLaunchingNotification, object: nil)

NotificationCenter.default.post(name: UIApplication.didBecomeActiveNotification, object: nil)

amplitude.waitForTrackingQueue()

let events = storageMem.events()
XCTAssertEqual(events.count, 2)
XCTAssertEqual(events[1].eventType, Constants.AMP_APPLICATION_OPENED_EVENT)
XCTAssertEqual(getDictionary(events[1].eventProperties!), [
Constants.AMP_APP_BUILD_PROPERTY: currentBuild,
Constants.AMP_APP_VERSION_PROPERTY: currentVersion,
Constants.AMP_APP_FROM_BACKGROUND_PROPERTY: false
])

// re-replace UIApplication.shared
method_setImplementation(originalMethod, originalImplementation)
}

func testDidEnterBackground() throws {
let configuration = Configuration(
apiKey: "api-key",
Expand Down

0 comments on commit dd43026

Please sign in to comment.