Sinch Video calling sound is coming from front speaker - ios

I have implemented sinch video calling in ios swift project i have followed all process given in sinch implementation document https://www.sinch.com/docs/video/ios/#calling. And i am successfully able to implement but i am getting on issue my video sound in coming from front speaker. how can i solve this problem?? Below my code:
var client: SINClient?
var sinCall : SINCall?
Configuring sinch
//MARK: Configuring Sinch Delegate
func configuringSinch(){
//Configuring Client Key
client = Sinch.client(withApplicationKey: Constants.SINCH_APP_KEY, applicationSecret: Constants.SINCH_PRIVATE_KEY, environmentHost: Constants.SANDBOX_ENVIRONMENT, userId: Utility().getUserId())
client?.call().delegate = self
client?.setSupportCalling(true)
client?.enableManagedPushNotifications()
client?.start()
client?.startListeningOnActiveConnection()
let vcCont = client?.videoController()
self.vwLocalView.addSubview((vcCont?.localView())!)
self.sinCall?.delegate = self
}
//MARK: Sinch Video Call Delegate
func clientDidStart(_ client: SINClient!) {
print("Client Did Start")
}
func clientDidFail(_ client: SINClient!, error: Error!) {
print("Client failed : \(error)")
player?.stop()
}
func clientDidStop(_ client: SINClient!) {
print("Client Did Stop")
player?.stop()
}
//MARK: Video Call Did Recieve
func client(_ client: SINCallClient!, didReceiveIncomingCall call: SINCall!) {
print("Did Recieve Incoming Call")
playRingtoneSound() // Playing Audio
call.delegate = self;
self.sinCall = call
}
//MARK: Call Did Add Video Track
func callDidAddVideoTrack(_ call: SINCall!) {
let videoCont = client?.videoController()
vwRemoteView.addSubview((videoCont?.remoteView())!)
}
func callDidEnd(_ call: SINCall!) {
sinCall?.hangup()
}

This is how you can manage SINAudioController to manage audio output.
func audioController() -> SINAudioController {
return (client?.audioController())!
}
//MARK: Video Call Did Recieve
func client(_ client: SINCallClient!, didReceiveIncomingCall call: SINCall!) {
audioController().enableSpeaker()
playRingtoneSound() // Playing Audio
call.delegate = self;
self.sinCall = call
}
// In SINCallDelegate
func callDidEstablish(_ call: SINCall!) {
//to disableSpeaker
audioController().disableSpeaker()
}
try this to manage AudioOutput Session manually
// MARK: AudioOutput Session
// to enable front speaker manually
func setSessionPlayerOn()
{
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.none)
} catch _ {
}
}
// to enable speaker manually
func setSessionPlayerSpeaker()
{
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
} catch _ {
}
}
// to turnoff AudioOutput Session manually
func setSessionPlayerOff()
{
do {
try AVAudioSession.sharedInstance().setActive(false)
} catch _ {
}
}

Use this function
func callDidEstablish(_ call: SINCall!) {
let audio = APPDELEGATE.client?.audioController()
audio?.disableSpeaker()
// self.startCallDurationTimerWithSelector()
appDelegate.client?.audioController().stopPlayingSoundFile()
}
this is work for me.

