1

I am trying to learn swift and want to use protocol oriented programming approach. What i want to achieve is simple but i could not find any way.

lets say i have Outlet which is textfield. I want that textfield conforms protocol like ValidatesName protocol. Is there any way to do it? I do not want to make new class which subclass UITextField and conforms protocol. I want to use for this specific property.

@IBOutlet weak var nameTextField:UITextField!<Conforms ValidatesName>
@IBOutlet weak var emailTextField:UITextField!<Conforms ValidatesEmail>
@IBOutlet weak var passwordTextField:UITextField!<Conforms ValidatesPassword>

Thanks

2
  • But the problem is that UITextField class don't conform your desired protocol, the protocol adoption is based on implementation of methods and properties defined in the protocol Commented Nov 24, 2016 at 21:15
  • If i understood correctly, I can already define some method like this. I thought i can do it for variable. <T:UITextField where T:protocol< ValidatesName>> Commented Nov 24, 2016 at 21:39

2 Answers 2

3

Your issue is that while you can add protocol conformance via an extension, the extension is applied to the class, not an instance of that class. This means that you can say something like:

extension UITextField: ValidatesName {...}

But this will make all instances of UITextField conform to ValidatesName

Similarly, you could also say

extension UITextField: ValidatesEmail{...}

But now all instances of UITextField will conform to ValidatesName and ValidatesEmail.

Having separate Validates... protocols doesn't seem like the right approach anyway. The basic signature of your protocol is something like var isValid: Bool; this doesn't change between name and email. What does change is the validation logic and this has to live somewhere. This, coupled with the fact that you need subclasses in order to work with Interface Builder would suggest that a single protocol Validatable that can be adopted by your various subclasses is a more reasonable approach.

protocol Validatable  {
    var isValid: Bool { get }
}

Now, you can define subclasses of UITextField that conform to this protocol (You can add the conformance via an extension to your subclass if you like, I just wanted to save space here)

class NameTextField: UITextField, Validatable {

    var isValid: Bool {
        get {
            guard let text = self.text else {
                return false
            }

            return !text.isEmpty
        }
    }
}

class EmailTextField: UITextField, Validatable {
    var isValid: Bool {
        get {
            guard let text = self.text else {
                return false
            }

            return text.contains("@")
        }
    }
}

Now, you can add your textfields to an array, and have something like:

@IBOutlet weak var nameTextField:NameTextField!
@IBOutlet weak var emailTextField:EmailTextField!

var validatableFields:[Validatable]!

override func viewDidLoad() {
    super.viewDidLoad()

    self.validatableFields = [nameTextField,emailTextField]
}

...

for field in validateableFields {
    if !field.isValid() {
        print("A field isn't valid")
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

How can i write IBOutletCollection array type as only protocols, i tried but i get error. I am getting error if try to implement it as [ProtocolName].
You're right, sorry. I updated my answer. You will need to build the array yourself. This is one of those places where the legacy of Objective-C limits what you can do in Swift.
This is good answer for using them as array for preventing lots of guards inside viewcontroller. Only thing that i will change is that nameTextField and emailTextField are views. They are not presenter(MVP), they are not view model(MVVM) so i will make little change from it but thanks for the validatableFields idea.
1

Unfortunately there are a few limitations preventing this:

  1. IBOutlets must refer to classes that inherit from NSObject, probably to enable archiving / decoding, so you can't use just a protocol for an IBOutlet's type

  2. There is no way in Swift to declare a variable's type to be a combination of concrete type + protocol conformance, the way you do in your example

  3. In Objective-C you can declare concrete type + protocol conformance, but protocols are ignored by IBOutlets anyway, possibly because protocol conformance is checked at runtime, and it isn't necessarily known by Xcode / Interface Builder at design time whether an object will eventually conform to a protocol.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.