SwiftUIでalertを表示するのに便利なExtension

SwiftUIのAlertが使いにくかったので、Extensionを作りました。

**2021/3/16追記**
View以外のコードからアラートを呼び出す方法もありました。コードから呼び出す方が汎用的だと思います。
[[SwiftUI] コード(ViewModelなど)からAlertを表示する方法](https://qiita.com/k-yamada-github/items/42dccf3a9be0e748ccdd)


# 実装


```AlertData.swift
struct AlertData {
    let title: String
    let message: String?
    let primaryText: String?
    let primaryAction: (() -> ())?
    let secondaryText: String?
    let secondaryAction: (() -> ())?

    init(title: String, message: String? = nil, primaryText: String? = nil, primaryAction: (() -> ())? = nil, secondaryText: String? = nil, secondaryAction: (() -> ())? = nil) {
        self.title = title
        self.message = message
        self.primaryText = primaryText
        self.primaryAction = primaryAction
        self.secondaryText = secondaryText
        self.secondaryAction = secondaryAction
    }
}
```



```View+.swift
import SwiftUI

extension View {
    /// Alertを表示する
    ///
    /// 使い方
    ///
    /// Button("Show Alert") {
    ///     showingAlert = true
    /// }
    /// .alert(isPresented: $showingAlert,
    ///        data: AlertData(title: "title",
    ///                        message: "message",
    ///                        primaryText: "primary",
    ///                        primaryAction: {
    ///                         print("primary tapped")
    ///                        },
    ///                        secondaryText: "secondary",
    ///                        secondaryAction: {
    ///                         print("secondary tapped")
    ///                        }))
    func alert(isPresented: Binding<Bool>, data: AlertData) -> some View {
        let title = Text(data.title)
        var messageText: Text? = nil
        if let message = data.message {
            messageText = Text(message)
        }

        var primaryButton: Alert.Button? = nil
        if let primaryText = data.primaryText {
            primaryButton = .default(Text(primaryText)) {
                _ = data.primaryAction?()
            }
        }

        var secondaryButton: Alert.Button? = nil
        if let secondaryText = data.secondaryText {
            secondaryButton = .default(Text(secondaryText)) {
                _ = data.secondaryAction?()
            }
        }

        if let primaryButton = primaryButton, let secondaryButton = secondaryButton {
            return AnyView(alert(isPresented: isPresented, title: title, message: messageText, primaryButton: primaryButton, secondaryButton: secondaryButton))
        } else if let primaryButton = primaryButton {
            return AnyView(alert(isPresented: isPresented, title: title, message: messageText, dismissButton: primaryButton))
        } else if let secondaryButton = secondaryButton {
            return AnyView(alert(isPresented: isPresented, title: title, message: messageText, dismissButton: secondaryButton))
        } else {
            return AnyView(alert(isPresented: isPresented, title: title, message: messageText))
        }
    }

    private func alert(isPresented: Binding<Bool>, title: Text, message: Text?) -> some View {
        alert(isPresented: isPresented) {
            Alert(title: title, message: message)
        }
    }

    private func alert(isPresented: Binding<Bool>, title: Text, message: Text?, primaryButton: Alert.Button, secondaryButton: Alert.Button) -> some View {
        alert(isPresented: isPresented) {
            Alert(title: title, message: message, primaryButton: primaryButton, secondaryButton: secondaryButton)
        }
    }

    private func alert(isPresented: Binding<Bool>, title: Text, message: Text?, dismissButton: Alert.Button) -> some View {
        alert(isPresented: isPresented) {
            Alert(title: title, message: message, dismissButton: dismissButton)
        }
    }
}
```