Skip to content

sentryco/PromptManager

Repository files navigation

Tests codebeat badge MIT License Swift 5.9 Swift Package Manager Platforms

PromptManager

Alert and sheet tools for macOS and iOS

PromptManager is a versatile library designed to simplify the creation and management of alerts and sheets on macOS and iOS platforms. It provides a robust set of tools to handle various dialog types with ease, ensuring a consistent and user-friendly interface across different Apple devices.

Features

  • Cross-Platform Support: Works seamlessly on both macOS (v14 and above) and iOS (v17 and above).
  • SwiftUI Ready: Fully compatible with SwiftUI, offering a modern way to manage UI components.
  • Extensible: Easily extendable to include more complex alert types or custom interactions.

Presenting a Simple Alert

This example demonstrates how to present a simple alert with primary and secondary actions on both iOS and macOS platforms.

promptAlert(
    title: "Save Changes?",
    msg: "Do you want to save the changes you made?",
    primaryText: "Save",
    secondaryText: "Cancel",
    primaryHandler: { print("Changes saved") },
    secondaryHandler: { print("Cancelled") }
)

Presenting an Alert with Text Input

This example shows how to present an alert that includes text input fields, suitable for scenarios like login forms.

showTextInputAlert(
    title: "Login",
    message: "Please enter your username and password",
    primaryTextPlaceholder: "Username",
    secondaryTextPlaceholder: "Password",
    primaryButtonText: "Login",
    secondaryButtonText: "Cancel",
    isSecure: true,
    onComplete: { credentials in
        if let username = credentials?.primary, let password = credentials?.secondary {
            print("Username: \(username), Password: \(password)")
        } else {
            print("User cancelled login")
        }
    }
)

Presenting an Input Alert in SwiftUI

You can present an input alert within a SwiftUI view using the configInputAlert function:

struct ContentView: View {
    @State private var presentAlert = false
    @State private var alertText: String = ""

    var body: some View {
        Button("Show Input Alert") {
            presentAlert = true
        }
        .configInputAlert(
            text: $alertText,
            title: "Enter Text",
            message: "Please enter some text below",
            isPresented: $presentAlert
        ) { input in
            print("User input: \(input)")
        }
    }
}

Presenting an Attributed Alert

This example demonstrates how to present an alert with attributed text, which allows for more stylized text presentations.

let attrTitle = NSAttributedString(string: "Important Update", attributes: [.foregroundColor: UIColor.red])
let attrMSG = NSAttributedString(string: "Please read the terms and conditions carefully.", attributes: [.foregroundColor: UIColor.blue])
UIAlertController.promptAlert(
    attrTitle: attrTitle,
    attrMSG: attrMSG,
    ok: { print("Accepted") },
    cancel: { print("Declined") },
    okTitle: "Accept",
    cancelTitle: "Decline"
)

Displaying an Alert with Custom Actions

Using promptAlert, you can display alerts with custom primary and secondary actions:

promptAlert(
    title: "Delete File?",
    msg: "Are you sure you want to delete this file?",
    primaryText: "Delete",
    secondaryText: "Cancel",
    primaryHandler: {
        print("File deleted")
    },
    secondaryHandler: {
        print("Deletion cancelled")
    }
)

Using SheetState for Managing Sheets and Alerts

Leverage SheetState to manage sheet presentations in a SwiftUI view:

class AlertPrompt: SheetState<AlertCategory> {
    enum AlertCategory: String {
        case save, delete, cancel
    }
}

struct ContentView: View {
    @StateObject var alert = AlertPrompt()

    var body: some View {
        VStack {
            Button("Save") { alert.state = .save }
            Button("Delete") { alert.state = .delete }
        }
        .alert(
            alert.state?.rawValue ?? "",
            isPresented: $alert.isShowing
        ) {
            Button("OK", action: { /* Handle OK */ })
        } message: {
            Text("Perform the \(alert.state?.rawValue ?? "") action?")
        }
    }
}

### Presenting an Alert with Text Input on macOS

