1

If delegates were designed for one-to-one relationships between objects and NSNotifications were designed for one-to-potentially-many relationships, is there a best practice for one-to-few?

I've seen a lot of custom multicasting delegates in iOS where an object can cast to multiple subscribers (i.e. Swift Language Multicast Delegate), but the implementations are often very involved and seem overkill. One such problem is safely storing an array of weak references (the delegates) (How do I declare an array of weak references in Swift?).

I've seen a lot of recommendations (like this one multiple listeners for delegate iOS) that suggest this is what NotificationCenter was made for. But the idea of broadcasting out into the ether for a one-to-few relationship itself seems overkill.

Is there a best practice for Apple's frameworks and the Swift language? I never see them write about this. Is NotificationCenter a suitable use for a one-to-few relationship where a multicasting delegate would otherwise be needed?

lurning too koad
  • 2,698
  • 1
  • 17
  • 47

1 Answers1

4

I would not use NotificationCenter because the type of the message and the data between the sender and receiver (observer) becomes lost. Using Notification Center will make your code to rely on Notification object where you need to use the userInfo dictionary of the notification to add the data which makes it harder to understand what exactly keeps the notification (will need to see how exactly the data is populated when the notification is send).

The delegate is a better solution and having more than 1 delegate in a weak list of delegates is ok. I have used such composition in many places where I need to register more then 1 listener to a particular event and works just fine.

You can create the delegates collection once and reuse it very easily across the code. Here is my solution:

class WeakContainer {

    private weak var value: AnyObject?

    public init(value: AnyObject) {
        self.value = value
    }

    func get() -> AnyObject? {
        return self.value
    }
}

class DelegatesCollection<T>: Sequence {

    private lazy var weakDelegates = [WeakContainer]()

    var delegates: [T] {
        return self.weakDelegates.map() { $0.get() as! T }
    }

    var hasDelegates: Bool {
        return !self.weakDelegates.isEmpty
    }

    init() { }

    func add(delegate: T) {
        var exists = false
        for currentDelegate in self.weakDelegates {
            if(currentDelegate.get() === (delegate as AnyObject)) {
                exists = true
                break
            }
        }

        if(!exists) {
            self.weakDelegates.append(WeakContainer(value: delegate as AnyObject))
        }
    }

    func remove(delegate: T) {
        var i = 0
        for currentDelegate in self.weakDelegates {
            if(currentDelegate.get() == nil || currentDelegate.get() === (delegate as AnyObject)) {
                self.weakDelegates.remove(at: i)
                break
            }

            i += 1
        }
    }

    func makeIterator() -> IndexingIterator<[T]> {
         return self.delegates.makeIterator()
    }
}

I can speculate that Apple frameworks use only single delegate because it is a business logic what actions to perform when delegate is called. From Apple's point of view it is enough to delegate that some event has happened and to leave the application to decide what to do next so there is no point to support multiple delegates on framework level.

plamkata__
  • 729
  • 5
  • 13