# TTGSnackbar: an Android-style snackbar for iOS in Swift

> TTGSnackbar is a Swift library that gives iOS the transient message bar Android has had for years, with action buttons, semantic styles, custom content views, a queue manager and Swift concurrency support. It suits an app that wants to tell the user something without a modal, and it costs you iOS 16 as a floor if you take the current version.

**zekunyan/TTGSnackbar** — TTGSnackbar shows simple message and action button on the bottom or top of the screen with multi kinds of animation, which is written in Swift and inspired by Snackbar in Android. It also support showing custom view, icon image or multi action button.

- Repository: https://github.com/zekunyan/TTGSnackbar
- Stars: 647 · Forks: 103
- Language: Swift
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/zekunyan-ttgsnackbar

## What the library is: a transient message bar with an Android pedigree

TTGSnackbar is a Swift implementation of an Android-style Snackbar for iOS. It presents short, actionable messages at the top or bottom of the screen, and the README is explicit about the range of things it covers: custom views, queue management, semantic styles, accessibility, haptics and Swift concurrency.

That combination is the reason to pick it over writing the view yourself. A snackbar is a small piece of UI with a surprising number of details: it must respect the safe area, it must animate in from the correct edge, it must time out, it must handle a touch without dismissing immediately, it must survive an app going to the background, and it must be readable with Dynamic Type and announced by VoiceOver. Most hand-rolled versions get one of those wrong.

The feature list in the README enumerates the scope. Messages can be presented at the bottom or the top. There can be one or two action buttons, either text actions or icon-only ones. There are built-in semantic styles named default, info, success, warning, error and loading. Custom UIView content can be presented, or the snackbar can display inside a custom container view. TTGSnackbarManager can queue, replace or deduplicate snackbars. With Swift concurrency you can await the action, the tap, the swipe, the dismiss, the manager drop or a presentation failure. Dynamic Type, VoiceOver announcements, Reduce Motion and haptic feedback are supported, the dismiss timer can be paused and resumed manually, on touch or during app lifecycle interruptions, and the package includes a privacy manifest for modern Apple platform requirements.

The privacy manifest is worth noting on its own. Apple has required apps and SDKs to declare their data usage, and a library that ships one is doing the paperwork for you.

The project is MIT licensed and lives in a repository that also carries an Objective-C example project alongside the Swift one, which is a detail that matters for anyone integrating from an existing Objective-C codebase.

## Installing through SPM, CocoaPods or Carthage

Three package managers are supported, and the README gives the exact snippet for each.

With Swift Package Manager you add the repository URL in Xcode:

```text
https://github.com/zekunyan/TTGSnackbar.git
```

and then import the module:

```swift
import TTGSnackbar
```

With CocoaPods you add the pod to your Podfile:

```ruby
pod "TTGSnackbar"
```

and then run:

```sh
pod install
```

With Carthage you add the library to your Cartfile:

```text
github "zekunyan/TTGSnackbar"
```

The repository supports all three, and the tree shows the corresponding project files: a Package.swift for SPM, a TTGSnackbar.podspec for CocoaPods, and the Carthage and CocoaPods directories as the older artifacts of those workflows.

The choice matters less than the version you pick, and the README's requirements table is the thing to read first. The current version targets Swift 5.9, Xcode 15 and above, and iOS 16.0 and above. The previous 1.6.0 line targeted Swift 4.x, Xcode 9 and iOS 8.0, and 1.5.3 targeted Swift 3.x, Xcode 8 and iOS 8.0.

So the library has a wide reach only if you stay on an old tag. If your app supports iOS 15 or earlier and you want the modern API, you are looking at the older version, and the Swift concurrency and semantic style work described later in this article belongs to the current line.

## The basic API: a message, a duration and a show call

The default style is described as working out of the box: create a snackbar, configure only the behavior you need, then call show().

```swift
let snackbar = TTGSnackbar(message: "TTGSnackbar!", duration: .short)
snackbar.show()
```

The four durations are .short, .middle, .long and .forever, and the choice maps onto how much the message matters and how long the user needs to read it.

The detail that matters in an app is the return value. The README states that show() returns false if the snackbar cannot be presented, for example when no active window or custom container is available:

```swift
if !snackbar.show() {
    print("Snackbar could not be presented")
}
```

That failure mode is easy to hit and hard to guess. A snackbar presented during app launch, before a window exists, or from a background context with no key window, simply has nowhere to go. Checking the return value is the difference between a message that reliably appears and one that vanishes silently on some screens.

