## File: README.md

Kingfisher is a powerful, pure-Swift library for downloading and caching images from the web. It provides you a chance to use a pure-Swift way to work with remote images in your next app.

## Features

- [x] Asynchronous image downloading and caching.
- [x] Loading image from either `URLSession`-based networking or local provided data.
- [x] Useful image processors and filters provided.
- [x] Multiple-layer hybrid cache for both memory and disk.
- [x] Fine control on cache behavior. Customizable expiration date and size limit.
- [x] Opt-in async cache probing in `KingfisherManager` to avoid blocking the caller thread on disk cache checks.
- [x] Cancelable downloading and auto-reusing previous downloaded content to improve performance.
- [x] Independent components. Use the downloader, caching system, and image processors separately as you need.
- [x] Prefetching images and showing them from the cache to boost your app.
- [x] Extensions for `UIImageView`, `NSImageView`, `NSButton`, `UIButton`, `NSTextAttachment`, `WKInterfaceImage`, `TVMonogramView` and `CPListItem` to directly set an image from a URL.
- [x] Built-in transition animation when setting images.
- [x] Customizable placeholder and indicator while loading images.
- [x] Extensible image processing and image format easily.
- [x] Low Data Mode support.
- [x] SwiftUI support.
- [x] Swift 6 & Swift Concurrency (strict mode) prepared.
- [x] Load & cache for Live Photo.

### Kingfisher 101

The simplest use-case is setting an image to an image view with the `UIImageView` extension:

```swift
import Kingfisher

let url = URL(string: "https://example.com/image.png")
imageView.kf.setImage(with: url)
```

Kingfisher will download the image from `url`, send it to both memory cache and disk cache, and display it in `imageView`. 
When you set it with the same URL later, the image will be retrieved from the cache and shown immediately.

It also works if you use SwiftUI:

```swift
var body: some View {
    KFImage(URL(string: "https://example.com/image.png")!)
}
```

### A More Advanced Example

With the powerful options, you can do hard tasks with Kingfisher in a simple way. For example, the code below: 

1. Downloads a high-resolution image.
2. Downsamples it to match the image view size.
3. Makes it round cornered with a given radius.
4. Shows a system indicator and a placeholder image while downloading.
5. When prepared, it animates the small thumbnail image with a "fade in" effect. 
6. The original large image is also cached to disk for later use, to get rid of downloading it again in a detail view.
7. A console log is printed when the task finishes, either for success or failure.

```swift
let url = URL(string: "https://example.com/high_resolution_image.png")
let processor = DownsamplingImageProcessor(size: imageView.bounds.size)
             |> RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.indicatorType = .activity
imageView.kf.setImage(
    with: url,
    placeholder: UIImage(named: "placeholderImage"),
    options: [
        .processor(processor),
        .scaleFactor(UIScreen.main.scale),
        .transition(.fade(1)),
        .cacheOriginalImage
    ])
{
    result in
    switch result {
    case .success(let value):
        print("Task done for: \(value.source.url?.absoluteString ?? "")")
    case .failure(let error):
        print("Job failed: \(error.localizedDescription)")
    }
}
```

It is a common situation I can meet in my daily work. Think about how many lines you need to write without
Kingfisher!

### Method Chaining

If you are not a fan of the `kf` extension, you can also prefer to use the `KF` builder and chained the method 
invocations. The code below is doing the same thing:

```swift
// Use `kf` extension
imageView.kf.setImage(
    with: url,
    placeholder: placeholderImage,
    options: [
        .processor(processor),
        .loadDiskFileSynchronously,
        .cacheOriginalImage,
        .transition(.fade(0.25)),
        .lowDataMode(.network(lowResolutionURL))
    ],
    progressBlock: { receivedSize, totalSize in
        // Progress updated
    },
    completionHandler: { result in
        // Done
    }
)

// Use `KF` builder
KF.url(url)
  .placeholder(placeholderImage)
  .setProcessor(processor)
  .loadDiskFileSynchronously()
  .cacheMemoryOnly()
  .fade(duration: 0.25)
  .lowDataModeSource(.network(lowResolutionURL))
  .onProgress { receivedSize, totalSize in  }
  .onSuccess { result in  }
  .onFailure { error in }
  .set(to: imageView)
```

And even better, if later you want to switch to SwiftUI, just change the `KF` above to `KFImage`, and you've done:

```swift
struct ContentView: View {
    var body: some View {
        KFImage.url(url)
          .placeholder(placeholderImage)
          .setProcessor(processor)
          .loadDiskFileSynchronously()
          .cacheMemoryOnly()
          .fade(duration: 0.25)
          .lowDataModeSource(.network(lowResolutionURL))
          .onProgress { receivedSize, totalSize in  }
          .onSuccess { result in  }
          .onFailure { error in }
    }
}
```

## Requirements

### Kingfisher 8.0

- (UIKit/AppKit) iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ / visionOS 1.0+
- (SwiftUI) iOS 14.0+ / macOS 11.0+ / tvOS 14.0+ / watchOS 7.0+ / visionOS 1.0+
- Swift 5.9+

### Kingfisher 7.0

- (UIKit/AppKit) iOS 12.0+ / macOS 10.14+ / tvOS 12.0+ / watchOS 5.0+ / visionOS 1.0+
- (SwiftUI) iOS 14.0+ / macOS 11.0+ / tvOS 14.0+ / watchOS 7.0+ / visionOS 1.0+
- Swift 5.0+

### Installation

Refer to one of the following tutorials to install and use the framework:

