How to show alert in all controllers without repeating the code? - ios

I want to write one function alert() and to run it. But I want to show this alert in any controllers without repeating the code.
For example: I have Presence.swift class and here I have some condition, as:
if myVar == 1 { // Just for presenting
runMyAlert()
}
and when in the background myVar == 1 user, regardless of the fact that where he is, on which screen, he gets alert on screen.
How can I do it without repeating my code? I tried to make it in AppDelegate as:
func alert(title : String,message : String,buttonTitle : String,window: UIWindow){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: buttonTitle, style: UIAlertActionStyle.Default, handler: nil))
window.rootViewController?.presentViewController(alert, animated: true, completion: nil)
}
And call it as:
if myVar == 1 {
AppDelegate().alert()
}

Developing on Swift you should know about protocols which can solve your problem easily. You can create a new protocol MyAlert and make default implementation for UIViewController class. And then just inherit your MyAlert protocol in view controller, where you need it, and call the function!
protocol MyAlert {
func runMyAlert()
}
extension MyAlert where Self: UIViewController {
func runMyAlert() {
let alert = UIAlertController(title: title, message: "message", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "buttonTitle", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
So you can implement and call it like that:
class MyViewController: UIViewController, MyAlert {
override func viewDidLoad() {
runMyAlert() // for test
}
}
UPD:
code for your case:
protocol MyAlert {
func runMyAlert()
}
extension MyAlert where Self: UIViewController {
func runMyAlert() {
let alert = UIAlertController(title: title, message: "message", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "buttonTitle", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
class OpenChatsTableViewController: UITableViewController, MyAlert, OneRosterDelegate, BuddyRequestProtocol {
override func viewDidLoad() {
runMyAlert()
}
}

Create a category on UIViewController, write a method there, calling which app will show an alert box.
Now create a new BaseViewController which will be inherited from UIViewController. Import your category in this BaseViewController;
From this point, whenever you create a new viewController, select your base class type as BaseViewController not UIViewController.
By doing so, you can call that alert showing method from anywhere.

Related

UIAlertController is not working in model class

I want to show an alert. But I want to show it by creating a function in another class and call that function from a viewcontroller. But it does not work.
Here is code from my LoginViewController:
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
LoginModel().show_alert()
}
}
Here is code from my LoginModel:
class LoginModel{
let controller = LoginViewController()
public func show_alert(){
let alert = UIAlertController(title: "Title", message: "Some Message",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
controller.present(alert, animated: true, completion: nil)
}
}
You need to pass the reference of UIViewController subclass to your LoginModel class, to present the UIAlertViewController on LoginViewController. You should call show alert once the LoginViewController view is appeared on the screen, move the call to ViewWillApear or viewDidApear method
final class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidApear(animated)
LoginModel().show_alert(on: self)
}
}
final class LoginModel {
public func show_alert(on vc: UIViewController) {
let alert = UIAlertController(title: "Title", message: "Some Message",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
vc.present(alert, animated: true, completion: nil)
}
}
ideally you should not create the UI related methods in model classes, they should be on on UIViewController/UIView classes or their extension methods. Model classes should not know anything about UI stuff. So you can easily create simple extension method on UIViewController and call the showAlert method from viewController.
extension UIViewController {
func showAlert(_ title: String = "Alert", message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
present(alert, animated: true, completion: nil)
}
}
you can call this method from UIViewController like
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showAlert(message: "This is alert message")
}
Reminder: Your model shouldn't know about any UI related stuffs. Instead you should create an extension to UIViewController, or create free function
As a free function
func showAlertViewOnto(controller: UIViewController, detailInfo: (title: String?, message: String?), handler: ((UIAlertAction) -> Void)? = nil ) {
let alert = UIAlertController(title:detailInfo.title , message: detailInfo.message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
handler))
controller.present(alert, animated: true, completion: nil)
}
As an extension to your UIViewController
extension UIViewController {
func showAlertView(detailInfo: (title: String?, message: String?), handler: ((UIAlertAction) -> Void)? = nil ) {
let alert = UIAlertController(title:detailInfo.title , message: detailInfo.message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
handler))
self.present(alert, animated: true, completion: nil)
}
}
To Use it
showAlertViewOnto(controller: self, detailInfo: (title: "Hello ", message: "welcome to our service"), handler: { _ in
// here you can add code once ok is pressed
})
There is a logical error in your code. An instance of LoginViewController is already present (in the navigation stack or is the initial view controller) which appears on the screen.
You created a new instance of LoginViewController in your model class
let controller = LoginViewController()
which is not added to your navigation stack, so you don’t see it on the screen.
controller.present(alert, animated: true, completion: nil)
Presenting UIAlertController over the new instance will not show user an alert since the controller here itself is not present on the screen.
You will need to present UIAlertController from the instance visible on screen(the initial one). You could change the functionshow_alert to the following:
class LoginModel{
func showAlert(forController controller: UIViewController /*you could add title, message and other stuff here if needed.*/){
let alert = UIAlertController(title: "Title", message: "Some Message",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
controller.present(alert, animated: true, completion: nil)
}
And modify your call as follows:
LoginModel().showAlert(forController: self)

How can I declare a function or variable in a file and use it always in Swift [duplicate]

This question already has answers here:
create alert function in all view controllers - swift
(7 answers)
Closed 5 years ago.
Currently I am working on a project and I have to use an alert message almost in every view controller. Particularly this one:
func showMessage(myMessage: String) {
let myAlert = UIAlertController(title: "ALERT", message: myMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
Using the same code over an over again makes my code verbose. This scenario goes with some other functions and some variables also. How can I declare this function somewhere in a different file and use it whenever necessary? Shall I use a singleton pattern like:
static let sharedInstance = viewController()
If so, please show me an example.
The best way to do this is using an extension such as:
extension UIViewController {
func showMessage(myMessage: String) {
let myAlert = UIAlertController(title: "ALERT", message: myMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
}
This way you can use it in any class subclassed from UIViewController. You could also just drop the function into a swift file as a "global function" but that's not very clean.
Your function showMessage(myMessage:) is common to all your ViewControllers. So you can either :
Use a super class ViewController and implement your function in there :
class BaseViewController {
func showMessage(myMessage: String) { ... }
}
Use swift protocols :
protocol MessageHelper {}
extension MessageHelper where Self: UIViewController {
func showMessage(myMessage: String) { ... }
}
extension MyViewController: MessageHelper {}
Using a shared instance that handles all your message could be another way to go.
class MessageController {
static let shared = MessageController()
private init() {}
func showMessage(myMessage: String, viewController: UIViewController) { ... }
}
And just call MessageController.shared.showMessage(myMessage:viewController:) when you need to display a message.
Using a message controller will give you more possibilities, you can either count the number of messages displayed or filter messages to display in the same and one place.
Use a custom class for the view controllers in your app. Instead of subclassing UIViewController, each of your view controllers would subclass your CustomViewController class. Here's an example:
class CustomViewController: UIViewController {
func showMessage(myMessage: String) {
let myAlert = UIAlertController(title: "ALERT", message: myMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
}
Each view controller you create that subclasses CustomViewController would then have access to the showMessage function.

UIAlertController in static method giving error: extra argument animated in cell

I wish to create a static method which I can place in a utility class which would launch a UIAlertController. However, I am getting the following error:
"extra argument animated in cell"
static func simpleAlertBox1(msg : String) -> Void{
let alertController = UIAlertController(title: "Alert!", message: msg, preferredStyle: .actionSheet)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)// error is being generated here
}
I tried this but it still gave me the same error:
presentViewController(alertController, animated: true, completion: nil)
but if I were to remove the static, then it works fine.
self is an instance of UIViewController , If you want to call this function in a static way just add an other param viewcontroller in which you want to present it. very important, you need to show your alertView after viewDidload.
here an example code:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
ViewController.simpleAlertBox1(msg: "test", viewController: self)
}
static func simpleAlertBox1(msg : String , viewController : UIViewController) -> Void{
let alertController = UIAlertController(title: "Alert!", message: msg, preferredStyle: .actionSheet)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
viewController.present(alertController, animated: true, completion: nil)// error is being generated here
}
}
The method present(_:animated:completion:) is an instance method of UIViewController. You need to send that method to a specific instance of UIViewController. By making your function a static function, it is a function of the class, not of an instance of a class.
(It's like sending a message to a car factory saying "set the radio station to 99.5 FM. That message only makes sense when sent to an instance of a car, not to the car factory, or to the entire Toyota Prius class of cars.)

create a display alert function globally and call it from any view controller

func displayalert(title:String, message:String, vc:UIViewController)
{
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction((UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
self.dismiss(animated: true, completion: nil)
})))
vc.present(alert, animated: true, completion: nil)
}
this is the function i have used.i tried to call it like this,
displayalert1(title:"dsfvasdcs", message:"easfSDXCSDZX", vc:validateOTPViewController())
it is returning error "BAD ACCESS". the vc.present is running like a loop. I cant understand what the problem is.
I run your code and it working fine. I thing you would pass self in vc.
self.displayalert(title: "Title", message: "Some Message", vc: self)
You can also make an extension of UIViewController-
extension UIViewController {
// Your Function...
}
Now You can globally access this function from any view controller, Just by typing-
self.displayalert(title: "Title", message: "Some Message", vc: self)
You're passing a new instance of the validateOTPViewController to the displayalert function.
Change it to:
displayalert1(title:"dsfvasdcs", message:"easfSDXCSDZX", vc:self)
This will pass the current view controller to the function instead of a new one that hasn't been presented.
Swift 4
Create an extension of UIViewController with your function to display alert with required parameter arguments
extension UIViewController {
func displayalert(title:String, message:String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction((UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
alert.dismiss(animated: true, completion: nil)
})))
self.present(alert, animated: true, completion: nil)
}
}
Now call this function from your view controller:
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.displayalert(title: <String>, message: <String>)
}
}

Make a class comprises of the functions like UIAlertView, UIActivityIndicator and call them back in various viewControllers

This is my current code:
import UIKit
class classViewController: UIViewController {
// The function i want to call in other view controllers..
func alertView(title: String, message: String) {
var alert:UIAlertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
In the other view controller, where I've made an IBAction to perform this alertView, I have done this:
#IBAction func button(sender: AnyObject) {
classViewController().alertView("title", message: "message")
}
When I run the app, after tapping the button I get this error, but no alertView:
Warning: Attempt to present on
whose view is not in the
window hierarchy!
Right. If you want to make a global class that displays alerts, you need to pass in a reference to the current view controller, and use that instead of "self" in calls like presentViewController.
Your class should probably not be a subclass of UIViewController, since it looks like you're never displaying it to the screen.
I created a Utils class that is a subclass of NSObject.
It has a method showAlertOnVC that looks like this:
class func showAlertOnVC(targetVC: UIViewController?, var title: String, var message: String)
{
title = NSLocalizedString(title, comment: "")
message = NSLocalizedString(message, comment: "")
if let targetVC = targetVC
{
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let okButton = UIAlertAction(
title:"OK",
style: UIAlertActionStyle.Default,
handler:
{
(alert: UIAlertAction!) in
})
alert.addAction(okButton)
targetVC.presentViewController(alert, animated: true, completion: nil)
}
else
{
println("attempting to display alert to nil view controller.")
println("Alert title = \(title)")
println("Alert message = \(message)")
}
}

Resources