The action form follows the same shape, with the visible title and the callback set together:

```swift
let snackbar = TTGSnackbar(
    message: "File deleted",
    duration: .middle,
    actionText: "Undo"
) { snackbar in
    restoreFile()
    snackbar.dismiss()
}

snackbar.show()
```

The callback receives the snackbar itself, so the action can dismiss it, and in this example the undo is performed before the dismissal, which is the order you want when the action reverses something the message announced.

## Actions, icons and semantic styles

The library offers several ways to make the message carry more than text.

An action button is the case above. An icon-only action uses the same mechanism with an icon property:

```swift
let snackbar = TTGSnackbar(message: "Tap the icon action", duration: .middle)
snackbar.actionIcon = UIImage(systemName: "hand.tap.fill")
snackbar.actionBlock = { snackbar in
    snackbar.dismiss()
}
snackbar.show()
```

A leading icon gives the message a compact visual cue, and it takes an SF Symbol:

```swift
let snackbar = TTGSnackbar(message: "New feature ready", duration: .middle)
snackbar.icon = UIImage(systemName: "sparkles")
snackbar.show()
```

Then there are the semantic styles, which the README presents as an alternative to hand-picking colours for common product states:

```swift
let snackbar = TTGSnackbar(message: "Saved successfully", duration: .middle)
snackbar.style = .success
snackbar.show()
```

The available styles are default, info, success, warning, error and loading. The important part is what a style carries. The README says styles apply recommended colors, icons, loading behavior and haptic defaults, so setting .success is not only a colour choice, it also picks the matching icon, whether an activity indicator appears, and which haptic fires.

The loading style is a distinct case, because it is meant to be paired with a duration of forever:

```swift
let snackbar = TTGSnackbar(
    message: "Uploading…",
    duration: .forever
)

snackbar.style = .loading
snackbar.actionText = "Cancel"
snackbar.actionBlock = { snackbar in
    cancelUpload()
    snackbar.dismiss()
}

snackbar.show()
```

A .forever duration keeps the message visible until the user cancels or your task finishes, and the loading style adds the activity indicator, which is the combination you want for work whose duration you do not know.

## Custom content and the configuration value object

When one label is not enough, the snackbar accepts a custom UIView, and it keeps doing its own job: the README says the snackbar still manages presentation, margins, safe areas and dismissal.

```swift
let contentView = UIStackView()
contentView.axis = .vertical
contentView.spacing = 4
contentView.isLayoutMarginsRelativeArrangement = true
contentView.layoutMargins = UIEdgeInsets(top: 16, left: 18, bottom: 16, right: 18)

let titleLabel = UILabel()
titleLabel.text = "Custom content view"
contentView.addArrangedSubview(titleLabel)

let snackbar = TTGSnackbar(customContentView: contentView, duration: .forever)
snackbar.shouldActivateLeftAndRightMarginOnCustomContentView = true
snackbar.show()
```

The last property is the one to notice. With shouldActivateLeftAndRightMarginOnCustomContentView set to true, the snackbar applies its own horizontal margins to your content, so a custom view looks like a snackbar rather than an edge-to-edge banner. Turn it off and your view controls its own inset.

There is also a configuration API for building snackbars from a single value object, TTGSnackbarConfiguration, while the property-based API remains supported:

```swift
let snackbar = TTGSnackbar(configuration: .init(
    message: "File deleted",
    duration: .long,
    style: .warning,
    actionText: "Undo",
    actionBlock: { snackbar in
        undoDelete()
        snackbar.dismiss()
    }
))

snackbar.show()
```

Having both is deliberate, and the README says so directly. The value object is easier to pass around, to build in a helper, or to construct in a test; the property-based form is easier to modify after construction, which is what you want when the message changes in response to something else happening.

## Concurrency, queue management, accessibility and lifecycle

Three behaviours set this library apart from a simpler implementation.

The first is concurrency. Swift concurrency callers can await the first action, tap, swipe, dismiss, manager drop or presentation failure. That last case is the useful one: a caller awaiting a snackbar can be told that it was never presented, rather than awaiting forever for an action that cannot happen.

The second is TTGSnackbarManager, which can queue, replace or deduplicate snackbars. Real applications produce overlapping notifications, and without a manager the second snackbar either overlaps the first or replaces it arbitrarily. A queue makes the behaviour predictable, and the manager drop result is one of the outcomes you can await, so a caller can tell the difference between a snackbar that was superseded and one that ran to completion.

