1

I want to declare a view that conform to a certain protocol. On one side, I need to be able to specify it is an UIView, to be able to add it as subview and also expect conformance with a protocol.

The problem started with, that I want to create a generic container for a textview which can be editable or not.

I decided that I will use different classes for this - UILabel when it's not editable and UITextField when it is. So in my generic container I want to be able to 1. add the view as a subview and 2. To be able to set the text.

UILabel and UITextField don't have a common interface to set the text, so I let them implement a new protocol using extensions:

protocol TextView {
    var text_:String? {get set}
}

extension UILabel:TextView {

    var text_:String? {
        get {
            return self.text
        }
        set {
            self.text = newValue
        }
    }
}


extension UITextField: TextView {

    var text_:String? {
        get {
            return self.text
        }
        set {
            self.text = newValue
        }
    }
}

So far so good - but in my container view I need to declare the generic view either as UIView or TextView. If I do it as TextView, I have to cast in order to add them as a subview, and if I declare it as UIView, I have to cast when I want to set the text.

I would like to be able to specify (compile time) something like "this variable is a UIView that conforms to TextView protocol". How can I do this? If it's not possible what is the recommended approach to solve this kind of problem?

Thanks!

User
  • 31,811
  • 40
  • 131
  • 232
  • Do you want to check whether the `UIView` instance variable conforms to your protocol? – rakeshbs Jan 15 '15 at 11:51
  • @rakeshbs Not quite, I know I can do these kind of checks at runtime with is/as. I want to be able to set a constraint or something such that the compiler enforces that this variable is a UIView that also conforms to the TextView protocol. But I suspect that's not possible. In that case I would like to know a usable design pattern / different approach to solve this. – User Jan 15 '15 at 11:55
  • Duplicate of [How do I declare a variable that has a type and implements a protocol?](http://stackoverflow.com/questions/25214484/how-do-i-declare-a-variable-that-has-a-type-and-implements-a-protocol) ? – Martin R Jan 15 '15 at 11:59
  • Ohhh, damn! Actually, the question in that thread answers my question - not sure if that's a valid duplicate. I knew I was missing something basic: `` – User Jan 15 '15 at 12:21
  • Then I think it is not a duplicate question. It *may* have been answered before, but it is probably difficult to find the exact duplicate. – Martin R Jan 15 '15 at 12:28

1 Answers1

1

I found the answer thanks to Martin R's comment, and it was way more trivial than I thought. In my container class I have to declare the type constraints:

class MyContainer<T where T:UIView, T:TextView> {...}

With a method to be overriden:

func createValueTextView() -> T {fatalError("must override!")}

Then I can use my instance of T both as a UIView and a TextView which is just what I need!

Then a subclass for a container with an editable field:

class MyEditableContainer<A>: MyContainer<UITextField> {...}

Where I override createValueTextView to return a UITextField:

func createValueTextView() -> UITextField {return UITextField()}

Similarly the not editable one:

class MyEditableContainer<A>: MyContainer<UILabel> {...} 
func createValueTextView() -> UILabel {return UILabel()}

(Note that the type parameter 'A' is a "throw away" parameter which is currently required by Swift, see this thread for more information).

Also, it may be good to rename TextView in TextHolder or something like that, as the protocol itself is not view related. Letting it as "TextView" though in this thread as I mentioned it in the comments and they are not editable anymore.

Community
  • 1
  • 1
User
  • 31,811
  • 40
  • 131
  • 232