4

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?

Community
  • 1
  • 1
RyanCosans
  • 442
  • 1
  • 4
  • 13
  • 1
    You cannot define an Array extension method which applies only to arrays of a certain type. That has also been asked before, e.g. here: http://stackoverflow.com/questions/24938948/array-extension-to-remove-object-by-value (which might qualify as a duplicate). – Martin R Apr 13 '15 at 12:59
  • @RyanCosans Use any of the above solutions, validate the content before execution and throw an exception if anything is wrong (or return a special result code if you are going to support Swift as it doesn't work with exceptions so well yet). I believe it has to qualify for "_not_ to be accessed on anything but an array containing objects of type UIView" due to the lack of other approaches, don't forget to add some documentation for the method though. – A-Live Apr 13 '15 at 15:43

1 Answers1

1

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 ShyObjects:

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() } }
}
Milos
  • 2,728
  • 22
  • 24