@@ -16,9 +16,8 @@ class BrowserModel: ObservableObject {
1616 @Published var canNavigateUp : Bool = false
1717 @Published var showingFileImporter : Bool = false
1818
19- // Caches to speed up metadata/icon recomputation in large directories
19+ // Cache to speed up metadata recomputation in large directories
2020 private var fileItemCache : [ URL : FileItem ] = [ : ]
21- private var iconNameCache : [ String : String ] = [ : ] // key: UTI identifier
2221
2322 private let fileManager = FileManager . default
2423
@@ -47,15 +46,15 @@ class BrowserModel: ObservableObject {
4746 currentDirectory. lastPathComponent
4847 }
4948
50- var imageCount : Int {
51- items. filter { isImageFile ( $0 ) } . count
49+ var supportedFileCount : Int {
50+ items. filter { $0 . mediaType != . unknown && !$0 . isDirectory } . count
5251 }
5352
54- func loadInitialDirectory( ) {
55- loadCurrentDirectory ( )
53+ @ MainActor func loadInitialDirectory( ) async {
54+ await loadCurrentDirectory ( )
5655 }
5756
58- func loadCurrentDirectory( ) {
57+ @ MainActor func loadCurrentDirectory( ) async {
5958 do {
6059 let contents = try fileManager. contentsOfDirectory (
6160 at: currentDirectory,
@@ -67,23 +66,18 @@ class BrowserModel: ObservableObject {
6766
6867 for url in contents {
6968 let resourceValues = try url. resourceValues ( forKeys: [ . isDirectoryKey, . fileSizeKey, . contentModificationDateKey, . isReadableKey, . contentTypeKey] )
70-
69+
7170 // GUARD: Check if the file is readable.
7271 guard resourceValues. isReadable ?? false else { continue }
73-
72+
7473 let uti = resourceValues. contentType
7574 let isDir = resourceValues. isDirectory ?? false
7675 let isAnimatedGif = uti? . conforms ( to: UTType . gif) ?? false
7776 let isVideo = uti? . conforms ( to: UTType . movie) ?? false
7877 let fileSize = resourceValues. fileSize ?? 0
7978 let modDate = resourceValues. contentModificationDate ?? Date ( )
8079
81- let iconName : String
82- if isDir {
83- iconName = " folder.fill "
84- } else {
85- iconName = self . iconName ( for: uti)
86- }
80+ // Directory check moved to FileItem initialization
8781
8882 // Reuse cached FileItem when unchanged to avoid recomputing
8983 if let cached = fileItemCache [ url] ,
@@ -93,16 +87,13 @@ class BrowserModel: ObservableObject {
9387 cached. uti == uti {
9488 fileItems. append ( cached)
9589 } else {
96- let fileItem = FileItem (
90+ let fileItem = await FileItem (
9791 url: url,
9892 name: url. lastPathComponent,
99- iconName: iconName,
10093 isDirectory: isDir,
10194 fileSize: fileSize,
10295 modificationDate: modDate,
103- uti: uti,
104- isAnimatedGif: isAnimatedGif,
105- isVideo: isVideo
96+ uti: uti
10697 )
10798 fileItems. append ( fileItem)
10899 fileItemCache [ url] = fileItem
@@ -131,7 +122,9 @@ class BrowserModel: ObservableObject {
131122 // Don't go above the user's home directory for safety
132123 if parentDirectory. path. count >= fileManager. homeDirectoryForCurrentUser. path. count {
133124 currentDirectory = parentDirectory
134- loadCurrentDirectory ( )
125+ Task {
126+ await loadCurrentDirectory ( )
127+ }
135128 }
136129 }
137130
@@ -154,58 +147,21 @@ class BrowserModel: ObservableObject {
154147 _ = try fileManager. contentsOfDirectory ( at: item. url, includingPropertiesForKeys: nil , options: [ ] )
155148 print ( " Navigating into: \( item. url. path) " )
156149 currentDirectory = item. url
157- loadCurrentDirectory ( )
150+ Task {
151+ await loadCurrentDirectory ( )
152+ }
158153 } catch {
159154 print ( " Cannot access directory \( item. url. path) : \( error) " )
160155 }
161156 }
162157
163158 func isImageFile( _ item: FileItem ) -> Bool {
164- guard !item. isDirectory else { return false }
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
159+ return item. mediaType == . staticImage
174160 }
175161
176- func openDirectory( _ item: FileItem ) {
162+ @ MainActor func openDirectory( _ item: FileItem ) async {
177163 self . currentDirectory = item. url
178- loadCurrentDirectory ( )
179- }
180-
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 " photo " }
184- let key = uti. identifier
185- if let cached = iconNameCache [ key] {
186- return cached
187- }
188-
189- let name : String
190- if uti == . livePhoto {
191- name = " livephoto "
192- } else if uti. conforms ( to: . gif) {
193- name = " rectangle.stack.badge.play "
194- } else if uti == . svg {
195- name = " square.on.square.squareshape.controlhandles "
196- } else if uti. conforms ( to: . rawImage) {
197- name = " camera.aperture "
198- } else if uti == . heic || uti == . heif {
199- name = " photo "
200- } else if uti. conforms ( to: UTType . rawImage) || uti. conforms ( to: UTType . image) {
201- name = " photo "
202- } else if uti. conforms ( to: UTType . movie) {
203- name = " film "
204- } else {
205- name = " questionmark.square.dashed "
206- }
207- iconNameCache [ key] = name
208- return name
164+ await loadCurrentDirectory ( )
209165 }
210166
211167 private func updateNavigationState( ) {
@@ -243,4 +199,3 @@ class BrowserModel: ObservableObject {
243199 return imageItems. last
244200 }
245201}
246-
0 commit comments