Reputation: 450
This question (Create swift array extension for typed arrays [duplicate]) gets very close to answering the question but what is being done within this question can be generic.
This question (How can I extend typed Arrays in Swift?) also gets close but isn't the same question due to the example not restricting the extension to a particular type of array.
Say I have an array of UIView
.
[node, node1, node2]
And I want to add an extension
func
to it like:
[node, node1, node2].hideAll()
extension UIView {
func hide() { self.hidden = true }
}
extension Array {
func hideAll() {
for node in self { (node as! UIView).hide() }
}
}
I don't want hideAll()
to be accessed on anything but an array containing objects of type UIView
. Can I and how do I restrict this?
Upvotes: 4
Views: 455
Reputation: 2758
You can try something like:
protocol ShyObject : AnyObject {
var hidden: Bool { get set }
}
extension SequenceType where Generator.Element : ShyObject {
func hideAll() { for e in self { e.hidden = true } }
func showAll() { for e in self { e.hidden = false } }
}
import UIKit
extension UIView : ShyObject {}
// TODO: extend more shy types...
let nodes = Array(count: 5, repeatedValue: UIView())
nodes.hideAll()
If you specifically need hide()
method as well, you can provide it generically for all ShyObject
s:
extension ShyObject {
func hide() { hidden = true }
func show() { hidden = false }
}
And to implement conformance for a type that does not have hidden
:
class MyClass {}
extension MyClass : ShyObject {
var hidden: Bool {
get { return /* true or */ false }
set { /* hide me or show me */ }
}
}
let myObjects = Array(count: 5, repeatedValue: MyClass())
myObjects.hideAll()
We can also have allHidden
and allShown
properties too:
extension SequenceType where Generator.Element : ShyObject {
var allShown: Bool { return !contains{ $0.hidden == true } }
var allHidden: Bool { return !contains{ $0.hidden == false } }
}
extension MutableCollectionType where Generator.Element : ShyObject {
var allShown: Bool {
get { return !contains{ $0.hidden == true } }
set { newValue ? showAll() : hideAll() }
}
var allHidden: Bool {
get { return !contains{ $0.hidden == false } }
set { newValue ? hideAll() : showAll() }
}
}
var nodes = Array(count: 5, repeatedValue: UIView()) // note the `var` :(
nodes.allShown = false
nodes.allHidden // true (does not in itself require a mutable collection!)
... which works, but it has a major drawback that the sequence has to be mutable if we want to use a setter. This is because all computed properties with a setter are assumed by compiler to be mutating
even when the sequence elements are of a reference type like here... Still, we can at least have getter only allShown
and allHidden
properties without paying for it with mutability.
Note that, as @dfri commented at one point, this answer is more general than the question explicitly calls for. If this is not desired, @dfri suggests:
protocol Hideable {
func hide()
}
extension UIView : Hideable {
func hide() { self.hidden = true }
}
extension Array where Element: Hideable {
func hideAll() { for e in self { e.hide() } }
}
Upvotes: 1