Home > Software engineering >  SwiftUI Alert shown once
SwiftUI Alert shown once

Time:10-02

My app consumes few API endpoints to fetch data, that is managed by respective ViewModels.

For example, this ViewModel fetch the languages

class LanguageViewModel: ObservableObject {
    var subscriptions: Set<AnyCancellable> = []

    @Published private(set) var languages: [LanguageItem]?
    @Published private(set) var error: Error?

    func get() {
        APIService.fetchData()
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { [weak self] value in
                guard let self = self else { return }
                if case let .failure(error) = value {
                    self.error = error
                }

            }, receiveValue: { (data: [LanguageItem]) in
                self.languages = data

            })
            .store(in: &self.subscriptions)
    }
}

and so on.

Now for the main UI

struct ContentView: View {
    @ObservedObject private var languageViewModel = LanguageViewModel()

    var body: some View {
        let hasError = Binding<Bool>(
            get: { self.languageViewModel.error != nil },
            set: { _ in }
        )

        Button(action: {
            self.languageViewModel.get()
        }) {
            Text("Hello, World")
        }
        .alert(isPresented: hasError) {
            let message = self.languageViewModel.error?.description

            return Alert(title: Text("Error"), message: Text(message ?? ""), dismissButton: .default(Text("OK")))
        }
    }
}

If somehow the API call fails on clicking the button, the first and only first time the alert is shown, then on subsequent clicks does not appear.

Placing a breakpoint on .alert show that the code is running correctly, just not showing again.

Please send help!!

CodePudding user response:

On alert dismiss button action reset error back to nil, like:

.alert(isPresented: hasError) {
    let message = self.languageViewModel.error?.description

    return Alert(title: Text("Error"), message: Text(message ?? ""), dismissButton: .default(Text("OK")) {
       self.languageViewModel.error = nil    // << here !!
    })
}
  • Related