class MainVC: UIViewController,SINCallDelegate {
// var client: SINClient?
private let videoController = SinchManager.sharedInstance.client!.videoController()
private let audioController = SinchManager.sharedInstance.client!.audioController()
private let callClient: SINCallClient
private var call: SINCall!
let username: String
#IBOutlet weak var otherView: UIView!
// private var mainView: SinchView { return view as! SinchView }
#IBAction func call_btn(_ sender: UIButton) {
answer()
}
#IBAction func end_btn(_ sender: UIButton) {
decline()
}
override func loadView() {
// view = SinchView()
view = otherView
}
init(username: String) {
self.username = username
self.callClient = SinchManager.sharedInstance.client!.call()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
print("init(coder:) has not been implemented " + String(describing: aDecoder))
fatalError("init(coder:) has not been implemented " + String(describing: aDecoder))
}
override func viewDidLoad() {
super.viewDidLoad()
call.delegate = self
//self.mainView.videoView.addSubview(self.videoController.localView())
otherView.addSubview((self.videoController?.localView())!)
self.videoController?.localView().contentMode = .scaleToFill
if self.call.direction == SINCallDirection.incoming {
self.audioController?.startPlayingSoundFile(self.pathForSound(string: "incoming.wav") as String, loop: true)
}
if self.call.details.isVideoOffered {
print("video offered")
//self.mainView.videoView.addSubview(self.videoController.localView())
otherView.addSubview((self.videoController?.localView())!)
self.videoController?.localView().contentMode = .scaleToFill
}
otherView.addSubview((self.videoController?.localView())!)
// mainView.answerButton.addTarget(self, action: #selector(answer), forControlEvents: .TouchUpInside)
// mainView.declineButton.addTarget(self, action: #selector(decline), forControlEvents: .TouchUpInside)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.audioController?.enableSpeaker()
}
func pathForSound(string: String) -> NSString {
let nsSt = Bundle.main.resourcePath! as NSString
return nsSt.appendingPathComponent(string) as NSString
}
func answer() {
call.answer()
}
func decline() {
call.hangup()
}
func callDidEstablish(call: SINCall!) {
print("callDidEstablish")
let audio = SinchManager.sharedInstance.client!.audioController()
audio?.disableSpeaker()
// self.startCallDurationTimerWithSelector()
SinchManager.sharedInstance.client!.audioController().stopPlayingSoundFile()
}
func callDidEnd(call: SINCall!) {
print("callDidEnd")
}
func callDidProgress(call: SINCall!) {
print("callDidProgress")
self.audioController?.startPlayingSoundFile(self.pathForSound(string: "ringback.wav") as String, loop: true)
}
func callDidAddVideoTrack(call: SINCall!) {
print("callDidAddVideoTrack")
otherView.addSubview((self.videoController?.localView())!)
}

Related

Delegated functions not working outside of UIViewController

I'm using the Startscream Websocket framework. Everything works fine as long as I keep all of the code in a UIViewController as seen here. But as soon as a create a wrapper class for Startscream all of the delegated functions stop working. Also my local websocket server is not getting a connection.
How can I get the code working inside a wrapper class?
MyService.swift:
import Starscream
public class MyService: WebSocketDelegate {
var socket = WebSocket(url: URL(string: "ws://localhost:3900/websocket")!)
func connect() {
socket.delegate = self
socket.connect()
print("Connecting")
}
// MARK: Websocket Delegate Methods.
public func websocketDidConnect(socket: WebSocket) {
print("websocket is connected")
}
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
if let e = error {
print("websocket is disconnected: \(e.localizedDescription)")
} else {
print("websocket disconnected")
}
}
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
print("Received text: \(text)")
}
public func websocketDidReceiveData(socket: WebSocket, data: Data) {
print("Received data: \(data.count)")
}
// MARK: Write Text Action
#IBAction func writeText(_ sender: UIBarButtonItem) {
socket.write(string: "hello there!")
}
// MARK: Disconnect Action
#IBAction func disconnect(_ sender: UIBarButtonItem) {
if socket.isConnected {
sender.title = "Connect"
socket.disconnect()
} else {
sender.title = "Disconnect"
socket.connect()
}
}
}
ViewController.swift:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let service = MyService()
service.connect()
}
}
The reference to Service in ViewController was not stored anywhere so as soon as the function was run it was cleaned up. This is how I fixed it:
class ViewController: UIViewController {
var service = MyService()
override func viewDidLoad() {
super.viewDidLoad()
service.connect()
}
...
I'm not sure what is wrong with your code, it can be an endpoint issue, or WS is not allocated somehow. I have same code with Starscream working in Swift 3, here is the main part of my class.
class ConnectionManager {
private var savedSocket: WebSocket?
fileprivate var socket: WebSocket {
if let saved = savedSocket {
return saved
}
let wsURL = URL(string: UserDefaultsManager.wsURLString)!
savedSocket = WebSocket(url: wsURL)
return savedSocket!
}
func startSession() {
if (socket.isConnected) { return }
socket.headers = headers
socket.delegate = self
socket.connect()
}
func endSession() {
if (socket.isConnected) {
socket.disconnect()
}
}
}
extension ConnectionManager: WebSocketDelegate {
func websocketDidConnect(socket: WebSocket) {
}
func websocketDidDisconnect(socket: WebSocket, error: NSError?){
if let e = error {
log.error("websocket is disconnected with ERROR: \(e.localizedDescription)")
} else {
log.error("websocket disconnected")
}
}
func websocketDidReceiveMessage(socket: WebSocket, text: String){
}
func websocketDidReceiveData(socket: WebSocket, data: Data){
}
}
My code is not a best practise for sure, but it works without any problem. Try to use my version, maybe it will work for you.

Cannot receive event with custom DelegateProxy and Protocol

I try to migrate delegate of DifficultyViewDelegate to observable. This is my DifficultyViewDelegate :
#objc protocol DifficultyViewDelegate: class {
func levelDidIncrease()
func levelDidDecrease()
}
And my DifficultyView :
weak var delegate: DifficultyViewDelegate?
#IBAction func decreaseLevel(_ sender: Any) {
delegate?.levelDidDecrease()
}
#IBAction func increaseLevel(_ sender: Any) {
delegate?.levelDidIncrease()
}
And this is my RxDifficultyViewDelegateProxy
class RxDifficultyViewDelegateProxy: DelegateProxy, DelegateProxyType {
static func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
let difficultyView: DifficultyView = object as! DifficultyView
return difficultyView.delegate
}
static func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let difficultyView: DifficultyView = object as! DifficultyView
difficultyView.delegate = delegate as? DifficultyViewDelegate
}
}
I also added an extension on my DifficultyView :
extension DifficultyView {
public var rx_delegate: RxDifficultyViewDelegateProxy {
return RxDifficultyViewDelegateProxy.proxyForObject(RxDifficultyViewDelegateProxy.self)
}
public var rx_levelDidIncrease: Observable<Void> {
return rx_delegate.methodInvoked(#selector(DifficultyViewDelegate.levelDidIncrease)).map { _ in return }
}
}
But it seems that when I do :
difficultyView.rx_levelDidIncrease.asObservable().subscribe(onNext: {
print("did increase")
}).addDisposableTo(disposeBag)
It's never called. Someone has any pointers ?
Try use PublishSubject:
DifficultyView:
class DifficultyView: UIView {
var levelDidIncrease = PublishSubject<Void>()
var levelDidDecrease = PublishSubject<Void>()
#IBAction func decreaseLevel(_ sender: Any) {
levelDidDecrease.onNext()
}
#IBAction func increaseLevel(_ sender: Any) {
levelDidIncrease.onNext()
}
}
And then:
var difficultyView = DifficultyView()
difficultyView.levelDidDecrease.asObservable()
.subscribe(onNext: {
print("did decrease")
})
.addDisposableTo(disposeBag)
difficultyView.decreaseLevel(theSender) // <- THIS line performs the side effect

