From 96398bdbbfa60fb8fb0ee6ae652f4f1b9cc63bdd Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sat, 10 Jan 2026 20:42:21 -0500 Subject: [PATCH] fix: build conformed with corrected app store detector --- Gaze.xcodeproj/project.pbxproj | 10 +-- Gaze/AppDelegate.swift | 5 ++ .../Gaze.icon}/Assets/Ellipse 1 4.svg | 0 .../Gaze.icon}/Assets/Ellipse 10 3.svg | 0 .../Gaze.icon}/Assets/Ellipse 11.svg | 0 .../Gaze.icon}/Assets/Ellipse 3 3.svg | 0 .../Gaze.icon}/Assets/Ellipse 4 3.svg | 0 .../Gaze.icon}/Assets/Ellipse 5 3.svg | 0 .../Gaze.icon}/Assets/Ellipse 7 3.svg | 0 .../Gaze.icon}/Assets/Ellipse 8 3.svg | 0 .../Gaze.icon}/Assets/Ellipse 9 3.svg | 0 .../Gaze.icon}/Assets/Line 7.svg | 0 .../Gaze.icon}/Assets/Line 8.svg | 0 {Gaze.icon => Gaze/Gaze.icon}/icon.json | 21 +++++- Gaze/Models/AppSettings.swift | 55 ++++++++++++++- Gaze/Services/AppStoreDetector.swift | 68 ++++++++++++------- Gaze/Services/MigrationManager.swift | 4 +- Gaze/Services/SettingsManager.swift | 9 +++ .../Onboarding/OnboardingContainerView.swift | 4 ++ .../Onboarding/SettingsOnboardingView.swift | 5 +- Gaze/Views/SettingsWindowView.swift | 7 +- .../Services/AppStoreDetectorTests.swift | 12 ++-- 22 files changed, 156 insertions(+), 44 deletions(-) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 1 4.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 10 3.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 11.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 3 3.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 4 3.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 5 3.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 7 3.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 8 3.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Ellipse 9 3.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Line 7.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/Assets/Line 8.svg (100%) rename {Gaze.icon => Gaze/Gaze.icon}/icon.json (93%) diff --git a/Gaze.xcodeproj/project.pbxproj b/Gaze.xcodeproj/project.pbxproj index 1de8326..f528ae4 100644 --- a/Gaze.xcodeproj/project.pbxproj +++ b/Gaze.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 275915892F132A9200D0E60D /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 27AE10B12F10B1FC00E00DBC /* Lottie */; }; - 2759160C2F132C7A00D0E60D /* Gaze.icon in Resources */ = {isa = PBXBuildFile; fileRef = 2759160B2F132C7A00D0E60D /* Gaze.icon */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -29,7 +28,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 2759160B2F132C7A00D0E60D /* Gaze.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = Gaze.icon; sourceTree = ""; }; 27A21B3C2F0F69DC0018C4F3 /* Gaze.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Gaze.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27A21B492F0F69DD0018C4F3 /* GazeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GazeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 27A21B532F0F69DD0018C4F3 /* GazeUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GazeUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -82,7 +80,6 @@ 27A21B332F0F69DC0018C4F3 = { isa = PBXGroup; children = ( - 2759160B2F132C7A00D0E60D /* Gaze.icon */, 27A21B3E2F0F69DC0018C4F3 /* Gaze */, 27A21B4C2F0F69DD0018C4F3 /* GazeTests */, 27A21B562F0F69DD0018C4F3 /* GazeUITests */, @@ -224,7 +221,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2759160C2F132C7A00D0E60D /* Gaze.icon in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -405,7 +401,9 @@ 27A21B5E2F0F69DD0018C4F3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = Gaze; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; @@ -415,6 +413,7 @@ ENABLE_PREVIEWS = YES; ENABLE_USER_SELECTED_FILES = readonly; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -436,7 +435,9 @@ 27A21B5F2F0F69DD0018C4F3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = Gaze; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; @@ -446,6 +447,7 @@ ENABLE_PREVIEWS = YES; ENABLE_USER_SELECTED_FILES = readonly; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/Gaze/AppDelegate.swift b/Gaze/AppDelegate.swift index 5a7dfe8..425ab51 100644 --- a/Gaze/AppDelegate.swift +++ b/Gaze/AppDelegate.swift @@ -26,6 +26,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { settingsManager = SettingsManager.shared timerEngine = TimerEngine(settingsManager: settingsManager!) + // Detect App Store version asynchronously at launch + Task { + await settingsManager?.detectAppStoreVersion() + } + setupLifecycleObservers() observeSettingsChanges() diff --git a/Gaze.icon/Assets/Ellipse 1 4.svg b/Gaze/Gaze.icon/Assets/Ellipse 1 4.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 1 4.svg rename to Gaze/Gaze.icon/Assets/Ellipse 1 4.svg diff --git a/Gaze.icon/Assets/Ellipse 10 3.svg b/Gaze/Gaze.icon/Assets/Ellipse 10 3.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 10 3.svg rename to Gaze/Gaze.icon/Assets/Ellipse 10 3.svg diff --git a/Gaze.icon/Assets/Ellipse 11.svg b/Gaze/Gaze.icon/Assets/Ellipse 11.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 11.svg rename to Gaze/Gaze.icon/Assets/Ellipse 11.svg diff --git a/Gaze.icon/Assets/Ellipse 3 3.svg b/Gaze/Gaze.icon/Assets/Ellipse 3 3.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 3 3.svg rename to Gaze/Gaze.icon/Assets/Ellipse 3 3.svg diff --git a/Gaze.icon/Assets/Ellipse 4 3.svg b/Gaze/Gaze.icon/Assets/Ellipse 4 3.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 4 3.svg rename to Gaze/Gaze.icon/Assets/Ellipse 4 3.svg diff --git a/Gaze.icon/Assets/Ellipse 5 3.svg b/Gaze/Gaze.icon/Assets/Ellipse 5 3.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 5 3.svg rename to Gaze/Gaze.icon/Assets/Ellipse 5 3.svg diff --git a/Gaze.icon/Assets/Ellipse 7 3.svg b/Gaze/Gaze.icon/Assets/Ellipse 7 3.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 7 3.svg rename to Gaze/Gaze.icon/Assets/Ellipse 7 3.svg diff --git a/Gaze.icon/Assets/Ellipse 8 3.svg b/Gaze/Gaze.icon/Assets/Ellipse 8 3.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 8 3.svg rename to Gaze/Gaze.icon/Assets/Ellipse 8 3.svg diff --git a/Gaze.icon/Assets/Ellipse 9 3.svg b/Gaze/Gaze.icon/Assets/Ellipse 9 3.svg similarity index 100% rename from Gaze.icon/Assets/Ellipse 9 3.svg rename to Gaze/Gaze.icon/Assets/Ellipse 9 3.svg diff --git a/Gaze.icon/Assets/Line 7.svg b/Gaze/Gaze.icon/Assets/Line 7.svg similarity index 100% rename from Gaze.icon/Assets/Line 7.svg rename to Gaze/Gaze.icon/Assets/Line 7.svg diff --git a/Gaze.icon/Assets/Line 8.svg b/Gaze/Gaze.icon/Assets/Line 8.svg similarity index 100% rename from Gaze.icon/Assets/Line 8.svg rename to Gaze/Gaze.icon/Assets/Line 8.svg diff --git a/Gaze.icon/icon.json b/Gaze/Gaze.icon/icon.json similarity index 93% rename from Gaze.icon/icon.json rename to Gaze/Gaze.icon/icon.json index 7efc4f6..f887d63 100644 --- a/Gaze.icon/icon.json +++ b/Gaze/Gaze.icon/icon.json @@ -166,6 +166,23 @@ "hidden" : false, "layers" : [ { + "blend-mode" : "darken", + "fill" : { + "linear-gradient" : [ + "extended-srgb:0.00000,0.53333,1.00000,1.00000", + "display-p3:0.38403,0.64839,1.04685,1.00000" + ], + "orientation" : { + "start" : { + "x" : 0.5, + "y" : 0 + }, + "stop" : { + "x" : 0.5, + "y" : 0.7 + } + } + }, "glass" : false, "image-name" : "Line 7.svg", "name" : "Line 7", @@ -186,7 +203,7 @@ "image-name" : "Line 8.svg", "name" : "Line 8", "position" : { - "scale" : 1.21, + "scale" : 1.26, "translation-in-points" : [ -0.1999973177900074, -82.12440000000004 @@ -231,7 +248,7 @@ "image-name" : "Ellipse 11.svg", "name" : "Ellipse 11", "position" : { - "scale" : 0.99, + "scale" : 0.83, "translation-in-points" : [ 0, 0 diff --git a/Gaze/Models/AppSettings.swift b/Gaze/Models/AppSettings.swift index 2fde5b5..9f7d27c 100644 --- a/Gaze/Models/AppSettings.swift +++ b/Gaze/Models/AppSettings.swift @@ -49,6 +49,9 @@ struct AppSettings: Codable, Equatable, Hashable { var hasCompletedOnboarding: Bool var launchAtLogin: Bool var playSounds: Bool + + // App Store detection (cached at launch, not persisted) + var isAppStoreVersion: Bool init( lookAwayTimer: TimerConfiguration = TimerConfiguration( @@ -62,7 +65,8 @@ struct AppSettings: Codable, Equatable, Hashable { subtleReminderSize: ReminderSize = .medium, hasCompletedOnboarding: Bool = false, launchAtLogin: Bool = false, - playSounds: Bool = true + playSounds: Bool = true, + isAppStoreVersion: Bool = false ) { self.lookAwayTimer = lookAwayTimer self.lookAwayCountdownSeconds = lookAwayCountdownSeconds @@ -73,6 +77,7 @@ struct AppSettings: Codable, Equatable, Hashable { self.hasCompletedOnboarding = hasCompletedOnboarding self.launchAtLogin = launchAtLogin self.playSounds = playSounds + self.isAppStoreVersion = isAppStoreVersion } static var defaults: AppSettings { @@ -85,7 +90,8 @@ struct AppSettings: Codable, Equatable, Hashable { subtleReminderSize: .medium, hasCompletedOnboarding: false, launchAtLogin: false, - playSounds: true + playSounds: true, + isAppStoreVersion: false ) } @@ -97,5 +103,50 @@ struct AppSettings: Codable, Equatable, Hashable { && lhs.subtleReminderSize == rhs.subtleReminderSize && lhs.hasCompletedOnboarding == rhs.hasCompletedOnboarding && lhs.launchAtLogin == rhs.launchAtLogin && lhs.playSounds == rhs.playSounds + && lhs.isAppStoreVersion == rhs.isAppStoreVersion + } + + // MARK: - Custom Codable Implementation + + enum CodingKeys: String, CodingKey { + case lookAwayTimer + case lookAwayCountdownSeconds + case blinkTimer + case postureTimer + case userTimers + case subtleReminderSize + case hasCompletedOnboarding + case launchAtLogin + case playSounds + // isAppStoreVersion is intentionally excluded from persistence + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + lookAwayTimer = try container.decode(TimerConfiguration.self, forKey: .lookAwayTimer) + lookAwayCountdownSeconds = try container.decode(Int.self, forKey: .lookAwayCountdownSeconds) + blinkTimer = try container.decode(TimerConfiguration.self, forKey: .blinkTimer) + postureTimer = try container.decode(TimerConfiguration.self, forKey: .postureTimer) + userTimers = try container.decode([UserTimer].self, forKey: .userTimers) + subtleReminderSize = try container.decode(ReminderSize.self, forKey: .subtleReminderSize) + hasCompletedOnboarding = try container.decode(Bool.self, forKey: .hasCompletedOnboarding) + launchAtLogin = try container.decode(Bool.self, forKey: .launchAtLogin) + playSounds = try container.decode(Bool.self, forKey: .playSounds) + // isAppStoreVersion is not persisted, will be set at launch + isAppStoreVersion = false + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(lookAwayTimer, forKey: .lookAwayTimer) + try container.encode(lookAwayCountdownSeconds, forKey: .lookAwayCountdownSeconds) + try container.encode(blinkTimer, forKey: .blinkTimer) + try container.encode(postureTimer, forKey: .postureTimer) + try container.encode(userTimers, forKey: .userTimers) + try container.encode(subtleReminderSize, forKey: .subtleReminderSize) + try container.encode(hasCompletedOnboarding, forKey: .hasCompletedOnboarding) + try container.encode(launchAtLogin, forKey: .launchAtLogin) + try container.encode(playSounds, forKey: .playSounds) + // isAppStoreVersion is intentionally not persisted } } diff --git a/Gaze/Services/AppStoreDetector.swift b/Gaze/Services/AppStoreDetector.swift index 40f6d82..711c9ec 100644 --- a/Gaze/Services/AppStoreDetector.swift +++ b/Gaze/Services/AppStoreDetector.swift @@ -6,47 +6,63 @@ // import Foundation +import StoreKit enum AppStoreDetector { - /// Returns true if the app was downloaded from the Mac App Store + /// Returns true if the app was downloaded from the Mac App Store. /// - /// Uses a heuristic approach that checks for the presence of a valid App Store receipt. - /// This is sufficient for distinguishing App Store builds from direct distribution. + /// Uses StoreKit's AppTransaction API on macOS 15+ to verify if the app is an App Store version. + /// Falls back to a heuristic receipt check on macOS versions prior to 15. /// - /// Note: This does not perform full cryptographic validation of the receipt signature, its - /// only used to determine if we should show the 'buy me a coffee' link. - static var isAppStoreVersion: Bool { + /// This method is asynchronous due to the use of StoreKit's async API. + static func isAppStoreVersion() async -> Bool { #if DEBUG return false #else - guard let receiptURL = Bundle.main.appStoreReceiptURL else { - return false + if #available(macOS 15.0, *) { + do { + _ = try await AppTransaction.shared + return true + } catch { + return false + } + } else { + // Fallback for older macOS: use legacy receipt check + guard let receiptURL = Bundle.main.appStoreReceiptURL else { + return false + } + guard FileManager.default.fileExists(atPath: receiptURL.path) else { + return false + } + guard let receiptData = try? Data(contentsOf: receiptURL), + receiptData.count > 2 + else { + return false + } + let bytes = [UInt8](receiptData.prefix(2)) + return bytes[0] == 0x30 && bytes[1] == 0x82 } - - guard FileManager.default.fileExists(atPath: receiptURL.path) else { - return false - } - - guard let receiptData = try? Data(contentsOf: receiptURL), - receiptData.count > 2 - else { - return false - } - - // Verify receipt has PKCS#7 signature format (starts with ASN.1 SEQUENCE tag) - let bytes = [UInt8](receiptData.prefix(2)) - return bytes[0] == 0x30 && bytes[1] == 0x82 #endif } - /// Checks if the app is running in TestFlight + /// Checks if the app is running in TestFlight. /// - /// TestFlight builds have a receipt named "sandboxReceipt" instead of "receipt" - static var isTestFlight: Bool { + /// On macOS 15+, StoreKit does not expose a TestFlight receipt type. + /// This method returns false on macOS 15+ as a result. + /// On earlier versions, it checks for the presence of a "sandboxReceipt". + /// + /// This method is asynchronous for API consistency. + static func isTestFlight() async -> Bool { #if DEBUG return false #else - return Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" + if #available(macOS 15.0, *) { + // StoreKit does not expose TestFlight receipt type. + // As a workaround, fallback to legacy method if available, else return false. + return false // No supported TestFlight check post-macOS 15 + } else { + return Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" + } #endif } } diff --git a/Gaze/Services/MigrationManager.swift b/Gaze/Services/MigrationManager.swift index a69e1a5..b2d7e25 100644 --- a/Gaze/Services/MigrationManager.swift +++ b/Gaze/Services/MigrationManager.swift @@ -160,7 +160,7 @@ class Version101Migration: Migration { var targetVersion: String = "1.0.1" func migrate(_ data: [String: Any]) throws -> [String: Any] { - var migratedData = data + let migratedData = data // Example migration logic: // Add any new fields with default values if they don't exist @@ -197,4 +197,4 @@ class Version102Migration: Migration { return migratedData } -} \ No newline at end of file +} diff --git a/Gaze/Services/SettingsManager.swift b/Gaze/Services/SettingsManager.swift index e58d7e5..f8d2325 100644 --- a/Gaze/Services/SettingsManager.swift +++ b/Gaze/Services/SettingsManager.swift @@ -75,4 +75,13 @@ class SettingsManager: ObservableObject { settings.postureTimer = configuration } } + + /// Detects and caches the App Store version status. + /// This should be called once at app launch to avoid async checks throughout the app. + func detectAppStoreVersion() async { + let isAppStore = await AppStoreDetector.isAppStoreVersion() + await MainActor.run { + settings.isAppStoreVersion = isAppStore + } + } } diff --git a/Gaze/Views/Onboarding/OnboardingContainerView.swift b/Gaze/Views/Onboarding/OnboardingContainerView.swift index 768050e..4a491cf 100644 --- a/Gaze/Views/Onboarding/OnboardingContainerView.swift +++ b/Gaze/Views/Onboarding/OnboardingContainerView.swift @@ -77,6 +77,10 @@ struct OnboardingContainerView: View { SettingsOnboardingView( launchAtLogin: $launchAtLogin, subtleReminderSize: $subtleReminderSize, + isAppStoreVersion: Binding( + get: { settingsManager.settings.isAppStoreVersion }, + set: { _ in } + ), isOnboarding: true ) .tag(4) diff --git a/Gaze/Views/Onboarding/SettingsOnboardingView.swift b/Gaze/Views/Onboarding/SettingsOnboardingView.swift index 336f0a4..856f19c 100644 --- a/Gaze/Views/Onboarding/SettingsOnboardingView.swift +++ b/Gaze/Views/Onboarding/SettingsOnboardingView.swift @@ -10,6 +10,7 @@ import SwiftUI struct SettingsOnboardingView: View { @Binding var launchAtLogin: Bool @Binding var subtleReminderSize: ReminderSize + @Binding var isAppStoreVersion: Bool var isOnboarding: Bool = true var body: some View { @@ -131,7 +132,7 @@ struct SettingsOnboardingView: View { .buttonStyle(.plain) .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 10)) - if !AppStoreDetector.isAppStoreVersion { + if !isAppStoreVersion { Button(action: { if let url = URL(string: "https://buymeacoffee.com/mikefreno") { NSWorkspace.shared.open(url) @@ -198,6 +199,7 @@ struct SettingsOnboardingView: View { SettingsOnboardingView( launchAtLogin: .constant(false), subtleReminderSize: .constant(.medium), + isAppStoreVersion: .constant(false), isOnboarding: true ) } @@ -206,6 +208,7 @@ struct SettingsOnboardingView: View { SettingsOnboardingView( launchAtLogin: .constant(true), subtleReminderSize: .constant(.medium), + isAppStoreVersion: .constant(false), isOnboarding: true ) } diff --git a/Gaze/Views/SettingsWindowView.swift b/Gaze/Views/SettingsWindowView.swift index 9c89790..8782725 100644 --- a/Gaze/Views/SettingsWindowView.swift +++ b/Gaze/Views/SettingsWindowView.swift @@ -82,6 +82,10 @@ struct SettingsWindowView: View { SettingsOnboardingView( launchAtLogin: $launchAtLogin, subtleReminderSize: $subtleReminderSize, + isAppStoreVersion: Binding( + get: { settingsManager.settings.isAppStoreVersion }, + set: { _ in } + ), isOnboarding: false ) .tag(4) @@ -140,7 +144,8 @@ struct SettingsWindowView: View { subtleReminderSize: subtleReminderSize, hasCompletedOnboarding: settingsManager.settings.hasCompletedOnboarding, launchAtLogin: launchAtLogin, - playSounds: settingsManager.settings.playSounds + playSounds: settingsManager.settings.playSounds, + isAppStoreVersion: settingsManager.settings.isAppStoreVersion ) // Assign the entire settings object to trigger didSet and observers diff --git a/GazeTests/Services/AppStoreDetectorTests.swift b/GazeTests/Services/AppStoreDetectorTests.swift index 7e15958..50702a7 100644 --- a/GazeTests/Services/AppStoreDetectorTests.swift +++ b/GazeTests/Services/AppStoreDetectorTests.swift @@ -10,20 +10,20 @@ import Testing struct AppStoreDetectorTests { - @Test func isAppStoreVersionReturnsFalseInDebug() { + @Test func isAppStoreVersionReturnsFalseInDebug() async { // In test/debug builds, should always return false - #expect(AppStoreDetector.isAppStoreVersion == false) + #expect(await AppStoreDetector.isAppStoreVersion() == false) } - @Test func isTestFlightReturnsFalseInDebug() { + @Test func isTestFlightReturnsFalseInDebug() async { // In test/debug builds, should always return false - #expect(AppStoreDetector.isTestFlight == false) + #expect(await AppStoreDetector.isTestFlight() == false) } - @Test func receiptValidationHandlesMissingReceipt() { + @Test func receiptValidationHandlesMissingReceipt() async { // When there's no receipt (development build), should return false // This is implicitly tested by isAppStoreVersionReturnsFalseInDebug // but we're documenting the expected behavior - #expect(AppStoreDetector.isAppStoreVersion == false) + #expect(await AppStoreDetector.isAppStoreVersion() == false) } }