# PINCache: a non-deadlocking two-tier cache for iOS, tvOS and macOS

> PINCache is Pinterest's fork of TMCache, split into a memory store and a disk store that are coordinated so writes do not block readers. It is for Objective-C and Swift apps that persist expensive objects, and it is not a general-purpose database.

**pinterest/PINCache** — Fast, non-deadlocking parallel object cache for iOS, tvOS and OS X

- Repository: https://github.com/pinterest/PINCache
- Stars: 2,684 · Forks: 366
- Language: Objective-C
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/pinterest-pincache

## What PINCache actually replaces in an iOS app

The README describes PINCache as a key/value store "designed for persisting temporary objects that are expensive to reproduce, such as downloaded data or the results of slow processing." That is a narrower job than a database. If your app decodes JSON into model objects, re-renders thumbnails, or runs a slow image pipeline every time a screen appears, PINCache is aimed at that repeated work.

The project is a fork of TMCache, and the README states the reason plainly: it was "re-architected to fix issues with deadlocking caused by heavy use." That sentence is the whole pitch. The interesting question is not whether a cache is useful, it is whether the locking discipline survives concurrent access from several threads, which is exactly where the original reportedly struggled.

The audience is narrow and specific. PINCache stores any object conforming to NSCoding, which means it is an Apple-platform tool. The podspec, Cartfile and Package.swift in the repository confirm CocoaPods, Carthage and Swift Package Manager as the supported distribution routes. There is no server component and no network protocol.

## How the memory cache and disk cache stay in sync

PINCache is not one store. It is two self-similar stores, PINMemoryCache and PINDiskCache, and PINCache coordinates them. The README says objects added to memory "are available immediately to other threads while being written to disk safely in the background." So a write returns to the caller after the memory tier is populated, and the disk write happens off the calling thread.

The README also states that both underlying caches "use locks to protect reads and writes," and that both are backed by GCD and safe to access from multiple threads. Because the two caches are public properties of PINCache, you can bypass the coordinator and talk to PINMemoryCache or PINDiskCache directly when you want only one tier.

Lifetime differs between the tiers, and this is the part people get wrong. On iOS, PINMemoryCache clears itself when the app receives a memory warning or moves to the background. PINDiskCache does not clear itself. The README says objects there "remain until you trim the cache yourself, either manually or by setting a byte or age limit." A cache that never evicts on its own is a disk-growth problem waiting to happen unless you configure a limit.

Storage is via NSCoding, and the README notes that because of NSKeyedArchiver, objects repeated inside a collection occupy the space of one on disk. Storing an array of three references to the same image costs roughly one image on disk, not three.

## Installing PINCache with CocoaPods, Carthage or Swift Package Manager

The README documents four installation routes: dragging the PINCache folder into Xcode, a git submodule, CocoaPods, and Carthage. The repository adds a Package.swift, so Swift Package Manager is also present, and the Makefile has an spm target that runs swift test.

For CocoaPods, the README says to add PINCache to your Podfile and run pod install. The podspec at the repository root is named PINCache.podspec, and the Makefile's cocoapods target runs pod lib lint against it.

```bash
pod lib lint
```

For Carthage, the README gives the Cartfile line and the platform flag. The repository ships both Cartfile and Cartfile.resolved, and the Makefile's carthage target builds with xcframeworks.

```bash
carthage update --platform ios
```

Requirements matter before you start. The README states PINCache requires iOS 12.0, tvOS 12.0, watchOS 4.0 or macOS 10.13 and greater. If your deployment target is older, this is not the cache for you.

A first real use is a write followed by an asynchronous read. The README's Objective-C example stores a UIImage under a key and returns immediately, then retrieves it with a block. Note that the getter is objectForKeyAsync:block:, not a synchronous accessor.

```objective-c
UIImage *img = [[UIImage alloc] initWithData:data scale:[[UIScreen mainScreen] scale]];
[[PINCache sharedCache] setObject:img forKey:@"image" block:nil]; // returns immediately

[[PINCache sharedCache] objectForKeyAsync:@"image" block:^(PINCache *cache, NSString *key, id object) {
    UIImage *image = (UIImage *)object;
    NSLog(@"image scale: %f", image.scale);
}];
```

The Swift form is the same shape, with PINCache.shared() and a trailing closure. In Swift, Array, String and Dictionary are value types, so the README casts a collection to NSArray before storing it.

## Where PINCache is the wrong tool

The disk tier does not evict by itself. If you never set a byte limit or an age limit and never call a trim method, PINDiskCache keeps what you put there. That is documented behaviour, not a bug, but it means PINCache is a poor fit if you expect the cache to manage its own footprint without configuration.

