1

I've created a simple audio player in swift 5 that implements a AVRoutePicker view. The routepicker is displayed without a problem, and I can change between headphones, phone speaker and phone call speakers.

The problem I have is that once I select my airplay speaker, the audio from my app doesn't play through the selected Airplay speaker and instead plays out of the phone speaker even though the routepicker has a tick next to the AirPlay device.

I can successfully connected to the AirPlay speaker and can play audio through youtube or spotify so I know that this should be possible.

I'm hoping that I've made a simple error in my code - I don't get any errors so it's a bit hard to debug.

My code:

import UIKit
import AVFoundation //audio-video foundation
import AVKit

let rect = CGRect(x: 10, y: 10, width: 100, height: 100)
let myView = UIView(frame: rect)

class ViewController: UIViewController, AVAudioRecorderDelegate, UITableViewDelegate, UITableViewDataSource {
    
    var recordingSession: AVAudioSession!
    var audioRecorder: AVAudioRecorder!
    var audioPlayer: AVAudioPlayer!
    var numberOfRecords = 0

@IBOutlet weak var TestButton1: UIButton!
@IBOutlet weak var MyTableView: UITableView!
@IBOutlet weak var buttonLabel: UIButton!
@IBAction func record(_ sender: Any)

{
    //Check if we have an active recorder
    
    if audioRecorder == nil
    
    {
        numberOfRecords += 1
        let filename = getDirectory().appendingPathComponent("\(numberOfRecords).m4a")
        
        let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 12000, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]
        
        //Start rec
        
        do
        {
            audioRecorder = try AVAudioRecorder(url: filename, settings: settings)
            audioRecorder.delegate = self
            audioRecorder.record()
            
            
            
            buttonLabel.setTitle("Stop Recording", for: .normal)
        }
        catch
        {
            displayAlert(title:"Ups!", message: "Recording failed")
        }
        
    }
    else
    {
        //stopping rec
        audioRecorder.stop()
        audioRecorder = nil
        
        UserDefaults.standard.set(numberOfRecords, forKey: "myNumber")
        MyTableView.reloadData()
        
        buttonLabel.setTitle("Start Recording", for: .normal)
        
    }
    
}




@IBAction func RoutePicker(_ sender: Any) {
    let routePickerView = AVRoutePickerView()

    self.view.addSubview(routePickerView)
    if let routePickerButton = routePickerView.subviews.first(where: { $0 is UIButton }) as? UIButton {
    
    routePickerButton.sendActions(for: .touchUpInside)
    }
}


override func viewDidLoad() {
    super.viewDidLoad()
    
    let routePickerView = AVRoutePickerView()
    
    self.view.addSubview(routePickerView)
  
    if let routePickerButton = routePickerView.subviews.first(where: { $0 is UIButton }) as? UIButton {
    
    routePickerButton.sendActions(for: .touchUpInside)

    }
    
    //Setting up session
    recordingSession = AVAudioSession.sharedInstance()
    
    if let number:Int = UserDefaults.standard.object(forKey: "muNumber") as? Int
    {
        numberOfRecords = number
    }
    
    if (recordingSession.responds(to: #selector(AVAudioSession.requestRecordPermission(_:)))) {
        AVAudioSession.sharedInstance().requestRecordPermission({(granted: Bool)-> Void in
            if granted {
            
                
                print("granted")
                
                do {
                    try self.recordingSession.setCategory(AVAudioSession.Category.playAndRecord)
                    try self.recordingSession.setActive(true)
                }
                catch {
                    
                    print("Couldn't set Audio session category")
                }
            } else{
                print("not granted")
            }
        })
    }

}

 //Function that gets path to directory
 func getDirectory() -> URL
    {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentDirectory = paths[0]
        return documentDirectory
    }

    //Function that displays an alert
    func displayAlert(title:String, message:String)
    {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "dismiss", style: .default, handler: nil))
        present(alert, animated: true, completion: nil)
        
    }
    
    //SETTING UP TABLE VIEW//
    
    func tableView (_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numberOfRecords
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = String(indexPath.row + 1)
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        
        let path = getDirectory().appendingPathComponent("\(indexPath.row + 1).m4a")
        
        do
        {
           // try recordingSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
            
            audioPlayer = try AVAudioPlayer(contentsOf: path)

            audioPlayer.play()
        }
        catch
        {
        }
        
    }

}

I think the line try recordingSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker) is key here as when this is uncommented, we cannot switch between the phone speaker or headphones. I'm wondering if there is an equivalent for Airplay speakers.

Xcode 12.5 Device IOS: 13.1.2 MacOS: Big Sur 11.0.1 Swift: 4

Greg
  • 476
  • 9
  • 23
  • Does anyone know if the settings here are correct: i.e, does airplay support .m4a files? # – Greg Oct 17 '21 at 13:23

0 Answers0