tToastKit / Docs

SWIFTUI · OPEN SOURCE

Notifications that
feel at home.

A guide to adding thoughtful, stacked toast notifications to your SwiftUI app — with gestures, timing, and style built in.

Start building ↓iOS 17+ · Swift 5.10
01

UP AND RUNNING

Get started

Add ToastKit with Swift Package Manager, then attach a ToastCenter to the view that should present notifications.

ContentView.swiftSWIFT
import SwiftUI
import ToastKit

struct ContentView: View {
    @State private var toasts = ToastCenter()

    var body: some View {
        Button("Save") {
            toasts.present(.normal, title: "Changes saved")
        }
        .toasts(toasts)
    }
}
✳

That’s it. The toast appears at the bottom of the safe area for four seconds. The default swipe actions let people delete it or set it aside for later.

In Xcode, choose File → Add Package Dependencies and enter https://github.com/DavidRicardoWilde/swiftui-toastkit.

02

THE MESSAGE

Toast messages

A ToastMessage holds its content, semantic status, duration, and optional tap and swipe actions. Use the convenience method for a simple message, or build a message when you need more control.

toasts.present(.warning,
    title: "Low storage",
    message: "Free up space to keep syncing."
)

let message = ToastMessage(
    status: .notification,
    title: "New photos",
    message: "12 photos from today",
    icon: .system("photo.on.rectangle"),
    duration: 6
)
toasts.present(message)
NORMAL

Neutral

Everyday confirmations with a quiet, neutral treatment.

WARNING

Needs attention

Something to review, before it becomes a failure.

ERROR

Something failed

Give errors a clear, recognizable visual signal.

NOTIFICATION

Worth a glance

Announcements use your app’s accent color.

Presenting a message with an id that is already visible updates and brings that toast forward. This works well for progress states such as “Syncing…” followed by “Synced”.

03

GIVE IT A MOMENT

Display duration

.automatic uses the center’s default duration of four seconds. Pass seconds directly or use .indefinite for a message that needs an explicit response.

// Two seconds
ToastMessage(title: "Copied", duration: 2)

// Stays until dismissed
ToastMessage(title: "Sync failed", duration: .indefinite)

let toasts = ToastCenter(defaultDuration: 6)
  • Only the toast at the front counts down.
  • Covered toasts keep their full duration until they come forward.
  • The timer pauses while someone touches the stack or the app leaves the foreground.
04

A CLEAR NEXT STEP

Gestures & actions

People can tap, swipe either direction, or press and hold to see both swipe choices before deciding. VoiceOver exposes the same actions by name.

GestureDefaultCustomize with
TapNo action; taps pass throughtapAction or onTap
Leading swipeDelete the toastswipeActions.leading
Trailing swipeBring it back next visitswipeActions.trailing
Press & holdPreview both swipe actionsUses the same swipe actions
let archive = ToastAction(
    id: "archive",
    title: "Archive",
    icon: .system("archivebox"),
    confirmation: "Archived"
) { toast in
    store.archive(toast.id)
}

toasts.present(ToastMessage(
    title: "Message from Ana",
    tapAction: ToastAction(title: "Open") { toast in
        router.open(toast.id)
    },
    swipeActions: ToastSwipeActions(leading: archive, trailing: .later)
))

Actions run in order: their handler, the stack’s onToastAction observer, then their effect. Built-in .later postpones the toast until its page appears again.

05

MAKE IT YOURS

Styles & themes

The default .glass style draws a frosted card tinted by status. Tune design tokens with ToastTheme, choose the unstyled .plain style, or conform to ToastStyle for a custom card.

MyScreen()
    .toasts(toasts)
    .toastTheme(ToastTheme(
        titleFont: .headline,
        tint: { status in
            status == .notification ? .pink : ToastTheme.standardTint(status)
        },
        cornerRadius: 16
    ))

// Start with no surface or fixed colors
MyScreen().toasts(toasts).toastStyle(.plain)
Motion Color Surface Type

A custom style receives the toast’s icon, title, message, status, and whether it is lifted onto the long-press track. Layout and gestures remain with the stack.

06

RIGHT WHERE IT BELONGS

Placement & width

By default, cards hang from the bottom and use 60% of the available safe-area width.

// Top edge
MyScreen().toasts(toasts, placement: .top)

// 90% of the safe area, or a fixed width
MyScreen().toasts(toasts, width: .fraction(0.9))
MyScreen().toasts(toasts, width: .points(320))

Inside a TabView, attach .toasts(toasts) to the tab’s content. System tab bars and accessories are already part of its safe area. Mark a custom floating bar with .toastObstruction() so the stack can lift clear of it.

✓Savedbottom · 60%
SAFE AREA
07

BUILT FOR EVERYONE

Accessibility

ToastKit keeps actions available without requiring a gesture and follows the accessibility settings in SwiftUI’s environment.

  • VoiceOver announces each card and offers both swipe actions in the actions rotor.
  • Reduce Motion replaces sliding movement with a cross-fade.
  • Reduce Transparency makes glass surfaces opaque.
  • Increase Contrast strengthens the glass border.
  • Dynamic Type scales text and the status icon.
08

SPEAK THEIR LANGUAGE

App localization

ToastKit ships its built-in action labels in English, Japanese, Simplified Chinese, and Traditional Chinese. Your app’s strings are separate: localize the title, message, and any custom action labels you provide.

// Keep app copy in your String Catalog or Localizable.strings.
toasts.present(.warning,
    title: String(localized: "storage.low.title"),
    message: String(localized: "storage.low.message")
)

ToastKit follows the host app’s locale for its own built-in labels. Custom ToastAction titles and confirmations are plain strings so you can localize them with the same tools as the rest of your app.

KEEP EXPLORING

Ready to add a little polish?

Explore the source and see every API in context.

Browse on GitHub ↗