Oleksandr Matrosov
Oleksandr Matrosov

Reputation: 27187

Extending UIViewController(s) to show UIAlertController without subclassing

I would like to reuse code to show alert when I delete some row in tableview in a few my view controllers:

func confirmDelete(item: String) {

    let alert = UIAlertController(title: "Delete Planet", message: "Are you sure you want to permanently delete \(item)?", preferredStyle: .actionSheet)
     
    let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: handleDeleteItem)
     
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: cancelDeleteItem)
    
    alert.addAction(deleteAction)
    alert.addAction(cancelAction)
    
    self.present(alert, animated: true, completion: nil)
}

func handleDeleteItem(alertAction: UIAlertAction!) -> Void {

    if let indexPath = deletePlanetIndexPath {
        presenter?.removePlanet(atIndex: indexPath, completion: { (result) in
            
            switch result {
            case .success(_):
                self.tableView.deleteRows(at: [indexPath], with: .fade)
                break
            case let .failure(error):
                print(error)
                break
            }
        })
        
        deletePlanetIndexPath = nil
    }
}


func cancelDeleteItem(alertAction: UIAlertAction!) {
    deletePlanetIndexPath = nil 
}

The only one part will be different everytime:

presenter?.removePlanet(atIndex: indexPath, completion: { (result) in
                
                switch result {
                case .success(_):
                    self.tableView.deleteRows(at: [indexPath], with: .fade)
                    break
                case let .failure(error):
                    print(error)
                    break
                }
            })

So as you can see I can simple do subclassing and declare some closure variable which will be triggered each time deleteAction invoked.

It's very simple way, but I am not super fun of subclassing. Maybe there is some help-full extension, protocol based thing or any other suggestions.

Upvotes: 1

Views: 126

Answers (3)

Oleksandr Matrosov
Oleksandr Matrosov

Reputation: 27187

Thanks to guys who already posted answers, much appreciated. I came up with another solution as well. So in case we use Tob example we extend functionality for all UIViewControllers in case we use h.and.h example we decorate our specific view controller but it force us to write additional lazy code with is not big deal and I mostly like this example which does not make any view controllers to be able using UIAlerController extension but I will follow a solution which is protocol based extension:

import UIKit

protocol UIAlertControllerManageable {
    func createAlert(with text: String, handleDeleteItem: @escaping (UIAlertAction) -> Void, cancelDeleteItem: @escaping  (UIAlertAction) -> Void)
}

extension UIAlertControllerManageable where Self: UIViewController {
   
    func createAlert(with text: String, handleDeleteItem: @escaping (UIAlertAction) -> Void, cancelDeleteItem: @escaping  (UIAlertAction) -> Void) {
        
        let alert = UIAlertController(title: "Delete", message: "Are you sure you want to permanently delete \(text)?", preferredStyle: .actionSheet)
         
        let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: handleDeleteItem)
         
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: cancelDeleteItem)
        
        alert.addAction(deleteAction)
        alert.addAction(cancelAction)
        
        self.present(alert, animated: true, completion: nil)
    }
}

Usage:

class HomeViewController: UIViewController, UIAlertControllerManageable {
        
        func confirmDelete(deleteText: String) {
            
            createAlert(with: deleteText) { (action) in
                
                if let indexPath = self.deleteItemIndexPath {
                    self.presenter?.removeItem(atIndex: indexPath, completion: { (result) in
                        
                        switch result {
                        case .success(_):
                            self.tableView.deleteRows(at: [indexPath], with: .fade)
                            break
                        case let .failure(error):
                            print(error)
                            break
                        }
                    })
                    
                    self.deleteItemIndexPath = nil
                }
                
            } cancelDeleteItem: { (action) in
                self.deleteItemIndexPath = nil
            }
        }
    
    }

Let me know you thoughts, thanks!

Upvotes: 1

h.and.h
h.and.h

Reputation: 776

An extension to UIAlertController could work:

class MyVC: UIViewController {
    private lazy var alertControllerA: UIAlertController = {
         return UIAlertController.customAlertController(/* params */)
    }()
}

private extension UIAlertController {
        static func customAlertController(_ item: String, handleDeleteItem: @escaping () -> Void, cancelDeleteItem: @escaping  () -> Void) -> UIAlertController {
            let alertController = UIAlertController(title: "Delete Planet", message: "Are you sure you want to permanently delete \(item)?", preferredStyle: .actionSheet)
         
        let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: handleDeleteItem)
     
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: cancelDeleteItem)
    
        alert.addAction(deleteAction)
        alert.addAction(cancelAction)
    
        return alertController
    }
}

Upvotes: 1

Tob
Tob

Reputation: 1025

You could write a class extension to view controller:

extension UIViewController {
    func createAlert(handleDeleteItem: @escaping () -> Void, cancelDeleteItem: @escaping  () -> Void) {
        let alert = UIAlertController(title: "Delete Planet", message: "Are you sure you want to permanently delete \(item)?", preferredStyle: .actionSheet)
     
        let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: handleDeleteItem)
     
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: cancelDeleteItem)
    
        alert.addAction(deleteAction)
        alert.addAction(cancelAction)
    
        self.present(alert, animated: true, completion: nil)
    }
}

And then pass in the appropriate delete and cancel functions for each different view controller.

Upvotes: 2

Related Questions