Everything stored must conform to NSCoding. A type that cannot be archived, or a value you would rather serialize yourself, does not fit the API. The README's own note about casting Swift collections to NSArray is a reminder that the storage layer is NSKeyedArchiver underneath, with the encoding cost that implies.

Thread safety is claimed, but the repository's Makefile carries a TODO in the analyze target: "Fix data races and enable thread sanitizer with '-enableThreadSanitizer YES'." The same TODO appears in the spm target for '--sanitize thread'. That is the maintainers' own note, and it tells you the sanitizer is not currently part of the default test run. The Makefile also pins the test destination to a specific simulator, PLATFORM="platform=iOS Simulator,name=iPhone 17", so the default test path assumes a recent Xcode.

Finally, this is an Apple-platform cache. If you need the same cache semantics on Android, on a server, or in a shared backend, PINCache will not help, and there is no cross-platform story in the README.

## PINCache versus YYCache and rolling your own NSCache layer

The obvious alternative in the same ecosystem is YYCache, which also offers a memory tier and a disk tier for iOS. The difference in approach is the storage format and the API surface. PINCache is built on NSCoding and NSKeyedArchiver, so anything you store must be archivable and the disk representation follows that format. A cache that instead writes its own compact representation, or stores raw data blobs, avoids the NSCoding requirement entirely and can be cheaper for large binary payloads where you already have NSData in hand.

A second option is to assemble the tiers yourself: NSCache for memory, plus your own file writes for disk. NSCache already evicts under memory pressure, so the memory half is not the hard part. The hard part is the coordination PINCache claims to solve, namely making a value visible in memory immediately while the disk write proceeds in the background, without the two tiers fighting over locks. If you build that yourself, you own the concurrency reasoning.

The related searches around this project also point at PINRemoteImage and Pinoperation, which are separate Pinterest libraries rather than replacements for PINCache. PINRemoteImage is an image fetching and caching component, not a general key/value store. If your actual problem is remote images, look there first; PINCache is the lower-level primitive.

## Maintenance, licensing and what an upgrade costs

The repository is not archived, and the last push was on 2026-09-18. Release history is uneven: 3.0.2 is labelled "Swift Package Manager Support" and shipped on 2020-10-06, 3.0.3 followed on 2020-10-22, and 3.0.4 landed on 2024-05-13. That gap between 3.0.3 and 3.0.4 is worth knowing if you are pinning a version, because a long quiet period means fewer intermediate patch releases to land on.

Upgrade cost is dominated by the NSCoding contract. If you change how a stored type encodes itself, previously written disk entries may not decode cleanly. The README does not document a migration path for on-disk entries, and it does not document rollback, so plan for the cache to be disposable rather than authoritative. Treating it as a cache and not a store is the safest reading of the documentation.

Licensing is Apache-2.0. The README carries the standard Apache text, with copyright lines for Tumblr, Inc. (2013) and Pinterest, Inc. (2015), and points at LICENSE.txt for the specific terms. Apache-2.0 is permissive and includes an explicit patent grant, which matters for a library you ship inside an app binary. That is a description of the licence file, not legal advice; if your organisation has a policy on bundled dependencies, run it past whoever owns that policy.

The build tooling assumes a modern Apple toolchain. The Makefile's example target refuses to run below Xcode 15, printing "Xcode 15 and Swift 5.9 required to build example project", and the analyze and spm targets both carry the thread-sanitizer TODO noted earlier.

## Conclusion

Adopt PINCache if you have an Objective-C or Swift app that repeatedly rebuilds the same NSCoding objects and you want a memory tier plus a disk tier behind one key/value API. Do not adopt it as a primary data store, a cross-platform cache, or a replacement for a networking layer. Before wiring it in, verify that every type you intend to store conforms to NSCoding, and decide what disk byte limit or age limit you will set, because PINDiskCache keeps objects until you trim it yourself.

## FAQ

### Does PINCache work on Android or on a server?

No. PINCache stores objects conforming to NSCoding and requires iOS 12.0, tvOS 12.0, watchOS 4.0 or macOS 10.13 and greater, so it is an Apple-platform library only.

### Does PINCache clear its disk cache automatically?

No. The README states that objects in PINDiskCache remain until you trim the cache yourself, either manually or by setting a byte or age limit. Only PINMemoryCache clears itself, and only on iOS when the app receives a memory warning or goes into the background.

### How do I install PINCache in an iOS project?

The README documents dragging the PINCache folder into Xcode, a git submodule, CocoaPods (add PINCache to your Podfile and run pod install), and Carthage (add the Cartfile line and run carthage update --platform ios). The repository also ships a Package.swift for Swift Package Manager.

### What kinds of objects can I store in PINCache?

Any object conforming to NSCoding. The README notes that because of NSKeyedArchiver, objects repeated inside a collection only occupy the space of one on disk.

## Sources

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

---

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