From 646a859e58b54a10c8622d8c926172be10f46809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Andr=C3=A9=20R=C3=B8nsen?= Date: Thu, 9 Apr 2026 05:16:41 +0200 Subject: [PATCH] perf(SnippetStore): cache loaded images to avoid repeated disk reads loadImage(for:) called NSImage(contentsOf:) on every SwiftUI render pass, doing synchronous disk I/O on the main thread for each visible image snippet. Add a simple NSCache so the file is read at most once per session; cache entries are evicted automatically on memory pressure. Invalidate the relevant entry on delete and on addImage so callers always see the current image. --- Sources/SnippetStore.swift | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Sources/SnippetStore.swift b/Sources/SnippetStore.swift index b2405f0..edd3a0e 100644 --- a/Sources/SnippetStore.swift +++ b/Sources/SnippetStore.swift @@ -8,6 +8,9 @@ class SnippetStore: ObservableObject { private let fileURL: URL private let imagesDir: URL + /// In-memory cache — keyed by imageFileName. NSCache evicts automatically + /// under memory pressure, so this never grows unboundedly. + private let imageCache = NSCache() init() { let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! @@ -70,6 +73,9 @@ class SnippetStore: ObservableObject { do { try pngData.write(to: fileURL) + // Prime the cache with the already-decoded image so the first + // render after adding doesn't hit disk. + imageCache.setObject(image, forKey: fileName as NSString) let snippet = Snippet(title: "", value: "[image]", imageFileName: fileName) snippets.insert(snippet, at: 0) save() @@ -83,8 +89,17 @@ class SnippetStore: ObservableObject { } func loadImage(for snippet: Snippet) -> NSImage? { - guard let url = imageURL(for: snippet) else { return nil } - return NSImage(contentsOf: url) + guard let name = snippet.imageFileName else { return nil } + // Return cached copy if available. + if let cached = imageCache.object(forKey: name as NSString) { + return cached + } + // Fall back to disk and populate the cache. + let url = imagesDir.appendingPathComponent(name) + guard FileManager.default.fileExists(atPath: url.path), + let image = NSImage(contentsOf: url) else { return nil } + imageCache.setObject(image, forKey: name as NSString) + return image } func update(_ snippet: Snippet) { @@ -95,10 +110,11 @@ class SnippetStore: ObservableObject { } func delete(_ snippet: Snippet) { - // Clean up image file + // Clean up image file and cache entry. if let name = snippet.imageFileName { let url = imagesDir.appendingPathComponent(name) try? FileManager.default.removeItem(at: url) + imageCache.removeObject(forKey: name as NSString) } snippets.removeAll { $0.id == snippet.id } save()