From 57abfdc2ba880be1fae4a1a184531e3228d62dae Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 19 May 2026 01:20:21 +0800
Subject: [PATCH] Prepare App Store release 7.3.5 build 744

---
 Apptag/DataLayer.swift |   53 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/Apptag/DataLayer.swift b/Apptag/DataLayer.swift
index 518258d..779beac 100644
--- a/Apptag/DataLayer.swift
+++ b/Apptag/DataLayer.swift
@@ -51,6 +51,10 @@
 // MARK: - App Scanner
 
 enum AppIndexer {
+    private static let scanCacheLock = NSLock()
+    private static var cachedScanApps: [AppInfo]? = nil
+    private static var cachedScanAt: Date? = nil
+    private static let scanCacheTTL: TimeInterval = 1800
 
     static let searchPaths: [URL] = [
         URL(fileURLWithPath: "/Applications"),
@@ -72,7 +76,42 @@
     }
 
     /// Scan all standard locations. Tags are annotated from TagDatabase by the caller.
-    static func scan() -> [AppInfo] {
+    static func scan(useCache: Bool = true) -> [AppInfo] {
+        if useCache, let cached = cachedScanIfFresh() {
+            return cached
+        }
+
+        let apps = performScan()
+        updateScanCache(apps)
+        return apps
+    }
+
+    static func invalidateScanCache() {
+        scanCacheLock.lock()
+        cachedScanApps = nil
+        cachedScanAt = nil
+        scanCacheLock.unlock()
+    }
+
+    private static func cachedScanIfFresh() -> [AppInfo]? {
+        scanCacheLock.lock()
+        defer { scanCacheLock.unlock() }
+
+        guard let cachedScanApps,
+              let cachedScanAt,
+              Date().timeIntervalSince(cachedScanAt) < scanCacheTTL
+        else { return nil }
+        return cachedScanApps
+    }
+
+    private static func updateScanCache(_ apps: [AppInfo]) {
+        scanCacheLock.lock()
+        cachedScanApps = apps
+        cachedScanAt = Date()
+        scanCacheLock.unlock()
+    }
+
+    private static func performScan() -> [AppInfo] {
         var seenResolvedPaths = Set<String>()
         var apps: [AppInfo] = []
 
@@ -220,12 +259,18 @@
         var seenAppIDsByGroup: [String: Set<URL>] = [:]
 
         for app in apps {
+            if app.isAppleApp {
+                appendGroupedApp(app, to: macCategory, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
+            }
+
             if app.tags.isEmpty {
-                let bucket = app.isAppleApp ? macCategory : defaultGroupName
-                appendGroupedApp(app, to: bucket, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
+                if !app.isAppleApp {
+                    appendGroupedApp(app, to: defaultGroupName, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
+                }
             } else {
                 for tag in uniqueOrdered(app.tags) {
                     let displayName = nameOverrides[tag] ?? tag
+                    guard displayName != macCategory else { continue }
                     appendGroupedApp(app, to: displayName, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
                 }
             }
@@ -1253,7 +1298,7 @@
         }
 
         var current = store.appTags[path] ?? []
-        if !copy, !sourceTag.isEmpty {
+        if !copy, !sourceTag.isEmpty, sourceTag != "Mac自带", sourceTag != tr("group.appleBuiltIn") {
             current.removeAll { $0 == sourceTag }
         }
         if !current.contains(targetTag) {

--
Gitblit v1.9.3