# PINRemoteImage: Pinterest's deduplicating image fetcher for UIKit

> PINRemoteImage is an Objective-C image downloading and caching layer built around one idea: if six image views ask for the same URL, the work happens once. Here is how its task model, its progressive JPEG support and its three packaging paths actually fit together.

**pinterest/PINRemoteImage** — A thread safe, performant, feature rich image fetcher

- Repository: https://github.com/pinterest/PINRemoteImage
- Stars: 4,023 · Forks: 517
- Language: Objective-C
- License: Apache-2.0
- Published: 2026-10-08 · Updated: 2026-10-08 · Language: en
- Canonical page: https://hysenlabs.com/projects/pinterest-pinremoteimage

## One download per URL, regardless of how many views ask

The design centre of this library is a single sentence in the README: it uses the concept of download and processing tasks to ensure that even if multiple calls to download or process an image are made, it only occurs one time, unless an item is no longer in the cache.

That is a cache miss problem with a concurrency problem inside it. The naive approach is a dictionary guarded by a lock. The approach here describes tasks, which means an in-flight request is a first-class thing you can join rather than a second download. When a view is recycled while its image is still loading, the queued task is what keeps the network from being hit again.

The threading model is stated explicitly. PINRemoteImageManager is backed by GCD and is safe to access from multiple threads simultaneously, and it ensures images are decoded off the main thread so animation performance is not affected. None of the exposed methods allow synchronous access, which is the right call for a UIKit library. The one concession is a cache hit: it is optimized to call completions on the calling thread if an item is in its memory cache, so a warm image costs you no hop to the main queue.

## The smallest useful call, on a plain UIImageView

The API is a category method, which means the entry cost is one line. In Swift:

```swift
let imageView = UIImageView()
imageView.pin_setImage(from: URL(string: "https://pinterest.com/kitten.jpg")!)
```

The Objective-C spelling is the same idea with a different prefix, `pin_setImageFromURL:` on a UIImageView allocated with `init`.

Worth noting what is not in that call: no placeholder, no cancellation handle, no completion block. The manager returns nothing synchronously because it cannot, and the design assumes you will not need to interrupt. That assumption is reasonable inside a table view cell and questionable in a screen that shows a dozen large images at once.

PINRemoteImageCategoryManager defines the protocol that UIView subclasses implement to get this access. The README names built-in categories on UIImageView, on PINAnimatedImageView and on UIButton, and says writing a new one is easy. Buttons are the interesting case, since a button's image has to survive being set while a download is already running.

## Progressive JPEG with a blur applied before the final scan

Progressive JPEG support is the feature that distinguishes this from a generic image loader, and Pinterest added something on top of the standard behaviour.

The README is explicit that this is not ordinary progressive JPEG support: PINRemoteImage adds an attractive blur to progressive scans before returning them. The reasoning is sound. A progressive file delivers a coarse scan first and refines it over several passes, and showing the half-decoded intermediate frames raw looks worse than showing nothing, because the user sees a blocky image appear and then sharpen. Applying a blur to the early scans turns the same mechanism into a smooth fade-in.

The method is a separate opt-in rather than the default:

```swift
imageView.pin_updateWithProgress = true
imageView.pin_setImage(from: URL(string: "https://pinterest.com/progressiveKitten.jpg")!)
```

The repository carries a `progressive.gif` at the top level as a visual demonstration, and the README embeds the same file. So the feature has a visual artifact you can inspect without running anything.

## WebP, animated GIF and the processor hook

Format support is broader than the name suggests. PNG and JPG are the base cases. WebP decoding works if Google's library is available, which makes it conditional rather than guaranteed. GIFs and animated WebP are handled through PINAnimatedImageView:

```swift
let animatedImageView = PINAnimatedImageView()
animatedImageView.pin_setImage(from: URL(string: "http://pinterest.com/flyingKitten.gif")!)
```

The processor hook is the part that pays off in a real app. You pass a `processorKey` and a block, and the processed result is cached under that key rather than being redone for every cell that needs the same transformation. The README's example rounds the corners of a 200 by 300 target with a radius of 7.0 and clips it with a UIBezierPath.

One inconsistency is worth flagging, because it will send you looking for a class that is not there. The Objective-C processor example allocates a `UIImageView`, while the Swift version allocates an `FLAnimatedImageView`. FLAnimatedImage is the older library that preceded PINAnimatedImageView in this space, so the Swift sample appears to have been carried over from a previous example rather than updated. If you copy it literally you will be missing an import.

