Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ios/AirshipFrameworkProxy/AirshipDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ extension AirshipDelegate: PushNotificationDelegate {
func receivedNotificationResponse(_ notificationResponse: UNNotificationResponse) async {
LaunchDeepLinkTracker.shared.onNotificationResponse(
userInfo: notificationResponse.notification.request.content.userInfo,
isDefaultAction: notificationResponse.actionIdentifier == UNNotificationDefaultActionIdentifier
isDefaultAction: notificationResponse.actionIdentifier == UNNotificationDefaultActionIdentifier,
isAppForegrounded: AppStateTracker.shared.isForegrounded
)

do {
Expand Down
18 changes: 11 additions & 7 deletions ios/AirshipFrameworkProxy/LaunchDeepLinkTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,21 @@ final class LaunchDeepLinkTracker {

/// Called from the notification response handler with the tapped
/// notification's payload. A default-action tap resolves the launch,
/// stashing the payload's deep link if present.
/// stashing the payload's deep link when the app was not already
/// foregrounded.
func onNotificationResponse(
userInfo: [AnyHashable: Any],
isDefaultAction: Bool
isDefaultAction: Bool,
isAppForegrounded: Bool
) {
guard isDefaultAction else { return }
let deepLink = Self.deepLinkActionKeys
.compactMap { userInfo[$0] as? String }
.first
if let deepLink {
stash = (deepLink, dateProvider())
if !isAppForegrounded {
let deepLink = Self.deepLinkActionKeys
.compactMap { userInfo[$0] as? String }
.first
if let deepLink {
stash = (deepLink, dateProvider())
}
}
launchResolved = true
resolveWaiters()
Expand Down
16 changes: 16 additions & 0 deletions ios/AirshipFrameworkProxy/Proxies/AirshipProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ public final class AirshipProxy: Sendable {
return Airship.isFlying
}

/// Returns the deep link that launched the app from a notification tap,
/// or nil if the app was not launched by a notification with a deep
/// link. One-shot: the value is consumed on read.
public func getLaunchDeepLink() async -> String? {
return await LaunchDeepLinkTracker.shared.takeLaunchDeepLink()
}

private static func ensureAirshipReady() throws {
guard Airship.isFlying else {
AirshipLogger.warn("Airship not ready. Ensure takeOff has completed before using proxy APIs.")
Expand Down Expand Up @@ -221,6 +228,15 @@ public final class AirshipProxy: Sendable {
}
}

if AppStateTracker.shared.isForegrounded {
LaunchDeepLinkTracker.shared.onLaunchResolved()
} else {
Task { @MainActor in
await AppStateTracker.shared.waitForActive()
LaunchDeepLinkTracker.shared.onLaunchResolved()
}
}

Airship.push.defaultPresentationOptions = self.proxyStore.foregroundPresentationOptions

if let categories = self.loadCategories() {
Expand Down
38 changes: 30 additions & 8 deletions ios/AirshipFrameworkProxyTests/LaunchDeepLinkTrackerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ final class LaunchDeepLinkTrackerTest: XCTestCase {
let tracker = LaunchDeepLinkTracker()
tracker.onNotificationResponse(
userInfo: ["^d": "myapp://home"],
isDefaultAction: true
isDefaultAction: true,
isAppForegrounded: false
)
let result = await tracker.takeLaunchDeepLink()
XCTAssertEqual(result, "myapp://home")
Expand All @@ -20,7 +21,8 @@ final class LaunchDeepLinkTrackerTest: XCTestCase {
let tracker = LaunchDeepLinkTracker()
tracker.onNotificationResponse(
userInfo: ["^d": "myapp://home"],
isDefaultAction: true
isDefaultAction: true,
isAppForegrounded: false
)
_ = await tracker.takeLaunchDeepLink()
let second = await tracker.takeLaunchDeepLink()
Expand All @@ -31,15 +33,31 @@ final class LaunchDeepLinkTrackerTest: XCTestCase {
let tracker = LaunchDeepLinkTracker()
tracker.onNotificationResponse(
userInfo: ["deep_link_action": "myapp://home"],
isDefaultAction: true
isDefaultAction: true,
isAppForegrounded: false
)
let result = await tracker.takeLaunchDeepLink()
XCTAssertEqual(result, "myapp://home")
}

func testForegroundTapSkipsStash() async {
let tracker = LaunchDeepLinkTracker()
tracker.onNotificationResponse(
userInfo: ["^d": "myapp://home"],
isDefaultAction: true,
isAppForegrounded: true
)
let result = await tracker.takeLaunchDeepLink()
XCTAssertNil(result)
}

func testTapWithoutDeepLinkResolvesNil() async {
let tracker = LaunchDeepLinkTracker()
tracker.onNotificationResponse(userInfo: [:], isDefaultAction: true)
tracker.onNotificationResponse(
userInfo: [:],
isDefaultAction: true,
isAppForegrounded: false
)
let result = await tracker.takeLaunchDeepLink()
XCTAssertNil(result)
}
Expand All @@ -48,7 +66,8 @@ final class LaunchDeepLinkTrackerTest: XCTestCase {
let tracker = LaunchDeepLinkTracker()
tracker.onNotificationResponse(
userInfo: ["^d": "myapp://home"],
isDefaultAction: false
isDefaultAction: false,
isAppForegrounded: false
)
tracker.onLaunchResolved()
let result = await tracker.takeLaunchDeepLink()
Expand All @@ -61,7 +80,8 @@ final class LaunchDeepLinkTrackerTest: XCTestCase {
await Task.yield()
tracker.onNotificationResponse(
userInfo: ["^d": "myapp://home"],
isDefaultAction: true
isDefaultAction: true,
isAppForegrounded: false
)
let result = await task.value
XCTAssertEqual(result, "myapp://home")
Expand All @@ -88,7 +108,8 @@ final class LaunchDeepLinkTrackerTest: XCTestCase {
let tracker = LaunchDeepLinkTracker(dateProvider: { now })
tracker.onNotificationResponse(
userInfo: ["^d": "myapp://home"],
isDefaultAction: true
isDefaultAction: true,
isAppForegrounded: false
)
now = now.addingTimeInterval(11.0)
let result = await tracker.takeLaunchDeepLink()
Expand All @@ -100,7 +121,8 @@ final class LaunchDeepLinkTrackerTest: XCTestCase {
let tracker = LaunchDeepLinkTracker(dateProvider: { now })
tracker.onNotificationResponse(
userInfo: ["^d": "myapp://home"],
isDefaultAction: true
isDefaultAction: true,
isAppForegrounded: false
)
now = now.addingTimeInterval(9.0)
let result = await tracker.takeLaunchDeepLink()
Expand Down
Loading