From 0ddee49dd50dfdc65b47665091e7b559ec333938 Mon Sep 17 00:00:00 2001 From: alexandrev-tibco Date: Sun, 1 Feb 2026 11:35:36 +0100 Subject: [PATCH] Tune app review prompt cadence --- .../Services/ReviewPromptService.swift | 45 +++++++++++++++++++ .../Views/Dashboard/MonthlyCheckInView.swift | 1 + 2 files changed, 46 insertions(+) create mode 100644 PortfolioJournal/Services/ReviewPromptService.swift diff --git a/PortfolioJournal/Services/ReviewPromptService.swift b/PortfolioJournal/Services/ReviewPromptService.swift new file mode 100644 index 0000000..17a3f80 --- /dev/null +++ b/PortfolioJournal/Services/ReviewPromptService.swift @@ -0,0 +1,45 @@ +import StoreKit +import UIKit + +final class ReviewPromptService { + static let shared = ReviewPromptService() + + private let lastPromptKey = "reviewPromptLastDate" + private let checkInCountKey = "reviewPromptCheckinCount" + private let minCheckInsBetweenPrompts = 3 + private let minDaysBetweenPrompts = 90 + + private init() {} + + func recordMonthlyCheckInCompleted() { + let currentCount = UserDefaults.standard.integer(forKey: checkInCountKey) + UserDefaults.standard.set(currentCount + 1, forKey: checkInCountKey) + requestReviewIfEligible() + } + + private func requestReviewIfEligible() { + let now = Date() + if let lastPrompt = UserDefaults.standard.object(forKey: lastPromptKey) as? Date { + let daysSince = now.timeIntervalSince(lastPrompt) / 86_400 + if daysSince < Double(minDaysBetweenPrompts) { + return + } + } + + let count = UserDefaults.standard.integer(forKey: checkInCountKey) + guard count >= minCheckInsBetweenPrompts else { return } + + requestReview() + } + + private func requestReview() { + guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return } + if #available(iOS 18.0, *) { + AppStore.requestReview(in: scene) + } else { + SKStoreReviewController.requestReview(in: scene) + } + UserDefaults.standard.set(Date(), forKey: lastPromptKey) + UserDefaults.standard.set(0, forKey: checkInCountKey) + } +} diff --git a/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift b/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift index 9b2755c..b559375 100644 --- a/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift +++ b/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift @@ -167,6 +167,7 @@ struct MonthlyCheckInView: View { ? now : min(referenceDate.endOfMonth, now) MonthlyCheckInStore.setCompletionDate(completionDate, for: referenceDate) + ReviewPromptService.shared.recordMonthlyCheckInCompleted() viewModel.refresh() } label: { Text("Mark Check-in Complete")