'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 |