Skip to content

Conversation

@HarrisHan
Copy link

Summary

This PR addresses the main thread blocking issue (#2323) by adding asynchronous disk cache existence checking. The current implementation blocks the main thread when checking if an image exists in disk cache, causing frame drops in scrolling lists.

Problem

As reported in #2323:

  • KingfisherManager.retrieveImage() performs disk cache checks on the calling thread
  • When called from the main thread (common in UI code), this causes synchronous disk I/O
  • Results in frame drops and scrolling performance issues, especially in SwiftUI

Solution

New APIs

  1. DiskStorage.itemExists() - Lightweight existence check without loading data
  
 func itemExists(forKey key: String, referenceDate: Date = Date(), forcedExtension: String? = nil) -> Bool


 2. ImageCache.cachedType() - Async cache type checking
 public func cachedType(
     forKey key: String,
     processorIdentifier: String = DefaultImageProcessor.default.identifier,
     forcedExtension: String? = nil,
     callbackQueue: CallbackQueue = .mainCurrentOrAsync,
     completionHandler: @escaping @Sendable (CacheType) -> Void)

 Implementation Details

 - Memory cache check remains synchronous (fast, no I/O)
 - Disk cache check is dispatched to background queue
 - Results are delivered on specified callback queue
 - Maintains full backward compatibility

 Usage Example

 // Before (blocks main thread)
 let image = cache.retrieveImageInDiskCache(forKey: key)

 // After (non-blocking)
 cache.cachedType(forKey: key) { cacheType in
     switch cacheType {
     case .memory:
         // Image in memory
     case .disk:
         // Image on disk, load if needed
     case .none:
         // Not cached, download required
     }
 }

Testing

Added comprehensive tests in ImageCacheAsyncTests.swift:

  • ✅ Correct cache type detection (memory/disk/none)
  • ✅ Main thread non-blocking verification
  • ✅ Performance testing with 1000+ items
  • ✅ Thread safety validation

Performance Impact

  • No impact on existing synchronous APIs
  • Eliminates main thread blocking for existence checks
  • Optimized disk check without loading full image data
  • Maintains existing cache performance characteristics

Migration Guide

This is a non-breaking change. Existing code continues to work. For better performance:

  // Instead of checking synchronously
  if let _ = imageCache.retrieveImageInDiskCache(forKey: key) {
      // Image exists
  }

  // Use async check
  imageCache.cachedType(forKey: key) { type in
      if type != .none {
          // Image exists
      }
  }

Checklist

  • Code compiles without warnings
  • All existing tests pass
  • Added new test coverage
  • API follows Kingfisher conventions
  • Thread-safe implementation
  • Backward compatible

- Add itemExists() method to DiskStorage for quick existence checks without loading data
- Add cachedType() method to ImageCache for async cache type checking
- Performs disk I/O on background queue to prevent main thread blocking
- Maintains backward compatibility with existing retrieve methods
- Add comprehensive unit tests to verify non-blocking behavior

This addresses issue onevcat#2323 where disk cache checks were blocking the main thread
and causing frame drops in scrolling lists, especially in SwiftUI apps.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant