Live Activities, nudging, unit tests

This commit is contained in:
2026-04-20 11:11:24 +02:00
parent 3858500a45
commit 7e25a4f978
15 changed files with 348 additions and 20 deletions
+49
View File
@@ -29,12 +29,60 @@ enum PurchaseResult: Equatable {
"donateanthology"
]
// MARK: - Nudge / Supporter tracking keys
private static let firstLaunchKey = "ma_firstLaunchDate"
private static let lastKeyDateKey = "ma_lastNudgeOrPurchaseDate"
private static let hasEverSupportedKey = "ma_hasEverSupported"
private let defaults = UserDefaults.standard
private var updateListenerTask: Task<Void, Never>?
init() {
// Persist first-launch date on very first run
_ = firstLaunchDate
updateListenerTask = listenForTransactions()
}
// MARK: - Supporter state
/// True once the user has completed at least one donation.
var hasEverSupported: Bool {
defaults.bool(forKey: Self.hasEverSupportedKey)
}
// MARK: - Nudge logic
/// The date the app was first launched (written once, then read-only).
var firstLaunchDate: Date {
if let date = defaults.object(forKey: Self.firstLaunchKey) as? Date { return date }
let now = Date()
defaults.set(now, forKey: Self.firstLaunchKey)
return now
}
/// True when the nudge sheet should be presented.
/// Rules:
/// - First show: 3 days after install and never shown/purchased before.
/// - Repeat: 6 months since last nudge dismissal OR last purchase.
var shouldShowNudge: Bool {
let threeDays: TimeInterval = 3 * 24 * 3600
let sixMonths: TimeInterval = 6 * 30 * 24 * 3600
guard Date().timeIntervalSince(firstLaunchDate) >= threeDays else { return false }
guard let last = defaults.object(forKey: Self.lastKeyDateKey) as? Date else { return true }
return Date().timeIntervalSince(last) >= sixMonths
}
/// Call when the nudge sheet is dismissed (regardless of purchase outcome).
func recordNudgeShown() {
defaults.set(Date(), forKey: Self.lastKeyDateKey)
}
/// Records a successful purchase: sets supporter flag and resets the nudge clock.
private func recordPurchase() {
defaults.set(true, forKey: Self.hasEverSupportedKey)
defaults.set(Date(), forKey: Self.lastKeyDateKey)
}
func loadProducts() async {
loadError = nil
do {
@@ -60,6 +108,7 @@ enum PurchaseResult: Equatable {
case .success(let verification):
let transaction = try checkVerified(verification)
await transaction.finish()
recordPurchase()
purchaseResult = .success(product)
case .userCancelled:
purchaseResult = .cancelled