Mixin — it is a protocol with default implementation of methods.
Let’s say we have a registration screen with email input text field that needs to be validated to be sure it is correct email address.
We can implement it in this way:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class RegisterViewController: UIViewController {
fileprivate func isValidEmail(_ email: String) -> Bool {
...
}
@IBOutlet var emailTextField: UITextField!
@IBAction func registerButtonDidPress() {
guard isValidEmail(emailTextField.text!) else {
print("Incorrect email provided")
return
}
...
}
}
We could put validation logic directly into view controller, but if we do that with everything then it will be too massive.
One way to improve this is to create extension for String
class which can be reused in future.
1
2
3
4
5
extension String {
var isValidEmail: Bool {
...
}
}
Now validation logic is removed from view controller and can be reused. Another alternative solution to improve our code is to use mixins.
1
2
3
protocol EmailValidatable {
func isValidEmail(_ email: String) -> Bool
}
Now we can create extension on protocol and add default implementation of isValidEmail
method.
1
2
3
4
5
extension EmailValidatable {
func isValidEmail(_ email: String) -> Bool {
...
}
}
Once our view controller confirms to EmailValidatable
protocol, it has access to isValidEmail
method.
1
2
3
4
5
6
7
8
9
10
class RegisterViewController: UIViewController, EmailValidatable {
@IBOutlet var emailTextField: UITextField!
@IBAction func registerButtonDidPress() {
guard isValidEmail(emailTextField.text!) else {
print("Incorrect email provided")
return
}
...
}
}
We just avoided extra helper objects and added new protocol :)
Type-safe API
Let’s create mixin Reusable
:
1
2
3
4
5
6
7
8
9
protocol Reusable: class {
static var reusableIdentifier: String { get }
}
extension Reusable {
static var reusableIdentifier: String {
return String(describing: Self.self)
}
}
Define new cell class which confirms to Reusable
protocol:
1
2
3
4
5
class MyCell: UITableViewCell, Reusable {
func updateWithData(_ data: Objects) {
...
}
}
Now we can extend UITableView
to dequeue cells in easy way:
1
2
3
4
5
extension UITableView {
func dequeueReusableCell<T>(for indexPath: IndexPath) -> T where T: UITableViewCell, T: Reusable {
return self.dequeueReusableCell(withIdentifier: T.reusableIdentifier, for: indexPath) as! T
}
}
Using new function we never reusable identifier as strings, so we can’t make typos.
1
2
let cell: MyCell = tableView.dequeueReusableCell(for: indexPath)
cell.updateWithData(someData)
Links:
https://alisoftware.github.io/swift/protocol/2015/11/08/mixins-over-inheritance https://github.com/AliSoftware/Reusable