fix: correct link highlighting, update app store validation

This commit is contained in:
Michael Freno
2026-01-10 10:31:09 -05:00
parent 4aa1d29c65
commit ef925391da
3 changed files with 54 additions and 16 deletions

View File

@@ -9,6 +9,12 @@ import Foundation
enum AppStoreDetector { 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.
///
/// 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 { static var isAppStoreVersion: Bool {
#if DEBUG #if DEBUG
return false return false
@@ -17,12 +23,30 @@ enum AppStoreDetector {
return false return false
} }
// Check if receipt exists and is in the expected App Store location guard FileManager.default.fileExists(atPath: receiptURL.path) else {
let fileManager = FileManager.default return false
let receiptExists = fileManager.fileExists(atPath: receiptURL.path) }
let isInMASReceipt = receiptURL.path.contains("_MASReceipt")
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 #endif
} }
} }

View File

@@ -5,8 +5,8 @@
// Created by Mike Freno on 1/7/26. // Created by Mike Freno on 1/7/26.
// //
import SwiftUI
import AppKit import AppKit
import SwiftUI
struct PostureSetupView: View { struct PostureSetupView: View {
@Binding var enabled: Bool @Binding var enabled: Bool
@@ -33,9 +33,11 @@ struct PostureSetupView: View {
VStack(spacing: 30) { VStack(spacing: 30) {
HStack(spacing: 12) { HStack(spacing: 12) {
Button(action: { Button(action: {
// Using properly URL-encoded text fragment
// Points to key findings about sitting posture and behavior relationship with LBP
if let url = URL( if let url = URL(
string: 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) #if os(iOS)
UIApplication.shared.open(url) UIApplication.shared.open(url)
@@ -96,7 +98,7 @@ struct PostureSetupView: View {
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
// Preview button // Preview button
Button(action: { Button(action: {
showPreviewWindow() showPreviewWindow()
@@ -120,34 +122,34 @@ struct PostureSetupView: View {
.padding() .padding()
.background(.clear) .background(.clear)
} }
private func showPreviewWindow() { private func showPreviewWindow() {
guard let screen = NSScreen.main else { return } guard let screen = NSScreen.main else { return }
let window = NSWindow( let window = NSWindow(
contentRect: screen.frame, contentRect: screen.frame,
styleMask: [.borderless, .fullSizeContentView], styleMask: [.borderless, .fullSizeContentView],
backing: .buffered, backing: .buffered,
defer: false defer: false
) )
window.level = .floating window.level = .floating
window.isOpaque = false window.isOpaque = false
window.backgroundColor = .clear window.backgroundColor = .clear
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
window.acceptsMouseMovedEvents = true window.acceptsMouseMovedEvents = true
let contentView = PostureReminderView(sizePercentage: 10.0) { [weak window] in let contentView = PostureReminderView(sizePercentage: 10.0) { [weak window] in
window?.close() window?.close()
} }
window.contentView = NSHostingView(rootView: contentView) window.contentView = NSHostingView(rootView: contentView)
window.makeFirstResponder(window.contentView) window.makeFirstResponder(window.contentView)
let windowController = NSWindowController(window: window) let windowController = NSWindowController(window: window)
windowController.showWindow(nil) windowController.showWindow(nil)
window.makeKeyAndOrderFront(nil) window.makeKeyAndOrderFront(nil)
previewWindowController = windowController previewWindowController = windowController
} }
} }

View File

@@ -9,9 +9,21 @@
import Testing import Testing
struct AppStoreDetectorTests { struct AppStoreDetectorTests {
@Test func isAppStoreVersionReturnsFalseInDebug() { @Test func isAppStoreVersionReturnsFalseInDebug() {
// In test/debug builds, should always return false // In test/debug builds, should always return false
#expect(AppStoreDetector.isAppStoreVersion == 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)
}
} }