- [UIKit Tutorial](https://swiftpackageindex.com/onevcat/kingfisher/master/tutorials/kingfisher/gettingstarteduikit)
- [SwiftUI Tutorial](https://swiftpackageindex.com/onevcat/kingfisher/master/tutorials/kingfisher/gettingstartedswiftui)

Alternatively, you can follow either of the methods below.

#### Swift Package Manager

- File > Swift Packages > Add Package Dependency
- Add `https://github.com/onevcat/Kingfisher.git`
- Select "Up to Next Major" with "8.0.0"

#### CocoaPods

```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '13.0'
use_frameworks!

target 'MyApp' do
  pod 'Kingfisher', '~> 8.0'
end
```

#### Pre-built Framework

1. Open the release page, download the latest version of Kingfisher from the assets section. 
2. Drag the `Kingfisher.xcframework` into your project and add it to the target (usually the app target).
3. Select your target, in the "General" Tab, find the "Frameworks, Libraries, and Embedded Content" section, set the `Embed Without Signing` to Kingfisher.

## Documentation

Check the documentation and tutorials:

- [Documentation Home](https://swiftpackageindex.com/onevcat/kingfisher/master/documentation/kingfisher)
- [Getting Started](https://swiftpackageindex.com/onevcat/kingfisher/master/documentation/kingfisher/gettingstarted)
    - [UIKit Tutorial](https://swiftpackageindex.com/onevcat/kingfisher/master/tutorials/kingfisher/gettingstarteduikit)
    - [SwiftUI Tutorial](https://swiftpackageindex.com/onevcat/kingfisher/master/tutorials/kingfisher/gettingstartedswiftui)
- [Common Tasks - General](https://swiftpackageindex.com/onevcat/kingfisher/master/documentation/kingfisher/commontasks)
    - [Common Tasks - Cache](https://swiftpackageindex.com/onevcat/kingfisher/master/documentation/kingfisher/commontasks_cache)
    - [Common Tasks - Downloader](https://swiftpackageindex.com/onevcat/kingfisher/master/documentation/kingfisher/commontasks_downloader)
    - [Common tasks - Processor](https://swiftpackageindex.com/onevcat/kingfisher/master/documentation/kingfisher/commontasks_processor)

### Migrating

- [Kingfisher 8.0 Migration](https://swiftpackageindex.com/onevcat/kingfisher/master/documentation/kingfisher/migration-to-8)
- [Kingfisher 7.0 Migration](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-7.0-Migration-Guide)

If you are using an even earlier version, see the guides below to know the steps for migrating.

## Other

### Future of Kingfisher

I want to keep Kingfisher lightweight. This framework focuses on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better.

### Developments and Tests

Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. It would be appreciated if your pull requests could build with all tests green. :)

### About the logo

The logo of Kingfisher is inspired by [Tangram (七巧板)](https://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions?

### Contact

Follow and contact me on [Twitter](https://twitter.com/onevcat) or [Sina Weibo](https://weibo.com/onevcat). If you find an issue, [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well.

## Backers & Sponsors

Open-source projects cannot live long without your help. If you find Kingfisher to be useful, please consider supporting this 
project by becoming a sponsor. Your user icon or company logo shows up [on my blog](https://onevcat.com/tabs/about/) with a link to your home page. 

Become a sponsor through [GitHub Sponsors](https://github.com/sponsors/onevcat). :heart:

Special thanks to:

[](https://img.ly/)

[](https://www.emergetools.com)



### License

Kingfisher is released under the MIT license. See LICENSE for details.

---

## File: docs/architecture.md

# Kingfisher Architecture Documentation

## High-Level System Organization

Kingfisher is a sophisticated image loading and caching library for Apple platforms, designed with a modular architecture that promotes separation of concerns and extensibility. At its core, the library employs a coordinator pattern where `KingfisherManager` serves as the central orchestrator, managing the flow between network operations, caching layers, and image processing pipelines. The architecture leverages protocol-oriented design principles, with all functionality exposed through a `.kf` namespace wrapper that provides a clean, chainable API surface.

The system is built on three fundamental pillars: downloading, caching, and processing. The `ImageDownloader` handles all network operations with support for authentication, retries, and progressive loading. The `ImageCache` implements a dual-layer caching strategy combining memory and disk storage for optimal performance. The `ImageProcessor` protocol enables a flexible transformation pipeline where multiple processors can be chained together. These components work in concert through a sophisticated options system (`KingfisherOptionsInfo`) that allows fine-grained control over every aspect of the image loading process.

Cross-platform compatibility is achieved through extensive use of conditional compilation and type aliases, allowing the same codebase to support iOS, macOS, tvOS, watchOS, and visionOS. The library provides both UIKit/AppKit extensions and dedicated SwiftUI components (`KFImage`, `KFAnimatedImage`), ensuring seamless integration regardless of the UI framework being used.

## Component Map

### Core Components

| Component | Location | Purpose |
|-----------|----------|---------|
| **KingfisherManager** | `Sources/General/KingfisherManager.swift` | Central coordinator managing image retrieval, caching, and processing workflows |
| **ImageDownloader** | `Sources/Networking/ImageDownloader.swift` | Handles all network operations for downloading images |
| **ImageCache** | `Sources/Cache/ImageCache.swift` | Dual-layer caching system with memory and disk storage |
| **ImageProcessor** | `Sources/Image/ImageProcessor.swift` | Protocol and implementations for image transformation pipeline |
| **KF** | `Sources/General/KF.swift` | Builder pattern entry point for fluent API |
| **Source** | `Sources/General/ImageSource/Source.swift` | Represents image data sources (network/provider) |
| **Resource** | `Sources/General/ImageSource/Resource.swift` | Protocol for cacheable resources with key/URL |

### Storage Layer

| Component | Location | Purpose |
|-----------|----------|---------|
| **MemoryStorage** | `Sources/Cache/MemoryStorage.swift` | In-memory cache implementation with LRU eviction |
| **DiskStorage** | `Sources/Cache/DiskStorage.swift` | File-based cache with expiration and size limits |
| **CacheSerializer** | `Sources/Cache/CacheSerializer.swift` | Handles image data serialization for cache storage |

### Networking Layer

| Component | Location | Purpose |
|-----------|----------|---------|
| **SessionDelegate** | `Sources/Networking/SessionDelegate.swift` | URLSession delegate for download management |
| **SessionDataTask** | `Sources/Networking/SessionDataTask.swift` | Wrapper for URLSessionDataTask with cancellation |
| **ImagePrefetcher** | `Sources/Networking/ImagePrefetcher.swift` | Preloads images for improved performance |
| **RequestModifier** | `Sources/Networking/RequestModifier.swift` | Protocol for modifying URL requests |
| **RetryStrategy** | `Sources/Networking/RetryStrategy.swift` | Configurable retry logic for failed downloads |

### UI Integration

| Component | Location | Purpose |
|-----------|----------|---------|
| **ImageView+Kingfisher** | `Sources/Extensions/ImageView+Kingfisher.swift` | UIImageView/NSImageView extensions |
| **KFImage** | `Sources/SwiftUI/KFImage.swift` | SwiftUI image component |
| **KFAnimatedImage** | `Sources/SwiftUI/KFAnimatedImage.swift` | SwiftUI animated image support |
| **AnimatedImageView** | `Sources/Views/AnimatedImageView.swift` | GIF animation support view |

## Key Files

### KingfisherManager.swift (Lines 107-420)
The heart of the library, containing:
- `shared` singleton instance (line 113)
- `retrieveImage()` main entry point (lines 196-210, 233-248)
- Cache lookup logic (lines 400-403)
- Download coordination (lines 415-418)
- Retry and alternative source handling (lines 306-385)

### ImageDownloader.swift (Lines 35-150)
Network layer implementation:
- `ImageLoadingResult` struct for download results (lines 36-58)
- `DownloadTask` class for cancellable downloads (lines 65-102)
- URLSession management and request handling

### ImageCache.swift (Lines 52-200)
Caching infrastructure:
- `CacheType` enum defining cache levels (lines 52-72)
- Memory and disk cache coordination
- Cache key generation and expiration logic

### KF.swift (Lines 50-100)
Builder pattern implementation:
- Static factory methods for creating builders (lines 56-99)
- Fluent API entry points for different source types

### ImageProcessor.swift (Lines 37-100)
Processing pipeline:
- `ImageProcessItem` enum for input types (lines 37-46)
- `ImageProcessor` protocol definition (lines 49-76)
- Processor chaining via `append()` (lines 85-95)

### KingfisherOptionsInfo.swift (Lines 43-250)
Configuration system:
- Option items enumeration with associated values
- Cache, downloader, and processor configuration
- Transition and placeholder settings

## Data Flow

### 1. Image Request Initiation

```
UIImageView.kf.setImage(with: url)
    │
    └─> ImageView+Kingfisher.swift (line 77-87)
        │
        └─> KingfisherManager.retrieveImage()
            Sources/General/KingfisherManager.swift (line 196)
```

### 2. Cache Lookup

```
KingfisherManager.retrieveImage()
    │
    └─> retrieveImageFromCache() (line 400)
        │
        ├─> ImageCache.retrieveImage()
        │   Sources/Cache/ImageCache.swift
        │   │
        │   ├─> MemoryStorage.value(forKey:)
        │   │   Sources/Cache/MemoryStorage.swift
        │   │
        │   └─> DiskStorage.value(forKey:)
        │       Sources/Cache/DiskStorage.swift
        │
        └─> [Cache Hit] → completionHandler(.success)
            [Cache Miss] → Continue to download
```

### 3. Network Download

```
KingfisherManager.loadAndCacheImage() (line 415)
    │
    └─> ImageDownloader.downloadImage()
        Sources/Networking/ImageDownloader.swift
        │
        ├─> SessionDelegate.downloadTask()
        │   Sources/Networking/SessionDelegate.swift
        │
        └─> URLSession.dataTask()
            │
            └─> CompletionHandler with ImageLoadingResult
```

### 4. Image Processing

```
Downloaded Data
    │
    └─> ImageProcessor.process() (line 434)
        Sources/Image/ImageProcessor.swift
        │
        ├─> DefaultImageProcessor (if none specified)
        │
        └─> Custom processors chain
            │
            └─> Processed KFCrossPlatformImage
```

### 5. Cache Storage

```
Processed Image
    │
    └─> KingfisherManager.cacheImage() (line 459)
        │
        ├─> ImageCache.store() (line 482)
        │   │
        │   ├─> MemoryStorage.store()
        │   │   In-memory cache with cost calculation
        │   │
        │   └─> DiskStorage.store()
        │       File system with expiration
        │
        └─> completionHandler(.success(RetrieveImageResult))
            │
            └─> UI Update on main queue
```

### 6. Error Handling and Retry

```
Download/Processing Error
    │
    └─> RetryStrategy.retry() (line 362)
        Sources/Networking/RetryStrategy.swift
        │
        ├─> [Retry] → startNewRetrieveTask() (line 306)
        │
        └─> [No Retry] → Check alternative sources (line 334)
            │
            ├─> [Alternative exists] → Start new task
            │
            └─> [No alternatives] → completionHandler(.failure)
```

This architecture enables Kingfisher to efficiently handle image loading with features like progressive downloading, multiple cache layers, flexible processing pipelines, and robust error handling, all while maintaining a clean and intuitive API surface for developers.

---

## File: docs/build-system.md

# Kingfisher Build System Documentation

## Overview

Kingfisher uses a dual build system approach supporting both Swift Package Manager and Fastlane/CocoaPods for maximum flexibility and distribution options.

### Primary Build Tools

- **Swift Package Manager** (`Package.swift`) - Modern dependency management and building
- **Fastlane** (`fastlane/Fastfile`) - Automated testing, building, and release workflows
- **CocoaPods** (`Kingfisher.podspec`) - Legacy distribution and integration
- **GitHub Actions** (`.github/workflows/`) - Continuous integration and testing

### Key Configuration Files

```
.
├── Package.swift                    # Swift Package Manager configuration
├── Kingfisher.podspec              # CocoaPods specification
├── Gemfile                         # Ruby dependencies for Fastlane/CocoaPods
├── fastlane/
│   ├── Fastfile                    # Fastlane automation workflows
│   └── actions/                    # Custom Fastlane actions
└── .github/workflows/
    ├── build.yaml                  # CI build workflow
    └── test.yaml                   # CI test workflow
```

## Build Workflows

### Building with Swift Package Manager

```bash
# Build for default platform
swift build

# Build with specific Swift version
swift build -Xswiftc -swift-version -Xswiftc 5

# Build for release
swift build -c release

# Build and run tests
swift test

# Generate Xcode project (if needed)
swift package generate-xcodeproj
```

### Building with Fastlane

First, install dependencies:
```bash
# Install Ruby dependencies
bundle install
```

Common build commands:
```bash
# Run all platform tests (iOS, macOS, tvOS, watchOS)
bundle exec fastlane tests

# Build for CI with specific destination
DESTINATION="platform=iOS Simulator,name=iPhone 15,OS=17.5" bundle exec fastlane build_ci

# Test for CI with specific destination  
DESTINATION="platform=macOS" bundle exec fastlane test_ci

# Build specific platform
bundle exec fastlane build destination:"platform=iOS Simulator,name=iPhone 15"

# Lint both CocoaPods and SPM
bundle exec fastlane lint
```

### Release Process

The release workflow automates versioning, tagging, and distribution:

```bash
# Full release (tests, lint, version bump, GitHub release, CocoaPods push)
bundle exec fastlane release version:X.X.X

# Skip tests during release
bundle exec fastlane release version:X.X.X skip_tests:true

# Create XCFramework for distribution
bundle exec fastlane xcframework version:X.X.X
```

Release steps performed:
1. Ensures clean git state and correct branch
2. Runs all tests (unless skipped)
3. Lints CocoaPods spec and SPM package
4. Updates version in all configuration files
5. Extracts and updates changelog
6. Creates signed git tag
7. Builds XCFramework for all platforms
8. Creates GitHub release with assets
9. Pushes to CocoaPods trunk

## Platform-specific Setup

### Supported Platforms

From `Package.swift` and `Kingfisher.podspec`:
- **iOS**: 13.0+
- **macOS**: 10.15+
- **tvOS**: 13.0+
- **watchOS**: 6.0+
- **visionOS**: 1.0+

### CI Test Matrix

From `.github/workflows/test.yaml`:
- **Destinations**: macOS, iOS Simulator, tvOS Simulator, watchOS Simulator
- **Xcode Versions**: 15.4, 16.2

### Platform Build Commands

```bash
# macOS
bundle exec fastlane test destination:"platform=macOS"

# iOS Simulator
bundle exec fastlane test destination:"platform=iOS Simulator,name=iPhone 15,OS=17.5"

# tvOS Simulator  
bundle exec fastlane test destination:"platform=tvOS Simulator,name=Apple TV,OS=17.5"

# watchOS Simulator (build only, no test)
bundle exec fastlane build destination:"platform=watchOS Simulator,name=Apple Watch Series 9 (41mm),OS=10.5"
```

## Reference

### Build Targets

From `Package.swift`:
- **Library**: `Kingfisher` (single library product)
- **Target**: `Kingfisher` (sources in `Sources/` directory)

### Fastlane Lanes

Available lanes in `fastlane/Fastfile`:

| Lane | Description | Parameters |
|------|-------------|------------|
| `tests` | Run tests on all platforms | None |
| `test` | Run tests on specific platform | `destination` |
| `build` | Build for specific platform | `destination` |
| `test_ci` | CI test lane (builds watchOS) | Uses `ENV["DESTINATION"]` |
| `build_ci` | CI build lane | Uses `ENV["DESTINATION"]` |
| `lint` | Lint CocoaPods spec and SPM | None |
| `release` | Full release workflow | `version`, `skip_tests` (optional) |
| `xcframework` | Build XCFramework | `version`, `swift_version`, `xcode_version` |

### Environment Variables

| Variable | Description | Used By |
|----------|-------------|---------|
| `DESTINATION` | Build/test destination | CI workflows |
| `XCODE_VERSION` | Xcode version to use | CI workflows, Fastlane |
| `GITHUB_TOKEN` | GitHub API token | Release workflow |

### Custom Fastlane Actions

Located in `fastlane/actions/`:
- `extract_current_change_log.rb` - Extract changelog for version
- `git_commit_all.rb` - Commit all changes
- `sync_build_number_to_git.rb` - Sync build number with git
- `update_change_log.rb` - Update changelog file

### Troubleshooting

#### Common Issues

1. **Bundle install fails**
   ```bash
   # Update bundler
   gem install bundler
   
   # Install with specific bundler version
   bundle _2.x.x_ install
   ```

2. **Xcode version mismatch**
   ```bash
   # Set Xcode version explicitly
   XCODE_VERSION=16.2 bundle exec fastlane tests
   
   # Or use xcode-select
   sudo xcode-select -s /Applications/Xcode_16.2.app
   ```

3. **Simulator not found**
   ```bash
   # List available simulators
   xcrun simctl list devices
   
   # Update destination string accordingly
   ```

4. **CocoaPods push fails**
   ```bash
   # Verify pod spec locally first
   pod lib lint Kingfisher.podspec
   
   # Register session if needed
   pod trunk register email@example.com
   ```

5. **Swift version issues**
   ```bash
   # Override Swift version in build
   bundle exec fastlane build xcargs:"SWIFT_VERSION=5.9"
   ```

### Build Settings

Key build settings used:
- `BUILD_LIBRARY_FOR_DISTRIBUTION`: YES (for XCFramework)
- `SKIP_INSTALL`: NO (for archiving)
- `SWIFT_VERSION`: 5.0 (default, can be overridden)

### Distribution Artifacts

Release builds generate:
- `Kingfisher-{version}.xcframework.zip` - All platforms XCFramework
- `Kingfisher-iOS-{version}.xcframework.zip` - iOS-only XCFramework

Both are code-signed with Apple Distribution certificate and uploaded to GitHub releases.

---

## File: docs/deployment.md

# Deployment Guide



This document provides comprehensive guidance for deploying Kingfisher across different platforms and distribution channels.

## Overview

Kingfisher supports multiple deployment strategies:

- **CocoaPods**: Traditional CocoaPods spec deployment
- **Swift Package Manager**: Native Swift package distribution
- **XCFramework**: Pre-built universal frameworks
- **GitHub Releases**: Automated release management

The deployment process is fully automated using Fastlane with comprehensive platform coverage across iOS, macOS, tvOS, watchOS, and visionOS.

## Package Types

### CocoaPods Distribution

**Configuration File**: `/Users/onevcat/Sync/github/Kingfisher/Kingfisher.podspec`

**Build Targets**:
- iOS 13.0+
- macOS 10.15+
- tvOS 13.0+
- watchOS 6.0+
- visionOS 1.0+

**Key Features**:
- Module stability enabled (`BUILD_LIBRARY_FOR_DISTRIBUTION`)
- Privacy manifest included (`PrivacyInfo.xcprivacy`)
- Weak framework dependencies (SwiftUI, Combine)
- Required frameworks (CFNetwork, Accelerate)

### Swift Package Manager

**Configuration File**: `/Users/onevcat/Sync/github/Kingfisher/Package.swift`

**Build Targets**:
- Single library target: `Kingfisher`
- Source path: `Sources/`
- Minimum Swift tools version: 5.1

**Platform Support**:
- iOS 13.0+
- macOS 10.15+
- tvOS 13.0+
- watchOS 6.0+

### XCFramework Distribution

**Output Locations**:
```
build/
├── Kingfisher-{version}.xcframework.zip          # All platforms
├── Kingfisher-iOS-{version}.xcframework.zip      # iOS only
├── Kingfisher-{version}/
│   └── Kingfisher.xcframework/
│       ├── ios-arm64/
│       ├── ios-arm64_x86_64-simulator/
│       ├── macos-arm64_x86_64/
│       ├── tvos-arm64/
│       ├── tvos-arm64_x86_64-simulator/
│       ├── watchos-arm64_arm64_32_armv7k/
│       ├── watchos-arm64_i386_x86_64-simulator/
│       ├── xros-arm64/
│       └── xros-arm64_x86_64-simulator/
```

**Platform-Specific Archives**:
```
build/
├── Kingfisher-iphoneos.xcarchive/
├── Kingfisher-iphonesimulator.xcarchive/
├── Kingfisher-macosx.xcarchive/
├── Kingfisher-appletvos.xcarchive/
├── Kingfisher-appletvsimulator.xcarchive/
├── Kingfisher-watchos.xcarchive/
├── Kingfisher-watchsimulator.xcarchive/
├── Kingfisher-xros.xcarchive/
└── Kingfisher-xrsimulator.xcarchive/
```

## Platform-Specific Deployment

### iOS Deployment
- **Device**: `iphoneos` SDK
- **Simulator**: `iphonesimulator` SDK
- **Architectures**: arm64, x86_64 (simulator)
- **Framework Path**: `build/Kingfisher-iOS-{version}.xcframework.zip`

### macOS Deployment
- **SDK**: `macosx`
- **Architectures**: arm64, x86_64 (universal)
- **Framework Structure**: Traditional bundle format with versioning

### tvOS Deployment
- **Device**: `appletvos` SDK
- **Simulator**: `appletvsimulator` SDK
- **Architectures**: arm64, x86_64 (simulator)

### watchOS Deployment
- **Device**: `watchos` SDK
- **Simulator**: `watchsimulator` SDK
- **Architectures**: arm64, arm64_32, armv7k (device), i386, x86_64, arm64 (simulator)

### visionOS Deployment
- **Device**: `xros` SDK
- **Simulator**: `xrsimulator` SDK
- **Architectures**: arm64, x86_64 (simulator)

## Deployment Commands

### Complete Release Process

```bash
# Full release workflow
bundle exec fastlane release version:X.X.X

# Skip tests during release (not recommended)
bundle exec fastlane release version:X.X.X skip_tests:true
```

### Individual Components

```bash
# Create XCFramework only
bundle exec fastlane xcframework version:X.X.X

# Run linting
bundle exec fastlane lint

# Build for specific platform
bundle exec fastlane build_ci
```

### CocoaPods Deployment

```bash
# Lint podspec
pod lib lint Kingfisher.podspec

# Push to CocoaPods trunk (automated in release)
pod trunk push Kingfisher.podspec
```

### Swift Package Manager

```bash
# Build with SPM
swift build

# Test with SPM
swift test

# Validate package
swift package resolve
```

## Continuous Integration

### GitHub Actions Workflows

**Build Workflow**: `.github/workflows/build.yaml`
- Builds across multiple Xcode versions (15.2, 15.3, 16.0, 16.1)
- Tests all platforms in matrix configuration
- Uses self-hosted runners

**Test Workflow**: `.github/workflows/test.yaml`
- Runs tests on Xcode 15.4 and 16.2
- Covers all platform destinations
- Concurrent execution with cancellation

**Matrix Configuration**:
```yaml
destination: [
  'macOS',
  'iOS Simulator,name=iPhone 15,OS=17.5',
  'tvOS Simulator,name=Apple TV,OS=17.5',
  'watchOS Simulator,name=Apple Watch Series 9 (41mm),OS=10.5'
]
```

## Release Management

### Automated Release Process

The release process handles:

1. **Pre-release Validation**:
   - Git branch verification
   - Clean git status check
   - Comprehensive testing across platforms
   - Podspec and SPM linting

2. **Version Management**:
   - Build number synchronization
   - Version number increment
   - Podspec version update
   - Changelog extraction

3. **Build Artifacts**:
   - XCFramework creation for all platforms
   - Code signing with Apple Distribution certificate
   - ZIP archive creation

4. **Distribution**:
   - Git tag creation with signing
   - GitHub release creation
   - Asset upload (both full and iOS-only XCFrameworks)
   - CocoaPods trunk push

### Changelog Management

**Configuration**: `/Users/onevcat/Sync/github/Kingfisher/pre-change.yml`

**Structure**:
```yaml
version: X.X.X
name: Release Name
fix:
  - Bug fix descriptions with issue links
add:
  - New feature descriptions
```

### Version Tagging

- **Format**: Semantic versioning (X.X.X)
- **Signing**: GPG signed tags
- **Automation**: Integrated with release workflow

## Reference

### Deployment Scripts

| Script Location | Purpose |
|----------------|---------|
| `fastlane/Fastfile` | Main deployment automation |
| `.github/workflows/build.yaml` | CI build workflow |
| `.github/workflows/test.yaml` | CI test workflow |
| `Gemfile` | Ruby dependencies |

### Build Output Locations

| Package Type | Location |
|-------------|----------|
| CocoaPods | Published to CocoaPods trunk |
| Swift Package Manager | Git repository tags |
| XCFramework (All) | `build/Kingfisher-{version}.xcframework.zip` |
| XCFramework (iOS) | `build/Kingfisher-iOS-{version}.xcframework.zip` |
| GitHub Release Assets | Attached to release tags |

### Environment Variables

| Variable | Purpose | Required |
|----------|---------|----------|
| `GITHUB_TOKEN` | GitHub API authentication | Yes (release) |
| `DESTINATION` | CI build destination | Yes (CI) |
| `XCODE_VERSION` | Xcode version selection | Yes (CI) |

### Code Signing

- **Certificate**: Apple Distribution: Wei Wang (A4YJ9MRZ66)
- **Timestamp**: Enabled for all XCFramework builds
- **Verification**: Automated signature verification

### Server Configurations

**GitHub Actions**:
- **Runner Type**: Self-hosted
- **Concurrency**: Group-based with cancellation
- **Shell**: `bash -leo pipefail {0}`

**Ruby Environment**:
- **Fastlane**: Latest version
- **CocoaPods**: Latest version  
- **xcodes**: For Xcode version management

This deployment system ensures reliable, automated distribution across all supported Apple platforms with comprehensive testing and validation at each step.

---

## File: docs/development.md

# Development Guide



## Overview

Kingfisher follows a modular architecture designed for maintainability, testability, and cross-platform compatibility. The development environment emphasizes protocol-oriented programming, namespace safety, and comprehensive test coverage. The codebase is organized into distinct functional modules with clear separation of concerns, following Swift's modern concurrency patterns and maintaining compatibility across iOS, macOS, tvOS, watchOS, and visionOS platforms.

The codebase implements several sophisticated design patterns including the namespace wrapper pattern for API safety, builder patterns for fluent configuration, and options patterns for flexible customization. All components are designed to be thread-safe with explicit concurrency annotations where needed. Development follows strict code style guidelines with comprehensive documentation, ensuring consistency across the large codebase.

Testing is integral to the development process, with extensive unit tests covering all major components, network mocking for reliable testing, and cross-platform validation. The build system uses Fastlane for automation, supporting multiple deployment targets and maintaining high quality standards through automated linting and testing workflows.

## Code Style Conventions

### File Headers and Documentation

All source files must include the standard license header:

```swift
//
//  FileName.swift
//  Kingfisher
//
//  Created by [Author] on [Date].
//
//  Copyright (c) 2019 Wei Wang <onevcat@gmail.com>
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software")...
```

### Naming Conventions

- **Files**: Use PascalCase with descriptive names (`ImageProcessor.swift`, `KingfisherManager.swift`)
- **Types**: PascalCase for classes, structs, protocols, and enums
- **Methods/Properties**: camelCase starting with lowercase
- **Constants**: Use `static let` for type constants, `let` for instance constants
- **Protocols**: Use descriptive names, often ending with `-able` or describing capability

Example from `/Users/onevcat/Sync/github/Kingfisher/Sources/General/Kingfisher.swift`:
```swift
public protocol KingfisherCompatible: AnyObject { }
public protocol KingfisherCompatibleValue {}
```

### Cross-Platform Type Aliases

Kingfisher uses consistent cross-platform type aliases defined in `/Users/onevcat/Sync/github/Kingfisher/Sources/General/Kingfisher.swift`:

```swift
#if os(macOS)
public typealias KFCrossPlatformImage       = NSImage
public typealias KFCrossPlatformView        = NSView
public typealias KFCrossPlatformColor       = NSColor
public typealias KFCrossPlatformImageView   = NSImageView
public typealias KFCrossPlatformButton      = NSButton
#else
public typealias KFCrossPlatformImage       = UIImage
public typealias KFCrossPlatformColor       = UIColor
// ... additional platform-specific definitions
#endif
```

### Sendable Compliance

Modern Swift concurrency is enforced throughout the codebase:

```swift
public struct KingfisherWrapper: @unchecked Sendable {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}
```

## Common Implementation Patterns

### 1. Namespace Wrapper Pattern

The core pattern used throughout Kingfisher, implemented in `/Users/onevcat/Sync/github/Kingfisher/Sources/General/Kingfisher.swift`:

```swift
/// Wrapper for Kingfisher compatible types
public struct KingfisherWrapper: @unchecked Sendable {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}

/// Protocol for types that can use .kf namespace
public protocol KingfisherCompatible: AnyObject { }

extension KingfisherCompatible {
    /// Gets a namespace holder for Kingfisher compatible types
    public var kf: KingfisherWrapper<Self> {
        get { return KingfisherWrapper(self) }
        set { }
    }
}

// Usage in extensions
extension KFCrossPlatformImage: KingfisherCompatible { }
```

### 2. Builder Pattern

Fluent API implementation in `/Users/onevcat/Sync/github/Kingfisher/Sources/General/KF.swift`:

```swift
public enum KF {
    /// Creates a builder for a given URL
    public static func url(_ url: URL?, cacheKey: String? = nil) -> KF.Builder {
        source(url?.convertToSource(overrideCacheKey: cacheKey))
    }
}

extension KF {
    public class Builder: @unchecked Sendable {
        private let source: Source?
        private var _options = KingfisherParsedOptionsInfo(KingfisherManager.shared.defaultOptions)
        
        // Fluent configuration methods
        public func placeholder(_ image: KFCrossPlatformImage?) -> Self {
            self.placeholder = image
            return self
        }
    }
}
```

### 3. Options Pattern

Comprehensive options system in `/Users/onevcat/Sync/github/Kingfisher/Sources/General/KingfisherOptionsInfo.swift`:

```swift
/// Represents the available option items
public enum KingfisherOptionsInfoItem: Sendable {
    case targetCache(ImageCache)
    case downloader(ImageDownloader)
    case transition(ImageTransition)
    case downloadPriority(Float)
    case forceRefresh
    case processor(any ImageProcessor)
    // ... many more options
}

/// Parsed options for internal use
public struct KingfisherParsedOptionsInfo: Sendable {
    public var targetCache: ImageCache? = nil
    public var downloader: ImageDownloader? = nil
    public var transition: ImageTransition = .none
    // ... corresponding properties
    
    public init(_ info: KingfisherOptionsInfo?) {
        guard let info = info else { return }
        for option in info {
            switch option {
            case .targetCache(let value): targetCache = value
            case .downloader(let value): downloader = value
            // ... handle all options
            }
        }
    }
}
```

### 4. Protocol-Oriented Design

Example from `/Users/onevcat/Sync/github/Kingfisher/Sources/Image/ImageProcessor.swift`:

```swift
/// Protocol for image processing
public protocol ImageProcessor: Sendable {
    /// Identifier for caching and retrieval
    var identifier: String { get }
    
    /// Process the input item
    func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage?
}

extension ImageProcessor {
    /// Append processors in pipeline
    public func append(another: any ImageProcessor) -> any ImageProcessor {
        let newIdentifier = identifier.appending("|>\(another.identifier)")
        return GeneralProcessor(identifier: newIdentifier) { item, options in
            if let image = self.process(item: item, options: options) {
                return another.process(item: .image(image), options: options)
            } else {
                return nil
            }
        }
    }
}
```

### 5. Fluent Configuration with KFOptionSetter

Protocol-based fluent API in `/Users/onevcat/Sync/github/Kingfisher/Sources/General/KFOptionsSetter.swift`:

```swift
@MainActor
public protocol KFOptionSetter {
    var options: KingfisherParsedOptionsInfo { get nonmutating set }
    var onFailureDelegate: Delegate<KingfisherError, Void> { get }
    var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { get }
    var onProgressDelegate: Delegate<(Int64, Int64), Void> { get }
}

extension KFOptionSetter {
    public func targetCache(_ cache: ImageCache) -> Self {
        options.targetCache = cache
        return self
    }
    
    public func downloader(_ downloader: ImageDownloader) -> Self {
        options.downloader = downloader
        return self
    }
}
```

## Development Workflows

### Setting Up Images for UI Components

**Common pattern for UIImageView/NSImageView** (file: `/Users/onevcat/Sync/github/Kingfisher/Sources/Extensions/ImageView+Kingfisher.swift`):

```swift
// Basic usage
imageView.kf.setImage(with: url)

// With configuration
imageView.kf.setImage(
    with: url,
    placeholder: placeholderImage,
    options: [.transition(.fade(0.2)), .cacheMemoryOnly],
    completionHandler: { result in
        // Handle result
    }
)
```

**Builder pattern approach**:
```swift
KF.url(imageURL)
    .placeholder(placeholderImage)
    .fade(duration: 0.2)
    .cacheMemoryOnly()
    .onSuccess { result in
        print("Image loaded: \(result.image)")
    }
    .set(to: imageView)
```

### Adding New Image Processors

1. **Create processor conforming to ImageProcessor protocol**:
```swift
struct CustomProcessor: ImageProcessor {
    var identifier: String { "com.example.custom" }
    
    func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
        // Implementation
    }
}
```

2. **Add convenience method to KFOptionSetter** (file: `/Users/onevcat/Sync/github/Kingfisher/Sources/General/KFOptionsSetter.swift`):
```swift
extension KFOptionSetter {
    public func customEffect() -> Self {
        appendProcessor(CustomProcessor())
    }
}
```

### Extending Platform Support

**Add platform-specific extensions** (pattern from existing platform extensions):

1. **Update type aliases** in `/Users/onevcat/Sync/github/Kingfisher/Sources/General/Kingfisher.swift`
2. **Add compatibility conformance**:
```swift
#if os(newOS)
extension NewOSImageView: KingfisherCompatible { }
#endif
```
3. **Implement platform-specific extensions** following the pattern in existing platform files

### Cache Management Tasks

**Working with ImageCache** (main class: `/Users/onevcat/Sync/github/Kingfisher/Sources/Cache/ImageCache.swift`):

```swift
// Configure cache
let cache = ImageCache(name: "custom")
cache.memoryStorage.config.totalCostLimit = 50 * 1024 * 1024 // 50MB
cache.diskStorage.config.sizeLimit = 200 * 1024 * 1024 // 200MB

// Use with options
imageView.kf.setImage(with: url, options: [.targetCache(cache)])

// Manual cache operations
cache.store(image, forKey: key)
cache.retrieveImage(forKey: key) { result in
    // Handle cached image
}
```

## Reference

### File Organization

```
Sources/
├── General/           # Core managers, options, data providers
│   ├── KingfisherManager.swift     # Central coordinator
│   ├── KF.swift                    # Builder pattern API
│   ├── Kingfisher.swift            # Core protocols and wrappers
│   ├── KingfisherOptionsInfo.swift # Options system
│   └── ImageSource/                # Data source abstractions
├── Networking/        # Download, prefetch, session management
│   ├── ImageDownloader.swift       # Network layer
│   ├── ImagePrefetcher.swift       # Batch prefetching
│   └── RetryStrategy.swift         # Retry logic
├── Cache/            # Multi-layer caching system
│   ├── ImageCache.swift            # Main cache interface
│   ├── MemoryStorage.swift         # Memory cache backend
│   └── DiskStorage.swift           # Disk cache backend
├── Image/            # Processing, filters, formats, transitions
│   ├── ImageProcessor.swift        # Processing protocols
│   ├── Filter.swift                # Built-in processors
│   └── ImageTransition.swift       # UI transition effects
├── Extensions/       # UIKit/AppKit integration
│   ├── ImageView+Kingfisher.swift  # Main UI extensions
│   └── UIButton+Kingfisher.swift   # Button extensions
├── SwiftUI/         # SwiftUI-specific components
│   ├── KFImage.swift               # SwiftUI image component
│   └── KFAnimatedImage.swift       # Animated SwiftUI component
├── Utility/         # Helper utilities and extensions
└── Views/           # Custom UI components
```

### Naming Conventions

- **Manager classes**: `*Manager` (e.g., `KingfisherManager`)
- **Data providers**: `*Provider` or `*DataProvider` (e.g., `ImageDataProvider`)
- **Processors**: `*Processor` or `*ImageProcessor` (e.g., `BlurImageProcessor`)
- **Extensions**: `Type+Kingfisher.swift` (e.g., `UIButton+Kingfisher.swift`)
- **Protocols**: Descriptive names often with `-able` suffix (`KingfisherCompatible`)
- **Internal utilities**: Plain descriptive names (`CallbackQueue`, `Result`)

### Common Issues and Solutions

**Thread Safety**: All public APIs are designed to be thread-safe. Use `@MainActor` for UI-related operations and `@unchecked Sendable` for wrapper types.

**Memory Management**: Kingfisher uses both memory and disk caching. Configure limits appropriately:
```swift
ImageCache.default.memoryStorage.config.totalCostLimit = 50 * 1024 * 1024
ImageCache.default.diskStorage.config.sizeLimit = 200 * 1024 * 1024
```

**Platform Differences**: Use platform-specific compilation directives and the provided cross-platform type aliases to ensure compatibility.

**Testing**: Use the testing utilities in `/Users/onevcat/Sync/github/Kingfisher/Tests/KingfisherTests/KingfisherTestHelper.swift` and follow the mocking patterns established in existing tests.

**Performance**: For large images, prefer `DownsamplingImageProcessor` over `ResizingImageProcessor` for better memory efficiency.

---

## File: docs/files.md

# File Catalog - Kingfisher



## Overview

Kingfisher is a modern Swift library for loading and caching images, built with a modular structure and protocol-oriented design. The project clearly separates different concerns and uses a namespace pattern (.kf) to provide a consistent API for UIKit, AppKit, and SwiftUI. Its core components include KingfisherManager as the central coordinator, ImageDownloader for handling network tasks, ImageCache for a dual-layer caching system, and ImageProcessor for managing image transformation pipelines.

The project supports multiple platforms (iOS, macOS, tvOS, watchOS, visionOS) and uses Fastlane as its main build system. It employs the XCTest framework for testing and integrates the DocC documentation system. Files are organized based on functionality, with the Sources directory divided by modules, configuration files centrally managed, and Demo projects offering complete usage examples.

## Core Source Files

### Primary Framework Components
- **`Sources/General/KingfisherManager.swift`** - Central coordinator managing image loading workflow
- **`Sources/General/KF.swift`** - Main entry point providing builder pattern API for image tasks  
- **`Sources/General/Kingfisher.swift`** - Core framework module with KingfisherCompatible protocol
- **`Sources/General/KingfisherOptionsInfo.swift`** - Configuration options container for all operations
- **`Sources/General/KingfisherError.swift`** - Error handling system with detailed error types
- **`Sources/General/KFOptionsSetter.swift`** - Options configuration builder for method chaining

### Image Source and Data Providers
- **`Sources/General/ImageSource/Resource.swift`** - URL resource definitions and transformations
- **`Sources/General/ImageSource/Source.swift`** - Abstract image source protocols and implementations
- **`Sources/General/ImageSource/ImageDataProvider.swift`** - Data provider protocol for custom image sources
- **`Sources/General/ImageSource/AVAssetImageDataProvider.swift`** - AVAsset image frame extraction
- **`Sources/General/ImageSource/PHPickerResultImageDataProvider.swift`** - Photo picker integration
- **`Sources/General/ImageSource/LivePhotoSource.swift`** - Live Photo support implementation

### Network Layer
- **`Sources/Networking/ImageDownloader.swift`** - HTTP image downloading with session management
- **`Sources/Networking/ImageDownloader+LivePhoto.swift`** - Live Photo downloading extensions
- **`Sources/Networking/ImageDownloaderDelegate.swift`** - Download progress and completion delegation
- **`Sources/Networking/SessionDelegate.swift`** - URLSession delegate handling authentication and redirects
- **`Sources/Networking/SessionDataTask.swift`** - Custom data task wrapper with cancellation support
- **`Sources/Networking/ImagePrefetcher.swift`** - Batch image prefetching for performance optimization
- **`Sources/Networking/RequestModifier.swift`** - HTTP request modification protocols
- **`Sources/Networking/ImageModifier.swift`** - Response image modification protocols
- **`Sources/Networking/RedirectHandler.swift`** - HTTP redirect handling strategies
- **`Sources/Networking/RetryStrategy.swift`** - Failed request retry logic and policies
- **`Sources/Networking/AuthenticationChallengeResponsable.swift`** - Authentication challenge handling
- **`Sources/Networking/ImageDataProcessor.swift`** - Raw image data processing pipeline

### Caching System
- **`Sources/Cache/ImageCache.swift`** - Dual-layer (memory + disk) caching coordinator
- **`Sources/Cache/MemoryStorage.swift`** - In-memory LRU cache with automatic cleanup
- **`Sources/Cache/DiskStorage.swift`** - Persistent disk storage with expiration policies
- **`Sources/Cache/Storage.swift`** - Abstract storage protocols and configurations
- **`Sources/Cache/CacheSerializer.swift`** - Image serialization for disk persistence
- **`Sources/Cache/FormatIndicatedCacheSerializer.swift`** - Format-aware image serialization

### Image Processing and Transformation
- **`Sources/Image/Image.swift`** - Cross-platform image type definitions and utilities
- **`Sources/Image/ImageProcessor.swift`** - Image transformation and processing pipeline
- **`Sources/Image/Filter.swift`** - Built-in image filters (blur, tint, overlay, etc.)
- **`Sources/Image/ImageFormat.swift`** - Image format detection and handling
- **`Sources/Image/ImageDrawing.swift`** - Core graphics drawing utilities and extensions
- **`Sources/Image/ImageTransition.swift`** - View transition animations for image loading
- **`Sources/Image/ImageProgressive.swift`** - Progressive JPEG loading implementation
- **`Sources/Image/GIFAnimatedImage.swift`** - GIF animation support and playback
- **`Sources/Image/GraphicsContext.swift`** - Graphics context management and utilities
- **`Sources/Image/Placeholder.swift`** - Placeholder image definitions and protocols

## Platform Implementation Files

### UIKit Extensions
- **`Sources/Extensions/ImageView+Kingfisher.swift`** - UIImageView integration with .kf namespace
- **`Sources/Extensions/UIButton+Kingfisher.swift`** - UIButton background/image loading support
- **`Sources/Extensions/NSTextAttachment+Kingfisher.swift`** - Text attachment image loading

### AppKit Extensions  
- **`Sources/Extensions/NSButton+Kingfisher.swift`** - macOS NSButton image loading integration
- **`Sources/Extensions/HasImageComponent+Kingfisher.swift`** - Generic image component protocol

### Cross-Platform Extensions
- **`Sources/Extensions/PHLivePhotoView+Kingfisher.swift`** - Live Photo view integration
- **`Sources/Extensions/CPListItem+Kingfisher.swift`** - CarPlay list item support

### SwiftUI Components
- **`Sources/SwiftUI/KFImage.swift`** - Main SwiftUI image view component
- **`Sources/SwiftUI/KFAnimatedImage.swift`** - Animated image support for SwiftUI
- **`Sources/SwiftUI/KFImageOptions.swift`** - SwiftUI-specific configuration options
- **`Sources/SwiftUI/KFImageProtocol.swift`** - Shared protocol for KF SwiftUI components
- **`Sources/SwiftUI/KFImageRenderer.swift`** - SwiftUI view rendering and update logic
- **`Sources/SwiftUI/ImageBinder.swift`** - Binding layer between SwiftUI and Kingfisher core
- **`Sources/SwiftUI/ImageContext.swift`** - SwiftUI image loading context management

### Custom Views
- **`Sources/Views/AnimatedImageView.swift`** - Custom animated image view for GIF playback
- **`Sources/Views/Indicator.swift`** - Loading indicator views and protocols

## Build System Files

### Package Management
- **`Package.swift`** - Swift Package Manager manifest with dependencies and targets
- **`Package@swift-5.9.swift`** - Swift 5.9 compatibility package manifest
- **`Kingfisher.podspec`** - CocoaPods specification for distribution
- **`Gemfile`** - Ruby dependencies for Fastlane and build tools
- **`Gemfile.lock`** - Locked Ruby gem versions for reproducible builds

### Fastlane Build Automation
- **`fastlane/Fastfile`** - Main Fastlane configuration with lanes for testing, building, and releasing
- **`fastlane/actions/extract_current_change_log.rb`** - Extracts current version changelog
- **`fastlane/actions/git_commit_all.rb`** - Git commit automation with custom messages
- **`fastlane/actions/sync_build_number_to_git.rb`** - Synchronizes build numbers with git commits
- **`fastlane/actions/update_change_log.rb`** - Automated changelog management
- **`fastlane/README.md`** - Fastlane documentation and usage instructions

### Xcode Project Files
- **`Kingfisher.xcodeproj/`** - Main Xcode project with targets and build settings
- **`Kingfisher.xcworkspace/`** - Xcode workspace for integrated development
- **`Demo/Kingfisher-Demo.xcodeproj/`** - Demo application Xcode project

## Configuration Files

### Framework Configuration
- **`Sources/Info.plist`** - Framework bundle information and version metadata
- **`Sources/PrivacyInfo.xcprivacy`** - Privacy manifest for App Store compliance

### Development Assets
- **`images/`** - Sample images for testing and demo applications
  - `kingfisher-1.jpg` through `kingfisher-10.jpg` - Test image assets
  - `logo.png` - Project logo and branding
- **`Tests/KingfisherTests/dancing-banana.gif`** - GIF test asset for animation testing
- **`Tests/KingfisherTests/single-frame.gif`** - Single-frame GIF for edge case testing

### Demo Applications
- **`Demo/Demo/Kingfisher-Demo/`** - iOS demo app showcasing all features
  - `ViewControllers/` - Various demo screens (GIF, transitions, processors, etc.)
  - `SwiftUIViews/` - SwiftUI demonstration views and regression tests
- **`Demo/Demo/Kingfisher-macOS-Demo/`** - macOS demo application
- **`Demo/Demo/Kingfisher-tvOS-Demo/`** - Apple TV demo application  
- **`Demo/Demo/Kingfisher-watchOS-Demo/`** - watchOS demo application

### Git and CI Configuration
- **`pre-change.yml`** - Pre-commit hook configuration for code quality
- **`.gitignore`** - Git ignore patterns for build artifacts and dependencies

## Utility and Helper Files

### Core Utilities
- **`Sources/Utility/ExtensionHelpers.swift`** - Cross-platform compatibility helpers
- **`Sources/Utility/CallbackQueue.swift`** - Thread-safe callback queue management
- **`Sources/Utility/Box.swift`** - Reference wrapper for value types
- **`Sources/Utility/Result.swift`** - Result type utilities and extensions
- **`Sources/Utility/Delegate.swift`** - Weak delegate wrapper to prevent retain cycles
- **`Sources/Utility/Runtime.swift`** - Runtime reflection and dynamic dispatch utilities
- **`Sources/Utility/DisplayLink.swift`** - Cross-platform display link abstraction
- **`Sources/Utility/SizeExtensions.swift`** - CGSize manipulation and calculations
- **`Sources/Utility/String+SHA256.swift`** - String hashing for cache key generation

### Test Infrastructure
- **`Tests/KingfisherTests/KingfisherTestHelper.swift`** - Shared testing utilities and mocks
- **`Tests/KingfisherTests/Utils/StubHelpers.swift`** - HTTP stubbing helpers for network tests
- **`Tests/Dependency/Nocilla/`** - HTTP mocking framework for isolated testing

### Documentation System
- **`Sources/Documentation.docc/`** - DocC documentation bundle
  - `Documentation.md` - Main documentation entry point
  - `GettingStarted.md` - Quick start guide for new users
  - `Tutorials/` - Step-by-step tutorials for UIKit and SwiftUI
  - `CommonTasks/` - Task-oriented documentation for common use cases
  - `Topics/` - Advanced topic guides (prefetching, indicators, etc.)
  - `Resources/` - Documentation assets, images, and code samples

## Reference

### File Organization Patterns
- **Modular Structure**: Core functionality separated into logical modules (General, Networking, Cache, Image, etc.)
- **Platform Abstraction**: Cross-platform code in core modules, platform-specific extensions separate
- **Protocol-Oriented**: Heavy use of protocols for customization and testing
- **Namespace Pattern**: All public APIs accessed through `.kf` property extension

### Naming Conventions
- **Files**: PascalCase with descriptive names indicating functionality
- **Protocols**: Suffix with `-able` for capabilities (e.g., `AuthenticationChallengeResponsable`)
- **Extensions**: Platform prefix for clarity (e.g., `UIButton+Kingfisher.swift`)
- **Tests**: Mirror source structure with `Tests` suffix

### Dependency Relationships
- **Core Dependencies**: Foundation, UIKit/AppKit conditionally imported
- **SwiftUI Module**: Depends on core modules but isolated for optional usage
- **Extensions**: Depend on core but platform-specific
- **Test Dependencies**: Isolated with Nocilla for HTTP mocking
- **Build Dependencies**: Fastlane for automation, Ruby gems for tooling

### Key Integration Points
- **KingfisherCompatible Protocol**: Entry point for all functionality via `.kf` namespace
- **Options System**: Centralized configuration through `KingfisherOptionsInfo`
- **Result Types**: Consistent error handling with `Result<Success, KingfisherError>`
- **Callback Queues**: Thread-safe completion handling across all async operations

---

## File: docs/issue-2495-analysis.md

# Issue #2495 Analysis: Disk Cache Deserialization Cannot Be Cancelled

## 1. Issue Overview

**Issue**: [#2495 - Images loading from cache are not canceled](https://github.com/onevcat/Kingfisher/issues/2495)
**Reporter**: FaizanDurrani
**Status**: Open

### Symptom

When scrolling quickly through a large collection of images, memory usage explodes (>1 GB). The reporter uses `KingfisherWebP` and observes that `ImageCache.retrieveImageInDiskCache` is called for every displayed item, even after `imageView.kf.cancelDownloadTask()` is invoked.

### Instruments Evidence

The attached Instruments Allocations screenshot shows:

- **Memory peak ~630 MB** during fast scrolling
- Heaviest allocation stack:
  - `ImageCache.retrieveImageInDiskCache(forKey:options:callbackQueue:completionHandler:)` — **218.55 MB**
  - `KingfisherWrapper.image(webpData:options:)` — **181.09 MB**
  - WebP decode + `CGImageCreate` calls downstream

The majority of memory is consumed by WebP deserialization, not by disk I/O itself.

---

## 2. Root Cause Analysis

The problem has two layers:

### Layer 1: Cache Retrieval Is Not Cancellable

`cancelDownloadTask()` calls `DownloadTask.cancel()`, which only cancels the **network** download (`SessionDataTask.cancel(token:)` in `ImageDownloader.swift:121-124`).

For images that hit the disk cache, `KingfisherManager.retrieveImage` returns `nil` at line 444 — there is no `DownloadTask` to cancel. Once `retrieveImageInDiskCache` dispatches its block to `ioQueue`, the entire chain (disk read → deserialization → memory promotion → callback) runs to completion with no interruption point.

### Layer 2: Eager Disk-to-Memory Promotion

Kingfisher's cache architecture is designed as "eager read into memory":

1. `ImageCache.retrieveImage` (line 608-655) checks memory first, then falls back to disk.
2. On disk hit, the image is **fully deserialized** via `CacheSerializer.image(with:data:options:)` on `ioQueue` (line 738).
3. The deserialized image is **unconditionally stored into memory cache** via `store(image, toDisk: false)` (line 641-645).
4. There is no lazy decode path, no thumbnail downsampling, and no option to skip memory promotion.

During fast scrolling, each reused cell triggers a new `setImage` call. The previous cell's disk cache retrieval is already enqueued on the serial `ioQueue`. With N cells scrolled through rapidly, N deserialization blocks queue up. WebP decoding is particularly expensive — each decode allocates significant memory. The combined allocations exceed `NSCache`'s eviction speed, causing the memory spike.

### Why Existing Mechanisms Don't Help

| Mechanism | Why It Doesn't Help |
|-----------|-------------------|
| `cancelDownloadTask()` | Returns nil for cache hits — nothing to cancel |
| `taskIdentifier` check in view completion (line 331) | Only runs **after** deserialization completes — the damage is done |
| `fromMemoryCacheOrRefresh` option | Skips disk cache entirely — loses all disk caching, not a valid workaround |
| `NSCache` eviction (totalCostLimit = 25% RAM) | LRU eviction is reactive, can't keep up with burst allocations |

---

## 3. Proposed Solution

### Approach: Source Task Identifier Check Before Deserialization

Leverage the existing `Source.Identifier` / `taskIdentifier` mechanism — which already works at the view layer — and extend it into the cache retrieval path. The check is inserted **before the expensive deserialization step**, skipping decode for tasks that are no longer relevant.

### Key Properties

- **No public API changes** — all modifications are internal
- **Backward compatible** — direct `KingfisherManager` callers are unaffected (checker is nil)
- **Completion handler contract preserved** — every `setImage` call produces exactly one completion callback
- **Worst case identical to current behavior** — if the check doesn't fire in time, the decode proceeds as it does today

### Changes Required

#### 3.1 `KingfisherParsedOptionsInfo` — New Internal Property

Add a closure property that flows through the options chain:

```swift
var sourceTaskIdentifierChecker: (@Sendable () -> Bool)?
```

Returns `true` if the task is still current, `false` if it has been superseded. This property is internal — not exposed in the public `KingfisherOptionsInfoItem` enum.

#### 3.2 `KingfisherManager.retrieveImage` — Store Checker in Options

Currently `referenceTaskIdentifierChecker` (line 306) is only applied to `onDataReceived` side effects (line 329-332). Add one line to also store it in options:

```swift
if let checker = referenceTaskIdentifierChecker {
    options.onDataReceived?.forEach {
        $0.onShouldApply = checker
    }
    options.sourceTaskIdentifierChecker = checker  // NEW
}
```

This ensures the checker propagates to `ImageCache` via the options struct.

#### 3.3 `ImageCache.retrieveImageInDiskCache` — Two Check Points

Insert checks **before disk read** and **before deserialization**:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Returning `.success(nil)` uses the same semantics as "image not found on disk", which is safely handled by all upstream callers.

#### 3.4 `KingfisherManager.retrieveImageFromCache` — Prevent Spurious Download (Path 2 Only)

`retrieveImageFromCache` has two code paths:

- **Path 1** (line 639-697): Processed image lookup. On `.none` result, calls `completionHandler(.failure(.imageNotExisting))`. Since `retrieveImageFromCache` already returned `true` synchronously, no download is triggered. **No change needed.**

- **Path 2** (line 716-793): Original image fallback with custom processor. On `.none` result, calls `self.loadAndCacheImage(...)` (line 732) — this **does** trigger a download. Add a check here:

```swift
guard let image = cacheResult.image else {
    // NEW: Task is stale — report error instead of downloading.
    if let checker = options.sourceTaskIdentifierChecker, !checker() {
        let error = KingfisherError.cacheError(reason: .imageNotExisting(key: key))
        options.callbackQueue.execute { completionHandler?(.failure(error)) }
        return
    }

    if options.onlyFromCache {
        let error = KingfisherError.cacheError(reason: .imageNotExisting(key: key))
        options.callbackQueue.execute { completionHandler?(.failure(error)) }
    } else {
        let task = self.loadAndCacheImage(
            source: source,
            context: context,
            completionHandler: completionHandler
        )
        downloadTaskUpdated?(task?.value)
    }
    return
}
```

### Summary of File Changes

| File | Change | Lines Affected |
|------|--------|---------------|
| `Sources/General/KingfisherOptionsInfoItems.swift` | Add `sourceTaskIdentifierChecker` property | ~1 line |
| `Sources/General/KingfisherManager.swift` | Store checker in options | ~1 line |
| `Sources/Cache/ImageCache.swift` | Two cancellation checks in `retrieveImageInDiskCache` | ~10 lines |
| `Sources/General/KingfisherManager.swift` | Prevent spurious download in path 2 | ~5 lines |

**Total: ~17 lines of effective code.**

---

## 4. Timing and Behavior Simulation

### Scenario A: Fast Scrolling (Primary Problem Case)

A cell is reused 5 times within one layout pass. All `setImage` calls complete on the main thread before the serial `ioQueue` processes any block.

```
Main thread (single runloop pass):
  cellForItem → cell X: setImage(url_1), X.taskId = 101, dispatch block_1 to ioQueue
  cellForItem → cell X: setImage(url_5), X.taskId = 102, dispatch block_2 to ioQueue
  cellForItem → cell X: setImage(url_9), X.taskId = 103, dispatch block_3 to ioQueue
  cellForItem → cell X: setImage(url_13), X.taskId = 104, dispatch block_4 to ioQueue
  cellForItem → cell X: setImage(url_17), X.taskId = 105, dispatch block_5 to ioQueue
                                                                         ↕ main thread yields

ioQueue (serial, starts processing):
  block_1: CHECK 1 → X.taskId==101? → 105≠101 → SKIP (no disk read, no decode)
  block_2: CHECK 1 → X.taskId==102? → 105≠102 → SKIP
  block_3: CHECK 1 → X.taskId==103? → 105≠103 → SKIP
  block_4: CHECK 1 → X.taskId==104? → 105≠104 → SKIP
  block_5: CHECK 1 → X.taskId==105? → 105==105 → read disk → CHECK 2 → still valid → DECODE ✓
```

**Result**: 4 out of 5 decodes skipped. Only the actually-needed image is decoded.
**Current behavior**: All 5 images decoded, all promoted to memory, 4 results discarded at view layer.
**Check hit rate**: (N-1)/N → approaches 100% with faster scrolling.

### Scenario B: Moderate Scrolling

User scrolls at a pace where each cell stays visible for ~200ms. Decode takes ~100ms per image.

```
Main thread:
  t=0ms    setImage(url_1), X.taskId = 101, dispatch block_1

ioQueue:
  t=5ms    block_1: CHECK 1 → 101==101 ✓ → read disk (10ms)
  t=15ms   CHECK 2 → 101==101 ✓ → decode (100ms)
  t=115ms  decode complete → store to memory → callback

Main thread:
  t=200ms  cell reuse, setImage(url_5), X.taskId = 102, dispatch block_2
```

**Result**: Decode completes before cell reuse. Check does not fire.
**Current behavior**: Identical — image was needed and displayed.
**Impact**: None — and none needed, since one-at-a-time decode doesn't cause memory pressure.

### Scenario C: Moderate Scrolling with Large File

Same pace as B, but a very large WebP file takes 300ms to read from disk.

```
Main thread:
  t=0ms    setImage(url_1), X.taskId = 101, dispatch block_1

ioQueue:
  t=5ms    block_1: CHECK 1 → 101==101 ✓ → start disk read (300ms)

Main thread:
  t=200ms  cell reuse, setImage(url_5), X.taskId = 102

ioQueue:
  t=305ms  disk read complete → CHECK 2 → 101==102? → 101≠102 → SKIP decode ✓
```

**Result**: CHECK 2 catches the stale task after a slow disk read. Decode skipped.
**Current behavior**: Full decode runs (expensive), result discarded at view layer.
**This is where the two-check design pays off** — CHECK 1 was valid at dispatch time, but CHECK 2 catches the change.

### Scenario D: Multiple Visible Cells (No Reuse)

10 cells appear simultaneously on initial load. Each cell has a different image, no cell reuse occurs.

```
Main thread:
  cell A: setImage(url_1), A.taskId = 101
  cell B: setImage(url_2), B.taskId = 102
  ...
  cell J: setImage(url_10), J.taskId = 110

ioQueue:
  block for cell A: CHECK → A.taskId==101? → 101==101 ✓ → decode
  block for cell B: CHECK → B.taskId==102? → 102==102 ✓ → decode
  ...
  block for cell J: CHECK → J.taskId==110? → 110==110 ✓ → decode
```

**Result**: All 10 images decoded. This is correct — all 10 are needed.
**Current behavior**: Identical.
**Impact**: None. The check is per-cell (each cell has its own `taskIdentifier`), so concurrent requests for different cells don't interfere with each other.

### Scenario E: Fast Scrolling with Custom Processor (Path 2)

User has a custom `ImageProcessor`. Original image is cached on disk, processed version is not. Cell reuses rapidly.

```
Main thread:
  setImage(url_1, processor: blur), X.taskId = 101
  setImage(url_5, processor: blur), X.taskId = 102

retrieveImageFromCache:
  Path 1: processed image not cached → validCache = false
  Path 2: original image cached → canUseOriginalImageCache = true

  originalCache.retrieveImage → ioQueue:
    block_1: CHECK → 101==102? → SKIP decode → .success(nil)

  Path 2 callback:
    cacheResult.image == nil
    → CHECK: 101==102? → stale
    → completionHandler(.failure(.imageNotExisting))   ← no download triggered ✓

  View layer:
    issuedIdentifier(101) ≠ taskIdentifier(102) → .notCurrentSourceTask
    completionHandler(.failure(.notCurrentSourceTask)) ← user gets callback ✓
```

**Without the path 2 fix**: `loadAndCacheImage` would be called, triggering a network download that is ultimately discarded.

---

## 5. Completion Handler Contract Verification

**Contract**: Every `setImage` call produces exactly one completion callback.

### For the "skipped" task (stale identifier):

```
retrieveImageInDiskCache → .success(nil)          // skipped decode
    ↓
ImageCache.retrieveImage → .success(.none)         // treated as "not found"
    ↓
retrieveImageFromCache:
  Path 1 → completionHandler(.failure(.imageNotExisting))    ✅ called
  Path 2 → checker prevents download,
           completionHandler(.failure(.imageNotExisting))    ✅ called
    ↓
handler (line 386) → failCurrentSource → completionHandler(.failure(...))
    ↓
View extension (line 331):
  issuedIdentifier ≠ taskIdentifier →
  completionHandler(.failure(.notCurrentSourceTask))         ✅ user receives callback
```

### For the "current" task (valid identifier):

Behavior is identical to today — full decode, memory promotion, success callback. No changes in this path.

---

## 6. Risk Assessment

### 6.1 Thread Safety of `taskIdentifier` Access

**Status**: Pre-existing issue, not introduced by this change.

The checker closure `{ issuedIdentifier == self.taskIdentifier }` reads `taskIdentifier` (associated object on UIImageView) from `ioQueue`, while writes happen on `@MainActor`. The associated object uses `OBJC_ASSOCIATION_RETAIN_NONATOMIC`.

**Mitigation factors**:
- `objc_getAssociatedObject` / `objc_setAssociatedObject` use an internal lock in the ObjC runtime (`AssociationsManager` mutex). Memory access is serialized at the runtime level.
- The value is wrapped in `Box`. After reading the Box reference, we immediately extract the `UInt` value (copy semantics). No retained reference escapes.
- The same pattern already exists in `onShouldApply` for `ImageProgressiveProvider` (called from background decoder thread).

**Worst case of torn read**:
- Read old value (checker thinks task is still valid) → proceed with decode → same as current behavior
- Read new value (checker thinks task is stale) → skip a decode that was actually still needed → cell shows placeholder until the truly-current task completes

**Recommendation**: Consider changing `OBJC_ASSOCIATION_RETAIN_NONATOMIC` to `OBJC_ASSOCIATION_RETAIN` in a separate PR, independent of this optimization. This would make the contract explicit without affecting performance (associated object access is already locked).

### 6.2 Spurious `.imageNotExisting` Error Propagation

When a skipped task returns `.success(nil)`, it becomes `.imageNotExisting` upstream. This error flows through `handler` → `failCurrentSource` (line 352-383).

`failCurrentSource` checks:
1. `error.isTaskCancelled` (line 354) — **no**, this is a cache error, not a cancellation error. Won't short-circuit.
2. `error.isLowDataModeConstrained` (line 359) — **no**. Won't trigger low-data-mode fallback.
3. `retrievingContext.popAlternativeSource()` (line 369) — **if alternative sources are configured**, the next source will be tried.

**Impact**: If a user configures `alternativeSources`, a skipped task may trigger retrieval from the next alternative source. This retrieval result will be discarded at the view layer (stale `taskIdentifier`), but it wastes a network request or cache lookup.

**Severity**: Low. `alternativeSources` is a rarely-used feature. The wasted work is bounded (one alternative attempt that gets discarded). The exact same waste occurs today when a network download completes for a stale task.

**Possible future improvement**: Introduce a dedicated `CacheErrorReason.taskCancelled` case so that `failCurrentSource` short-circuits at line 354. This would require adding a case to the public `KingfisherError.CacheErrorReason` enum — a source-breaking change if users have exhaustive switches. Could be considered for the next major version.

### 6.3 Already-Running Decode Cannot Be Interrupted

Once `cacheSerializer.image(with:data:options:)` begins execution, it runs to completion. There is no mid-decode cancellation.

**Mitigating factor**: The `ioQueue` is serial. At most one decode runs at any time. The memory consumed by a single in-flight decode is bounded and manageable. The issue in #2495 is caused by **dozens** of decodes queuing up and running sequentially while their decoded images accumulate in memory — our check prevents the queue from growing unboundedly.

### 6.4 `loadDiskFileSynchronously` Mode

When `loadDiskFileSynchronously` is `true`, the loading queue is `.untouch` (caller's thread, typically main). In this case:

- CHECK 1 runs on the main thread synchronously — `taskIdentifier` was **just** set, so the check always passes.
- The disk read and decode run synchronously on the main thread, blocking the runloop. No opportunity for cell reuse.

**Impact**: No behavior change for synchronous mode. This is expected — synchronous mode is inherently non-cancellable.

### 6.5 Direct `KingfisherManager` / `ImageCache` API Users

Users who call `KingfisherManager.shared.retrieveImage(with:options:completionHandler:)` or `ImageCache.default.retrieveImage(forKey:...)` directly:

- `sourceTaskIdentifierChecker` is never set (it's only populated from the view extension path).
- All checks evaluate to `if let checker = nil` → skip → original behavior.

**Impact**: None. Full backward compatibility.

---

## 7. Summary

### What This Fix Achieves

- **Skips expensive deserialization** for disk-cached images when the requesting view has already moved on to a different image
- **Prevents spurious network downloads** when a cancelled cache lookup for a custom-processed image returns "not found"
- **Preserves the completion handler contract** — every `setImage` always results in exactly one completion callback

### What This Fix Does NOT Achieve

- Cannot interrupt a decode that is already in progress (inherent limitation of `CacheSerializer` protocol)
- Does not add a first-class cancellation API for cache lookups (would require public API changes)
- Does not address the `taskIdentifier` data race (pre-existing, should be fixed independently)
- Does not change the eager disk-to-memory promotion design (a larger architectural topic)

### Estimated Effectiveness

| Scrolling Speed | Decodes Prevented | Memory Pressure Reduction |
|----------------|-------------------|--------------------------|
| Fast (>10 cells/sec) | ~90-100% of stale decodes | Significant |
| Moderate (~5 cells/sec) | ~50-70% of stale decodes | Moderate |
| Slow (<2 cells/sec) | ~0% (but no pressure) | N/A |

---

## 8. Implementation Plan

### Step 1: Add `sourceTaskIdentifierChecker` to `KingfisherParsedOptionsInfo`

**File**: `Sources/General/KingfisherOptionsInfo.swift`
**Location**: Line 420, after `onDataReceived` (the other internal property)

Add:
```swift
var onDataReceived: [any DataReceivingSideEffect]? = nil
var sourceTaskIdentifierChecker: (@Sendable () -> Bool)? = nil  // NEW
```

This is an internal property (no `public` modifier), matching the pattern of `onDataReceived`. It does not need handling in the `init(_ info:)` switch since it is never set from `KingfisherOptionsInfoItem`.

**Verification**: Build succeeds. No public API surface change.

### Step 2: Store `referenceTaskIdentifierChecker` into options

**File**: `Sources/General/KingfisherManager.swift`
**Location**: Line 329-333, inside `retrieveImage(with:options:...referenceTaskIdentifierChecker:...)`

Change from:
```swift
if let checker = referenceTaskIdentifierChecker {
    options.onDataReceived?.forEach {
        $0.onShouldApply = checker
    }
}
```

To:
```swift
if let checker = referenceTaskIdentifierChecker {
    options.onDataReceived?.forEach {
        $0.onShouldApply = checker
    }
    options.sourceTaskIdentifierChecker = checker
}
```

This one-line addition causes the checker to propagate through `RetrievingContext.options` into every downstream call that receives `KingfisherParsedOptionsInfo`, including `ImageCache.retrieveImage` and `retrieveImageInDiskCache`.

**Verification**: Build succeeds. Existing tests pass — no behavioral change yet.

### Step 3: Insert cancellation checks in `retrieveImageInDiskCache`

**File**: `Sources/Cache/ImageCache.swift`
**Location**: Line 730-749, the `loadingQueue.execute` block in the internal `retrieveImageInDiskCache`

Change from:
```swift
loadingQueue.execute {
    do {
        var image: KFCrossPlatformImage? = nil
        if let data = try self.diskStorage.value(
            forKey: computedKey,
            forcedExtension: options.forcedExtension,
            extendingExpiration: options.diskCacheAccessExtendingExpiration
        ) {
            image = options.cacheSerializer.image(with: data, options: options)
        }
        if options.backgroundDecode {
            image = image?.kf.decoded(scale: options.scaleFactor)
        }
        callbackQueue.execute { [image] in completionHandler(.success(image)) }
    } catch let error as KingfisherError {
        callbackQueue.execute { completionHandler(.failure(error)) }
    } catch {
        assertionFailure("The internal thrown error should be a `KingfisherError`.")
    }
}
```

To:
```swift
loadingQueue.execute {
    // CHECK 1: For blocks queued behind others on the serial ioQueue,
    // the task is likely already stale by the time execution begins.
    if let checker = options.sourceTaskIdentifierChecker, !checker() {
        callbackQueue.execute { completionHandler(.success(nil)) }
        return
    }

    do {
        var image: KFCrossPlatformImage? = nil
        if let data = try self.diskStorage.value(
            forKey: computedKey,
            forcedExtension: options.forcedExtension,
            extendingExpiration: options.diskCacheAccessExtendingExpiration
        ) {
            // CHECK 2: Disk read completed, but deserialization has not started.
            // Catches staleness that occurred during a slow disk read.
            if let checker = options.sourceTaskIdentifierChecker, !checker() {
                callbackQueue.execute { completionHandler(.success(nil)) }
                return
            }
            image = options.cacheSerializer.image(with: data, options: options)
        }
        if options.backgroundDecode {
            image = image?.kf.decoded(scale: options.scaleFactor)
        }
        callbackQueue.execute { [image] in completionHandler(.success(image)) }
    } catch let error as KingfisherError {
        callbackQueue.execute { completionHandler(.failure(error)) }
    } catch {
        assertionFailure("The internal thrown error should be a `KingfisherError`.")
    }
}
```

**Why two checks**:
- CHECK 1 fires for queued blocks whose task became stale while waiting. Skips both disk I/O and decode. This is the high-hit-rate path during fast scrolling.
- CHECK 2 fires when disk read was slow (large file) and the task became stale during the read. Skips only decode.

**Verification**: Build succeeds. Test with a fast-scrolling collection view and Instruments to confirm decode skipping.

### Step 4: Prevent spurious download in `retrieveImageFromCache` path 2

**File**: `Sources/General/KingfisherManager.swift`
**Location**: Line 724-738, inside the `originalCache.retrieveImage` callback in `retrieveImageFromCache`

Change from:
```swift
result.match(
    onSuccess: { cacheResult in
        guard let image = cacheResult.image else {
            if options.onlyFromCache {
                let error = KingfisherError.cacheError(reason: .imageNotExisting(key: key))
                options.callbackQueue.execute { completionHandler?(.failure(error)) }
            } else {
                let task = self.loadAndCacheImage(
                    source: source,
                    context: context,
                    completionHandler: completionHandler
                )
                downloadTaskUpdated?(task?.value)
            }
            return
        }
```

To:
```swift
result.match(
    onSuccess: { cacheResult in
        guard let image = cacheResult.image else {
            // If the task is stale (cancelled by a newer setImage call),
            // report cache miss instead of triggering a network download.
            if let checker = options.sourceTaskIdentifierChecker, !checker() {
                let error = KingfisherError.cacheError(reason: .imageNotExisting(key: key))
                options.callbackQueue.execute { completionHandler?(.failure(error)) }
                return
            }

            if options.onlyFromCache {
                let error = KingfisherError.cacheError(reason: .imageNotExisting(key: key))
                options.callbackQueue.execute { completionHandler?(.failure(error)) }
            } else {
                let task = self.loadAndCacheImage(
                    source: source,
                    context: context,
                    completionHandler: completionHandler
                )
                downloadTaskUpdated?(task?.value)
            }
            return
        }
```

**Why this is needed**: Path 2 handles original-image fallback with custom processors. When the original image's decode is skipped (returning `.none`), the existing code falls through to `loadAndCacheImage`, triggering a network download. The check prevents this wasted request while still calling `completionHandler` to preserve the contract.

**Verification**: Build succeeds. Existing tests pass. For targeted testing: configure a custom `ImageProcessor` with disk-cached original images, fast-scroll, and verify no spurious network requests in Charles/Instruments.

### Step 5: Testing

#### Unit Tests

Add test cases in `Tests/KingfisherTests/`:

1. **Cache retrieval skip on stale identifier**: Set up a disk-cached image. Simulate a stale `sourceTaskIdentifierChecker` (returns `false`). Verify `retrieveImageInDiskCache` returns `.success(nil)` without invoking `CacheSerializer.image(with:)`.

2. **Completion handler contract**: Call `setImage`, then immediately call `setImage` again on the same view. Verify the first call's completion receives `.notCurrentSourceTask` error.

3. **Valid identifier proceeds normally**: Set up a disk-cached image. Provide a checker that returns `true`. Verify the image is decoded and returned normally.

4. **Path 2 download prevention**: Configure a custom processor, cache only the original image on disk. Provide a stale checker. Verify `loadAndCacheImage` is NOT called and completion receives `.imageNotExisting`.

#### Manual / Integration Testing

- **Instruments**: Profile a fast-scrolling collection view with WebP images. Compare memory peak before and after the change.
- **Functional**: Verify images display correctly at all scrolling speeds. No blank or stuck cells.
- **Edge cases**: Test with `loadDiskFileSynchronously = true`, `fromMemoryCacheOrRefresh = true`, and `alternativeSources` configured.

### Execution Order

```
Step 1 (add property)
  └─ Step 2 (wire checker into options)
       └─ Step 3 (cache-level checks)   ── can be done in parallel ──  Step 4 (path 2 guard)
                                                                           │
                                                                       Step 5 (tests)
```

Steps 1→2 are prerequisites. Steps 3 and 4 are independent of each other. Step 5 should cover all changes.

## Review From Codex

### Overall Assessment

I agree with the root cause analysis in this document:

- The main issue is not `cancelDownloadTask()` itself, but that the disk-cache retrieval path has no cancellation point once work is enqueued.
- The current disk-hit behavior eagerly deserializes the image and then promotes it into memory cache, even when the requesting view has already moved on to another source.
- The existing `taskIdentifier` check at the view layer happens too late. It prevents stale results from being applied to the view, but it does not prevent the expensive decode work from already happening.

These points are consistent with the current implementation in:

- `Sources/Extensions/ImageView+Kingfisher.swift`
- `Sources/General/KingfisherManager.swift`
- `Sources/Cache/ImageCache.swift`
- `Sources/Networking/ImageDownloader.swift`

### Main Concern With the Proposed Fix

I do **not** recommend shipping the proposed solution exactly as written.

The main problem is that it represents a stale request as `.success(nil)` inside `retrieveImageInDiskCache`, which then becomes a normal cache miss (`.none` / `.imageNotExisting`) upstream.

That encoding leaks the wrong semantics into higher layers:

- `ImageCache.retrieveImage(forKey:options:...)` treats `nil` as a normal disk miss.
- `KingfisherManager.retrieveImageFromCache` then treats that result as an ordinary cache failure path.
- At the manager level, ordinary failures can still flow into retry logic or `alternativeSources`.

So the side effect is broader than only "path 2 may trigger a spurious download". Both retry and alternative-source logic can be activated by a stale request that should have been short-circuited as "no longer relevant", not "cache miss".

This is the strongest reason I would not merge the current proposal unchanged.

### Thread-Safety Concern

The document's thread-safety discussion is somewhat optimistic.

Today, the checker closure reads `taskIdentifier` from a background queue, while writes happen on `@MainActor`. The storage behind `taskIdentifier` is implemented with associated objects and `OBJC_ASSOCIATION_RETAIN_NONATOMIC`.

Changing that association to atomic might reduce some risk, but it does not really solve the larger issue: a background queue is still observing UI-associated state across actor boundaries.

If this optimization is added, I suggest tightening this part of the design as well, instead of relying on the current associated-object pattern more heavily than it already is.

### Additional Observation

The proposed check points are directionally correct, but there is one more cheap place worth checking:

- after disk read
- before serializer decode
- before `backgroundDecode`

The dominant cost for WebP is likely already inside the serializer path, so this does not change the main conclusion. Still, adding a final stale check before `backgroundDecode` is nearly free and keeps the behavior more consistent.

### Recommended Direction

I recommend keeping the overall approach, but changing the internal representation of the outcome.

Instead of encoding "stale request" as `.success(nil)` or `.imageNotExisting`, use an explicit internal stale state, for example an internal retrieval result such as:

```swift
enum DiskRetrievalResult {
    case image(KFCrossPlatformImage)
    case none
    case stale
}
```

Then:

- `ImageCache` can stop work early and report `.stale` internally.
- `KingfisherManager` can short-circuit `.stale` without entering retry logic.
- `retrieveImageFromCache` path 2 can avoid download fallback for stale work.
- completion behavior at the view layer remains unchanged, because the view will still eventually surface `.notCurrentSourceTask` for the outdated request.

This preserves the optimization benefit without polluting existing cache-miss semantics.

### Recommended Checker Design

I would also prefer that the checker read from a dedicated thread-safe token holder instead of reading the view's associated-object-backed `taskIdentifier` directly from a background queue.

Using `sourceTaskIdentifierChecker` as an internal option is fine. The weak point is not the propagation mechanism; it is the current state source used by the checker.

### Memory-Model Note

This issue is primarily about avoiding stale decode work, and that is worth fixing first.

However, the current memory spike is amplified by another design choice as well: on disk hit, the decoded image is unconditionally promoted back into memory cache. If memory pressure reports continue even after stale decode skipping is added, the next place to revisit is whether disk-hit promotion should always be eager.

That is a larger architectural question and should probably remain separate from the narrow fix for #2495.

### Documentation Note

One minor accuracy issue in the implementation plan: the file name should be `Sources/General/KingfisherOptionsInfo.swift`, not `KingfisherOptionsInfoItems.swift`.

### Merge Recommendation

My recommendation is:

1. Keep the idea of checking staleness before expensive disk-cache deserialization.
2. Do not encode stale work as a normal cache miss.
3. Introduce an internal explicit stale outcome and short-circuit it in `KingfisherManager`.
4. Tighten the checker state source so it does not depend on reading UI-associated state from a background queue.
5. Add regression tests for retry strategy and `alternativeSources`, not only for path 2 fallback.

With those adjustments, the fix looks worthwhile and technically sound. Without them, the current proposal is likely to reduce memory pressure in the target scenario, but it also introduces semantic leakage into error handling and recovery paths.

## Response From Claude

### Overall

The Codex review is thorough and technically accurate. All five recommendations are accepted and should be incorporated into the final implementation.

### On the Main Concern (`.success(nil)` Semantic Leakage)

This is the most valuable point in the review, and it identifies a flaw that our original analysis underestimated.

Section 6.2 of this document assessed the `alternativeSources` interaction as "Low" severity. That assessment was incomplete. The retry strategy interaction is worse: a stale request encoded as `.imageNotExisting` enters `handler` (KingfisherManager.swift:394), which consults `retryStrategy`. If the user has configured a retry strategy (e.g., `DelayRetryStrategy(maxRetryCount: 3)`), the stale request loops through `startNewRetrieveTask` → `imageCachedType` (still cached) → `retrieveImage` → ioQueue → checker (still stale) → `.imageNotExisting` → retry again, up to `maxRetryCount` times. Each cycle is individually cheap (the checker skips decode), but the loop is semantically wrong and wastes work.

The `DiskRetrievalResult` enum approach cleanly eliminates this. By distinguishing `.stale` from `.none` at the `ImageCache` boundary, `KingfisherManager` can short-circuit before entering retry or alternative-source logic. The additional complexity is minimal (~10 lines for the enum + ~10 lines for handling), and it makes the control flow explicit and correct.

### On Thread Safety

The review correctly notes that our thread-safety discussion is optimistic. The practical risk is indeed low — `objc_getAssociatedObject` uses an internal runtime lock, and the `Box` value is immediately copied — but "probably safe due to runtime implementation details" is not a strong engineering argument for a library consumed by thousands of apps.

More importantly, we are not introducing this pattern — the existing `onShouldApply` in `ImageProgressiveProvider` already reads `taskIdentifier` from a background thread. Our change would amplify the reliance on this pattern (every disk cache retrieval instead of only progressive loads), which makes the case for fixing it stronger.

A `CancellationToken` (~15 lines) replaces the cross-actor read with a lock-protected `Bool`. The checker becomes `{ !token.isCancelled }` instead of `{ issuedIdentifier == self.taskIdentifier }`. This is self-evidently correct and does not depend on ObjC runtime internals. It should be included in the initial PR rather than deferred, since the incremental cost is small and the design becomes clean from the start.

### On `backgroundDecode` Check

Agreed. `decoded(scale:)` forces bitmap rendering into a new context. The cost is secondary to WebP deserialization, but the check is nearly free and keeps the pattern consistent. Include it.

### On Regression Tests

The original test plan (Section 8, Step 5) covered the basic cases but missed two important scenarios:

1. **Retry strategy with stale request**: Verify that a stale disk cache retrieval does not enter the retry loop. With `DiskRetrievalResult.stale`, the handler should short-circuit before consulting `retryStrategy`.

2. **Alternative sources with stale request**: Verify that a stale result does not trigger `popAlternativeSource()`. The `.stale` result should bypass `failCurrentSource` entirely.

These should be added to the test plan.

### On the Documentation Note

Confirmed. Section 3.1 and the Summary table in Section 3 reference `KingfisherOptionsInfoItems.swift`. The correct file name is `KingfisherOptionsInfo.swift`. This will be corrected when the implementation plan is finalized.

### Summary of Accepted Changes

All five recommendations are accepted for the initial PR:

| # | Recommendation | Rationale | Additional Code |
|---|---------------|-----------|-----------------|
| 1 | Keep staleness checking before deserialization | Core optimization, already planned | 0 |
| 2 | Use `DiskRetrievalResult` instead of `.success(nil)` | Prevents semantic leakage into retry and alternative-source logic | ~10 lines |
| 3 | Short-circuit `.stale` in `KingfisherManager` | Avoids retry loops and spurious downloads for stale requests | ~10 lines |
| 4 | `CancellationToken` for thread-safe state | Eliminates cross-actor associated-object reads from ioQueue | ~15 lines |
| 5 | Regression tests for retry and `alternativeSources` | Covers the interaction that the original test plan missed | Test code |

Total additional production code over the original proposal: ~35 lines. The overall change remains small and focused.

---

## File: docs/project-overview.md

# Kingfisher Project Overview

## Project Purpose

Kingfisher is a powerful, pure-Swift library for downloading and caching images from the web. It provides an elegant, asynchronous API for managing remote images in iOS, macOS, tvOS, watchOS, and visionOS applications. The library handles the complete lifecycle of image loading - from network downloading to multi-layer caching (memory and disk), with built-in image processing capabilities and extensive platform-specific UI component integrations.

The framework follows a modular architecture with clear separation of concerns, allowing developers to use individual components (downloader, cache, processors) independently or as a unified solution. Through its namespace wrapper pattern (`.kf` property) and builder pattern (`KF.url()`), Kingfisher offers both UIKit and SwiftUI support with minimal code overhead.

## Main Entry Points

### Core Configuration
- **Package.swift** - Swift Package Manager manifest defining library targets and platform requirements
- **Kingfisher.podspec** - CocoaPods specification (version 8.3.2)
- **Sources/General/Kingfisher.swift** - Core type definitions and protocol declarations
- **Sources/General/KingfisherManager.swift** - Central coordinator managing download and cache operations

### Primary APIs
- **Sources/General/KF.swift** - Builder pattern entry point for fluent API
- **Sources/Extensions/ImageView+Kingfisher.swift** - UIImageView/NSImageView extensions
- **Sources/SwiftUI/KFImage.swift** - SwiftUI image component
- **Sources/SwiftUI/KFAnimatedImage.swift** - SwiftUI animated image support

## Technology Stack

### Core Components
- **Image Downloading**: `Sources/Networking/ImageDownloader.swift` - URLSession-based networking layer
- **Cache System**: 
  - `Sources/Cache/ImageCache.swift` - Dual-layer cache coordinator
  - `Sources/Cache/MemoryStorage.swift` - In-memory cache implementation
  - `Sources/Cache/DiskStorage.swift` - Persistent disk storage
- **Image Processing**: `Sources/Image/ImageProcessor.swift` - Transformation pipeline with filters
- **Format Support**: `Sources/Image/ImageFormat.swift` - Multi-format detection (JPEG, PNG, GIF, WebP)

### Platform Integrations
- **UIKit Extensions**: `Sources/Extensions/UIButton+Kingfisher.swift`, `Sources/Extensions/NSTextAttachment+Kingfisher.swift`
- **SwiftUI Components**: `Sources/SwiftUI/KFImageProtocol.swift`, `Sources/SwiftUI/ImageBinder.swift`
- **Specialized Views**: `Sources/Views/AnimatedImageView.swift`, `Sources/Extensions/PHLivePhotoView+Kingfisher.swift`

### Build & Testing
- **Fastlane**: `fastlane/Fastfile` - Primary build automation
- **Test Suite**: `Tests/KingfisherTests/` - XCTest-based unit tests with Nocilla HTTP stubbing
- **Documentation**: `Sources/Documentation.docc/` - DocC integrated documentation

## Platform Support

### Minimum Requirements
- **Swift**: 5.9+ (Swift 6 strict concurrency ready)
- **UIKit/AppKit**: 
  - iOS 13.0+ (`#if os(iOS)`)
  - macOS 10.15+ (`#if os(macOS)`)
  - tvOS 13.0+ (`#if os(tvOS)`)
  - watchOS 6.0+ (`#if os(watchOS)`)
  - visionOS 1.0+ (`#if os(visionOS)`)
- **SwiftUI**: iOS 14.0+ / macOS 11.0+ / tvOS 14.0+ / watchOS 7.0+ / visionOS 1.0+

### Platform-Specific Files
- **macOS**: `Sources/Extensions/NSButton+Kingfisher.swift` - NSButton image loading
- **iOS/tvOS**: `Sources/Extensions/UIButton+Kingfisher.swift` - UIButton extensions
- **watchOS**: `Sources/Extensions/WKInterfaceImage+Kingfisher.swift` - WatchKit support
- **CarPlay**: `Sources/Extensions/CPListItem+Kingfisher.swift` - CarPlay list items (iOS 14.0+)
- **tvOS**: `Sources/Extensions/TVMonogramView+Kingfisher.swift` - Apple TV monogram views

---

## File: docs/testing.md

# Kingfisher Testing Documentation

## Overview

Kingfisher's test suite is located in the `Tests/KingfisherTests/` directory and provides comprehensive coverage for all major components of the library. The test suite uses XCTest framework with custom helper utilities and the Nocilla dependency for HTTP request stubbing.

### Test Infrastructure

- **Test Framework**: XCTest
- **Network Mocking**: Nocilla (located at `Tests/Dependency/Nocilla/`)
- **Test Helper**: `Tests/KingfisherTests/KingfisherTestHelper.swift`
- **Stub Utilities**: `Tests/KingfisherTests/Utils/StubHelpers.swift`
- **Test Assets**: 
  - `Tests/KingfisherTests/dancing-banana.gif` - Animated GIF for testing
  - `Tests/KingfisherTests/single-frame.gif` - Single frame GIF for testing

## Test Categories

### 1. Core Component Tests

**Cache Layer Tests**
- `Tests/KingfisherTests/ImageCacheTests.swift` - Tests for the main ImageCache functionality
- `Tests/KingfisherTests/MemoryStorageTests.swift` - Memory cache specific tests
- `Tests/KingfisherTests/DiskStorageTests.swift` - Disk storage specific tests
- `Tests/KingfisherTests/StorageExpirationTests.swift` - Cache expiration policy tests

**Networking Tests**
- `Tests/KingfisherTests/ImageDownloaderTests.swift` - Image downloading and session management
- `Tests/KingfisherTests/ImagePrefetcherTests.swift` - Batch image prefetching functionality
- `Tests/KingfisherTests/DataReceivingSideEffectTests.swift` - Data processing side effects

**Manager Tests**
- `Tests/KingfisherTests/KingfisherManagerTests.swift` - Central coordinator tests
- `Tests/KingfisherTests/KingfisherOptionsInfoTests.swift` - Configuration options tests

### 2. Image Processing Tests

- `Tests/KingfisherTests/ImageProcessorTests.swift` - Image transformation pipeline tests
- `Tests/KingfisherTests/ImageDrawingTests.swift` - Image drawing and rendering tests
- `Tests/KingfisherTests/ImageExtensionTests.swift` - Core image extensions tests
- `Tests/KingfisherTests/ImageModifierTests.swift` - Request modifier tests

### 3. UI Integration Tests

**UIKit Tests**
- `Tests/KingfisherTests/ImageViewExtensionTests.swift` - UIImageView extension tests
- `Tests/KingfisherTests/UIButtonExtensionTests.swift` - UIButton extension tests

**AppKit Tests**
- `Tests/KingfisherTests/NSButtonExtensionTests.swift` - NSButton extension tests (macOS)

### 4. Specialized Feature Tests

- `Tests/KingfisherTests/LivePhotoSourceTests.swift` - Live Photo support tests
- `Tests/KingfisherTests/ImageDataProviderTests.swift` - Custom data provider tests
- `Tests/KingfisherTests/RetryStrategyTests.swift` - Network retry strategy tests
- `Tests/KingfisherTests/StringExtensionTests.swift` - String utility extension tests

## Running Tests

### Using Fastlane

```bash
# Install dependencies first
bundle install

# Run all tests across all platforms
bundle exec fastlane tests

# Expected output:
# [08:30:00]: ------------------------------
# [08:30:00]: --- Step: default_platform ---
# [08:30:00]: ------------------------------
# [08:30:00]: Driving the lane 'ios tests' 🚀
# [08:30:01]: ------------------
# [08:30:01]: --- Step: scan ---
# [08:30:01]: ------------------
# [08:30:01]: Running Tests: ▸ Touching Kingfisher.framework
# [08:30:45]: Test Succeeded

# Run tests for specific platform
bundle exec fastlane test destination:"platform=iOS Simulator,name=iPhone 15"
bundle exec fastlane test destination:"platform=macOS"
bundle exec fastlane test destination:"platform=tvOS Simulator,name=Apple TV"

# CI-specific test command (used in continuous integration)
DESTINATION="platform=iOS Simulator,name=iPhone 15,OS=17.5" bundle exec fastlane test_ci

# Build only (for watchOS where full testing isn't supported)
bundle exec fastlane build destination:"platform=watchOS Simulator,name=Apple Watch Series 9 (41mm)"
```

### Using Xcode

```bash
# Open workspace in Xcode
open Kingfisher.xcworkspace

# Then use Xcode's test navigator or press Cmd+U to run all tests
# Or use xcodebuild directly:
xcodebuild test -workspace Kingfisher.xcworkspace -scheme Kingfisher -destination "platform=iOS Simulator,name=iPhone 15"
```

## Test File Organization Reference

### Directory Structure
```
Tests/
├── Dependency/
│   └── Nocilla/              # HTTP stubbing framework
│       ├── LICENSE
│       ├── README.md
│       └── Nocilla/          # Nocilla source files
│           ├── Categories/   # NSData and NSString extensions
│           ├── DSL/          # Domain-specific language for stubbing
│           ├── Diff/         # Request diff utilities
│           ├── Hooks/        # HTTP client hooks (NSURLSession, etc.)
│           ├── Matchers/     # Request matching logic
│           ├── Model/        # HTTP request/response models
│           └── Stubs/        # Stub implementation
└── KingfisherTests/
    ├── Info.plist
    ├── KingfisherTestHelper.swift      # Main test helper utilities
    ├── KingfisherTests-Bridging-Header.h  # Objective-C bridging
    ├── Utils/
    │   └── StubHelpers.swift           # Network stubbing helpers
    ├── dancing-banana.gif              # Animated test image
    ├── single-frame.gif                # Static test image
    └── *Tests.swift                    # Individual test files
```

### Build System Test Targets

**Xcode Scheme**: `Kingfisher.xcscheme`
- Configured for testing on all supported platforms
- Includes code coverage collection
- Uses parallel testing when available

**Fastlane Configuration**: `fastlane/Fastfile`
- `tests` lane: Runs tests on all platforms
- `test_ci` lane: CI-specific testing with environment-based destination
- `test` lane: Core test execution with scan action
- `build` lane: Build-only verification (used for watchOS)

**Platform Test Destinations**:
- iOS: `platform=iOS Simulator,name=iPhone 15,OS=17.5`
- macOS: `platform=macOS`
- tvOS: `platform=tvOS Simulator,name=Apple TV,OS=17.5`
- watchOS: `platform=watchOS Simulator,name=Apple Watch Series 9 (41mm),OS=10.5` (build only)

### Test Helper Utilities

The `KingfisherTestHelper.swift` provides:
- Pre-encoded test image data in various formats (PNG, JPEG, GIF, HEIC, MOV)
- Test URLs and keys for stubbing
- Cache cleanup utilities (`cleanDefaultCache`, `clearCaches`)
- Image comparison with tolerance (`renderEqual`)
- Timing utilities (`delay`)
- Platform-specific test helpers

The `StubHelpers.swift` provides:
- `stub()` - Create HTTP response stubs with custom data and headers
- `delayedStub()` - Create delayed response stubs for timing tests
- Network error stubbing capabilities

---

## File: README-LLM.md

# Kingfisher

Kingfisher is a powerful, pure-Swift library for downloading and caching images from the web, providing elegant async APIs for iOS, macOS, tvOS, watchOS, and visionOS applications. The library handles the complete image lifecycle with multi-layer caching, built-in processing, and extensive UI component integrations.

## Quick Start

**Core API Entry Points:**
- `Sources/General/KingfisherManager.swift` - Central coordinator
- `Sources/General/KF.swift` - Builder pattern API (`KF.url()...`)
- `Sources/Extensions/ImageView+Kingfisher.swift` - UIKit/AppKit extensions
- `Sources/SwiftUI/KFImage.swift` - SwiftUI components

**Essential Build Commands:**
```bash
# Install dependencies and run all tests
bundle install && bundle exec fastlane tests

# Build for specific platform
swift build

# Full release workflow
bundle exec fastlane release version:X.X.X
```

## Documentation

**For LLMs and Developers:**

- **[Project Overview](docs/project-overview.md)** - What Kingfisher does, core purpose, technology stack, and platform support
- **[Architecture](docs/architecture.md)** - System organization, component map, key files, and data flow with specific file references  
- **[Build System](docs/build-system.md)** - Swift Package Manager and Fastlane workflows, platform setup, and troubleshooting
- **[Testing](docs/testing.md)** - Test categories, running tests, and test infrastructure with file locations
- **[Development](docs/development.md)** - Code style, implementation patterns, workflows, and common solutions
- **[Deployment](docs/deployment.md)** - Package types, platform deployment, release management, and CI/CD
- **[File Catalog](docs/files.md)** - Comprehensive file organization with specific file purposes and relationships

**Configuration Files:**
- `Package.swift` - Swift Package Manager manifest
- `Kingfisher.podspec` - CocoaPods specification  
- `fastlane/Fastfile` - Build automation
- `Sources/Documentation.docc/` - DocC documentation

**Key Patterns:**
- Namespace wrapper (`.kf` property) in `Sources/General/Kingfisher.swift`
- Builder pattern API in `Sources/General/KF.swift` 
- Options system in `Sources/General/KingfisherOptionsInfo.swift`
- Protocol-oriented design throughout `Sources/Image/ImageProcessor.swift`

## Requirements

- **Swift 5.9+** (Swift 6 strict concurrency ready)
- **iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ / visionOS 1.0+**
- **SwiftUI support**: iOS 14.0+ / macOS 11.0+ / tvOS 14.0+ / watchOS 7.0+ / visionOS 1.0+