0

I'm attempting to use CADisplayLink to update a UIProgressView. Here's CADisplayLink's initialiation signature

Here's a summary of the relevant variables from MyAudioPlayer class.

protocol AudioPlayerDelegate: class {
    func updateSlider(sender: AudioPlayer)
}

class AudioPlayer: NSObject {
    // relevant vars
    var updater: CADisplayLink?
    weak var delegate: AudioPlayerDelegate?

    func play() {
        // some setup stuff

        updater = CADisplayLink(target: self, selector: #selector(delegate?.updateSlider(sender:)))

        // regular stuff to play an audio file
    }
}

When I initialize updater in the play() method, the compiler says, :

Argument of #selector refers to instance method updateSlider(sender:) that is no exposed to Objective-C.

Easy enough...add objc in front of func updateSlider(sender: AudioPlayer), like so:

@objc func updateSlider(sender: AudioPlayer)

The compiler error goes away for a nanosecond, then the the line I just updated says:

@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes.

There are a few answers covering this topic, but I haven't found one that works.

I tried throwing updateSlider in an extension instead of in the protocol section, but I keep getting this ping pong of the compiler wanting to add @objc in front followed by wanting to remove it.

I also attempted to use an instance of the Timer class instead of the CADisplayLink class to do this, but I encounter the same issue.

Thank you for reading. I welcome your suggestions.

Adrian
  • 16,233
  • 18
  • 112
  • 180
  • The protocol needs to marked as @objc, not the method inside it. Also, the target of your display link should be `delegate` and the selector should be `#selector(AudioPlayerDelegate.updateSlider(sender:))`. You should also probably unwrap your delegate before passing it to the display link, but I don't know what that method actually does if you pass it nil. – dan May 30 '17 at 19:13
  • That was it. I unwrapped the delegate and initialized the CADisplayLink, but the declaring the protocol objc got me over the finish line. I was just declaring the method. Post as an answer to claim your 25. :) – Adrian May 30 '17 at 19:23

2 Answers2

1

Dan got me over the finish line on this issue. Instead of adding @objc in front of just my delegate method, I needed to add @objc in front of the protocol declaration.

// First change
@objc protocol AudioPlayerDelegate: class {
    @objc func updateSlider(sender: AudioPlayer)
}

Further down in the AudioPlayer class...

// Delegate declaration
weak var delegate: AudioPlayerDelegate?

// Second Change
if let delegate = delegate {
    updater = CADisplayLink(target: delegate, selector: #selector(AudioPlayerDelegate.updateSlider(sender:)))
    updater?.preferredFramesPerSecond = 30
    updater?.add(to: RunLoop.current, forMode: .commonModes)
}

Lastly, when done with the updater, invalidate it and nil it out (most likely in whatever method you use to stop your AVAudioPlayer instance.

    updater?.invalidate()
    updater = nil
Adrian
  • 16,233
  • 18
  • 112
  • 180
0

It could be fixed so :)

func play() {
    // some setup stuff

    updater = CADisplayLink(target: self, selector: #selector(updateSlider(sender:)))

    // regular stuff to play an audio file
}

func updateSlider(sender: AudioPlayer) {
    delegate?.updateSlider(sender: sender)
}
Vasilii Muravev
  • 3,063
  • 17
  • 45