Sinch video doen't want to work on iOS (Swift)

So basically I want to enable Sinch Video in iOS application.
For testing purposes I've created SinchManaevger which is singleton and I instatiate it in AppDelegate:
class SinchManager: NSObject, SINClientDelegate, SINCallClientDelegate {
static let sharedInstance = SinchManager()
var client: SINClient?
func initSinchClientWithUserId(id: String) {
if client == nil {
if case .Authenticated(let currentUser, _) = SessionManager.sharedInstance.state.value {
self.client = Sinch.clientWithApplicationKey("xyz", applicationSecret: "xyz", environmentHost: "sandbox.sinch.com", userId: currentUser.username)
print("sinchClient")
print(client!)
self.client!.delegate = self
self.client!.setSupportCalling(true)
self.client!.enableManagedPushNotifications()
self.client!.start()
self.client!.startListeningOnActiveConnection()
}
}
}
func clientDidStart(client: SINClient!) {
print("clientDidStart")
self.client!.callClient().delegate = self
}
func clientDidStop(client: SINClient!) {
print("clientDidStop")
}
func clientDidFail(client: SINClient!, error: NSError!) {
print("clientDidFail")
}
func client(client: SINCallClient!, didReceiveIncomingCall call: SINCall!) {
print("didReceiveIncomingCall")
let sinchVC = SinchVC(username: currentUser.username)
let sinchNC = DNMMainNC(rootViewController: sinchVC)
sinchVC.call = call
}
}
And I've created Sinch ViewController which is initialized with username which will be called:
class SinchVC: UIViewController, SINCallDelegate {
private let videoController = SinchManager.sharedInstance.client!.videoController()
private let audioController = SinchManager.sharedInstance.client!.audioController()
private let callClient: SINCallClient
private var call: SINCall!
let username: String
private var mainView: SinchView { return view as! SinchView }
override func loadView() {
view = SinchView()
}
init(username: String) {
self.username = username
self.callClient = SinchManager.sharedInstance.client!.callClient()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
call.delegate = self
self.mainView.videoView.addSubview(self.videoController.localView())
self.videoController.localView().contentMode = .ScaleToFill
if self.call.direction == SINCallDirection.Incoming {
self.audioController.startPlayingSoundFile(self.pathForSound("incoming.wav") as String, loop: true)
}
if self.call.details.videoOffered {
print("video offered")
self.mainView.videoView.addSubview(self.videoController.localView())
self.videoController.localView().contentMode = .ScaleToFill
}
mainView.videoView.addSubview(self.videoController.localView())
mainView.answerButton.addTarget(self, action: #selector(answer), forControlEvents: .TouchUpInside)
mainView.declineButton.addTarget(self, action: #selector(decline), forControlEvents: .TouchUpInside)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.audioController.enableSpeaker()
}
func pathForSound(string: String) -> NSString {
let nsSt = NSBundle.mainBundle().resourcePath! as NSString
return nsSt.stringByAppendingPathComponent(string)
}
func answer() {
call.answer()
}
func decline() {
call.hangup()
}
func callDidEstablish(call: SINCall!) {
print("callDidEstablish")
}
func callDidEnd(call: SINCall!) {
print("callDidEnd")
}
func callDidProgress(call: SINCall!) {
print("callDidProgress")
self.audioController.startPlayingSoundFile(self.pathForSound("ringback.wav") as String, loop: true)
}
func callDidAddVideoTrack(call: SINCall!) {
print("callDidAddVideoTrack")
mainView.videoView.addSubview(self.videoController.remoteView())
}
}
Problem is when I try to call from my app to other phone with my app nothing happens (didReceiveIncomingCall delegate method doesn't get called at all)
If I try to call from my app to SinchVideo sample app then video call gets initiated normal. But when i call from SinchVideo app to my app nothing happens in my app. So probably i've forgot to add some notification or something to tell my app when the call is incoming. If you could help I would be very grateful. Thanks
EDIT: I managed to make didReceiveIncomingCall work but now call.answer isnt working. (nothing happens when call.answer is called and i see that my phone is ringing)
I am not sure what DNMMainNC does in your did recieve incoming call,
let sinchNC = DNMMainNC(rootViewController: sinchVC) What does DNMMainNC do?
sinchVC.call = call // private var?
But its looks kind of weird to set a private var call from your code, should that not be public or have a constructor like your init but with a call

Let AVPlayer continue playing when pressing the 'back' button on the UINavigation Controller in Swift 2

I’m developing a Radio Streaming app, it streams OK. Also it plays in background when press on the ‘Home’ button and ‘lock’ button.
The application is embedded into a UINavigationController and when I press on the ‘back’ button in the UINavigationController it stops playing.
My question is: How do I let the UIViewController that contain the AVPlayer to remain active when pressing the ‘back’ button in the navigation controller so that the AVPlayer continue streaming?
My code
import UIKit
import AVFoundation
import MediaPlayer
import Foundation
class RadioFunctionViewController: UIViewController {
#IBOutlet var playButton: UIButton!
#IBOutlet var statusLabel: UILabel!
var player:AVPlayer = AVPlayer()
private let ObservatingKeyPath = "currentItem.status"
private let PlayerStatusObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
private var playingState:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
setStatus(4)
getAudioData("http://184.107.179.162:7546/;")
playingState = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed(sender: AnyObject)
{
toggle()
}
func getAudioData(audioURL:String)
{
player = AVPlayer(URL: NSURL(string: audioURL)!)
player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.Initial, context: nil)
}
func setStatus(rawValue:Int)
{
if rawValue == 1
{
statusLabel.textColor = UIColor.blueColor()
statusLabel.text = "Ready for Streaming"
}else if rawValue == 2
{
statusLabel.textColor = UIColor.redColor()
statusLabel.text = "Failed"
}else if rawValue == 0
{
statusLabel.textColor = UIColor.redColor()
statusLabel.text = "Failed to load data"
}else if rawValue == 3
{
statusLabel.textColor = UIColor.blueColor()
statusLabel.text = "Streaming"
}else if rawValue == 4
{
statusLabel.textColor = UIColor.purpleColor()
statusLabel.text = "Gather data..."
}
print("The raw value send is: \(rawValue)")
}
func audioBackgroundPlayback()
{
do{
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}catch {
print("Could not play audio in the background")
}
if (NSClassFromString("MPNowPlayingInfoCenter") != nil)
{
let artWorkImage = MPMediaItemArtwork(image: UIImage(named: "ws")!)
let songInfo2: [String: AnyObject] = [MPMediaItemPropertyTitle: "Wide Streamings ABC Edition", MPMediaItemPropertyArtist: "Rumbera Network", MPMediaItemPropertyAlbumTitle: "107.9 FM", MPMediaItemPropertyArtwork: artWorkImage]
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = songInfo2
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
}
}
func toggle()
{
if playButton.titleLabel?.text == "Play"
{
print("The play option is chosen")
playRadio()
}else{
print("The pause option is chosen")
pauseRadio()
}
}
func playRadio()
{
player.play()
setStatus(3)
playButton.setTitle("Pause", forState: UIControlState.Normal)
audioBackgroundPlayback()
}
func pauseRadio()
{
player.pause()
playButton.setTitle("Play", forState: UIControlState.Normal)
}
override func remoteControlReceivedWithEvent(event: UIEvent?) {
if event?.type == UIEventType.RemoteControl
{
if event?.subtype == UIEventSubtype.RemoteControlPlay
{
toggle()
}else if event?.subtype == UIEventSubtype.RemoteControlPause
{
pauseRadio()
}else if event?.subtype == UIEventSubtype.RemoteControlTogglePlayPause
{
toggle()
}
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>)
{
if (keyPath!.containsString("status"))
{
if player.status == AVPlayerStatus.ReadyToPlay
{
player.prerollAtRate(0.001, completionHandler: {(succes:Bool)-> Void in
if succes{
self.setStatus(1)
self.setStatus(3)
self.playRadio()
}else{
self.setStatus(1)
self.setStatus(2)
}
})
}else if player.status == AVPlayerStatus.Failed{
self.setStatus(2)
}else if player.status == AVPlayerStatus.Unknown
{
self.setStatus(0)
}
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBarHidden = false
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBarHidden = false
if playingState == true
{
audioBackgroundPlayback()
player.removeObserver(self, forKeyPath: "status")
print("The AVPlayer is playing in background")
}else{
player.removeObserver(self, forKeyPath: "status")
print("The view Dissapear")
}
}
Hope someone could help me out with this one
Thanks in advance
AVPlayer will continue to work as long as it is active. Once the referencing UIViewController is released by popping back AVPlayer will also be discarded from memory.
I would advise you to create a singleton player class and create APIs to start/stop/play/pause AVPlayer in that. Now, you can access it globally from everywhere in your app.
EDIT: For OP convenience (a sample to start with):
class MyAVPlayer {
static let sharedInstance = MyAVPlayer()
var player:AVPlayer = AVPlayer()
func play() {
// Put play code here
}
func playWithURL(url : NSURL) {
// Put play code here
}
}
Call it like (from anywhere in your application):
MyAVPlayer.sharedInstance.playWithURL(myURL)
The problem here is that the player is Embed in your controller
var player:AVPlayer = AVPlayer()
So when you press the back button, the controller is popped and deallocated and your player with it.
What you have to do is to put the player property elsewhere (AppDelegate, Custom navigation controller for exemple) to keep a reference on it that'll keep it alive.
I manage to fix it by doing some modification to the AppDelegate Class.
The modification into the AppDelegate:
var player:AVPlayer = AVPlayer()
internal var avPlayerUpdateNotification = NSNotificationCenter.defaultCenter()
let notificationStateupdate = "RadioStationChangeUpdate"
let radioStationChangeNSString:NSString = "RadioStationChangeNotification"
private var isPlaying:Bool = false
private var selectedRadioStation:String = ""
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
// Override point for customization after application launch.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setNewSelectedRadioStation:", name: radioStationChangeNSString as String, object: nil)
return true
}
Other functions that has been added into the AppDelegate Class
func streamAudio(audioLink:String)
{
player = AVPlayer(URL: NSURL(string: audioLink)!)
player.play()
isPlaying = true
}
func play()
{
player.play()
isPlaying = true
}
func pause()
{
player.pause()
isPlaying = false
}
func getPlayerState() -> Bool
{
return isPlaying
}
func setCurrentSelectedRadioStation(selectedStation:String)
{
self.selectedRadioStation = selectedStation
}
func getCurrentSelectedRadioStation() -> String
{
return selectedRadioStation
}
func setNewSelectedRadioStation(notification: NSNotification)
{
let radioStation = notification.object!
if (radioStation.containsString("Rumbera network"))
{
if(selectedRadioStation == radioStation as! String)
{
print("Rumbera Network is already playing")
}else{
print("Rumbera Network is selected in AppDelegate")
streamAudio("http://184.107.179.162:7546/;")
setCurrentSelectedRadioStation(radioStation as! String)
}
}else if (radioStation.containsString("RocKorsow"))
{
if(selectedRadioStation == radioStation as! String)
{
print("RocKorsow is already playing")
}else{
print("RocKorsow is selected in AppDelegate")
streamAudio("http://youngfreshfast.serverroom.us:9166")
setCurrentSelectedRadioStation(radioStation as! String)
}
}else if (radioStation.containsString("Pause"))
{
pause()
}else if (radioStation.containsString("Play"))
{
play()
}else{
print("Nothing is found")
}
}
The class that handles the AVPlayer:
import UIKit
import AVFoundation
import MediaPlayer
import Foundation
class RadioFunctionViewController: UIViewController {
#IBOutlet var playButton: UIButton!
var player:AVPlayer = AVPlayer()
private let ObservatingKeyPath = "currentItem.status"
private let PlayerStatusObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
private var playingState:Bool = false
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
checkPlayerCurrentState()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func performAction(sender: UIButton)
{
let buttonLabel = (sender.titleLabel?.text)!
switch buttonLabel
{
case "Play":
print("The AVPlayer is playing")
NSNotificationCenter.defaultCenter().postNotificationName("RadioStationChangeNotification", object: NSString(string: "Play"))
playButton.setTitle("Pause", forState: UIControlState.Normal)
case "Pause":
print("The AVPlayer is pause")
NSNotificationCenter.defaultCenter().postNotificationName("RadioStationChangeNotification", object: NSString(string: "Pause"))
playButton.setTitle("Play", forState: UIControlState.Normal)
default:
break
}
}
func checkPlayerCurrentState()
{
let player_state = getPlayerState()
if player_state == true
{
print("The AVPlayer is playing")
playButton.setTitle("Pause", forState: UIControlState.Normal)
}else{
print("The AVPlayer is not playing")
playButton.setTitle("Play", forState: UIControlState.Normal)
}
}
func getPlayerState() -> Bool
{
let state = appDelegate.getPlayerState()
return state
}
func audioBackgroundPlayback()
{
do{
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}catch {
print("Could not play audio in the background")
}
}
Hope my solution could help someone that encounter the same problem.
Thanks guys for your feedback.

Swift: use of 'self' in method call before super.init initializes self compile error

I made a custom class that handles audio recording/playback and put a Protocol in that class. I implemented the Protocol in a UIViewController class and called my setDelegate method for my AudioHelper class.
I am getting a compile error that has to do with my init(). Not exactly sure how to get rid of the error:
use of 'self' in method call 'setupAudioSession' before super.init initializes self
override init() {
setupAudioSession()
super.init()
}
How do I resolve this error? And why do I have to override init()?
My AudioHelper class
import Foundation
import AVFoundation
class AudioHelper: NSObject, AVAudioRecorderDelegate {
var audioSession: AVAudioSession?
var audioRecorder: AVAudioRecorder?
var delegate: AudioRecorderProtocol?
class var sharedInstance: AudioHelper {
struct Static {
static var instance: AudioHelper?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = AudioHelper()
}
return Static.instance!
}
override init() {
setupAudioSession()
super.init()
}
func setDelegate(delegate: AudioRecorderProtocol) {
self.delegate = delegate
}
func setupAudioSession() {
audioSession = AVAudioSession.sharedInstance()
audioSession?.setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)
audioSession?.setActive(true, error: nil)
}
func createAudioMessageDirectory() {
let fm = NSFileManager.defaultManager()
if !fm.fileExistsAtPath(GlobalVars.kAudioMessageDirectory) {
var error: NSError?
if !fm.createDirectoryAtPath(GlobalVars.kAudioMessageDirectory, withIntermediateDirectories: true, attributes: nil, error: &error) {
println("Unable to create audio message directory: \(error)")
}
}
}
// MARK: Recording
func beginRecordingAudio() {
createAudioMessageDirectory()
var filepath = GlobalVars.kAudioMessageDirectory.stringByAppendingPathComponent("audiofile.aac")
var url = NSURL(fileURLWithPath: filepath)
var recordSettings = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVSampleRateKey: 8000.0,
AVNumberOfChannelsKey: 1,
AVEncoderBitRateKey: 12800,
AVLinearPCMBitDepthKey: 16,
AVEncoderAudioQualityKey: AVAudioQuality.Max.rawValue
]
println("Recorded Audio Message Saved: \(url!)")
var error: NSError?
audioRecorder = AVAudioRecorder(URL: url, settings: recordSettings as [NSObject : AnyObject], error: &error)
if error == nil {
if audioRecorder != nil {
audioRecorder!.delegate = self
audioRecorder!.record()
}
}
else {
println(error!.localizedDescription)
}
}
func stopRecordingAudio() {
if audioRecorder != nil {
audioRecorder!.stop()
}
}
func handleRecordAudioButtonLongPressGestureForState(state: UIGestureRecognizerState) {
if state == UIGestureRecognizerState.Ended {
stopRecordingAudio()
delegate?.onRecordAudioStop()
}
else if state == UIGestureRecognizerState.Began {
beginRecordingAudio()
delegate?.onRecordAudioStop()
}
}
func audioRecorderDidFinishRecording(recorder: AVAudioRecorder!, successfully flag: Bool) {
println("Record Audio Success: \(flag)")
delegate?.onRecordAudioFinished()
}
func audioRecorderEncodeErrorDidOccur(recorder: AVAudioRecorder!, error: NSError!) {
println("Record Audio Encode Error: \(error.localizedDescription)")
}
// MARK: Playback
func playAudioMessageFromUrl(messageId: String) {
if let url = NSURL(string: GlobalVars.kUrlAudioMessage + messageId) {
if let data = NSData(contentsOfURL: url) {
var error: NSError? = nil
let audioPlayer = AVAudioPlayer(data: data, error: &error)
if error == nil {
if audioPlayer != nil {
audioPlayer.numberOfLoops = 0
audioPlayer.volume = 1.0
audioPlayer.prepareToPlay()
audioPlayer.play()
}
}
else {
println("Audio playback error: \(error?.localizedDescription)")
}
}
}
}
}
protocol AudioRecorderProtocol {
func onRecordAudioStart()
func onRecordAudioStop()
func onRecordAudioFinished()
}
My UIViewController that implements the protocol (cut out extraneous code)
class ChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, AudioRecorderProtocol {
let audioHelper = AudioHelper.sharedInstance
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
// addDemoMessages()
setupGestureRecognizer()
setupKeyboardObserver()
setupViews()
setupTableView()
audioHelper.setDelegate(self)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
getUsersFromDb()
getMessagesFromDb()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
setCurrentVC()
tableView.reloadData()
if partnerUserId != nil && !db.doesUserExist(partnerUserId!) {
HttpPostHelper.profileGet(userId: partnerUserId!)
}
requestMessagesFromServer()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
ViewHelper.scrollTableViewToBottom(tableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func handleRecordAudioButtonHold(sender: UILongPressGestureRecognizer) {
audioHelper.handleRecordAudioButtonLongPressGestureForState(sender.state)
}
func onRecordAudioStart() {
dispatch_async(dispatch_get_main_queue(), {
ViewHelper.showToast(NSLocalizedString("RECORDING", comment: ""))
self.recordAudioButton.imageView!.image = UIImage(named: "RecordAudioClicked")
})
}
func onRecordAudioStop() {
dispatch_async(dispatch_get_main_queue(), {
self.recordAudioButton.imageView!.image = UIImage(named: "RecordAudio")
})
}
func onRecordAudioFinished() {
HttpPostHelper.messageAudio(partnerUserId: partnerUserId)
}
func playAudioFromUrl(sender: UIButton) {
let messageId = messages[sender.tag].id
audioHelper.playAudioMessageFromUrl(messageId)
}
}
Just place it under super.init().
The object needs to be initialized first by the super class and then you can do your custom initialization.
override init() {
super.init()
setupAudioSession()
}
If you are working with the MVVM pattern, and you really need to call to some function before the super.init, you can always move that function to the ViewModel and call it from there. Just inject the viewModel as an injected dependency.

Resources