how to show different alerts based on a condition after clicking a button in swiftui - ios

I did some research before posting it here but I was not able to fix it.
In the register View I want the user to register.
I created a linked list and as user registers a username my program checks whether or not the username is already taken.
if it is taken it should give an alert saying that the username is already taken as the user clicks the register button.
if the username is not taken then it should show an alert saying the registration is successful
import SwiftUI
struct registerScreen: View {
#State var username: String = ""
#State var password: String = ""
#State private var sucessfulRegister = false
#State private var failedRegister = false
var body: some View {
VStack {
TextField()
SecureField()
Button(action: {
let userinfo = linkedList()
if (userinfo.contains(value: self.username)){
// self.failedRegister = true
self.failedRegister.toggle()
// show alert that it failed
} else {
userinfo.insert(value: user(username: self.username, password: self.password))
// show alert that it is successfull
self.sucessfulRegister.toggle()
}
})
{
Text("Register")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(width: 220, height: 60)
.background(Color.green)
.cornerRadius(15.0)
}
}
}
}

It is possible to do. Though you don't need to track as many states as you are.
Firstly, you only need to track if they have failed or not. So your failedRegister will track if the user has successfully registered or not. That means we can get remove the successfulRegister.
We need a variable to track whether an alert is showing or not, for this we will use the variable showAlert
As you have a linked list that provides the userinfo, we will mock that with just an array containing a couple of usernames.
So here is a simplified version of your code that should work.
struct ContentView: View {
var names: [String] = ["John", "Mike"]
#State var username: String = ""
#State var password : String = ""
#State private var failedRegister = false
// this value is used for tracking whether the alert should be shown
#State private var showAlert = false
var body: some View {
VStack {
TextField("Enter username", text: $username)
Button(action: {
// reset to false as this is the initial state
self.failedRegister = false
if (self.names.contains(self.username)){
self.failedRegister.toggle()
} else {
// insert the value into the user info
}
self.showAlert.toggle()
}) {
Text("Register")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(width: 220, height: 60)
.background(Color.green)
.cornerRadius(15.0)
}
}.alert(isPresented: $showAlert) {
// it would be nice to set failedRegister back to false in this function but you cannot modify state here.
if self.failedRegister {
return Alert(title: Text("Failed to register"), message: Text("Unfortunately that username is taken"), dismissButton: .default(Text("OK")))
} else {
return Alert(title: Text("Welcome"), message: Text("You have registered"), dismissButton: .default(Text("OK")))
}
}
}
}
Update using Identifiable
There is an alternative way to show different Alerts on the same View. This is to use a binding to an object that is Identifiable.
If we look at the ways we can initialise an Alert on a View we see there are two ways. The first has the following signature:
.alert(isPresented: Binding<Bool>, content: () -> Alert)
Which is what is used in the example above.
However there is a second way which has the following signature:
.alert(item: Binding<Identifiable?>, content: (Identifiable) -> Alert)
This second way can allow for more complex alerts to be managed. To utilise this we need something to track the state of the alerts. We can create a simple struct that conforms to Identifiable and contains an enum of the different choices that we have for an alert.
We then create an #State variable to track the AlertIdentifier and initialise to nil so that its state is empty and will not show any alerts until it is changed.
We can then add our .alert(item:content:) to our View.
Here is a simple example showing it in action.
struct ContentView:View {
private struct AlertIdentifier: Identifiable {
var id: Choice
enum Choice {
case success
case failure
}
}
#State private var showAlert: AlertIdentifier? // init this as nil
var body: some View {
VStack(spacing: 20) {
Button(action: {
self.showAlert = AlertIdentifier(id: .success)
}, label: {
Text("Show success alert")
})
Button(action: {
self.showAlert = AlertIdentifier(id: .failure)
}, label: {
Text("Show failure alert")
})
}
.alert(item: $showAlert) { alert -> Alert in
switch alert.id {
case .success:
return Alert(title: Text("Success"), message: Text("You have successfully registered"), dismissButton: .default(Text("OK")))
case .failure:
return Alert(title: Text("Failure"), message: Text("You have failed to register"), dismissButton: .default(Text("OK")))
}
}
}
}
Notice that in the buttons we set the showAlert to be an instance of the struct AlertIdentifier with the type of alert we want to show. In this case we have two types: success and failure (but we could have as many types as we want, and we don't need to use the names success and failure). When that is set, it will show the appropriate alert.
In our .alert(item:content:) we switch over the different ids so that we can make sure that the correct alert is shown for the correct choice.
This method is much easier than having multiple booleans, and it is easier to extend.
Addendum for Sheets and ActionSheets
Sheets and ActionSheets are very similar to Alerts in how they are presented. There are four ways to present Sheets.
These two require a Bool binding:
.sheet(isPresented: Binding<Bool>, content: () -> View)
.sheet(isPresented: Binding<Bool>, onDismiss: (() -> Void)?, content: () -> Void)
These two require an Identifiable binding:
.sheet(item: Binding<Identifiable?>, content: (Identifiable) -> View)
.sheet(item: Binding<Identifiable?>, onDismiss: (() -> Void)?, content: (Identifiable) -> View)
For ActionSheets there are two ways, like Alerts.
With the Bool binding:
.actionSheet(isPresented: Binding<Bool>, content: () -> ActionSheet)
With the Identifiable binding:
.actionSheet(item: Binding<Identifiable?>, content: (Identifiable) -> ActionSheet)
Which binding should I use?
Binding<Bool>
If you only need to show one type of Alert, Sheet or ActionSheet then use the Bool binding, it saves you having to write some extra lines of code.
Binding<Identifiable?>
If you many different types of Alerts, Sheets or ActionSheets to show then choose the Identifiable binding as it makes it much easier to manage.
A simpler identifiable
A simpler version of the identifiable object would be to use an enum without wrapping it in a struct. In this case we need to conform to Identifiable so we need a computed property to store the id value. We also need to make sure that the enum uses a RawRepresentable so that we can get a value for the id that is unique. I would suggest using an Int or a String. In the example below I am using an Int.
enum Choice: Int, Identifiable {
var id: Int {
rawValue
}
case success, failure
}
Then in the view we could do the following:
struct ContentView:View {
enum Choice: Int, Identifiable {
var id: Int {
rawValue
}
case success, failure
}
#State private var showAlert: Choice? // init this as nil
var body: some View {
VStack(spacing: 20) {
Button(action: {
self.showAlert = .success
}, label: {
Text("Show success alert")
})
Button(action: {
self.showAlert = .failure
}, label: {
Text("Show failure alert")
})
}
.alert(item: $showAlert) { alert -> Alert in
switch alert {
case .success:
return Alert(title: Text("Success"), message: Text("You have successfully registered"), dismissButton: .default(Text("OK")))
case .failure:
return Alert(title: Text("Failure"), message: Text("You have failed to register"), dismissButton: .default(Text("OK")))
}
}
}
}

The same as Andrew solution, but with enum out of the scope of ContentView, which allow to use in other views, grouped in one place
enum Choice {
case success
case failure
}
extension Choice: Identifiable {
var id: Choice { self }
}
struct ContentView:View {
.../...
}

While Andrew's answer is very informative here is a "long-story-short" answer, which works on iOS14:
struct YourView: View {
enum AlertType: Identifiable {
case first, second
var id: Int {
hashValue
}
}
#State var alertType: AlertType?
var body: some View {
VStack {
Button("Show alert #1") {
alertType = .first
}
Button("Show alert #2") {
alertType = .second
}
}
.alert(item: $alertType) { type in
switch type {
case .first:
return Alert(title: Text("First alert"))
case .second:
return Alert(title: Text("Second alert"))
}
}
}
}

Related

SwiftUI isPresented Environment not updated for sheet dismissed

Given the following minimal reproducible example:
struct ParentView: View {
#State private var showSheet = false
var body: some View {
return Button("Show sheet") {
showSheet.toggle()
}.sheet(
isPresented: $showSheet,
onDismiss: {
print("Parent onDismiss")
},
content: {
NavigationView {
SheetView(parentShowSheet: $showSheet)
}
}
)
}
}
struct SheetView: View {
#Environment(\.dismiss) private var dismiss
#Environment(\.isPresented) private var isPresented
#Binding var parentShowSheet: Bool
var body: some View {
Button("Manual close") {
dismiss()
}
.onChange(of: isPresented) { isPresented in
print("Sheet isPresented", isPresented)
}
.onChange(of: parentShowSheet) { parentShowSheet in
print("Sheet parentShowSheet", parentShowSheet)
}
}
}
The SheetView's onChange methods are not triggering in the way I would expect. In this example there are two ways to close a sheet once it's opened :
Click the "Manual close" button which triggers dismiss(), or
Pull down the sheet to trigger an "interactive close" (the thing that is disabled when .interactiveDismissDisabled(true) is added to the view).
If you try these both out you'll see that for (1) you'll get two print statements "Sheet parentShowSheet" and "Parent onDismiss", while for (2) you'll just get one print statement "Parent onDimiss".
Three questions:
Why does "Sheet parentShowSheet" not print in case (2)?
Why does "Sheet isPresented" not print in either case? I'd think that dismissing a sheet is very literally changing it's Environment(\.isPresented) value.
How can I add a hook inside of the SheetView that triggers when it is dismissed with either method (1) or method (2) (e.g. something that operates like either of my onChange methods, but actually works in both cases)?

SwiftUI #FocusState - how to give it initial value

I am excited to see the TextField enhancement: focused(...): https://developer.apple.com/documentation/swiftui/view/focused(_:)
I want to use it to show a very simple SwitfUI view that contains only one TextField that has the focus with keyboard open immediately. Not able to get it work:
struct EditTextView: View {
#FocusState private var isFocused: Bool
#State private var name = "test"
// ...
var body: some View {
NavigationView {
VStack {
HStack {
TextField("Enter your name", text: $name).focused($isFocused)
.onAppear {
isFocused = true
}
// ...
Anything wrong? I have trouble to give it default value.
I was also not able to get this work on Xcode 13, beta 5. To fix, I delayed the call to isFocused = true. That worked!
The theory I have behind the bug is that at the time of onAppear the TextField is not ready to become first responder, so isFocused = true and iOS calls becomeFirstResponder behind the scenes, but it fails (ex. the view hierarchy is not yet done setting up).
struct MyView: View {
#State var text: String
#FocusState private var isFocused: Bool
var body: some View {
Form {
TextEditor(text: $text)
.focused($isFocused)
.onChange(of: isFocused) { isFocused in
// this will get called after the delay
}
.onAppear {
// key part: delay setting isFocused until after some-internal-iOS setup
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
isFocused = true
}
}
}
}
}
I was also not able to get this work on Xcode 13, beta 5. To fix, I delayed the call to isFocused = true. That worked!
It also works without delay.
DispatchQueue.main.async {
isFocused = true
}
//This work in iOS 15.You can try it.
struct ContentView: View {
#FocusState private var isFocused: Bool
#State private var username = "Test"
var body: some View {
VStack {
TextField("Enter your username", text: $username)
.focused($isFocused).onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
isFocused = true
}
}
}
}
}
I've had success adding the onAppear to the outermost view (in your case NavigationView):
struct EditTextView: View {
#FocusState private var isFocused: Bool
#State private var name = "test"
// ...
var body: some View {
NavigationView {
VStack {
HStack {
TextField("Enter your name", text: $name).focused($isFocused)
}
}
}
.onAppear {
isFocused = true
}
}
// ...
I’m not certain but perhaps your onAppear attached to the TextField isn’t running. I would suggest adding a print inside of the onAppear to confirm the code is executing.
I faced the same problem and had the idea to solve it by embedding a UIViewController so could use viewDidAppear. Here is a working example:
import SwiftUI
import UIKit
struct FocusTestView : View {
#State var presented = false
var body: some View {
Button("Click Me") {
presented = true
}
.sheet(isPresented: $presented) {
LoginForm()
}
}
}
struct LoginForm : View {
enum Field: Hashable {
case usernameField
case passwordField
}
#State private var username = ""
#State private var password = ""
#FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Username", text: $username)
.focused($focusedField, equals: .usernameField)
SecureField("Password", text: $password)
.focused($focusedField, equals: .passwordField)
Button("Sign In") {
if username.isEmpty {
focusedField = .usernameField
} else if password.isEmpty {
focusedField = .passwordField
} else {
// handleLogin(username, password)
}
}
}
.uiKitOnAppear {
focusedField = .usernameField
// If your form appears multiple times you might want to check other values before setting the focus.
}
}
}
struct UIKitAppear: UIViewControllerRepresentable {
let action: () -> Void
func makeUIViewController(context: Context) -> UIAppearViewController {
let vc = UIAppearViewController()
vc.action = action
return vc
}
func updateUIViewController(_ controller: UIAppearViewController, context: Context) {
}
}
class UIAppearViewController: UIViewController {
var action: () -> Void = {}
override func viewDidLoad() {
view.addSubview(UILabel())
}
override func viewDidAppear(_ animated: Bool) {
// had to delay the action to make it work.
DispatchQueue.main.asyncAfter(deadline:.now()) { [weak self] in
self?.action()
}
}
}
public extension View {
func uiKitOnAppear(_ perform: #escaping () -> Void) -> some View {
self.background(UIKitAppear(action: perform))
}
}
UIKitAppear was taken from this dev forum post, modified with dispatch async to call the action. LoginForm is from the docs on FocusState with the uiKitOnAppear modifier added to set the initial focus state.
It could perhaps be improved by using a first responder method of the VC rather than the didAppear, then perhaps the dispatch async could be avoided.

How to Show Alert from Anywhere in app SwiftUI?

I have condition to show alert in a view which can able to show from anywhere in the app. Like I want to present it from root view so it can possibly display in all view. Currently what happens when I present from very first view it will display that alert until i flow the same Navigation View. Once any sheets open alert is not displayed on it. Have any solutions in SwiftUI to show alert from one place to entire app.
Here is my current Implementation of code.
This is my contentView where the sheet is presented and also alert added in it.
struct ContentView: View {
#State var showAlert: Bool = false
#State var showSheet: Bool = false
var body: some View {
NavigationView {
Button(action: {
showSheet = true
}, label: {
Text("Show Sheet")
}).padding()
.sheet(isPresented: $showSheet, content: {
SheetView(showAlert: $showAlert)
})
}
.alert(isPresented: $showAlert, content: {
Alert(title: Text("Alert"))
})
}
}
Here from sheet I am toggle the alert and the alert is not displayed.
struct SheetView: View {
#Binding var showAlert: Bool
var body: some View {
Button(action: {
showAlert = true
}, label: {
Text("Show Alert")
})
}
}
here is the error in debug when we toggle button
AlertDemo[14187:3947182] [Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x109009c00> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x103908b50> (from <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x103908b50>) which is already presenting <_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView_: 0x103d05f50>.
Any solution for that in SwiftUI? Thanks in Advance.
I was able to achieve this with this simplified version of what #workingdog suggested in their answer. It works as follows:
create the Alerter class that notifies the top-level and asks to display an alert
class Alerter: ObservableObject {
#Published var alert: Alert? {
didSet { isShowingAlert = alert != nil }
}
#Published var isShowingAlert = false
}
render the alert at the top-most level, for example in your #main struct or the ContentView
#main
struct MyApp: App {
#StateObject var alerter: Alerter = Alerter()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(alerter)
.alert(isPresented: $alerter.isShowingAlert) {
alerter.alert ?? Alert(title: Text(""))
}
}
}
}
set the alert that should be displayed from inside a child view
struct SomeChildView: View {
#EnvironmentObject var alerter: Alerter
var body: some View {
Button("show alert") {
alerter.alert = Alert(title: Text("Hello from SomeChildView!"))
}
}
}
Note on sheets
If you present views as sheets, each sheet needs to implement its own alert, just like MyApp does above.
If you have a NavigationView inside your sheet and present other views within this navigation view in the same sheet, the subsequent sheets can use the first sheet's alert, just like SomeChildView does in my example above.
Here is a possible example solution to show an Alert anywhere in the App.
It uses "Environment" and "ObservableObject".
import SwiftUI
#main
struct TestApp: App {
#StateObject var alerter = Alerter()
var body: some Scene {
WindowGroup {
ContentView().environment(\.alerterKey, alerter)
.alert(isPresented: $alerter.showAlert) {
Alert(title: Text("This is the global alert"),
message: Text("... alert alert alert ..."),
dismissButton: .default(Text("OK")))
}
}
}
}
struct AlerterKey: EnvironmentKey {
static let defaultValue = Alerter()
}
extension EnvironmentValues {
var alerterKey: Alerter {
get { return self[AlerterKey] }
set { self[AlerterKey] = newValue }
}
}
class Alerter: ObservableObject {
#Published var showAlert = false
}
struct ContentView: View {
#Environment(\.alerterKey) var theAlerter
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: SecondView()) {
Text("Click for second view")
}.padding(20)
Button(action: { theAlerter.showAlert.toggle()}) {
Text("Show alert here")
}
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct SecondView: View {
#Environment(\.alerterKey) var theAlerter
var body: some View {
VStack {
Button(action: { theAlerter.showAlert.toggle()}) {
Text("Show alert in second view")
}
}
}
}