```swift
showAlertWithTextFields(
    title: "Login",
    message: "Enter your credentials",
    primaryTextPlaceholder: "Username",
    secondaryTextPlaceholder: "Password",
    isSecure: true
) { credentials in
    if let username = credentials?.primary, let password = credentials?.secondary {
        print("Username: \(username), Password: \(password)")
    } else {
        print("User cancelled login")
    }
}

Example Usage

Here's how you can present a custom alert using PromptManager:

import PromptManager
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("Show Alert") {
            promptAlert(
                title: "Hello World",
                msg: "This is a custom alert message.",
                primaryText: "OK",
                secondaryText: "Cancel",
                primaryHandler: {
                    print("OK tapped")
                },
                secondaryHandler: {
                    print("Cancel tapped")
                }
            )
        }
    }
}

Swift Package Manager

.package(url: "https://github.com/sentryco/PromptManager", branch: "main")

Todo:

  • Generate Online Documentation Add Support for SPM DocC Documentation
  • Use Modern Swift Features: Refactor the codebase to leverage the latest Swift language enhancements, such as @MainActor for UI updates.
  • Add Asynchronous Support: Implement async/await for asynchronous operations.
  • Improve macOS Previews: Enhance SwiftUI previews for macOS components.
  • Refine Error Handling: Standardize error handling across the library.
@MainActor
class AlertManager {
    // UI-related code guaranteed to run on the main thread
}
  • Adopt Modern Swift Concurrency Use async/await: Refactor asynchronous code to leverage Swift's modern concurrency model. This makes your code more readable and efficient.
@MainActor
public func showAsyncAlert(title: String, message: String) async -> Bool {
    await withCheckedContinuation { continuation in
        promptAlert(
            title: title,
            msg: message,
            primaryText: "OK",
            secondaryText: "Cancel",
            primaryHandler: {
                continuation.resume(returning: true)
            },
            secondaryHandler: {
                continuation.resume(returning: false)
            }
        )
    }
}
  • Define Custom Error Types: Create specific error types conforming to Error for more granular error handling.
enum AlertError: Error {
    case invalidInput
    case presentationFailed
}
  • Use Result Types: Update function signatures to use Result where appropriate.
func showAlert(
    title: String,
    message: String,
    completion: @escaping (Result<Void, AlertError>) -> Void
) {
    // Implementation
}
  • Simplify Function Signatures: Use structs to pass configurations, making your API cleaner and more maintainable.
struct AlertConfiguration {
    let title: String
    let message: String
    let primaryButtonTitle: String
    let secondaryButtonTitle: String?
    let isSecureTextEntry: Bool
}

func presentAlert(configuration: AlertConfiguration, completion: @escaping (Result<Void, AlertError>) -> Void) {
    // Implementation
}
  • Create Custom View Modifiers for SwiftUI

  • Streamline Alert Presentation: Develop view modifiers to make presenting alerts more Swifty and convenient.

  • Example:

extension View {
    func promptAlert(
        isPresented: Binding<Bool>,
        title: String,
        message: String,
        primaryButtonTitle: String = "OK",
        secondaryButtonTitle: String? = nil,
        onPrimaryAction: @escaping () -> Void,
        onSecondaryAction: (() -> Void)? = nil
    ) -> some View {
        self.alert(
            title,
            isPresented: isPresented,
            presenting: nil,
            actions: {
                Button(primaryButtonTitle, action: onPrimaryAction)
                if let secondaryTitle = secondaryButtonTitle {
                    Button(secondaryTitle, role: .cancel, action: onSecondaryAction ?? {})
                }
            },
            message: {
                Text(message)
            }
        )
    }
}
  • Add Support for Combine or Async Publishers

  • Reactive Programming: If applicable, provide support for Combine publishers to handle alert actions reactively.

  • Example:

import Combine

class AlertViewModel: ObservableObject {
    @Published var isAlertPresented = false
    private var cancellables = Set<AnyCancellable>()

    func showAlert() {
        isAlertPresented = true
    }

    func bindAlertActions() {
        // Implementation
    }
}
  • Implement Theming Support

  • Customize Appearance: Allow users to customize the appearance of alerts to match their app's theme.

  • Example:

    struct AlertStyle {
        let titleFont: Font
        let messageFont: Font
        let titleColor: Color
        let messageColor: Color
    }
    
    func promptAlert(
        title: String,
        msg: String,
        style: AlertStyle,
        primaryHandler: @escaping () -> Void
    ) {
        // Implementation using the style parameters
    }

About

Alert and sheet tools for macOS and iOS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages