Open-source project
JanGorman/Agrume avatar
JanGorman/Agrume

Agrume: a small iOS image viewer for zoom, GIFs and collection views

🍋 A lemony fresh iOS image viewer written in Swift.

892 stars121 forksSwiftMIT

At a glance

What is it?
Agrume is a MIT licensed Swift image viewer for iOS that adds pinch to zoom, a swipeable multi-image library, animated GIF support and both UIKit and SwiftUI entry points, distributed through SPM, CocoaPods and Carthage. The judgement is that it remains a tidy, focused drop-in, while the release history stopped in 2023 and the stated requirements are old enough to check against your deployment target.
Who is it for?
Agrume suits an iOS team that needs a zoomable image viewer in an afternoon, especially one that already shows images in a collection view, since the didScroll callback and the array initialisers exist for exactly that case, and it suits anyone who wants animated GIFs without writing the decoding glue.
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 6 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 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One job, done in a small library

Agrume is described as an iOS image viewer written in Swift with support for multiple images, and the description in the repository title adds a lemon emoji and calls it lemony fresh. The scope is narrow in a way that makes it easy to judge. It presents images full screen, lets the user pinch and zoom them, lets them flick a picture away to dismiss it, and can page through a set of images as a gallery. What it is not is a media browser with sharing, editing, saving to camera roll or thumbnails, and there is no sign of any of those in the documented API. The audience is an app developer who has an image and needs a decent viewer, which is a need that comes up constantly in apps that show user content, product photos or receipts. The repository carries an MIT licence, a Carthage compatible badge, a CocoaPods version badge, a Swift Package Manager badge, and the source tree holds the framework sources under Agrume/, a sample app under Example/, tests under AgrumeTests/ and a Frameworks directory.

Three package managers, and requirements from 2019

Installation is offered three ways, which is generous for a library this small and tells you the author wanted it to drop into whatever build system the adopter already had. Swift Package Manager is the first option mentioned, and there is a Package.swift and a Package.resolved in the tree. CocoaPods is the second, where you add the dependency to your Podfile and run pod install:

ruby
pod "Agrume"

Carthage is the third, with the dependency in a Cartfile and an update command:

code
github "JanGorman/Agrume"

The requirements section is where you need to look before choosing. The README lists Swift 5.0, iOS 9.0 and later, and Xcode 10.2 or later. Those numbers are conservative, which is good news for adoption since a library with a low floor usually compiles on newer toolchains, but the branch has moved since then, so the real question is what the current source needs rather than what the README still claims. The repository also carries a Cartfile.resolved, a Gemfile and Gemfile.lock, a .swiftlint.yml configuration, a Dangerfile, a .travis.yml file and a .gitmodules file, which together tell you that the project grew up with a Carthage, CocoaPods and linting setup.

Presenting from UIKit takes one closure

The API is at its simplest in the UIKit case. You construct an Agrume instance with an image and call show(from:) with the presenting view controller:

swift
import Agrume

private lazy var agrume = Agrume(image: UIImage(named: "…")!)

@IBAction func openImage(_ sender: Any) {
  agrume.show(from: self)
}

The same object can be built from a URL instead, and the README notes that Agrume takes care of the download in that case. That distinction matters because the download path is where the library leaves you an escape hatch, and it is covered in detail further down. The included sample project demonstrates all of the ways the viewer can be used, so the shortest useful answer to how it works is that the sample app is the reference documentation, and the README points at it explicitly.

A SwiftUI path that mirrors the UIKit flow

The SwiftUI implementation supports the same basic presentation flow as the UIKit API and surfaces common configuration options such as background, dismissal and Live Text. The view is driven by a binding rather than a presentation call, and the configuration options are passed in as values:

swift
AgrumeView(
  images: images,
  dismissal: .withPan(.standard),
  enableLiveText: true,
  isPresenting: $showAgrume
)

Two details in that call are worth noticing. Live Text can be switched on, so the viewer is not limited to presenting pictures, it can surface the text recognised in them. And the isPresenting binding is the mechanism by which the host view knows to hide its own launch button, since the sample wraps the whole presentation in a conditional and animates the transition. Whether this path still compiles cleanly against a current SwiftUI toolchain is the question the documentation does not answer, so the sensible check is to build the sample.

Backgrounds, dismissal and syncing with a collection view