The third is the lifecycle handling. The dismiss timer can be paused and resumed manually, on touch, or during app lifecycle interruptions. The interruption case is the one that matters in practice: a snackbar that times out while the app is in the background has vanished by the time the user comes back, and pausing the timer through the interruption is what keeps the message on screen.

Accessibility is treated as part of the feature set rather than as an afterthought. Dynamic Type, VoiceOver announcements and Reduce Motion are supported, and haptic feedback comes with the semantic styles. The README also states that the package includes a privacy manifest for modern Apple platform requirements.

## Maintenance and where TTGSnackbar does not fit

The repository has no GitHub releases at all, so there is no tag to point at, and the last push was on 2026-06-21 with the project not archived. The version history is visible in the requirements table instead, where the current version sits above 1.6.0 and 1.5.3, and the podspec and Package.swift carry the version for each distribution channel.

The absence of releases means the dependency is a branch or a tag rather than a semver range, which is worth considering in a project that has a version pinning policy.

The first wrong case is an app that supports iOS versions before 16. The current line needs iOS 16.0, and the last version that supported iOS 8 is 1.5.3, which predates Swift concurrency, the semantic styles and the manager. Choosing between them is choosing between supporting old devices and having the modern API, and the library does not offer both in one package.

The second wrong case is a message that must not be missed. A snackbar is transient by design and dismissible by the user, so it is wrong for a destructive confirmation, for a legal notice, or for anything that has to be acknowledged.

The third is an app whose design system already has its own toast or banner component with a house style, since this library imposes its own spacing, its own safe area handling and its own semantic colours, and the work of matching it to a design system may exceed the work of building the component.

The realistic alternative for a team that already has a notification abstraction is to use it and drop the dependency. The case for taking TTGSnackbar instead is the accessibility and lifecycle work, which is the part of a snackbar that is genuinely hard to get right and that this library has already done.

## Conclusion

TTGSnackbar earns its place in an iOS app that needs transient, actionable feedback without interrupting the user, since it covers the cases that usually get hand-rolled badly: an action that undoes, a style that matches the state, a queue when several messages arrive at once. It is the wrong choice if you must support iOS versions before 16, since the current version needs Swift 5.9 and Xcode 15, and the older releases are the ones that covered iOS 8. It is also the wrong choice for a message that must not be missed, since a snackbar is by design dismissible. Verify first that the version you pin matches your deployment target, that you have a window or container available when show() is called, and that the async result you await covers the dismissal case and not only the action case.

## FAQ

### What are the requirements for the current TTGSnackbar version?

The current version targets Swift 5.9, Xcode 15 and above, and iOS 16.0 and above. The previous 1.6.0 line targeted Swift 4.x, Xcode 9 and iOS 8.0, and 1.5.3 targeted Swift 3.x, Xcode 8 and iOS 8.0.

### How do I install TTGSnackbar?

With Swift Package Manager, add the repository URL https://github.com/zekunyan/TTGSnackbar.git in Xcode and then import TTGSnackbar. With CocoaPods, add pod "TTGSnackbar" to the Podfile and run pod install. With Carthage, add github "zekunyan/TTGSnackbar" to the Cartfile.

### What happens when show() fails?

show() returns false if the snackbar cannot be presented, for example when no active window or custom container is available, so the README recommends checking the return value and handling the case where the message could not be shown.

### Which durations and styles does TTGSnackbar offer?

The durations are .short, .middle, .long and .forever, with .forever keeping the message visible until the user cancels or the work finishes. The styles are .default, .info, .success, .warning, .error and .loading, and a style applies recommended colors, icons, loading behavior and haptic defaults.

### Can I await the result of a snackbar in Swift concurrency?

Yes. Callers can await the first action, tap, swipe, dismiss, manager drop or presentation failure. TTGSnackbarManager can also queue, replace or deduplicate snackbars, so several overlapping messages behave predictably.

### Does TTGSnackbar handle accessibility and app lifecycle interruptions?

It supports Dynamic Type, VoiceOver announcements, Reduce Motion and haptic feedback, and the dismiss timer can be paused and resumed manually, on touch, or during app lifecycle interruptions so a message does not expire while the app is in the background.

## Sources

- [Issues](https://github.com/zekunyan/TTGSnackbar/issues)
- [License: MIT](https://github.com/zekunyan/TTGSnackbar/blob/master/LICENSE)
- [README](https://github.com/zekunyan/TTGSnackbar/blob/master/README.md)
- [zekunyan/TTGSnackbar on GitHub](https://github.com/zekunyan/TTGSnackbar)

---

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