ivan-magda/swiftui-toast
Queue-managed toast notifications for SwiftUI, built on @Observable (iOS 17+).
Table of Contents
Background
Fire several toasts in quick succession and most SwiftUI implementations stack them on top of each other, leaving the user with a pile of overlapping messages. SwiftUIToast routes every toast through a single ToastManager that shows them one at a time in first-in, first-out order. Trigger five at once and they play back as a clean sequence.
The library targets iOS 17 and later, so it builds on @Observable and @MainActor rather than Combine. It compiles under Swift 6 strict concurrency.
Philosophy
- One manager, shared through the environment. A single
ToastManagerowns the queue. You inject it once at the app root and call the.toast()modifier anywhere below. - Bindings drive presentation. You toggle a
Boolbinding to show a toast; it flips back tofalseon dismiss. No imperative show/hide calls to keep in sync. - Presets first, full control when you need it. Common cases are one-liners (
.success,.top,.bouncy()). The fullToastConfigurationinitializer is there when you want to set duration, position, and animation yourself.
Features
- Automatic queue — toasts display sequentially, never overlapping. The queue holds 10 by default and is configurable.
- Three semantic types —
.info,.success, and.error, each with its own SF Symbol and color. - Custom content — pass any SwiftUI view as the toast body via a
@ViewBuildermodifier. - Six animation presets —
slide,fade,scale,bounce,flip, andslideWithBounce, or build your own from anyAnimationandAnyTransition. - Auto-dismiss — configurable duration, 3 seconds by default.
- Tap-to-dismiss — on by default, with a short configurable delay before the exit animation.
- Top or bottom placement — position toasts where they fit your layout.
- VoiceOver support — toasts carry accessibility labels and traits.
Requirements
- iOS 17.0+ / macOS 14.0+ / tvOS 17.0+
- Swift 6.0+
- Xcode 16.0+
Installation
Xcode
Open File → Add Package Dependencies, paste the repository URL, and add the SwiftUIToast library to your target:
https://github.com/ivan-magda/swiftui-toast.gitPackage.swift
Add the package to your dependencies and list SwiftUIToast as a target dependency:
dependencies: [
.package(url: "https://github.com/ivan-magda/swiftui-toast.git", from: "1.2.0")
],
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "SwiftUIToast", package: "swiftui-toast")
]
)
]Usage
Set up the manager
Create one ToastManager at the app root and inject it into the environment:
import SwiftUI
import SwiftUIToast
@main
struct MyApp: App {
@State private var toastManager = ToastManager()
var body: some Scene {
WindowGroup {
ContentView()
.environment(toastManager)
}
}
}Show a toast
Bind the modifier to a Bool and set it to true. The toast appears and resets the binding when it dismisses:
struct ContentView: View {
@State private var showToast = false
var body: some View {
Button("Save") {
showToast = true
}
.toast(isPresented: $showToast, message: "Saved!", type: .success)
}
}Semantic types
.toast(isPresented: $show, message: "Syncing…", type: .info) // accent-colored info icon
.toast(isPresented: $show, message: "Done!", type: .success) // green checkmark
.toast(isPresented: $show, message: "Upload failed", type: .error) // red exclamationCustom content
Use the @ViewBuilder variant when you need full control over the toast body. Supply your own background and padding:
.toast(isPresented: $show, configuration: .bouncy(position: .top)) {
HStack(spacing: 8) {
Image(systemName: "star.fill")
.foregroundStyle(.yellow)
Text("Added to favorites")
.fontWeight(.medium)
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
.background(.ultraThinMaterial, in: Capsule())
}Animation presets
.toast(isPresented: $show, message: "Bounce!", configuration: .bouncy())
.toast(isPresented: $show, message: "Fade!", configuration: .fade())
.toast(isPresented: $show, message: "Flip!", configuration: .flip())Each preset also takes an optional position, for example .fade(position: .top).
Full configuration
ToastConfiguration exposes every knob. Set duration: 0 to disable auto-dismiss:
let config = ToastConfiguration(
duration: 4.0,
position: .top,
tapToDismiss: true,
dismissDelay: 0.2,
animation: .slideWithBounce(edge: .top)
)
.toast(
isPresented: $show,
message: "Custom config",
type: .success,
configuration: config
)To swap only the animation and keep the other defaults, use .with(animation:):
.toast(isPresented: $show, message: "Hello", configuration: .with(animation: .scale()))Queue management
Multiple toasts queue on their own. Trigger them together and they play in order:
struct ContentView: View {
@State private var toast1 = false
@State private var toast2 = false
@State private var toast3 = false
var body: some View {
Button("Show 3 Toasts") {
toast1 = true
toast2 = true
toast3 = true
}
.toast(isPresented: $toast1, message: "First", type: .info)
.toast(isPresented: $toast2, message: "Second", type: .success)
.toast(isPresented: $toast3, message: "Third", type: .error)
}
}Raise the limit for high-frequency scenarios by passing maxQueueSize when you create the manager:
@State private var toastManager = ToastManager(maxQueueSize: 25)Configuration presets
| Preset | Position | Animation | |--------|----------|-----------| | .standard | bottom | slide | | .top | top | slide | | .bottom | bottom | slide | | .bouncy() | bottom | bounce | | .fade() | bottom | fade | | .flip() | bottom | flip |
Full API documentation lives on the Swift Package Index.
Project Structure
Sources/SwiftUIToast/
├── ToastManager.swift # @Observable queue, displays toasts one at a time
├── ToastConfiguration.swift # Duration, position, animation, and presets
├── ToastAnimation.swift # Six animation presets plus custom factory
├── ToastType.swift # .info / .success / .error styling
├── ToastPosition.swift # Top or bottom placement
├── ToastView.swift # Default styled toast view
├── ToastModifier.swift # Visibility, timing, and dismiss handling
├── LayoutInsets.swift # Safe-area padding
├── Extensions/
│ └── View+Toast.swift # The .toast(…) modifiers
├── Helpers/
│ └── FlipEffect.swift # 3D rotation used by .flip
└── Examples/
└── ToastExamples.swift # SwiftUI previewsContributing
Issues and pull requests are welcome.
- Search existing issues before opening a new one.
- Include reproduction steps for bug reports.
- Run
swiftlint --strictandswift testbefore submitting a pull request.
License
Released under the MIT License. See LICENSE for the full text.
Package Metadata
Repository: ivan-magda/swiftui-toast
Default branch: main
README: README.md