Multiple Alerts in one view can not be called SwiftUI

The method of calling an alert in swiftUI is supposed to be simpler than ever, a simple example of this would be
struct ContentView: View {
#State private var showingAlert = false
var body: some View {
Button(action: {
self.showingAlert = true
}) {
Text("Show Alert")
}
.alert(isPresented: $showingAlert) {
Alert(title: Text("Important message"), message: Text("Wear sunscreen"), dismissButton: .default(Text("Got it!")))
}
}
}
However, even though I am calling an alert and can verify the method is being called, my alert does not display.
In my code I have the following Struct
private struct resetButton: View {
#State var isResetting = false
var body: some View {
Button("Reset"){
self.isResetting = true
}.alert(isPresented: $isResetting) {
print("We get here")
return
Alert(title: Text("Are you sure?"), message: Text("This will erase your Access Key."), primaryButton: .destructive(Text("Reset")) {
clearToken()
clearAccessKey()
clearUsername()
}, secondaryButton: .cancel(Text("Cancel")))
}
}
}
Which is called in the main view simply by resetButton().
However, when I push this button, despite isResetting equalling true, the alert does not display. I have verified that my .alert method is being called as the console prints
we get here
the first time the button is pushed.
However, the Alert is never displayed.
An alert is displayed correctly elsewhere in this same view with
.alert(isPresented: $failedLogin) {
self.password = ""
return
Alert(title: Text("Invalid Login"), message: Text("Please verify your credentials and try again"), dismissButton: .default(Text("Ok")))
}
When a #State variable named failedLogin is set true, however, the aforementioned alert is never presented.
My first impression is that this may be a bug in Swift, if it is I'll report it to apple. However, maybe something I'm doing isn't working because of an error on my part.
Edit:
As clarified in the answer below, the problem seems to be relating to the fact that my resetButton() was trying to throw up an alert in a view that already contains an alert. Apparently you can't have multiple alerts in one view.
You can show alerts dynamically by using .alert(item:) instead .alert(isPresented:):
struct AlertItem: Identifiable {
var id = UUID()
var title: Text
var message: Text?
var dismissButton: Alert.Button?
}
struct ContentView: View {
#State private var alertItem: AlertItem?
var body: some View {
VStack {
Button("First Alert") {
self.alertItem = AlertItem(title: Text("First Alert"), message: Text("Message"))
}
Button("Second Alert") {
self.alertItem = AlertItem(title: Text("Second Alert"), message: nil, dismissButton: .cancel(Text("Some Cancel")))
}
Button("Third Alert") {
self.alertItem = AlertItem(title: Text("Third Alert"))
}
}
.alert(item: $alertItem) { alertItem in
Alert(title: alertItem.title, message: alertItem.message, dismissButton: alertItem.dismissButton)
}
}
}
So i'm still not 100% sure why this was unable to post an alert this way? But I was able to fix this issue simply by placing the alert in the main view.
I changed the #State variable isResetting to a binding bool and paced a new state variable in the main class. I then just copied over the .alert and this seems to solve the issue.
The struct now looks like this
private struct resetButton: View {
#Binding var isResetting: Bool
var body: some View {
Button("Reset"){
self.isResetting = true
}
}
}
and the alert is the same but is now in the class calling resetButton().
Edit:
So it appears that SwiftUI won't let you call multiple alerts from the same view, however, if you want multiple alerts in the same view then there is a way around this.
You can call an alert from anywhere inside of a view so the following solution works.
private struct exampleAlert: View {
#State var alert1 = false
#State var alert2 = false
var body: some View {
Vstack{
Button("alert 1"){
self.alert1 = true
}.alert(isPresented: $alert1) {
Alert(title: Text("Important message"), message: Text("This alert can show"), dismissButton: .default(Text("Got it!")))
}
Button("alert 2"){
self.alert2 = true
}.alert(isPresented: $alert2) {
Alert(title: Text("Important message"), message: Text("This alert can also show"), dismissButton: .default(Text("Got it!")))
}
}
}
}
The catch is here that if either of these alerts were to be placed on the Vstack, one will not function. If they are placed on separate views though then both can be called as expected.
Perhaps this is something that will be rectified in a future update? In the meantime though, here is a solution for working around this problem.
In my case I solved it by adding an empty Text in front of the alert.
private struct exampleAlert: View {
#State var alert1 = false
#State var alert2 = false
var body: some View {
Vstack{
Button("alert 1"){
self.alert1 = true
}
Button("alert 2"){
self.alert2 = true
}
}
Vstack{
Text("")
.alert(isPresented: $alert1) {
Alert(title: Text("Important message"), message: Text("This alert can show"), dismissButton: .default(Text("Got it!")))
}
Text("")
.alert(isPresented: $alert2) {
Alert(title: Text("Important message"), message: Text("This alert can also show"), dismissButton: .default(Text("Got it!")))
}
}
}
}

