195 lines
5.5 KiB
Swift
195 lines
5.5 KiB
Swift
import Foundation
|
|
import Combine
|
|
|
|
enum FreemiumLimits {
|
|
static let maxSources = 5
|
|
static let maxHistoricalMonths = 12
|
|
}
|
|
|
|
class FreemiumValidator: ObservableObject {
|
|
private let iapService: IAPService
|
|
|
|
init(iapService: IAPService) {
|
|
self.iapService = iapService
|
|
}
|
|
|
|
// MARK: - Source Limits
|
|
|
|
var isPremium: Bool {
|
|
iapService.isPremium
|
|
}
|
|
|
|
func canAddSource(currentCount: Int) -> Bool {
|
|
if iapService.isPremium { return true }
|
|
return currentCount < FreemiumLimits.maxSources
|
|
}
|
|
|
|
func remainingSources(currentCount: Int) -> Int {
|
|
if iapService.isPremium { return Int.max }
|
|
return max(0, FreemiumLimits.maxSources - currentCount)
|
|
}
|
|
|
|
var sourceLimit: Int {
|
|
iapService.isPremium ? Int.max : FreemiumLimits.maxSources
|
|
}
|
|
|
|
var sourceLimitDescription: String {
|
|
if iapService.isPremium {
|
|
return "Unlimited"
|
|
}
|
|
return "\(FreemiumLimits.maxSources) sources"
|
|
}
|
|
|
|
// MARK: - Historical Data Limits
|
|
|
|
func filterSnapshots(_ snapshots: [Snapshot]) -> [Snapshot] {
|
|
if iapService.isPremium { return snapshots }
|
|
|
|
let cutoffDate = Calendar.current.date(
|
|
byAdding: .month,
|
|
value: -FreemiumLimits.maxHistoricalMonths,
|
|
to: Date()
|
|
) ?? Date()
|
|
|
|
return snapshots.filter { $0.date >= cutoffDate }
|
|
}
|
|
|
|
func isSnapshotAccessible(_ snapshot: Snapshot) -> Bool {
|
|
if iapService.isPremium { return true }
|
|
|
|
let cutoffDate = Calendar.current.date(
|
|
byAdding: .month,
|
|
value: -FreemiumLimits.maxHistoricalMonths,
|
|
to: Date()
|
|
) ?? Date()
|
|
|
|
return snapshot.date >= cutoffDate
|
|
}
|
|
|
|
var historicalLimit: Int {
|
|
iapService.isPremium ? Int.max : FreemiumLimits.maxHistoricalMonths
|
|
}
|
|
|
|
var historicalLimitDescription: String {
|
|
if iapService.isPremium {
|
|
return "Full history"
|
|
}
|
|
return "Last \(FreemiumLimits.maxHistoricalMonths) months"
|
|
}
|
|
|
|
// MARK: - Feature Access
|
|
|
|
func canExport() -> Bool {
|
|
return iapService.isPremium
|
|
}
|
|
|
|
func canViewPredictions() -> Bool {
|
|
return iapService.isPremium
|
|
}
|
|
|
|
func canViewAdvancedCharts() -> Bool {
|
|
return iapService.isPremium
|
|
}
|
|
|
|
func canAccessFeature(_ feature: PremiumFeature) -> Bool {
|
|
if iapService.isPremium { return true }
|
|
|
|
switch feature {
|
|
case .multipleAccounts:
|
|
return false
|
|
case .unlimitedSources:
|
|
return false
|
|
case .fullHistory:
|
|
return false
|
|
case .advancedCharts:
|
|
return false
|
|
case .predictions:
|
|
return false
|
|
case .export:
|
|
return false
|
|
case .noAds:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// MARK: - Premium Features Enum
|
|
|
|
enum PremiumFeature: String, CaseIterable, Identifiable {
|
|
case multipleAccounts = "multiple_accounts"
|
|
case unlimitedSources = "unlimited_sources"
|
|
case fullHistory = "full_history"
|
|
case advancedCharts = "advanced_charts"
|
|
case predictions = "predictions"
|
|
case export = "export"
|
|
case noAds = "no_ads"
|
|
|
|
var id: String { rawValue }
|
|
|
|
var title: String {
|
|
switch self {
|
|
case .multipleAccounts: return "Multiple Accounts"
|
|
case .unlimitedSources: return "Unlimited Sources"
|
|
case .fullHistory: return "Full History"
|
|
case .advancedCharts: return "Advanced Charts"
|
|
case .predictions: return "Predictions"
|
|
case .export: return "Export Data"
|
|
case .noAds: return "No Ads"
|
|
}
|
|
}
|
|
|
|
var icon: String {
|
|
switch self {
|
|
case .multipleAccounts: return "person.2"
|
|
case .unlimitedSources: return "infinity"
|
|
case .fullHistory: return "clock.arrow.circlepath"
|
|
case .advancedCharts: return "chart.bar.xaxis"
|
|
case .predictions: return "wand.and.stars"
|
|
case .export: return "square.and.arrow.up"
|
|
case .noAds: return "xmark.circle"
|
|
}
|
|
}
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .multipleAccounts:
|
|
return "Track separate portfolios for family or business"
|
|
case .unlimitedSources:
|
|
return "Track as many investment sources as you want"
|
|
case .fullHistory:
|
|
return "Access your complete investment history"
|
|
case .advancedCharts:
|
|
return "5 types of detailed analytics charts"
|
|
case .predictions:
|
|
return "AI-powered 12-month forecasts"
|
|
case .export:
|
|
return "Export to CSV and JSON formats"
|
|
case .noAds:
|
|
return "Ad-free experience forever"
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Paywall Triggers
|
|
|
|
enum PaywallTrigger: String {
|
|
case sourceLimit = "source_limit"
|
|
case historyLimit = "history_limit"
|
|
case advancedCharts = "advanced_charts"
|
|
case predictions = "predictions"
|
|
case export = "export"
|
|
case settingsUpgrade = "settings_upgrade"
|
|
case manualTap = "manual_tap"
|
|
}
|
|
|
|
func shouldShowPaywall(for trigger: PaywallTrigger) -> Bool {
|
|
guard !iapService.isPremium else { return false }
|
|
|
|
switch trigger {
|
|
case .sourceLimit, .historyLimit, .advancedCharts, .predictions, .export:
|
|
return true
|
|
case .settingsUpgrade, .manualTap:
|
|
return true
|
|
}
|
|
}
|
|
}
|