Last active
March 26, 2021 17:15
-
-
Save lahariganti/14d18956e07a86d69fc55c899753b5ed to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Swift doesn’t yet provide a way to restrict <ProtocolType> to protocols only | |
// One could pass a concrete class type instead of a protocol for ProtocolType | |
// However, we'll use only a protocol; hence, the generic type is named as ProtocolType instead of just Type | |
public class MulticastDelegate<ProtocolType> { | |
// MARK: - DelegateWrapper | |
/// Used to wrap delegate objects as a weak property | |
/// so that the multicast delegate can hold onto strong wrapper instances, instead of the delegates directly that is | |
private class DelegateWrapper { | |
weak var delegate: AnyObject? | |
init(_ delegate: AnyObject) { | |
self.delegate = delegate | |
} | |
} | |
// MARK: - Instance Properties | |
/// DelegateWrapper instances which will be created under the hood by MulticastDelegate from delegates passed to it | |
private var delegateWrappers: [DelegateWrapper] | |
/// Filters out delegates from delegateWrappers that have already been released | |
/// and then returns an array of definitely non-nil delegates | |
public var delegates: [ProtocolType] { | |
delegateWrappers = delegateWrappers.filter { $0.delegate != nil } | |
// swiftlint:disable force_cast | |
return self.delegateWrappers.map { $0.delegate! } as! [ProtocolType] | |
// swiftlint:enable force_cast | |
} | |
// MARK: - Object Lifecycle | |
/// Initializer that accepts an array of delegates and maps them to create delegateWrappers | |
public init(delegates: [ProtocolType] = []) { | |
self.delegateWrappers = delegates.map { | |
DelegateWrapper($0 as AnyObject) | |
} | |
} | |
// MARK: - Delegate Management | |
/// Used to add a delegate instance, which creates a DelegateWrapper and appends it to the delegateWrappers | |
public func addDelegate(_ delegate: ProtocolType) { | |
let wrapper = DelegateWrapper(delegate as AnyObject) | |
if self.delegateWrappers.contains(where: { $0.delegate === (delegate as AnyObject) }) { | |
return | |
} else { | |
self.delegateWrappers.append(wrapper) | |
} | |
} | |
/// If a matching delegate (pointer equality) is found, remove the delegate wrapper at the index | |
public func removeDelegate(_ delegate: ProtocolType) { | |
guard let index = self.delegateWrappers.firstIndex(where: { | |
$0.delegate === (delegate as AnyObject) | |
}) else { | |
return | |
} | |
self.delegateWrappers.remove(at: index) | |
} | |
/// Iterate through delegates, the computed property you defined before that automatically filters out nil instances, | |
/// and call the passed-in closure on each delegate instance. | |
public func invokeDelegates(_ closure: (ProtocolType) -> ()) { | |
self.delegates.forEach { closure($0) } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment