2

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
   {
   someDelegate.colorPicked(blah)
   }

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 SnapProtocol. 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) ?? responder.next()
        }
    }

courtesy these guys and safely find any SnapProtocol above you,

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

That's great.

But. What if the object that wants to get the colorPicked 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?

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 2
    I haven't used it yet so I can't speak for its quality, but this is a protocol-oriented, type safe notification center: https://github.com/100mango/SwiftNotificationCenter. – Martin R Jun 06 '16 at 13:58
  • that's a great tip, MartinR. Indeed that's a tidy version of what I mention above. fascinating. – Fattie Jun 06 '16 at 14:13
  • Say, in a sense that's exactly the answer to my literal question, @MartinR. **("register “anywhere” to be a delegate of a protocol")** Perhaps you should pop it in as an answer - it could be useful to folks googling for just that. – Fattie Jun 07 '16 at 13:09

2 Answers2

5

"Send out messages to anyone who wants them" is pretty much the description of NSNotificationCenter.

True, it's not an API that's designed from the beginning for Swift patterns like closures, strong typing, and protocol-oriented programming. (As noted in other comments/answers, the open source SwiftNotificationCenter is a good alternative if you really want such features.)

However, NSNotificationCenter is robust and battle-hardened — it's the basis for thousands of messages that get sent around between hundreds of objects on each pass through the run loop.


Here's a very concise how-to for using NSNotificationCenter in Swift:

https://stackoverflow.com/a/24756761/294884

Community
  • 1
  • 1
rickster
  • 124,678
  • 26
  • 272
  • 326
  • I suppose you're right! :/ Protocols are so much better than notification center though. Perhaps one could use the notification center to set delegates of protocols :O – Fattie Jun 06 '16 at 14:12
2

There is no "protocol based" notification mechanism in the Swift standard libraries or runtime. A nice implementation can be found here https://github.com/100mango/SwiftNotificationCenter. 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) {
    $0.colorPicked(x)
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks again for the specific answer ("There is no "protocol based" notification mechanism in the Swift standard libraries or runtime") and the tremendously explanatory/useful point, good one – Fattie Jun 08 '16 at 14:39
  • [This question](http://stackoverflow.com/questions/37707450/function-that-takes-a-protocol-and-a-conforming-class-instance-as-parameters) was motivated by the code in SwiftNotificationCenter. – Martin R Jun 08 '16 at 20:25