From ef925391da8c0b506c4ffecbcbedff1f9a99ec0b Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sat, 10 Jan 2026 10:31:09 -0500 Subject: [PATCH] fix: correct link highlighting, update app store validation --- Gaze/Services/AppStoreDetector.swift | 34 ++++++++++++++++--- Gaze/Views/Onboarding/PostureSetupView.swift | 22 ++++++------ .../Services/AppStoreDetectorTests.swift | 14 +++++++- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/Gaze/Services/AppStoreDetector.swift b/Gaze/Services/AppStoreDetector.swift index 7c3090a..40f6d82 100644 --- a/Gaze/Services/AppStoreDetector.swift +++ b/Gaze/Services/AppStoreDetector.swift @@ -9,6 +9,12 @@ import Foundation enum AppStoreDetector { /// 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. + /// + /// 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 { #if DEBUG return false @@ -17,12 +23,30 @@ enum AppStoreDetector { return false } - // Check if receipt exists and is in the expected App Store location - let fileManager = FileManager.default - let receiptExists = fileManager.fileExists(atPath: receiptURL.path) - let isInMASReceipt = receiptURL.path.contains("_MASReceipt") + guard FileManager.default.fileExists(atPath: receiptURL.path) else { + return false + } - return receiptExists && isInMASReceipt + 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 + /// + /// TestFlight builds have a receipt named "sandboxReceipt" instead of "receipt" + static var isTestFlight: Bool { + #if DEBUG + return false + #else + return Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" #endif } } diff --git a/Gaze/Views/Onboarding/PostureSetupView.swift b/Gaze/Views/Onboarding/PostureSetupView.swift index 7ce0e51..04fc2b7 100644 --- a/Gaze/Views/Onboarding/PostureSetupView.swift +++ b/Gaze/Views/Onboarding/PostureSetupView.swift @@ -5,8 +5,8 @@ // Created by Mike Freno on 1/7/26. // -import SwiftUI import AppKit +import SwiftUI struct PostureSetupView: View { @Binding var enabled: Bool @@ -33,9 +33,11 @@ struct PostureSetupView: View { VStack(spacing: 30) { HStack(spacing: 12) { Button(action: { + // Using properly URL-encoded text fragment + // Points to key findings about sitting posture and behavior relationship with LBP if let url = URL( string: - "https://pubmed.ncbi.nlm.nih.gov/40111906/#:~:text=For studies exploring sitting posture, seven found a relationship with LBP. Regarding studies on sitting behavior, only one showed no relationship between LBP prevalence, while twelve indicated a relationship." + "https://pubmed.ncbi.nlm.nih.gov/40111906/#:~:text=For%20studies%20exploring%20sitting%20posture%2C%20seven%20found%20a%20relationship%20with%20LBP.%20Regarding%20studies%20on%20sitting%20behavior%2C%20only%20one%20showed%20no%20relationship%20between%20LBP%20prevalence" ) { #if os(iOS) UIApplication.shared.open(url) @@ -96,7 +98,7 @@ struct PostureSetupView: View { .font(.caption) .foregroundColor(.secondary) } - + // Preview button Button(action: { showPreviewWindow() @@ -120,34 +122,34 @@ struct PostureSetupView: View { .padding() .background(.clear) } - + private func showPreviewWindow() { guard let screen = NSScreen.main else { return } - + let window = NSWindow( contentRect: screen.frame, styleMask: [.borderless, .fullSizeContentView], backing: .buffered, defer: false ) - + window.level = .floating window.isOpaque = false window.backgroundColor = .clear window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] window.acceptsMouseMovedEvents = true - + let contentView = PostureReminderView(sizePercentage: 10.0) { [weak window] in window?.close() } - + window.contentView = NSHostingView(rootView: contentView) window.makeFirstResponder(window.contentView) - + let windowController = NSWindowController(window: window) windowController.showWindow(nil) window.makeKeyAndOrderFront(nil) - + previewWindowController = windowController } } diff --git a/GazeTests/Services/AppStoreDetectorTests.swift b/GazeTests/Services/AppStoreDetectorTests.swift index 213508b..7e15958 100644 --- a/GazeTests/Services/AppStoreDetectorTests.swift +++ b/GazeTests/Services/AppStoreDetectorTests.swift @@ -9,9 +9,21 @@ import Testing struct AppStoreDetectorTests { - + @Test func isAppStoreVersionReturnsFalseInDebug() { // In test/debug builds, should always return false #expect(AppStoreDetector.isAppStoreVersion == false) } + + @Test func isTestFlightReturnsFalseInDebug() { + // In test/debug builds, should always return false + #expect(AppStoreDetector.isTestFlight == false) + } + + @Test func receiptValidationHandlesMissingReceipt() { + // 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) + } }