SwiftUI: How to execute closure when Alert is dismissed?

I've been trying out swiftUI and looked at this Ray Wenderlich tutorial... I noticed they didn't re-implement the "nextRound" functionality... so I tried to do it myself. Ran into a problem (which maybe they did, also):
The basic question is more general:
Using swiftUI, how do you trigger a function when an Alert is dismissed -- when the user clicks "OK." ?
I've tried using the dismissButton argument of the Alert constructor...
(and also the .onDisappear method of View but I can't figure out how to apply it to the Alert view.)
Code:
import SwiftUI
struct ContentView: View {
#State var shouldShowAlert: Bool = false
// this never gets called
func onAlertDismissed() {
print("you will not see this in the console")
}
// this doesn't seem to work
var dismissButton: some View {
Button(action: {
self.onAlertDismissed()
}) {
// Bilbo Baggins does not appear -- "OK" still shows
Text("BILBO BAGGINS")
}
}
var body: some View {
VStack {
Spacer()
Button(action: {
self.shouldShowAlert = true
}) {
Text("show the alert!")
}
Spacer()
}.alert(isPresented: $shouldShowAlert, content: {
// what to add here?
Alert(title: Text("Alert:"), message: Text("press OK to execute onAlertDismissed()..."))
// what I have tried and doesn't work:
/*
Alert(title: Text("Alert:"), message: Text("press OK to execute onAlertDismissed()..."), dismissButton: self.dismissButton as? Alert.Button)
*/
})
}
}
The button is constructed a little differently. You basically have to use a static factory method from Alert.Button to construct them and pass those in.
Alert(title: Text("Alert:"),
message: Text("press OK to execute default action..."),
dismissButton: Alert.Button.default(
Text("Press ok here"), action: { print("Hello world!") }
)
)
Alert(title: Text("Alert!"), message: Text("Message"),
primaryButton: Alert.Button.default(Text("Yes"), action: {
print("Yes")
}),
secondaryButton: Alert.Button.cancel(Text("No"), action: {
print("No")
})
)
It's possible to create alerts like this:
import SwiftUI
struct ContentView: View {
#State var showingAlert = false
var body: some View {
VStack {
HStack {
Button(action: {
self.showingAlert = true
})
{
Text("Save")
.font(.headline)
}
.alert(isPresented: $showingAlert, content: {
return Alert(
title: Text("Save Product"),
message: Text("Are you sure you want to save the changes made?"),
primaryButton: .default(Text("Yes"), action: {
//insert an action here
}),
secondaryButton: .destructive(Text("No")))
})
}
}
}
}
By looking at your code, it appears you don’t include a button in the alert propert, so your alert is not executing any action, in swiftui the alert signature is
init(title: Text, message: Text? = nil, primaryButton: Alert.Button, secondaryButton: Alert.Button)
Implement the signature properly is the first step

Resources