How to Show an Alert from Another Class in Swift

How to show an alert from another class in Swift?

You can create extension method for UIApplication (for example) which will return your topViewController:

extension UIApplication {

static func topViewController(base: UIViewController? = UIApplication.sharedApplication().delegate?.window??.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController, selected = tab.selectedViewController {
return topViewController(selected)
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}

return base
}
}

And then your class will look like this:

class ErrorReporting {

static func showMessage(title: String, msg: String) {
let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
UIApplication.topViewController()?.presentViewController(alert, animated: true, completion: nil)
}
}

Method need to be static to be able to call it as ErrorReporting.showMessage.

How to display an Alert from a different class

You have to add an additional parameter in your alertUser function, which would be the VC that will present the alert controller.

for example:

public func alertUser(strTitle: String, strMessage: String, viewController: UIViewController) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
viewController.present(myAlert, animated: true, completion: nil)
}

But I would recommend that you just make an extension of UIViewController and add your func alertUser()* there because you would surely use this alertUser in different VCs and complexity wise in my opinion, this would be more optimized.

Like this:

extension UIViewController {

func showAlert(title: String, message: String, callback: @escaping () -> ()) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {
alertAction in
callback()
}))

self.present(alert, animated: true, completion: nil)
}

//add additional functions here if necessary
//like a function showing alert with cancel
}

NOTE : Please don't make your Utilities class a subclass of UIViewController, it would also be better to make it a struct handling static functions and/or variables

SwiftUI - How to trigger an Alert from a seperate class

Got it..

E.Coms answer is very good, however, I was calling the func which would be responsible for this action from within another function, within another function from this other class, not directly from the scene delegate.

To get it, I had to pass the DataBusy object dataBusy from my scene delegate to my class func as a parameter:

//Scene Delegate:

let dataBusy = DataBusy() //my Observed Object Class
let myClass = MyClass(); //instantiate my class

myClass.myFunc(data: dataBusy) //pass the object through to the class function

This way, when the time arises within my class function, I can toggle the variable and it all works perfectly.

Thank you for your input.

Calling alert view from another class, but one is not in hierarchy

While using UIAlertController it should be presented form the topmost ViewController in the view hierarchy.

so i suggest:

var rate = RateMyApp.sharedInstance 
rate.showRatingAlert(self)

In RateMyApp class:

func showRatingAlert(sender:UIViewController){
//..... your UIAlertController here
sender.presentViewController(alert, animated: true, completion: nil)
}

On a more general note, the func presentViewController(:_) should be called only from the top-most View Controller.

You can create an extension for it like so:

extension UIViewController {
@warn_unused_result
static func topViewController(base: UIViewController? = UIApplication.sharedApplication().windows.first?.rootViewController) -> UIViewController? {

if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}

if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController

if let top = moreNavigationController.topViewController where top.view.window != nil {
return topViewController(top)
} else if let selected = tab.selectedViewController {
return topViewController(selected)
}
}

if let presented = base?.presentedViewController {
return topViewController(presented)
}

if let splitVC = base as? UISplitViewController {
if let lastVC = splitVC.viewControllers.last {
return topViewController(lastVC)
}
else {
return splitVC
}
}

if let lastChildVC = base?.childViewControllers.last {
return topViewController(lastChildVC)
}

return base
}
}

You can call the alert as follows:

func showRatingAlert() {
if let vc = UIViewController.topViewController() {
vc.presentViewController(alert, animated: true, completion: nil)
}
}

Calling a Function from another Class in Swift 2.1

So it looks like there are a few things going on here.

  • I am going to guess that logInViewController inherits from ViewController instead of UIViewController. If it doesn't, it should.
  • ViewController().displayAlert(...) is initializing a ViewController instance and then calling displayAlert(...) on that instance. The initialized ViewController instance is not in the view hierarchy, so the alert will not be displayed anywhere. You should not be calling ViewController() anyway.
  • Simply calling displayAlert(...) without ViewController() in front will call the instance method of the current view controller (self), which is correct! So, you SHOULD be calling it like this or like self.displayAlert(...) (this assumes that logInViewController inherits from ViewController).

How do I present an alert from a custom class in swiftui?

Paul has a point - here's a possible implementation:

// In CustomClass.swift

import Combine

class CustomClass : ObservableObject {

@Published var dataRecieved = PassthroughSubject<DetailResponse, Never>()

init() {
performNetWorkRequest()
}

func performNetWorkRequest() {
URLSession.shared.dataTask(with: url) { (data, response, error) in

let response = try JSONDecoder().decode(DetailResponse.self, from: data)

DispatchQueue.main.async {
self.dataRecieved.send(response)
}
}
.resume()
}
}

// In SomeView.swift

import SwiftUI
import Combine

struct ContentView: View {

@State var showAlert = false
var customClass = CustomClass()

var body: some View {
Text("Hello, World!")
.onReceive(customClass.dataRecieved) { _ in
self.showAlert = true
}
.alert(isPresented: $showAlert) {
// your alert
}
}
}

Notice I didn't mention the SceneDelegate in any of these - this approach (called MVVM) is more flexible, in my opinion - besides, the way it is set up, performNetWorkRequest() will be executed as soon as your view is initialized, anyway.
You can also tweak the PassthroughSubject - I didn't know if you needed the DetailResponse or not.
Hope this helped!

Edit:

I just reread your question and it seems that this implementation is at fault as you noted there was no way to know what view the user would be on in the case of a network change. In that case, you can feed the same instance of CustomClass in your SceneDelegate as an EnvironmentObject.



Related Topics



Leave a reply



Submit