2
\$\begingroup\$

As I'm beginner with mobile/OO programming, I reached a situation that confused me and made me wonder if I was coding correctly...

The main class:

    class ViewController: UIViewController, SoundAnalyserDelegate{

        lazy var fontSize : SoundAnalyser = {
            return SoundAnalyser(delegate: self) 
        }()

        @IBOutlet var frequencyLabel: UILabel!
        @IBOutlet var amplitudeLabel: UILabel!
        @IBOutlet var note: UILabel!

        override func viewDidLoad() {
            super.viewDidLoad()
            soundAnalyser.start()

        }

        func setLabelAmp (_ text:String) {

            frequencyLabel.text = text

        }

        func setLabelFreq (_ text:String) {

            frequencyLabel.text = text

        }

        func setLabelNote (_ text:String) {

            frequencyLabel.text = text

        }        

And the Second class:

    class SoundAnalyser {

        var delegate:SoundAnalyserDelegate?

        let minimum = Double(160)
        let maximum = Double(1400)

        var mic: AKMicrophone
        var tracker: AKFrequencyTracker
        fileprivate var silence: AKBooster
        fileprivate var timer: Timer?
        fileprivate var notesArray: [(String, Double)]?

        init(delegate: SoundAnalyserDelegate){
            mic = AKMicrophone()
            tracker = AKFrequencyTracker(mic, hopSize: minimum, peakCount: maximum)
            silence = AKBooster(tracker, gain:0)
            notesArray = self.getNotesFrequencies()
            self.delegate = delegate

        }


        func tick(){
            let amp = tracker.amplitude
            let freq = tracker.frequency


            if amp > 0.05 && freq > minimum && freq < maximum{
                let readNote = notesArray![analyseCapturedSound()]

                delegate?.setLabelAmp(String(format:"%f", amp)) //Setting the text of a label from the UI

                delegate?.setLabelFreq(String(format:"%f", freq))

    //            delegate?.setLabelNote(String(format:"%f", readNote))

                print("freq:", freq," amp:", amp," NOTE: \(readNote)")
            }
        }  

I just included the most important code to express what I am trying to ask.

Summary: Class ViewController initiates SoundAnalyser. Under SoundAnalyser, there's a function that executes a function from ViewController ("delegate?.setLabel..."). I've made it with a delegate class using protocol, but I don't know why I feel that something is wrong.. I mean, executing a method of its creator... is it right? If not, what's the most 'good citizen' way to implement something with the same result?

\$\endgroup\$
1
\$\begingroup\$

This is mostly correct. The reasoning for using delegates is that the class should inform its delegate, not instruct it. A symptom of this is that SoundAnalyser is formatting the text, which should be the responsibility of the view controller.

Consider the SoundAnalyzer to be filling the role of the model in the MVC pattern. The model is only responsible for providing data, and should not be concerned with reinterpreting the data for display.

You can use a helper class to encapsulate formatting if the functionality is needed in multiple places.

Examples

SoundAnalyserDelegate: The delegate methods are in the form of advisory notifications or events, instead of instructions. The delegate receives data, instead of pre-formatted text. This can be further improved by passing an aggregate object if the frequency and amplitude always change at the same time (which seems to be the case):

struct SoundAnalyserSample {
    let frequency: Double
    let amplitude: Double
}

protocol SoundAnalyserDelegate {
    func soundAnalyserSample(sample: SoundAnalyserSample)
    // ... and so on
}

SoundAnalyser: Notify the delegate passing a sample.

func tick(){
    let amp = tracker.amplitude
    let freq = tracker.frequency

    if amp > 0.05 && freq > minimum && freq < maximum {
        let readNote = notesArray![analyseCapturedSound()]
        let sample = SoundAnalyserSample(
            frequency: freq,
            amplitude: amp
        }
        delegate?.soundAnalyserSample(sample: sample)
    }
}

ViewController: The view controller responds to notifications from the sender (the sound analyser in this case), formats the data, and updates the UI. Note that you also need to ensure that the UI update occurs on the main queue, which is not necessarily guaranteed by the caller.

class ViewController: UIViewController, SoundAnalyserDelegate {

    func soundAnalyserSample(sample: SoundAnalyserSample) {
        DispatchQueue.main.async {
            let frequencyText = String(format:"%f", sample.frequency)
            let amplitudeText = String(format:"%f", sample.amplitude)
            frequencyLabel.text = frequencyText
            amplitudeLabel.text = amplitudeText
        }
    }
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.