A production-ready iOS toast notification library that feels unmistakably Apple-native.
Toast provides transient, non-blocking feedback notifications for iOS 17+ apps — restrained visuals, spring-based motion, full accessibility support, and a minimal public surface. Zero dependencies.
- iOS 17+
- Swift 5.9+
- Xcode 15+
Add Toast to your Package.swift:
dependencies: [
.package(url: "https://github.com/6639835/Toast.git", from: "1.0.0")
]Or add it via Xcode: File → Add Package Dependencies and enter the repository URL.
1. Install the host modifier once at your scene root:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.toastHost()
}
}
}2. Show toasts from anywhere:
// From a view model or async action:
ToastManager.shared.show(.success, title: "Photo saved")
ToastManager.shared.show(.error, title: "Upload failed", subtitle: "Check your connection")
// From a SwiftUI view via environment:
@Environment(\.toastManager) var toastManager
Button("Save") {
toastManager?.show(.success, title: "Saved")
}3. Binding-driven presentation:
@State private var toast: ToastItem?
Button("Save") {
toast = ToastItem(kind: .success, title: "Saved")
}
.toast(item: $toast)The primary entry point. One instance per UIWindowScene.
| Method | Description |
|---|---|
ToastManager.shared |
Convenience accessor for single-scene apps |
ToastManager.manager(for:) |
Per-scene accessor for multi-scene apps |
show(_ kind:title:subtitle:duration:position:) |
Show a toast with semantic convenience |
show(_ item:) |
Show a fully configured ToastItem |
dismissCurrent() |
Dismiss the visible toast immediately |
dismiss(id:) |
Dismiss a toast by its UUID |
cancelAll() |
Cancel all pending and visible toasts |
removeManager(for:) |
Clean up when a scene disconnects |
Controls icon, haptic feedback, and default display duration.
| Case | Icon | Default Duration |
|---|---|---|
.success |
checkmark.circle.fill |
3s |
.info |
info.circle.fill |
3s |
.warning |
exclamationmark.circle.fill |
4s |
.error |
xmark.circle.fill |
4s |
| Value | Duration |
|---|---|
.default |
Kind-appropriate (see above) |
.short |
2 seconds |
.standard |
3.5 seconds |
.long |
5 seconds |
.indefinite |
No auto-dismiss |
.custom(_ seconds:) |
1–8 seconds (clamped) |
| Case | Description |
|---|---|
.bottom |
Above home indicator (default) |
.center |
Vertically centered |
.top |
Below status bar |
ToastItem(
kind: .success,
title: "File saved",
subtitle: "Tap to open", // optional
iconName: "square.and.arrow.up", // nil = kind default, "" = suppress icon
duration: .standard,
position: .bottom,
tapToDismiss: true
)| Modifier | Description |
|---|---|
.toastHost() |
Installs toast infrastructure — call once at scene root |
.toast(item:) |
Presents a toast when binding becomes non-nil |
@Environment(\.toastManager) |
Access the scene-bound manager from any view |
Toast uses a UIWindowScene-backed overlay window sitting above the key window. This ensures toasts appear over sheets, full-screen covers, and navigation stacks — something pure SwiftUI overlay modifiers cannot guarantee.
A Swift actor-isolated queue serializes all enqueue, dismiss, and timer operations, eliminating concurrency bugs between auto-dismiss timers and rapid-fire enqueue events.
Key behaviors:
- FIFO queueing — multiple toasts are shown in order, one at a time
- Deduplication — identical consecutive toasts refresh the timer instead of stacking
- Safe area awareness — respects Dynamic Island, home indicator, and keyboard
- Hit-test pass-through — touches outside the toast reach your app normally
- Accessibility — VoiceOver announcements, Reduce Motion support, semantic traits
- Haptics —
UINotificationFeedbackGeneratorfeedback per kind
For apps with multiple UIWindowScene instances, use the per-scene API:
// In your SceneDelegate or scene-lifecycle handler:
let manager = ToastManager.manager(for: windowScene)
manager.show(.info, title: "Scene-specific toast")
// Clean up when the scene disconnects:
ToastManager.removeManager(for: windowScene)Toast is available under the MIT license. See LICENSE for details.