From 6478431003fb31889825145ceb9180dcd4317494 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 04 Jun 2026 01:13:46 +0800
Subject: [PATCH] Add 7.8.12 App Store candidate DMG
---
Apptag/DataLayer.swift | 53 +++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 45 insertions(+), 8 deletions(-)
diff --git a/Apptag/DataLayer.swift b/Apptag/DataLayer.swift
index a3fc089..060c58c 100644
--- a/Apptag/DataLayer.swift
+++ b/Apptag/DataLayer.swift
@@ -94,9 +94,16 @@
// MARK: - App Scanner
enum AppIndexer {
+ private struct SearchPathSignature: Equatable {
+ let path: String
+ let exists: Bool
+ let contentModificationTime: TimeInterval?
+ }
+
private static let scanCacheLock = NSLock()
private static var cachedScanApps: [AppInfo]? = nil
private static var cachedScanAt: Date? = nil
+ private static var cachedSearchPathSignature: [SearchPathSignature]? = nil
private static let scanCacheTTL: TimeInterval = 1800
static let searchPaths: [URL] = [
@@ -120,12 +127,13 @@
/// Scan all standard locations. Tags are annotated from TagDatabase by the caller.
static func scan(useCache: Bool = true) -> [AppInfo] {
- if useCache, let cached = cachedScanIfFresh() {
+ let searchPathSignature = currentSearchPathSignature()
+ if useCache, let cached = cachedScanIfFresh(matching: searchPathSignature) {
return cached
}
let apps = performScan()
- updateScanCache(apps)
+ updateScanCache(apps, searchPathSignature: searchPathSignature)
return apps
}
@@ -133,25 +141,49 @@
scanCacheLock.lock()
cachedScanApps = nil
cachedScanAt = nil
+ cachedSearchPathSignature = nil
scanCacheLock.unlock()
}
- private static func cachedScanIfFresh() -> [AppInfo]? {
+ private static func cachedScanIfFresh(matching searchPathSignature: [SearchPathSignature]) -> [AppInfo]? {
scanCacheLock.lock()
defer { scanCacheLock.unlock() }
guard let cachedScanApps,
let cachedScanAt,
+ cachedSearchPathSignature == searchPathSignature,
Date().timeIntervalSince(cachedScanAt) < scanCacheTTL
else { return nil }
return cachedScanApps
}
- private static func updateScanCache(_ apps: [AppInfo]) {
+ private static func updateScanCache(_ apps: [AppInfo], searchPathSignature: [SearchPathSignature]) {
scanCacheLock.lock()
cachedScanApps = apps
cachedScanAt = Date()
+ cachedSearchPathSignature = searchPathSignature
scanCacheLock.unlock()
+ }
+
+ private static func currentSearchPathSignature() -> [SearchPathSignature] {
+ searchPaths.map { url in
+ var isDirectory: ObjCBool = false
+ let exists = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory)
+ guard exists, isDirectory.boolValue else {
+ return SearchPathSignature(
+ path: url.path,
+ exists: false,
+ contentModificationTime: nil
+ )
+ }
+
+ let values = try? url.resourceValues(forKeys: [.contentModificationDateKey])
+ return SearchPathSignature(
+ path: url.path,
+ exists: true,
+ contentModificationTime: values?.contentModificationDate?.timeIntervalSinceReferenceDate
+ )
+ }
}
private static func performScan() -> [AppInfo] {
@@ -215,12 +247,13 @@
guard !isNestedInsideAppBundle(resolvedURL) else { return }
guard seenResolvedPaths.insert(resolvedURL.path).inserted else { return }
+ let bundle = Bundle(url: displayURL) ?? Bundle(url: resolvedURL)
+ let bundleId = bundle?.bundleIdentifier
+ guard !isTagLauncherBundle(bundleId) else { return }
+
let name = displayURL.deletingPathExtension().lastPathComponent
let icon = NSWorkspace.shared.icon(forFile: displayURL.path)
icon.size = NSSize(width: 96, height: 96)
-
- let bundle = Bundle(url: displayURL) ?? Bundle(url: resolvedURL)
- let bundleId = bundle?.bundleIdentifier
let localizedNamesByLanguage = localizedAppNameMap(
bundle: bundle,
fallbackName: name
@@ -240,6 +273,10 @@
localizedNamesByLanguage: localizedNamesByLanguage,
icon: icon
))
+ }
+
+ private static func isTagLauncherBundle(_ bundleIdentifier: String?) -> Bool {
+ bundleIdentifier?.caseInsensitiveCompare(AppIdentity.bundleIdentifier) == .orderedSame
}
private static func localizedAppNameMap(
@@ -443,7 +480,7 @@
private static func deduplicationIdentity(for app: AppInfo) -> String {
if let bundleIdentifier = app.bundleIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines),
!bundleIdentifier.isEmpty {
- return "bundle:\(bundleIdentifier.lowercased())"
+ return "bundle:\(bundleIdentifier.lowercased())|name:\(normalizedAppName(app.name))"
}
return "name:\(normalizedAppName(app.name))"
}
--
Gitblit v1.9.3