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 fromViewController
instead ofUIViewController
. If it doesn't, it should. ViewController().displayAlert(...)
is initializing aViewController
instance and then callingdisplayAlert(...)
on that instance. The initializedViewController
instance is not in the view hierarchy, so the alert will not be displayed anywhere. You should not be callingViewController()
anyway.- Simply calling
displayAlert(...)
withoutViewController()
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 likeself.displayAlert(...)
(this assumes thatlogInViewController
inherits fromViewController
).
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
How to Create a Packed Data Structure in Swift
How to Apply Shadow to Interior Views in Swiftui
Swift Remove Object from Realm
Xcode Error: Missing Required Module 'Firebase'
Insert, Update and Delete Animations with Foreach in Swiftui
Update Core Data Object Order - Not Working
If a Function Returns an Unsafemutablepointer Is It Our Responsibility to Destroy and Dealloc
Vapor 3 Beta Example Endpoint Request
Difference Between String Interpolation and String Concatenation
Using a Property as a Default Parameter Value for a Method in the Same Class
How to Alloc/Dealloc Unsafe Pointers in Swift
Is Self Captured Within a Nested Function
Get Playground to Display All Loop Results
Difference Between Switch Cases "@Unknown Default" and "Default" in Swift 5
Difference Between Packed VS Normal Data Type