0

I am new to iOS /swift programming and I am working on an app developed by someone else, fixing some bugs.

The app is essentially a music player and the music has to be played also in background, giving the possibility to play/pause/skip from the lock screen. The app has several views, one of them, the main one, contains all the code related to the player itself (player.swift), the other ones containing other additional pages/features.

The commands from the lock screen works only when I lock the screen starting from the main view, if I do it starting from another view (e.g. the help view, which is just a page which is displayed over the player when the help link is tapped) they don't work. Reading several articles here I've realized that the reason is that the related code is in player.swift:

override func remoteControlReceivedWithEvent(event: UIEvent) {
    if (event.type == UIEventType.RemoteControl) {
        switch (event.subtype) {
        case UIEventSubtype.RemoteControlPlay:
            self.onPlayPause(self);
        case UIEventSubtype.RemoteControlPause:
            self.onPlayPause(self);
        case UIEventSubtype.RemoteControlTogglePlayPause:
            self.onPlayPause(self);
        case UIEventSubtype.RemoteControlNextTrack:
            onNext(nil)
        default:
            break
        }
    }

}

so I have understood the problem, but even if I've read several related articles (including remoteControlReceivedWithEvent called on iOS 7.0 device but not iOS 8.0, Using lock screen for my app?, Swift. Receive remote control events to work with MPNowPLayingInfoCenter) I can't figure out where do I need to move this code and if I need to move something else or make modifications.

EDIT. I moved the code in AppDelegate.swift (deleting the code in player.swift) , as suggested. It seems it now intercepts commands even if I lock the device from a view different than player.swift. I have two problems, though:

1) It seems it works just once, If I click on "next" from the lock screen I can see from a debug string that the command is intercepted, If I do it a second time nothing happens

2) I need to call the methods (onPlayPause and onNext) in player.swift from AppDelegate.swift, I guess those methods expect to have a player object set and/or they refer to variables declared in player.swift and I don't know how to handle this. For example the onNext method is declared as

 @IBAction func onNext(sender: AnyObject?) {
    oldImage = iAlbumArt.image
.......

and if I call the method as a new instance from AppDelegate

player().onNext(nil)

I get an error because iAlbumArt.image is NIL. iAlbumArt is a variable declared in the Player class as

 @IBOutlet weak var iAlbumArt: UIImageView!

Sorry for the naive questions but I've been looking into iOS development just since a couple of weeks ago.

Community
  • 1
  • 1
Eugenio
  • 3,195
  • 5
  • 33
  • 49

1 Answers1

0

Try adding it to your App Delegate class.

Edit:

To forward remote control events to the view controller, add this code to the app delegate (assuming your player view controller is called PlayerViewController):

let vcs = (self.window!.rootViewController as! UINavigationController).viewControllers
let indexOfPlayer = (vcs as! NSArray).indexOfObjectPassingTest { (vc, idx, stop) in
    return (vc.isKindOfClass(PlayerViewController))
}
let playerVC = vcs[indexOfPlayer];

Edit 2:

  1. Place the override func remoteControlReceivedWithEvent method in your App Delegate Class.
  2. At the top of that method, place the code snippet shown above.
  3. In that method, replace self with playerVC.
  4. In your player view controller, add code to respond to onPlayPause and onNext functions.

Note: The reason why this code:

player().onNext(nil)

was throwing an error was because player() creates a brand-new instance of your player class. You want to use the existing instance so the changes get reflected on the screen.

Jed Fox
  • 2,979
  • 5
  • 28
  • 38
  • Thanks for your answer; I did it, but now I have two issues, explained in the (edited) question. – Eugenio Sep 28 '15 at 08:55
  • @Eugenio do you use a `UINavigationController` or `UITabBarController` Or do you just use modal transitions to display the other view controllers? – Jed Fox Sep 28 '15 at 10:20
  • I think the answer is UINavigationController, here is a snippet from my MenuTableViewController.swift: self.slidingViewController().topViewController = self.storyboard?.instantiateViewControllerWithIdentifier("SettingsNavigation") as! UINavigationController – Eugenio Sep 28 '15 at 10:46
  • where exactly do I have to add the code? I tried both after: `class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow?` and after: `func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {` but in both cases I got several compiler errors, in the first case the first error is "AppDelegate' does not have a member named 'window'", in the second case the first error is "Value of optional type 'UIWindow?' not unwrapped; did you mean to use '!' or '?'?" – Eugenio Sep 29 '15 at 08:12
  • @Eugenio Put it in the `remoteControlReceivedWithEvent` method, under each `case` statement. – Jed Fox Sep 29 '15 at 10:18
  • I tried to add the code under the first `case UIEventSubtype.RemoteControlPlay:` but I got several compiler errors, the first one is: Value of optional type 'UIWindow?' not unwrapped; did you mean to use '!' or '?'? – Eugenio Sep 29 '15 at 14:17
  • I still have several errors on the lines added, inlcuding: `[AnyObject]' does not have a member named 'indexOfObjectPassingTest'` `Expected identifier in class declaration` `Consecutive statements on a line must be separated by ';'` – Eugenio Sep 30 '15 at 07:24
  • hi, the second line still has several errors, including `'[AnyObject]' does not have a member named 'indexOfObjectPassingTest'` and `Expected identifier in class declaration` – Eugenio Oct 01 '15 at 19:47
  • Use of unresolved identifier 'vc' ... Use of unresolved identifier 'PlayerViewController'. I guess vc should be vcs? I changed but I still get the error on PlayerViewController – Eugenio Oct 02 '15 at 07:23
  • `PlayerViewController` should be replaced with the name of the player class in `player.swift`. – Jed Fox Oct 02 '15 at 19:17
  • Hi and thanks; it is still not clear to me how to handle the whole thing. Should I have to keep the `override func remoteControlReceivedWithEvent` in both `player.swift` and `AppDelegate.swift` ? How to call the methods in `Player.swift` from `AppDelegate.swift` (se my question 2 in the original question)? Could you be so kind to provide both the code I need in AppDelegate.swift and the one I need in player.swift? – Eugenio Oct 05 '15 at 17:09