Search code examples
iosswiftavplayerwatchconnectivitywcsession

what are the functions to start and stop a segued AVPlayer?


How do I trigger a segued iOS AVPlayer to start and stop playing (using Swift 2.2)?

My Watch Connectivity is sending "start" and "stop" message commands. How do I code the functions here to respond and react with the player?

The Watch connectivity messages are working correctly to play audio with AudioPlayer in a different view controller. I'm now trying to adapt this to control a video.

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    let destination = segue.destinationViewController as!
    AVPlayerViewController
    let url = NSURL(string: self.specimen.filmMovieLink)
    destination.player = AVPlayer(URL: url!)
}

// WATCH CONNECTIVITY MESSAGE TO TRIGGER START AND STOP VIDEO
    func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
        var replyValues = Dictionary<String, AnyObject>()
        switch message["command"] as! String {
        case "start" :
            //play video - correct here
           player.Play()
           replyValues["status"] = "Playing"
        case "stop" :
            //stop video - correct here
            player.stopPlay()
            replyValues["status"] = "Stopped"
       case "volume" :
            let level = message["level"] as! Float
            //player.adjustVolume(level)
            replyValues["status"] = "Vol = \(level)"
        default:
            break
       }
       replyHandler(replyValues)
    }
}

I am getting the error messages "use of unresolved identifier 'player' on the lines player.Play() and player.StopPlay(). I have tried replacing 'player' with 'AVPlayer', 'destination', and 'AVPlayerViewController' but it doesn't fix it.


Solution

  • I could post an example showing how you could "fix" your existing code, but it wouldn't do you any favors. The way you're currently trying to implement this is very convoluted, and it would benefit your app to rethink how data could be passed between different parts of your app.

    A view controller is intended to control its own views. It shouldn't be trying to control something in a different controller which it presented (via the segue).

    • I would not recommend passing an AVPlayer object from the presenting (source) view controller to the presented (destination) view controller. Instead you could simply pass the url in prepareForSegue.

    • This would allow the destination view controller to instantiate its own AVPlayer, and let the player be encapsulated within that view controller. In keeping with encapsulation, no other view controller should need to know about (or try to interact) with that object.

    Regarding "controlling" the player from the watch session, what I would suggest is setting up an app-wide session manager. This moves the session responsibility out from the view controller (where it really doesn't belong), and eliminates the complexity of trying to let one (inactive, source) view controller handle WCSession messages on behalf of another (active, destination) view controller.

    • There are several posts, as well as internet tutorials that discuss how to setup a WCSession manager.

    • Separating the functionality into separate modules also makes it easier to maintain the code (and lets you inject or mock different portions of the code while unit testing).

    If you redesign your app in this manner, you could then use notifications to accomplish what you want. For example, your session manager could post the appropriate notification based on the message that it receives from the watch, and your AVPlayer view controller could observe and react to "start", "stop", and "volume" notifications.

    Are there other options?

    If you simply wanted to get the convoluted approach to work, you could try setting up a delegate (letting the second view controller delegate control of its player to the first view controller), or having the first view controller maintain a reference to the second view controller, so it could directly manipulate its player.

    But keep in mind that you (or someone else) will have to maintain this code down the road. Any time you try to save now by "cutting corners," or using "quick and dirty hacks," will be wasted later. Don't be surprised to find yourself horrified at old code you (or someone else) wrote, or trying to remember/understand why you/they chose to do it that way, and hoping you don't inadvertently break things because of all the convolution.