Class to protocol conversation in swift

Alix

I have this heavy basicVC Class subclass of UIViewController that I am trying to convert as vcprotocol.

It's the basicVC that is doing all work like god class. Which I would like to break into as vcProtocol.

I am trying to do is a separation of concern. Not all of the ViewControllers need to show the Alert View or Network not connected msg.

For example, I have indicatorView that I create in protocol extension as computed property. No error warning but no indicator is being shown. When I try to debug and do po acticvityIndicator I get the following error, which indicates that activityIndicator was never allocated.

error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x5a1012de027).
The process has been returned to the state before expression evaluation.

Code snippet:

protocol vcProtocol {
    var activityIndicator: UIActivityIndicatorView { get }
}

protocol extension:

extension vcProtocol where Self: UIViewController {

    var activityIndicator: UIActivityIndicatorView {
        let indicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.gray)
        indicator.hidesWhenStopped = true
        indicator.style = .whiteLarge
        indicator.color = .red
        indicator.backgroundColor = UIColor.gray
        indicator.translatesAutoresizingMaskIntoConstraints = false
        return indicator
    }

    func showLoadingIndicator() {
        activityIndicator.startAnimating()
        activityIndicator.isHidden = false
    }

    func hideLoadingIndicator() {
        activityIndicator.stopAnimating()
        activityIndicator.isHidden = true
    }
}

I am not able to wrap my head around how to solve this. as I can only have computed properties in the protocol. so I have them as get only properties. my plan is to use the protocol extension to provide a default implementation.

Any thoughts on how to solve this problem.

Rob

This activityIndicator is a computed property, so every time you reference the computed property, that get block will be called. The net effect is that, as written, each time you reference activityIndicator, you’re going to get a new instance of UIActivityIndicatorView, which is obviously not your intent.

Consider your showLoadingIndicator:

func showLoadingIndicator() {
    activityIndicator.startAnimating()
    activityIndicator.isHidden = false
}

The first line (with startAnimating) will return a new UIActivityIndicatorView and the second line (with isHidden) will return yet another. And neither of these will be the same one that you presumably added to your subview.

This activityIndicator really should be instantiated once and only once. Unfortunately, you can’t define the stored property in an extension, so there are a few approaches:

  1. You can let the UIViewController declare the stored property and just define methods method to configure, show, and hide it:

    protocol LoadingIndicatorProtocol: class {
        var loadingActivityIndicator: UIActivityIndicatorView? { get set }
    }
    
    extension LoadingIndicatorProtocol where Self: UIViewController {
    
        func addLoadingIndicator() {
            let indicator = UIActivityIndicatorView(style: .gray)
            indicator.hidesWhenStopped = true
            indicator.style = .whiteLarge
            indicator.color = .red
            indicator.backgroundColor = .gray
            indicator.translatesAutoresizingMaskIntoConstraints = false
    
            view.addSubview(indicator)
            // you might want to add the constraints here, too
    
            loadingActivityIndicator = indicator
        }
    
        func showLoadingIndicator() {
            loadingActivityIndicator?.startAnimating()
            loadingActivityIndicator?.isHidden = false
        }
    
        func hideLoadingIndicator() {
            loadingActivityIndicator?.stopAnimating()
            loadingActivityIndicator?.isHidden = true
        }
    }
    

    And then a UIViewController subclass simply has to define its own ivar for the activityIndicator, e.g.

    class ViewController: UIViewController, LoadingIndicatorProtocol {
        var loadingActivityIndicator: UIActivityIndicatorView?
    
        override viewDidLoad() {
            super.viewDidLoad()
    
            addLoadingIndicator()
        }
    
        ...
    }
    
  2. The other approach is to use associated objects via objc_getAssociatedObject and objc_setAssociatedObject, to achieve stored property behavior:

    protocol LoadingIndicatorProtocol {
        var loadingActivityIndicator: UIActivityIndicatorView { get }
    }
    
    private var associatedObjectKey = 0
    
    extension LoadingIndicatorProtocol {
    
        var loadingActivityIndicator: UIActivityIndicatorView {
            if let indicatorView = objc_getAssociatedObject(self, &associatedObjectKey) as? UIActivityIndicatorView {
                return indicatorView
            }
    
            let indicator = UIActivityIndicatorView(style: .gray)
            indicator.hidesWhenStopped = true
            indicator.style = .whiteLarge
            indicator.color = .red
            indicator.backgroundColor = .gray
            indicator.translatesAutoresizingMaskIntoConstraints = false
            objc_setAssociatedObject(self, &associatedObjectKey, indicator, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    
            return indicator
        }
    
        func showLoadingIndicator() {
            loadingActivityIndicator.startAnimating()
            loadingActivityIndicator.isHidden = false
        }
    
        func hideLoadingIndicator() {
            loadingActivityIndicator.stopAnimating()
            loadingActivityIndicator.isHidden = true
        }
    }
    

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

When to use `protocol` and `protocol: class` in Swift?

How to make a class conform to a protocol in Swift?

Importing a Swift protocol in Objective-C class

Class conforming to protocol as function parameter in Swift

Swift Property that conforms to a Protocol and Class

Trying to add a protocol to a Class signature in swift

Requiring Protocol and Class in Swift Properties

Swift protocol in Objective-C class

Declare class variable as conforming to Swift protocol

Swift protocol defining class method returning self

Swift: the type of a class that implements a protocol

Swift Class Extension Only When Conforming to Protocol

Protocol Extension in Swift Where Object Is a Class and conforms to a Protocol

Swift Protocol of a particular class

Swift protocol & weak references with class

Implementing Objective-C protocol in Swift class

Swift 3, function in protocol that return object of class that conforms to a protocol

Swift Protocol as Generic Parameter to Class

Swift Generic Protocol Class Type in Array

Add class property to protocol in Swift

How to convert/cast from a protocol to a class in swift?

Object initialization with protocol and class name in swift

Swift class "does not implement (objc) protocol"

Get the class type that implements a protocol swift

Swift Class Pointer as? Class Protocol?

Are mixed class/protocol type constraints allowed in Swift protocol extensions?

ios swift class conforming protocol

Swift Protocol on a specific class?

How to conform a class to a protocol in Swift and can protocol methods be overridden?