APNGKit: an APNG playback framework for Apple platform apps
High performance and delightful way to play with APNG format in iOS.
At a glance
- What is it?
- A Swift library that decodes APNG animations and plays them through a plain UIView subclass. Here is what the API covers, where the README runs out, and the platform question the repository leaves open.
- Who is it for?
- APNGKit earns its place when an Apple platform app has to display APNG files rather than only GIFs, and the surface you actually touch is small: one image type, one view class and a delegate. It stops being the right answer when you need frame scheduling control, memory limits or a platform the requirements line does not spell out, because none of that is documented on the repository page.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 111 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What APNG adds to PNG and where it still falls short
The README spends its longest section on the format rather than the code, and that is the right order to think about it. APNG extends the ordinary PNG format so a single file can carry an animation, and the two concrete gains the README names are 24-bit images and 8-bit transparency, neither of which GIF offers. File size is described as comparable to, or even smaller than, an equivalent GIF when the file is created carefully. For an app shipping animation, that is the trade you are making: better colour and alpha at roughly the same weight.
The portability property matters just as much. An APNG file starts with a standard PNG header, so a platform that knows nothing about APNG still recognises the file and draws its first frame as a static image. That graceful degradation is why the format spread despite the PNG group not accepting it. The README calls it a rebel format, notes that it is mentioned in the W3C standards for the img element, and points at WebKit core as the place where APNG support had already been added.
The MNG comparison is the author's way of explaining why a technically richer format can lose. MNG comes from the same team as PNG and is far more comprehensive, and the README says it is so complex that, despite being a standard, it was almost universally rejected, with Konqueror named as the one browser that supports it. The lesson drawn from that is about adoption rather than technical merit, which is the part that matters when you are deciding whether a format has a future on your users' devices.
Installing through Swift Package Manager, with a version note worth reading
Swift Package Manager is the recommended path, and the README gives the Xcode menu route rather than a manifest snippet: open the project's Swift Packages menu, choose Add Package Dependency, add the repository URL, then select Up to Next Major starting at 2.0.0.
Add https://github.com/onevcat/APNGKit.gitThe repository tree backs this up. Alongside the README there is a Package.swift, a Package.resolved and a [email protected], the last being the manifest a newer toolchain picks automatically when the two disagree. The README documents none of those three files, so on a recent Swift toolchain the manifest you actually resolve is the 5.9 variant and not the one the README's prose implies.
That version arithmetic is worth pausing on. The README says to enter 2.0.0, while the newest release on the repository is 2.4.0. Up to Next Major from 2.0.0 still resolves inside the 2.x line, so the instruction is a floor rather than a ceiling. The consequence is that your resolved version will almost certainly be newer than the one the README was written against, and CHANGELOG.md in the tree is where the differences are recorded. Requirements are listed as iOS 9.0 or later, macOS 10.11 or later and tvOS 9.0 or later.
The CocoaPods route and what the repository scripts reveal
CocoaPods is documented properly, with the gem install first and then a Podfile. The README is explicit about one detail that catches people out: after installing, open the generated xcworkspace rather than the xcodeproj.
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
target 'your_app' do
pod 'APNGKit', '~> 2.0'
end$ pod installNote that the sample Podfile pins the platform to iOS 9.0 with no macOS or tvOS entry, even though the requirements line lists all three systems. Copying that Podfile into a multiplatform target is the kind of small thing that produces a confusing build failure much later.
The tree also shows APNGKit.podspec, a Gemfile and Gemfile.lock, and a fastlane directory. That combination says the pod is published to a spec repo and released by script rather than by hand, which is the arrangement you want in a library other people consume through CocoaPods. Carthage appears only as a compatibility badge in the README header, with no instructions at all, so read that badge as an announcement rather than a documented path. Swift Package Manager has prose behind it, CocoaPods has a complete example, and Carthage has a badge.
Three throwing initializers for loading an APNGImage
The loading API is three initializers on one type, and all three can fail. `APNGImage(named:)` reads from the main bundle, `APNGImage(fileURL:)` takes a specific location, and `APNGImage(data:)` takes a Data value from disk, a network response or anywhere else.
import APNGKit// Load an APNG image from file in main bundle
var image = try APNGImage(named: "your_image")
// Or
// Load an APNG image from file at specified path
if let url = Bundle.main.url(forResource: "your_image", withExtension: "apng") {
image = try APNGImage(fileURL: path)
}The design choice worth naming is that every initializer throws rather than returning an optional or a placeholder. The README frames this as explicit error reporting: if something goes wrong while building the image you find out and can handle it, instead of holding an object that fails later on screen. For a decoder that parses a binary container, that is the right default, and the throwable signature is the API contract telling you so.
The second code block is the README's own sample, copied as printed, and it contains a mistake. The `if let` binds the constant `url`, then hands a different identifier, `path`, to the initializer, and no `path` is defined anywhere in the snippet. As written it will not compile. The intent is not in doubt, since the surrounding prose calls the argument a file URL, so the fix is to pass the constant you just bound. Copy the sample and rename that one argument rather than debugging the compiler.
APNGImageView starts playing the moment you hand it an image
Display is one class. `APNGImageView` is a subclass of `UIView` or `NSView`, not of `UIImageView` or `NSImageView`, which matters if you subclass it or swap it into existing layout code.
let image: APNGImage = ... // You already have an APNG image object.
let imageView = APNGImageView(image: image)
view.addSubview(imageView)Playback is automatic. Once the view is constructed with a valid image the animation begins on its own, and the escape hatch is a boolean property rather than a separate lifecycle call: set `autoStartAnimationWhenSetImage` to false before assigning the image, then call `startAnimating()` yourself.
let imageView = APNGImageView(frame: .zero)
imageView.autoStartAnimationWhenSetImage = false
imageView.image = image
// Start the animation manually:
imageView.startAnimating()That default has a consequence worth planning around. In a reused list cell, an image that begins animating on assignment means every cell starts its own animation as it scrolls into view, with no obvious way to stop it. The property is the mechanism for controlling that, and the README points straight at it.
For Interface Builder the instructions are unusually specific. Drag a plain `UIView` or `NSView` onto the canvas, change its class to `APNGImageView`, connect an outlet, and assign the image as usual. The emphasis on not using `UIImageView` is not a crash warning. It is telling you the view you should look for in the object library is a custom class, and the class of the view you dragged is what gets replaced.
Loop counts come from the file, and the delegate walkthrough is incomplete
APNG encodes a play count in the file itself, and `APNGImage` exposes it as `numberOfPlays`. The README states that APNGKit respects that value by default rather than looping forever, which is what you want for an image carrying an explicit loop instruction from whoever exported it.
To observe the end of each loop you register yourself as a delegate. The README introduces exactly that and then breaks off mid-sentence as it describes the delegate, so the protocol, its methods and their signatures are not set out anywhere on the repository page. That gap is the most useful thing to resolve yourself, because loop boundaries are where most custom animation work happens: pausing on a specific frame, driving a carousel, or skipping the animation entirely under a reduced motion setting.
The tree shows a Source directory and a Tests directory, so the implementation and its test suite exist as real directories. Neither is described, and the README says nothing about memory behaviour, frame caching or thread safety. The delegate question is the concrete next step: take the 2.4.0 source into a checkout and read the protocol before you design around it.
iOS in the description, three systems in the requirements line
The project's own repository description says APNGKit is a way to play with APNG in iOS. The README's opening line says iOS and macOS, and the requirements line lists iOS 9.0 or later, macOS 10.11 or later and tvOS 9.0 or later. All three statements sit in front of you at once and they do not agree with each other.
Neither one is necessarily the error. A description field is short by design, and iOS is where the framework is most used. The requirements line may be listing the floors its build settings were checked against rather than three equally supported targets. The README also never mentions tvOS again after that line, offers no tvOS-specific note, and gives a Podfile example for iOS alone, so the tvOS claim rests on a single thin line.
Settling it is cheap and does not require anyone else's opinion. Open APNGKit.podspec in the tree and read its declared platforms, then check the platform settings inside both Package.swift and [email protected], because the two manifests can disagree. If you need tvOS or a Catalyst build, that check is the one to do before writing any code. Those same files also give you the real deployment floor, which matters more than the README's number if you target devices older than iOS 9.
Releases, licence and what an upgrade actually costs you
Activity is easy to establish here. The last push was on 2026-06-22, the same day version 2.4.0 was published, and before that 2.3.0 arrived on 2024-03-19 and 2.2.5 on 2023-12-11. The repository is not archived, the default branch is master, and the licence field reads MIT with a LICENSE file at the root, which matches the badge in the README header.
The gaps in that cadence are worth naming rather than smoothing over. A two year gap between 2.2.5 and 2.3.0 followed by one in 2026 means the release history does not tell you much about how often breaking changes arrive. The version numbering is the more useful signal: the README's own instructions point at 2.0.0, placing the project on a 2.x major line that has since added four minor versions.
For an upgrade, CHANGELOG.md is the only place the answer is recorded. The README does not discuss version guarantees, deprecation policy or migration steps, so anyone moving to 2.4.0 reads that file first. Check the dependency surface at the same time, since a self-contained decoder that pulls nothing else in is a far easier upgrade than a library arriving with a transitive graph.
Editorial conclusion
APNGKit earns its place when an Apple platform app has to display APNG files rather than only GIFs, and the surface you actually touch is small: one image type, one view class and a delegate. It stops being the right answer when you need frame scheduling control, memory limits or a platform the requirements line does not spell out, because none of that is documented on the repository page. Start with the Swift Package Manager route and 2.0.0 as the README instructs, then open APNGKit.podspec and both Package.swift files before you commit a deployment target, and read CHANGELOG.md to see what moved between 2.0.0 and the 2.4.0 release.
Frequently asked questions
Is APNG better than GIF?
On image quality the README argues yes. APNG supports 24-bit images and 8-bit transparency, which GIF cannot, and file size is described as comparable to or even smaller than a GIF when the file is made carefully. The catch is support: where only a subset of platforms can animate an APNG, everything else falls back to a static first frame.
What is APNG used for?
It is used for animated images where GIF's colour depth and transparency limits show. APNGKit targets that case on Apple platforms, giving an app a way to load an APNG from the bundle, a file or raw data and play it through a view. Because the file begins with a standard PNG header, non-supporting platforms still display the first frame.
How do I add APNGKit to an Xcode project, and does tvOS work?
The recommended route is Swift Package Manager, adding the repository and selecting Up to Next Major from 2.0.0, with CocoaPods documented in full. tvOS appears once, in a requirements line listing iOS 9.0, macOS 10.11 and tvOS 9.0, and nowhere else in the documentation, so check the platform declarations in APNGKit.podspec and Package.swift before targeting it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/onevcat-apngkit)