iOS client for Grain grain.social
ios photography atproto
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: never disk-cache fullsize images

Fullsizes were eating the 150 MB Nuke disk budget and crowding out the
thumbs that actually drive feed/grid snappiness. Sets
.disableDiskCacheWrites on every fullsize ImageRequest — ZoomableImage,
StoryViewer, and all three branches of ImagePrefetchPlanning (carousel,
feed, stories). Thumbs and avatars still disk-cache normally.

The in-memory cache is untouched, so within a session zoom/re-open still
hits memory. Cross-session re-opens of the same fullsize re-fetch from
network, which is fine — the user is looking at one specific image, not
scrolling past hundreds.

authored by

Hima Aramona and committed by
Chad Miller
e9d2b428 73270824

+15 -16
+13 -13
Grain/Utilities/ImagePrefetchPlanning.swift
··· 35 35 // Prefetch first 2 fullsize 36 36 for i in 0 ..< min(2, photos.count) { 37 37 if let url = URL(string: photos[i].fullsize) { 38 - high.append(ImageRequest(url: url, priority: .high)) 38 + high.append(ImageRequest(url: url, priority: .high, options: .disableDiskCacheWrites)) 39 39 } 40 40 } 41 41 } else if currentPage == 1 { 42 42 // Fullsize #2 at high 43 43 if photos.count > 2, let url = URL(string: photos[2].fullsize) { 44 - high.append(ImageRequest(url: url, priority: .high)) 44 + high.append(ImageRequest(url: url, priority: .high, options: .disableDiskCacheWrites)) 45 45 } 46 46 // All thumbs at normal 47 47 for photo in photos { ··· 55 55 if let url = URL(string: photos[i].fullsize) { 56 56 let priority: ImageRequest.Priority = i == currentPage + 1 ? .high : .normal 57 57 if priority == .high { 58 - high.append(ImageRequest(url: url, priority: .high)) 58 + high.append(ImageRequest(url: url, priority: .high, options: .disableDiskCacheWrites)) 59 59 } else { 60 - normal.append(ImageRequest(url: url, priority: .normal)) 60 + normal.append(ImageRequest(url: url, priority: .normal, options: .disableDiskCacheWrites)) 61 61 } 62 62 } 63 63 } ··· 98 98 if let fullsize = gallery.firstFullsize, let url = URL(string: fullsize) { 99 99 switch offset { 100 100 case 1: 101 - high.append(ImageRequest(url: url, priority: .high)) 101 + high.append(ImageRequest(url: url, priority: .high, options: .disableDiskCacheWrites)) 102 102 case 2: 103 - normal.append(ImageRequest(url: url, priority: .normal)) 103 + normal.append(ImageRequest(url: url, priority: .normal, options: .disableDiskCacheWrites)) 104 104 default: 105 - low.append(ImageRequest(url: url, priority: .low)) 105 + low.append(ImageRequest(url: url, priority: .low, options: .disableDiskCacheWrites)) 106 106 } 107 107 } 108 108 } ··· 150 150 let storyEnd = min(currentStoryIndex + 3, currentStories.count) 151 151 for i in stride(from: currentStoryIndex + 1, to: storyEnd, by: 1) where i < currentStories.count { 152 152 if let url = URL(string: currentStories[i].fullsize) { 153 - high.append(ImageRequest(url: url, priority: .high)) 153 + high.append(ImageRequest(url: url, priority: .high, options: .disableDiskCacheWrites)) 154 154 } 155 155 } 156 156 157 157 // 2. First story of next author — high 158 158 if let first = nextAuthorStories?.first, let url = URL(string: first.fullsize) { 159 - high.append(ImageRequest(url: url, priority: .high)) 159 + high.append(ImageRequest(url: url, priority: .high, options: .disableDiskCacheWrites)) 160 160 } 161 161 162 162 // 3. Rest of current author's stack — normal ··· 164 164 if restStart < currentStories.count { 165 165 for i in restStart ..< currentStories.count { 166 166 if let url = URL(string: currentStories[i].fullsize) { 167 - normal.append(ImageRequest(url: url, priority: .normal)) 167 + normal.append(ImageRequest(url: url, priority: .normal, options: .disableDiskCacheWrites)) 168 168 } 169 169 } 170 170 } ··· 173 173 if let stories = secondNextAuthorStories { 174 174 for i in 0 ..< min(2, stories.count) { 175 175 if let url = URL(string: stories[i].fullsize) { 176 - normal.append(ImageRequest(url: url, priority: .normal)) 176 + normal.append(ImageRequest(url: url, priority: .normal, options: .disableDiskCacheWrites)) 177 177 } 178 178 } 179 179 } 180 180 181 181 // 5. First story of next 2 authors beyond — low 182 182 if let story = thirdNextFirstStory, let url = URL(string: story.fullsize) { 183 - low.append(ImageRequest(url: url, priority: .low)) 183 + low.append(ImageRequest(url: url, priority: .low, options: .disableDiskCacheWrites)) 184 184 } 185 185 if let story = fourthNextFirstStory, let url = URL(string: story.fullsize) { 186 - low.append(ImageRequest(url: url, priority: .low)) 186 + low.append(ImageRequest(url: url, priority: .low, options: .disableDiskCacheWrites)) 187 187 } 188 188 189 189 return PrioritizedRequests(high: high, normal: normal, low: low)
+1 -2
Grain/Views/Components/ZoomableImage.swift
··· 294 294 private var sourceView: some View { 295 295 switch source { 296 296 case let .url(url, thumbURL): 297 - LazyImage(request: ImageRequest(url: URL(string: url), priority: .veryHigh)) { state in 297 + LazyImage(request: ImageRequest(url: URL(string: url), priority: .veryHigh, options: .disableDiskCacheWrites)) { state in 298 298 if let image = state.image { 299 299 image 300 300 .resizable() ··· 305 305 thumb 306 306 .resizable() 307 307 .aspectRatio(aspectRatio, contentMode: .fit) 308 - .blur(radius: 20) 309 308 .clipped() 310 309 } else { 311 310 Rectangle()
+1 -1
Grain/Views/Stories/StoryViewer.swift
··· 417 417 LazyImage(request: { 418 418 guard lr.action != .hide || labelRevealed, 419 419 let url = URL(string: story.fullsize) else { return ImageRequest(url: nil) } 420 - return ImageRequest(url: url, priority: .veryHigh) 420 + return ImageRequest(url: url, priority: .veryHigh, options: .disableDiskCacheWrites) 421 421 }()) { state in 422 422 if let image = state.image { 423 423 image