A lightweight, customizable toast notification library for SwiftUI applications, designed to provide elegant and user-friendly toast with minimal setup.
- ๐ฏ Simple and intuitive API
- ๐จ Four built-in toast types (info, success, warning, error)
- โก๏ธ Smooth animations and transitions
- ๐ Auto-dismissal with customizable duration
- ๐ Tap to dismiss
- โฟ๏ธ Enhanced accessibility support with VoiceOver announcements
- ๐ฑ Safe area aware
- ๐ญ Flexible positioning (top, bottom, center, leading, trailing with variants)
- ๐ก๏ธ Race condition protection for rapid toast calls
- โ Input validation for empty messages and invalid durations
- ๐จ Customizable appearance through configuration
- ๐งช Comprehensive test coverage
- ๐ Queue system - stack toasts or replace them (configurable)
Add the following to your Package.swift
file:
dependencies: [
.package(url: "https://github.com/yourusername/Toasty.git", from: "0.1.10")
]
- Add the
.toastable()
modifier to your root view:
import Toasty
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.toastable() // Add this modifier
}
}
}
You can use the @Toast
property wrapper (recommended) or @EnvironmentObject
:
struct ContentView: View {
@Toast private var toast
var body: some View {
Button("Show Toast") {
// Simple usage
toast.show(message: "Hello, World!")
// With type and duration
toast.show(
message: "Operation successful!",
type: .success,
duration: 3.0
)
// Using ToastData
let customToast = ToastData(
message: "Custom toast",
type: .warning,
duration: 2.0
)
toast.show(toast: customToast)
}
}
}
.info
- For general information.success
- For successful operations.warning
- For warning messages.error
- For error messages
Adjust toast position using the alignment parameter:
// Basic positions
ContentView()
.toastable(alignment: .top) // Top center
.toastable(alignment: .bottom) // Bottom center
.toastable(alignment: .center) // Screen center
// Corner positions
ContentView()
.toastable(alignment: .topLeading) // Top left
.toastable(alignment: .topTrailing) // Top right
.toastable(alignment: .bottomLeading) // Bottom left
.toastable(alignment: .bottomTrailing)// Bottom right
// Side positions
ContentView()
.toastable(alignment: .leading) // Left center
.toastable(alignment: .trailing) // Right center
Customize toast appearance using ToastConfiguration
:
import Toasty
// Create custom configuration
var customConfig = ToastConfiguration()
customConfig.cornerRadius = 15
customConfig.shadowRadius = 8
customConfig.messageFont = .title3
customConfig.maxLines = 5
// Apply to your view
ContentView()
.toastable()
.toastConfiguration(customConfig)
@Toast private var toast
var body: some View {
VStack {
if toast.isShowingToast {
Button("Dismiss Current Toast") {
toast.dismiss()
}
}
Button("Show Toast") {
toast.show(message: "Hello!")
}
}
}
By default, new toasts replace currently shown ones. You can enable queue mode to stack multiple toasts:
// Enable queue mode - toasts will be shown one after another
ContentView()
.toastable(alignment: .top, queueMode: .queue)
// Default replace mode - new toasts replace current ones
ContentView()
.toastable(alignment: .top, queueMode: .replace)
Queue Management:
@Toast private var toast
// Show multiple toasts - they'll queue up in queue mode
toast.show(message: "First toast", type: .info)
toast.show(message: "Second toast", type: .success)
toast.show(message: "Third toast", type: .warning)
// Check queue status
if toast.hasQueuedToasts {
print("There are \(toast.queueCount) toasts in queue")
}
// Dismiss current toast (next will show automatically in queue mode)
toast.dismiss()
// Clear entire queue and current toast
toast.dismissAll()
Example with Queue Controls:
struct QueueExampleView: View {
@Toast private var toast
@State private var queueMode: ToastQueueMode = .replace
var body: some View {
VStack(spacing: 20) {
Picker("Mode", selection: $queueMode) {
Text("Replace").tag(ToastQueueMode.replace)
Text("Queue").tag(ToastQueueMode.queue)
}
.pickerStyle(SegmentedPickerStyle())
Button("Show 3 Quick Toasts") {
toast.show(message: "First toast ๐ฅ", type: .info)
toast.show(message: "Second toast ๐ฅ", type: .success)
toast.show(message: "Third toast ๐ฅ", type: .warning)
}
if toast.hasQueuedToasts {
Text("Queue: \(toast.queueCount) toasts")
.foregroundColor(.secondary)
Button("Clear Queue") {
toast.dismissAll()
}
.foregroundColor(.red)
}
}
.toastable(alignment: .top, queueMode: queueMode)
}
}
Queue Mode Guidelines:
- Use
.replace
for: Status updates, progress notifications, real-time data - Use
.queue
for: Multiple user actions, batch operations, step-by-step feedback
The library automatically validates inputs:
- Empty or whitespace-only messages are ignored
- Duration is clamped between 0.5 and 10.0 seconds
- Race conditions are handled automatically
Toasty includes comprehensive accessibility support:
- VoiceOver announcements when toasts appear
- Proper accessibility labels and traits
- Voice Control support with "Dismiss" action
- Screen Reader optimized content structure
- iOS 15.0+
- macOS 12.0+
- Xcode 15.0+
- Swift 5.5+
We welcome contributions! Please follow these steps:
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Write tests for your changes
- Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
Annurdien Rasyid
- GitHub: @annurdien
- LinkedIn: Annurdien Rasyid
Made with โค๏ธ by Annurdien Rasyid