Review prompt tras check-in: ≥2 meses de histórico, una vez por versión (build 85) #41

- ReviewRequestService: gate por meses distintos con snapshots + versión
- QuickUpdateView.saveAll y AddSnapshotView.performSave (solo alta, no edición) piden reseña via \.requestReview tras cerrar el sheet
- Motivo: 0 ratings visibles en US, 1 en ES — la ficha no convierte

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VXtwQP6nYnbLFVqsHiTK5G
This commit is contained in:
alexandrev-tibco
2026-09-01 11:23:31 +02:00
parent 66dc854461
commit fa7df50400
3 changed files with 53 additions and 0 deletions
@@ -0,0 +1,38 @@
import Foundation
import CoreData
/// Decides when to ask for an App Store rating: after a "positive moment"
/// (a saved check-in), only once the habit is established (2 distinct months
/// of snapshots) and at most once per app version. Apple additionally caps
/// the system prompt at 3 displays per year, so over-calling is safe.
///
/// Call sites own the actual prompt via SwiftUI's `\.requestReview` this
/// service only answers "should we ask now?" and records that we did.
@MainActor
enum ReviewRequestService {
private static let lastVersionKey = "reviewRequest.lastVersion"
/// Returns true at most once per app version, and only when the user has
/// snapshots in 2+ distinct months (they've seen the app deliver value).
/// Marks the version as asked when returning true.
static func shouldRequestAfterCheckIn() -> Bool {
guard distinctSnapshotMonths() >= 2 else { return false }
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "unknown"
let defaults = UserDefaults.standard
guard defaults.string(forKey: lastVersionKey) != version else { return false }
defaults.set(version, forKey: lastVersionKey)
return true
}
private static func distinctSnapshotMonths() -> Int {
let request = NSFetchRequest<Snapshot>(entityName: "Snapshot")
guard let snapshots = try? CoreDataStack.shared.viewContext.fetch(request) else { return 0 }
let calendar = Calendar.current
let months = Set(snapshots.map { snapshot in
calendar.dateComponents([.year, .month], from: snapshot.date)
})
return months.count
}
}