Skip to content

Instantly share code, notes, and snippets.

@lahariganti
Last active March 26, 2021 17:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lahariganti/14d18956e07a86d69fc55c899753b5ed to your computer and use it in GitHub Desktop.
Save lahariganti/14d18956e07a86d69fc55c899753b5ed to your computer and use it in GitHub Desktop.
// 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