Contents

jaywcjlove/toast

Toast

✨ Features

  • 🎯 Simple API - Clean, intuitive interface: Toast.success("Hello!")
  • ⚑ High Performance - Smooth animations and optimized memory usage
  • πŸ“ Flexible Positioning - Multiple positioning options (top, bottom, center)
  • 🎨 Customizable - Theming, styling, and configuration support
  • πŸ”„ Promise Support - Built-in async operation handling
  • πŸ“± SwiftUI Native - Perfect integration with SwiftUI environment

πŸš€ Quick Start

1. Installation

// Package.swift
dependencies: [
    .package(url: "https://github.com/jaywcjlove/Toast.git", from: "1.0.0")
]

2. Setup

Enable Toast in your app:

import SwiftUI
import Toast

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .toast() // Enable toast container
        }
    }
}

3. Basic Usage

import Toast

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Success") {
                // Static API (recommended)
                Toast.success("Operation completed!")
            }
            
            Button("Error") {
                Toast.error("Something went wrong")
            }
            
            Button("Info") {
                Toast.info("Just so you know")
            }
            
            Button("Loading") {
                Toast.loading("Processing...")
            }
            
            Button("Clear") {
                Toast.dismissAll()
            }
        }
        .toast() // Enable toast container
    }
}

API Reference

Basic Messages

// Method 1: Static API (works everywhere)
Toast.success("Success message")
Toast.error("Error message") 
Toast.info("Info message")
Toast.loading("Loading message")

// Method 2: Full API (advanced usage)
ToastManager.shared.success("Success!")
ToastManager.shared.error("Error occurred")
ToastManager.shared.info("Information")
ToastManager.shared.loading("Loading...")

Positioning

// Available positions
Toast.success("Message", position: .topLeft)
Toast.info("Message", position: .topCenter)     // default
Toast.error("Message", position: .topRight)
Toast.success("Message", position: .bottomLeft)
Toast.info("Message", position: .bottomCenter)
Toast.error("Message", position: .bottomRight)
Toast.loading("Message", position: .center)

Custom Toasts

// Custom SwiftUI content
Toast.custom {
    HStack {
        Image(systemName: "star.fill")
            .foregroundColor(.yellow)
        Text("Custom message")
            .fontWeight(.bold)
    }
    .padding()
    .background(Color.purple)
    .foregroundColor(.white)
    .cornerRadius(8)
}

Promise Support

Automatic async operation state handling:

Toast.promise(
    operation: {
        // Your async operation
        try await Task.sleep(for: .seconds(2))
        return "Upload completed"
    },
    messages: .init(
        loading: "Uploading...",
        success: "Upload successful!",
        error: "Upload failed"
    )
)

Configuration

// Global configuration
ToastManager.shared.configuration.duration = 5.0
ToastManager.shared.configuration.position = .topCenter

// Per-toast configuration
// ⚠️ Static API doesn't support duration parameter
// Use ToastManager for custom duration:
ToastManager.shared.success("Custom duration", duration: 8.0)
ToastManager.shared.info("Manual dismiss", duration: 0) // Never auto-dismiss

Control Methods

// Dismiss toasts
Toast.dismiss()         // Dismiss latest toast
Toast.dismissAll()      // Dismiss all toasts

// For ID-specific operations, use ToastManager:
ToastManager.shared.info("Loading...", id: "specific-id")
DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
    Toast.dismiss(id: "specific-id") // Dismiss by ID
}

Complete Example

import SwiftUI
import Toast

struct ContentView: View {
    @State private var counter = 0
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Toast Demo")
                .font(.largeTitle)
            
            // Basic toast types
            HStack {
                Button("Success") {
                    counter += 1
                    Toast.success("Success #\(counter)")
                }
                
                Button("Error") {
                    Toast.error("Something went wrong")
                }
                
                Button("Info") {
                    Toast.info("Information message")
                }
            }
            
            // Loading and promise
            Button("Loading") {
                Toast.loading("Processing...")
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    Toast.success("Done!")
                }
            }
            
            // Async promise
            Button("Async Task") {
                Toast.promise(
                    operation: {
                        try await Task.sleep(for: .seconds(2))
                        return "Task completed"
                    },
                    messages: .init(
                        loading: "Working...",
                        success: "Finished!",
                        error: "Failed"
                    )
                )
            }
            
            // Custom toast
            Button("Custom") {
                Toast.custom {
                    HStack {
                        Image(systemName: "star.fill")
                            .foregroundColor(.yellow)
                        Text("Custom Toast")
                    }
                    .padding()
                    .background(Color.purple)
                    .foregroundColor(.white)
                    .cornerRadius(8)
                }
            }
            
            // Clear all
            Button("Clear All") {
                Toast.dismissAll()
            }
            .foregroundColor(.red)
        }
        .padding()
        .toast() // Enable toast container
    }
}

Troubleshooting

"Use of 'toast' refers to instance method" Error

This error occurs due to naming conflict between the global toast instance and the .toast() modifier method.

❌ Problem:

struct MyView: View {
    var body: some View {
        Button("Test") {
            toast.success("Hello") // Error: naming conflict
        }
        .toast() // This creates the conflict
    }
}

βœ… Solution (Recommended):

Use Static API (always works)

Button("Test") {
    Toast.success("Hello") // βœ… Always reliable
}

Alternative Solutions:

// Method 1: ToastManager directly
Button("Test") {
    ToastManager.shared.success("Hello") // βœ… Always works
}

// Method 2: Only in completely separate contexts
class SomeService {
    func performAction() {
        // βœ… Works in non-SwiftUI contexts
        toast.success("Service action completed")
    }
}

Configuration Options

struct ToastConfiguration {
    var position: ToastPosition = .topCenter
    var duration: TimeInterval = 3.0
    var theme: ToastTheme = .system
    var maxVisibleToasts: Int = 3
    var animationDuration: TimeInterval = 0.3
    var spacing: CGFloat = 8
}

License

Licensed under the MIT License.

Package Metadata

Repository: jaywcjlove/toast

Default branch: main

README: README.md