The configuration options are where a viewer either feels native or does not, and Agrume covers the three that matter. Backgrounds come in two families, a blurred style that blurs the view it covers and a colored style, so a green background is a one line change. Dismissal is either the default pan and flick gesture or a close button, and the close button can be a default one that displays a localized Close string or your own UIBarButtonItem, which the README demonstrates with a system stop button tinted red. The multiple image case is where this library earns its keep, since you can hand it an array of images or an array of URLs along with the index to start at, and get back a didScroll callback:

swift
let agrume = Agrume(images: images, startIndex: indexPath.item, background: .blurred(.light))

agrume.didScroll = { [unowned self] index in
  self.collectionView.scrollToItem(at: IndexPath(item: index, section: 0), at: [], animated: false)
}
agrume.show(from: self)

That callback is what keeps a zoomed gallery and the collection view behind it in step, which is the small piece of glue that a naive implementation always ends up writing by hand.

Animated GIFs arrive with SwiftyGif bundled in

Animated images are supported by bundling SwiftyGif, and the README is explicit that you use SwiftyGif's custom UIImage initialiser rather than an Agrume specific one:

swift
let image = UIImage(gifName: "animated.gif")
let agrume = Agrume(image: image)

The same approach works with raw data through the gifData initialiser, and you can mix animated and still images in a single array because the library detects the type itself. Remote animated images are supported too, through the URL initialisers, again with detection handled for you. There is one caveat in the README that would cost an afternoon if you missed it. If you are using Agrume from a custom UIImageView, you may need to rebuild the UIImage from the original data in order to preserve the animation, rather than passing the UIImage instance you got from the image view. That is the sort of detail that comes from the library having been written against a real image loading layer rather than in isolation.

Taking over downloads, globally or per instance

If you want control over how images are fetched, Agrume gives you three levels of intervention, which is a thoughtful design for something this small. The first is a per instance download closure, where you receive a URL and a completion and hand back an image, which the README shows using the author's own MapleBacon downloader. The second is a global handler set on the service locator, which applies to every download unless an instance overrides it:

swift
AgrumeServiceLocator.shared.setDownloadHandler { url, completion in
  // Download data, cache it and call the completion with the resulting UIImage
}

The third is a data source protocol, AgrumeDataSource, which supplies images dynamically, where Agrume asks the source how many images there are and reloads its scrolling view if that number changes. That last one is the answer for a library that is populated from a server, and it is the reason the library can sit behind an infinite feed rather than a fixed array. A small statusBarStyle property on Agrume covers the last piece of presentation polish, letting you set the status bar appearance while the zoomed view is up.

Editorial conclusion

Agrume suits an iOS team that needs a zoomable image viewer in an afternoon, especially one that already shows images in a collection view, since the didScroll callback and the array initialisers exist for exactly that case, and it suits anyone who wants animated GIFs without writing the decoding glue. It is the wrong choice if you need a maintained release cadence, since the newest tag is 5.8.10 from 2023-12-15, and a poor fit for a modern SwiftUI codebase that expects current APIs, because the documented requirements stop at Swift 5.0, iOS 9 and Xcode 10.2. Before you adopt it, read the status bar and dismissal options in the README, check that the SwiftyGif dependency resolves for your deployment target, and verify the SwiftUI example compiles against your current toolchain rather than assuming the branch state is release state.

Frequently asked questions

How do I install Agrume in an iOS app?

Use Swift Package Manager, or add pod "Agrume" to your Podfile and run pod install, or add github "JanGorman/Agrume" to your Cartfile and run carthage update. The README lists the requirements as Swift 5.0, iOS 9.0 and later, and Xcode 10.2 or later.

Can I use Agrume from SwiftUI?

Yes. The SwiftUI implementation supports the same basic presentation flow as the UIKit API, and AgrumeView takes a set of images plus options for background, dismissal, Live Text and an isPresenting binding, so the host view can react when the viewer opens or closes.

Does Agrume show animated GIFs and remote images?

Yes. Agrume bundles SwiftyGif and you create animated images with the UIImage(gifName:) and gifData: initialisers, remote animated images are supported through the URL initialisers, and the library detects the image type itself so animated and regular images can be mixed in one array.

Official sources

  1. Issues
  2. JanGorman/Agrume on GitHub
  3. License: MIT
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jangorman-agrume.svg)](https://hysenlabs.com/projects/jangorman-agrume)

Was this useful?