diff --git a/berkeley-mobile.xcodeproj/project.pbxproj b/berkeley-mobile.xcodeproj/project.pbxproj index e4e5b0bfc..b1b7b5ed4 100644 --- a/berkeley-mobile.xcodeproj/project.pbxproj +++ b/berkeley-mobile.xcodeproj/project.pbxproj @@ -184,6 +184,8 @@ E8FCD6842C374A81004B66A3 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = E8FCD6832C374A81004B66A3 /* SwiftSoup */; }; E8FCD6882C38AEC9004B66A3 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FCD6872C38AEC9004B66A3 /* String+Extension.swift */; }; E8FCD68A2C3A382F004B66A3 /* EventScrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8FCD6892C3A382F004B66A3 /* EventScrapper.swift */; }; + FAFF001C2E84C62100551C44 /* ReviewPrompter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAFF001B2E84C61B00551C44 /* ReviewPrompter.swift */; }; + FD44FE6125EB0EAD00F713EB /* AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD44FE6025EB0EAD00F713EB /* AlertView.swift */; }; FDD7946C260FE09D00ABE60E /* Colors+AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD7946B260FE09D00ABE60E /* Colors+AlertView.swift */; }; /* End PBXBuildFile section */ @@ -383,6 +385,8 @@ E8EBE7612CEC2FD700A220BB /* SafetyLogDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafetyLogDetailView.swift; sourceTree = ""; }; E8FCD6872C38AEC9004B66A3 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; E8FCD6892C3A382F004B66A3 /* EventScrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventScrapper.swift; sourceTree = ""; }; + FAFF001B2E84C61B00551C44 /* ReviewPrompter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewPrompter.swift; sourceTree = ""; }; + FD44FE6025EB0EAD00F713EB /* AlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertView.swift; sourceTree = ""; }; FDD7946B260FE09D00ABE60E /* Colors+AlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Colors+AlertView.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -534,6 +538,7 @@ 1396012D23865E2E005E4788 /* Common */ = { isa = PBXGroup; children = ( + FAFF001B2E84C61B00551C44 /* ReviewPrompter.swift */, 2913595524B135B600DE9AD6 /* DetailView */, 13B39AB123E777AC0039FBA2 /* FilterView */, 29481560253274CA00D113B6 /* Images */, @@ -1143,6 +1148,7 @@ E8C2BDA72D912B3A00165554 /* CalendarView.swift in Sources */, E83B6DA52D7A85D500AA9422 /* GymOccupancyScrapper.swift in Sources */, E8472B062DE453D6005C24EA /* BMAddedCalendarStatusOverlayView.swift in Sources */, + FAFF001C2E84C62100551C44 /* ReviewPrompter.swift in Sources */, 13EA64D02399D50C00FD8E13 /* DataManager.swift in Sources */, E80330EB2CE431C200DC9574 /* DepthButtonStyle.swift in Sources */, C14CCB612D9CC2730075FE69 /* BMFilterButton.swift in Sources */, diff --git a/berkeley-mobile/AppDelegate.swift b/berkeley-mobile/AppDelegate.swift index 2a99430a7..fec40e5f4 100644 --- a/berkeley-mobile/AppDelegate.swift +++ b/berkeley-mobile/AppDelegate.swift @@ -19,6 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() + UserDefaults.standard.increment(forKey: UserDefaultsKeys.numAppLaunchForAppStoreReview) self.checkForUpdate() DataManager.shared.fetchAll() BMLocationManager.shared.requestLocation() diff --git a/berkeley-mobile/Common/ReviewPrompter.swift b/berkeley-mobile/Common/ReviewPrompter.swift new file mode 100644 index 000000000..21bb9ed9a --- /dev/null +++ b/berkeley-mobile/Common/ReviewPrompter.swift @@ -0,0 +1,38 @@ +// +// ReviewPrompter.swift +// berkeley-mobile +// +// Created by Jayana Nanayakkara on 9/24/25. +// Copyright © 2025 ASUC OCTO. All rights reserved. +// + +import Foundation +import StoreKit +import UIKit + +enum ReviewPrompter { + private static let numLaunchesForReview: Int = 30 + + private static func shouldPromptForReview() -> Bool { + let key = UserDefaultsKeys.numAppLaunchForAppStoreReview.rawValue + let launches = UserDefaults.standard.integer(forKey: key) + if launches == numLaunchesForReview { + UserDefaults.standard.set(0, forKey: key) + return true + } + return false + } + + @MainActor + static func presentReviewIfNeeded() { + guard shouldPromptForReview() else { + return + } + + if let scene = UIApplication.shared.connectedScenes + .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { + AppStore.requestReview(in: scene) + + } + } +} diff --git a/berkeley-mobile/Home/Map/MapViewController.swift b/berkeley-mobile/Home/Map/MapViewController.swift index 9237de425..00e1fbdfd 100644 --- a/berkeley-mobile/Home/Map/MapViewController.swift +++ b/berkeley-mobile/Home/Map/MapViewController.swift @@ -138,6 +138,12 @@ class MapViewController: UIViewController, SearchDrawerViewDelegate { self.createMapMarkerDropdownButton() self.showSelectedMapMarkerTypeAnnotations(forType: types.first!) + + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + if self.mainContainer?.presentedViewController == nil { + ReviewPrompter.presentReviewIfNeeded() + } + } } } diff --git a/berkeley-mobile/MainContainerViewController.swift b/berkeley-mobile/MainContainerViewController.swift index f50b40d34..c80372cf6 100644 --- a/berkeley-mobile/MainContainerViewController.swift +++ b/berkeley-mobile/MainContainerViewController.swift @@ -10,7 +10,7 @@ import UIKit import SwiftUI class MainContainerViewController: UIViewController, MainDrawerViewDelegate { - + // MainDrawerViewDelegate properties var drawerStack: [DrawerViewDelegate] = [] var positions: [DrawerState?] = [] diff --git a/berkeley-mobile/Utils/UserDefaults+Extension.swift b/berkeley-mobile/Utils/UserDefaults+Extension.swift index f2de226d0..3b62bfa95 100644 --- a/berkeley-mobile/Utils/UserDefaults+Extension.swift +++ b/berkeley-mobile/Utils/UserDefaults+Extension.swift @@ -10,6 +10,7 @@ import Foundation enum UserDefaultsKeys: String { case numAppLaunchForFeedbackForm = "numAppLaunchForFeedbackForm" + case numAppLaunchForAppStoreReview = "numAppLaunchForAppStoreReview" case academicEventsLastSavedDate = "academicEventsLastSavedDate" case campuswideEventsLastSavedDate = "campuswideEventsLastSavedDate" case recentSearches = "recentSearches" @@ -27,4 +28,8 @@ extension UserDefaults { func data(forKey key: UserDefaultsKeys) -> Data? { data(forKey: key.rawValue) } + + func increment(forKey key: UserDefaultsKeys) { + set(integer(forKey: key) + 1, forKey: key) + } }