WeakArray for the Last Time (Swift 4.2)
I am obsessed with weak arrays. If you're trying to use multiple "protocolized" delegates, you need a weakly-held array of delegates.
This one started with this great article and expanded from there. Big thanks to Varindra Hart for the wise counsel as always.
@objc public protocol Equalable: class {
@objc func isEqual(_ object: Any?) -> Bool
}
/// Store AnyObject subclasses weakly
/// * Note: if you wish to use a protocol, it must:
/// - be marked with `@objc`
/// - have all methods marked with `@objc`
/// - refine Equalable
public struct WeakArray<Element: Equalable> {
private var items: [WeakBox<Element>] = []
public init(_ elements: [Element]? = nil) {
guard let elements = elements else { return }
items = elements.map { WeakBox($0) }
}
public mutating func append(_ newElement: Element) {
let box = WeakBox(newElement)
items.append(box)
}
public mutating func remove(_ element: Element) {
items.removeAll { item in
return item.unbox?.isEqual(element) ?? false
}
}
public var unboxed: [Element] {
let filtered = items.filter { $0.unbox != nil }
return filtered.compactMap { $0.unbox }
}
public var boxedCount: Int {
return items.count
}
}
extension WeakArray: Collection {
public var startIndex: Int { return items.startIndex }
public var endIndex: Int { return items.endIndex }
public subscript(_ index: Int) -> Element? {
return items[index].unbox
}
public func index(after idx: Int) -> Int {
return items.index(after: idx)
}
}
private final class WeakBox<T: Equalable> {
weak var unbox: T?
init(_ value: T) {
unbox = value
}
}
The need to have your protocol conform to @objc is a bug in Swift. See the StackOverflow question about this.