Skip to content

Commit

Permalink
✨ Add push primer support
Browse files Browse the repository at this point in the history
@appcues/request-push action and _pushPrimerEligible auto-property
  • Loading branch information
mmaatttt authored and iujames committed Sep 17, 2024
1 parent fd30c9d commit 7a093de
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ internal class AutoPropertyDecorator: AnalyticsDecorating {
"_lastSeenAt": now,
"_updatedAt": now,
"_lastContentShownAt": storage.lastContentShownAt,
"_sessionId": appcues?.sessionID?.appcuesFormatted
"_sessionId": appcues?.sessionID?.appcuesFormatted,
"_pushPrimerEligible": pushMonitor.pushPrimerEligible
]

// Note: additional (custom) go first, as they may be overwritten by merged system items
Expand Down
15 changes: 13 additions & 2 deletions Sources/AppcuesKit/Data/PushMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import UIKit
internal protocol PushMonitoring: AnyObject {
var pushEnabled: Bool { get }
var pushBackgroundEnabled: Bool { get }
var pushPrimerEligible: Bool { get }

func refreshPushStatus(completion: ((UNAuthorizationStatus) -> Void)?)
}

internal class PushMonitor: PushMonitoring {
Expand All @@ -27,6 +30,10 @@ internal class PushMonitor: PushMonitoring {
storage.pushToken != nil
}

var pushPrimerEligible: Bool {
pushAuthorizationStatus == .notDetermined
}

init(container: DIContainer) {
self.storage = container.resolve(DataStoring.self)

Expand All @@ -45,14 +52,18 @@ internal class PushMonitor: PushMonitoring {
refreshPushStatus()
}

private func refreshPushStatus() {
func refreshPushStatus(completion: ((UNAuthorizationStatus) -> Void)? = nil) {
// Skip call to UNUserNotificationCenter.current() in tests to avoid crashing in package tests
#if DEBUG
guard ProcessInfo.processInfo.environment["XCTestBundlePath"] == nil else { return }
guard ProcessInfo.processInfo.environment["XCTestBundlePath"] == nil else {
completion?(pushAuthorizationStatus)
return
}
#endif

UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in
self?.pushAuthorizationStatus = settings.authorizationStatus
completion?(settings.authorizationStatus)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// AppcuesRequestPushAction.swift
// AppcuesKitTests
//
// Created by Matt on 2024-02-26.
// Copyright © 2024 Appcues. All rights reserved.
//

import Foundation
import UserNotifications

@available(iOS 13.0, *)
internal class AppcuesRequestPushAction: AppcuesExperienceAction {

static let type = "@appcues/request-push"

private weak var appcues: Appcues?

required init?(configuration: AppcuesExperiencePluginConfiguration) {
self.appcues = configuration.appcues
}

func execute(completion: @escaping ActionRegistry.Completion) {
guard let appcues = appcues else {
completion()
return
}

let resultHandler: (Bool, Error?) -> Void = { _, error in
if let error = error {
appcues.config.logger.error("@appcues/request-push failed with %{public}@", error.localizedDescription)
}

let pushMonitor = appcues.container.resolve(PushMonitoring.self)
let analyticsPublisher = appcues.container.resolve(AnalyticsPublishing.self)

pushMonitor.refreshPushStatus { _ in
analyticsPublisher.publish(TrackingUpdate(
type: .event(name: Events.Device.deviceUpdated.rawValue, interactive: false),
isInternal: true
))

completion()
}
}

// Skip call to UNUserNotificationCenter.current() in tests to avoid crashing in package tests
#if DEBUG
guard ProcessInfo.processInfo.environment["XCTestBundlePath"] == nil else {
resultHandler(false, AppcuesTraitError(description: "Executing in test environment"))
return
}
#endif

let options: UNAuthorizationOptions = [.alert, .sound, .badge]
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: resultHandler)
}
}

0 comments on commit 7a093de

Please sign in to comment.