Joe Blow Joe Blow - 1 year ago 68
Swift Question

In Swift, register "anywhere" to be a delegate of a protocol

I have a complicated view class,

class Snap:UIViewController, UIScrollViewDelegate

and the end result is the user can, let's say, pick a color...

protocol SnapProtocol:class
func colorPicked(i:Int)
class Snap:UIViewController, UIScrollViewDelegate

So who's going to handle it.

Let's say that you know for sure there is something up the responder chain, even walking through container views, which is a
. If so you can use this lovely code to call it

var r : UIResponder = self
repeat { r = r.nextResponder()! } while !(r is SnapProtocol)
(r as! SnapProtocol).colorPicked(x)

If you prefer, you can use this superlative extension

public extension UIResponder // walk up responder chain
public func next<T>() -> T?
guard let responder = self.nextResponder()
else { return nil }
return (responder as? T) ??

courtesy these guys and safely find any
above you,

(next() as SnapProtocol?)?.colorPicked(x)

That's great.

But. What if the object that wants to get the
is a knight-move away from you, down some complicated side chain, and/or you don't even know which object wants it.

My current solution is this, I have a singleton "game manager" -like class,

public class .. a singleton

// anyone who wants a SnapProtocol:
var useSnap:SnapProtocol! = nil


Some freaky class, anywhere, wants to eat SnapProtocol ...

class Dinosaur:NSObject, SnapProtocol
func colorPicked(index: Int)

... so, to set that as the desired delegate, use the singleton

thatSingleton.useSnap = dinosaur

Obviously enough, this works fine.

Note too that I could easily write a little system in the singleton, so that any number of users of the protocol could dynamically register/deregister there and get the calls.

But it has obvious problems, it's not very "pattern" and seem violently non-idiomatic.

So. Am I really, doing this the right way in the Swift milieu?

Have I indeed confused myself, and there is some entirely different pattern I should be using in today's iOS, to send out such "messages to anyone who wants them?" ... maybe I shouldn't even be using a protocol?

Answer Source

There is no "protocol based" notification mechanism in the Swift standard libraries or runtime. A nice implementation can be found here From the README:

A Protocol-Oriented NotificationCenter which is type safe, thread safe and with memory safety.

  • Type Safe

    No more userInfo dictionary and Downcasting, just deliver the concrete type value to the observer.

  • Thread Safe

    You can register, notify, unregister in any thread without crash and data corruption.

  • Memory Safety

    SwiftNotificationCenter store the observer as a zeroing-weak reference. No crash and no need to unregister manually.

It's simple, safe, lightweight and easy to use for one-to-many communication.

Using SwiftNotificationCenter, a (instance of a) conforming class could register itself for example like this:

class MyObserver: SnapProtocol {
    func colorPicked(i: Int) {
        print("color picked:", i)
    init() {
        NotificationCenter.register(SnapProtocol.self, observer: self)

and a broadcast notification to all conforming registered observers is done as

NotificationCenter.notify(SnapProtocol.self) {