## Three ways to install it, and a Makefile that runs all of them

The repository supports CocoaPods, Carthage and Swift Package Manager, with a podspec, a Cartfile and Cartfile.resolved, and a Package.swift all committed. There is also an `.xcodeproj` and an `.xcworkspace`, so the source can be built directly.

The Makefile at the repository root is the interesting part, because it wires the three managers into one verification target. The Carthage path deliberately avoids prebuilt binaries and builds from source:

```bash
carthage update --no-use-binaries --no-build
carthage build --no-skip-current --use-xcframeworks
```

That is the slow, honest configuration. A normal Carthage consumer downloads prebuilt frameworks, which would skip exactly the build verification this project wants from itself. The same reasoning applies to `pod lib lint --allow-warnings` for CocoaPods and `swift build` for Swift Package Manager.

The `test` target runs `xcodebuild clean test` against the simulator destination, and `analyze` runs the static analyzer and fails the build if it produced any HTML report. The `example` target refuses to run on anything older than Xcode 16, printing a message that Xcode 16 and Swift 5.9 are required. The simulator destination is pinned to `platform=iOS Simulator,name=iPhone 17`, so anyone without that simulator installed edits the Makefile before the test target works.

## A 2024 tag on a branch pushed in 2026, and a stale path in the README

Two facts in this repository deserve to be reconciled before you plan an upgrade.

The first is versioning. The most recent release is 3.0.4, published on 2024-05-15. Before it come 3.0.3 on 2020-10-23 and 3.0.2 on 2020-10-22, a two-week gap between the two that suggests the current line was established quickly and then left alone. The repository itself was last pushed on 2026-09-18 and is not archived. So the branch receives commits while the newest tag is more than two years older. Depending on which manager you use, you may be building from a tag or from a branch, and those are not the same code.

The second is documentation drift. The README points at `Source/Classes/PINRemoteImageManager.h` for the manager and links a category header at `/Pod/Classes/Image Categories/UIImageView+PINRemoteImage.h`, and it refers to PINRemoteImageCategoryManager at `Pod/Classes/PINRemoteImageCategoryManager.h`. The repository tree shows both `Source/` and a `PINRemoteImage/` directory and no `Pod/` directory. Those links predate a layout change and will not resolve.

The examples directory tells you the supported platforms more honestly than the prose does: there are separate projects for iOS, macOS, tvOS, an Xcode Swift Package Manager example and a shared target, plus a Swift example.

## Conclusion

PINRemoteImage is worth adopting when your UIKit app shows remote images inside scrolling lists and you have already felt duplicate requests or main thread decode stalls. It is the wrong choice for a new SwiftUI codebase, because the public surface is UIKit categories and a manager class rather than an async or Combine interface. Check two things before you commit: whether the version you can install matches the 3.0.4 tag from 2024-05-15 while the branch itself was pushed on 2026-09-18, and which of the three distribution paths, CocoaPods, Carthage or Swift Package Manager, your project already uses. Start with the `pin_setImage(from:)` call on a plain UIImageView before wiring up processors.

## FAQ

### How do you download an image into a UIImageView with PINRemoteImage?

Call the category method on the image view: in Swift, `imageView.pin_setImage(from: URL(string: "https://pinterest.com/kitten.jpg")!)`, or in Objective-C, `[imageView pin_setImageFromURL:[NSURL URLWithString:@"http://pinterest.com/kitten.jpg"]]`. The built-in categories cover UIImageView, PINAnimatedImageView and UIButton.

### Does PINRemoteImage download the same image only once?

Yes. The manager uses download and processing tasks so that multiple calls to download or process an image result in the work happening a single time, unless the item is no longer in the cache. Concurrent requests for the same URL join the existing task rather than starting another download.

### Does PINRemoteImage support WebP and animated GIFs?

It supports PNG and JPG natively, decodes WebP when Google's library is available, and handles GIFs and animated WebP through PINAnimatedImageView. It also supports progressive JPGs, adding a blur to the early scans so the image fades in rather than appearing blocky.

## Sources

- [Issues](https://github.com/pinterest/PINRemoteImage/issues)
- [License: Apache-2.0](https://github.com/pinterest/PINRemoteImage/blob/master/LICENSE)
- [pinterest/PINRemoteImage on GitHub](https://github.com/pinterest/PINRemoteImage)
- [README](https://github.com/pinterest/PINRemoteImage/blob/master/README.md)
- [Releases](https://github.com/pinterest/PINRemoteImage/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pinterest-pinremoteimage
