Skip to content

Commit 20e7486

Browse files
committed
added cache and UTI file type checking (instead of extension list)
1 parent c657238 commit 20e7486

File tree

1 file changed

+64
-36
lines changed

1 file changed

+64
-36
lines changed

MacImageManager/MacImageManager/Models/BrowserModel.swift

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,11 @@ class BrowserModel: ObservableObject {
1616
@Published var canNavigateUp: Bool = false
1717
@Published var showingFileImporter: Bool = false
1818

19-
private let fileManager = FileManager.default
20-
private let imageTypes: Set<String> = {
21-
var types = Set<String>()
22-
23-
// Common image extensions
24-
let extensions = ["jpg", "jpeg", "png", "gif", "bmp", "tiff", "tif", "heic", "heif", "webp", "svg", "ico"]
19+
// Caches to speed up metadata/icon recomputation in large directories
20+
private var fileItemCache: [URL: FileItem] = [:]
21+
private var iconNameCache: [String: String] = [:] // key: UTI identifier
2522

26-
for ext in extensions {
27-
types.insert(ext.lowercased())
28-
}
29-
30-
return types
31-
}()
23+
private let fileManager = FileManager.default
3224

3325
init() {
3426
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
@@ -89,35 +81,40 @@ class BrowserModel: ObservableObject {
8981
let iconName: String
9082
if isDir {
9183
iconName = "folder.fill"
92-
} else if uti?.conforms(to: UTType.image) ?? false {
93-
iconName = "photo"
94-
} else if uti?.conforms(to: UTType.movie) ?? false {
95-
iconName = "film"
96-
} else if uti?.conforms(to: UTType.text) ?? false {
97-
iconName = "doc.text"
98-
} else if uti?.conforms(to: UTType.sourceCode) ?? false {
99-
iconName = "doc.text.fill"
10084
} else {
101-
iconName = "doc"
85+
iconName = self.iconName(for: uti)
86+
}
87+
88+
// Reuse cached FileItem when unchanged to avoid recomputing
89+
if let cached = fileItemCache[url],
90+
cached.isDirectory == isDir,
91+
cached.fileSize == fileSize,
92+
cached.modificationDate == modDate,
93+
cached.uti == uti {
94+
fileItems.append(cached)
95+
} else {
96+
let fileItem = FileItem(
97+
url: url,
98+
name: url.lastPathComponent,
99+
iconName: iconName,
100+
isDirectory: isDir,
101+
fileSize: fileSize,
102+
modificationDate: modDate,
103+
uti: uti,
104+
isAnimatedGif: isAnimatedGif,
105+
isVideo: isVideo
106+
)
107+
fileItems.append(fileItem)
108+
fileItemCache[url] = fileItem
102109
}
103-
104-
let fileItem = FileItem(
105-
url: url,
106-
name: url.lastPathComponent,
107-
iconName: iconName,
108-
isDirectory: isDir,
109-
fileSize: fileSize,
110-
modificationDate: modDate,
111-
uti: uti,
112-
isAnimatedGif: isAnimatedGif,
113-
isVideo: isVideo
114-
)
115-
fileItems.append(fileItem)
116110
}
117111

118112
fileItems.sort { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
119113

120114
self.items = fileItems
115+
// Prune cache to current directory entries to bound memory usage
116+
let currentURLs = Set(fileItems.map { $0.url })
117+
fileItemCache = fileItemCache.filter { currentURLs.contains($0.key) }
121118
print("Loaded \(items.count) items.")
122119

123120
updateNavigationState()
@@ -165,15 +162,46 @@ class BrowserModel: ObservableObject {
165162

166163
func isImageFile(_ item: FileItem) -> Bool {
167164
guard !item.isDirectory else { return false }
168-
let ext = item.url.pathExtension.lowercased()
169-
return imageTypes.contains(ext)
165+
166+
// Prefer the UTI from metadata; if missing, derive from the filename extension
167+
let type = item.uti ?? UTType(filenameExtension: item.url.pathExtension.lowercased())
168+
169+
if let type {
170+
return type.conforms(to: .rawImage) || type.conforms(to: .image)
171+
}
172+
173+
return false
170174
}
171175

172176
func openDirectory(_ item: FileItem) {
173177
self.currentDirectory = item.url
174178
loadCurrentDirectory()
175179
}
176180

181+
// Returns an SF Symbol name for a given UTI, with simple caching
182+
private func iconName(for uti: UTType?) -> String {
183+
guard let uti = uti else { return "doc" }
184+
let key = uti.identifier
185+
if let cached = iconNameCache[key] {
186+
return cached
187+
}
188+
189+
let name: String
190+
if uti.conforms(to: UTType.rawImage) || uti.conforms(to: UTType.image) {
191+
name = "photo"
192+
} else if uti.conforms(to: UTType.movie) {
193+
name = "film"
194+
} else if uti.conforms(to: UTType.text) {
195+
name = "doc.text"
196+
} else if uti.conforms(to: UTType.sourceCode) {
197+
name = "doc.text.fill"
198+
} else {
199+
name = "doc"
200+
}
201+
iconNameCache[key] = name
202+
return name
203+
}
204+
177205
private func updateNavigationState() {
178206
let homeDirectory = fileManager.homeDirectoryForCurrentUser
179207
canNavigateUp = currentDirectory.path != homeDirectory.path &&

0 commit comments

Comments
 (0)