'Swift protocol properties with delegates

I am trying to setup a success/error view on a controller via protocol and extensions.

What I want to achieve is that I want to get into the state where it is enough to implement the protocol on a controller, and from there get access to the successView (no additional boilerplate).

This is what I have so far:

protocol SucessViewProtocol where Self: UIViewController {
    
    func initSuccessView()

    var successView: UIView! { get set }
    var topConstraint: NSLayoutConstraint! { set get }

    func showSuccess()
    func hideSucess()
}

extension SucessViewProtocol {

    func showSuccess() {
        //animate displaying success message
    }

    func hideSucess() {
        //animate hiding success message
    }

    func initSuccessView()  {
        successView = UIView()
        topConstraint = NSLayoutConstraint()
        // init success view and top constraint
    }
}

Now when I implement the protocol on the controller it looks like this:

// MARK: SuccessView
extension ConsumingViewController: SucessViewProtocol {
    var successView: UIView! {
        get {
            //getter
        }
        set {
            //setter
        }
    }

    var topConstraint: NSLayoutConstraint! {
        get {
            //getter
        }
        set {
            //setter
        }
    }
 
}

I guess my problem is obvious because I get the successView and topConstraint as properties inside my controller that is implementing the SucessViewProtocol. I am initializing the properties from the protocol inside the extension, so what I need would be just an access to these properties (not declaring them again in my controller). I guess I am missing some "glue" part between the protocol - extension - controller

I want to be able to implement the protocol on a controller, call initSuccessView() and from there it should just be enough to call showSuccess and hideSuccess.

Edit:

This is how I want to use this construct:

class ConsumingViewController: UIViewController {
   func viewDidLoad() {
     initSuccessView()
     
     loadData()
   }

   private func loadData() {
     //successfullyloaded
     showSuccess()
   }

}

// MARK: SuccessView
extension ConsumingViewController: SucessViewProtocol {
  var successView: UIView! {
    get {
        //getter
    }
    set {
        //setter
    }
  } *PROBLEMATIC*

  var topConstraint: NSLayoutConstraint! {
    get {
        //getter
    }
    set {
        //setter
    }
  } *PROBLEMATIC*

}

As I said, the problem is that the properties successView and topConstraing are being redeclared inside ConsumingViewController (because they are part of the protocol). I would need to actually not be visibile inside the controller, but just being used inside the extension. But then there is the problem with stored properties inside extensions ...



Solution 1:[1]

May be you want this?

protocol SucessViewProtocol {
    func showSuccess()
    func hideSucess()
}

fileprivate struct Key {
    static var runtimeKey: Int = 0
}

extension SucessViewProtocol where Self: UIViewController  {
    var successView: UIView? {
        get {
            return objc_getAssociatedObject(self, &Key.runtimeKey) as? UIView
        }
        set {
            objc_setAssociatedObject(self, &Key.runtimeKey, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }

    func showSuccess() {
        successView = UIView()
        //animate displaying success message
        view.addSubview(successView)
    }

    func hideSucess() {
        //animate hiding success message
        successView?.removeFromSuperview()
    }
}

Solution 2:[2]

add protocol variables inside main scope not extension scope UIViewController like this.

public class ViewController: UIViewController, SucessViewProtocol {
    var successView: UIView!
    var topConstraint: NSLayoutConstraint!    
}

By doing this you do not need to define getter and setter for properties

and inside ViewDidLoaded you can initSuccessView:

public override func viewDidLoad() {
        super.viewDidLoad()
        
        self.initSuccessView()
    }

and call custom function:

func show() {
        self.showSuccess()
    }
    
    func hide() {
        self.hideSucess()
    }

Solution 3:[3]

Solution = Optional Porotocols

You just need add properties to the extension as well to make them optional.

protocol SucessViewProtocol where Self: UIViewController {

    func initSuccessView()

    var successView: UIView! { get set }
    var topConstraint: NSLayoutConstraint! { set get }

    func showSuccess()
    func hideSucess()
}

extension SucessViewProtocol {

    // ??------ these 2 properties added ------??
    var successView: UIView! { get{ nil } set{} }
    var topConstraint: NSLayoutConstraint! { get{ nil } set{} }

    func showSuccess() {}

    func hideSucess() {}

    func initSuccessView()  {
        successView = UIView()
        topConstraint = NSLayoutConstraint()
    }
}

Then, when you conform SucessViewProtocol on ConsumingViewController you won't require to implement protperties.

// MARK: SuccessView
extension ConsumingViewController: SucessViewProtocol {
    // There's NO compiler error here!
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 hessam mahdiabadi
Solution 3 Mamad Farrahi