10

Since iOS 13, showsRouteButton from MPVolumeView has been deprecated

let vv = MPVolumeView()
vv.showsRouteButton = false

Warning is :

'showsRouteButton' was deprecated in iOS 13.0: Use AVRoutePickerView instead.

Apple is telling me to use AVRoutePickerView for routing, which makes no sense as in my case I do not want to use any routing stuff, I only want to hide it. It seems there's no more not deprecated way to do this.

If it's deprecated it should be hidden by default else apple should allow us to hide it...

Am I right to say it's an apple API error ?

batsansierra
  • 315
  • 1
  • 12

3 Answers3

2

For now just to remove the warning and the default route button I used this immediately after initializing the MPVolumeView.

    if volumeView.value(forKey: #keyPath(MPVolumeView.showsRouteButton)) as? Bool == true {
        volumeView.setValue(false, forKey: #keyPath(MPVolumeView.showsRouteButton))
    }

I check via key value paths if the value of showsRouteButton is true and sets it to false if it is.

Dharman
  • 30,962
  • 25
  • 85
  • 135
spasbil
  • 239
  • 1
  • 12
  • 2
    Even though it's a custom workaround by now, what if that keypath disappears due to a system software update? a NSUnknownKeyException would kill the app. – kikeenrique Aug 06 '21 at 13:54
  • 1
    @kikeenrique if they fully remove the property the `value(forKey:)` will return nil and nothing will happen. It may not look perfect but the app will not crash. – spasbil Aug 09 '21 at 05:54
  • `#keyPath(MPVolumeView.showsRouteButton)` shows the same deprecation warning. The default value is still `true` in iOS 15. – pommy Oct 08 '21 at 17:50
  • @pommy yes, Apple introduced deeper checks with the latest XCode. To supress the warning in XCode 13+ you will need to replace `#keyPath(MPVolumeView.showsRouteButton)` with `"showsRouteButton"`, so not going through the keypath and just hardcoding it. – spasbil Oct 12 '21 at 09:00
  • @spasbil Replacing the #keypad(...) by the string "showsRouteButton" does work on iOS 15 and avoids the warning on Xcode 13. However, when I replace the string by a string not representing a valid property, the if statement crashes with `[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key`. So once Apple eventually removes the deprecated setter, your modified solution will crash the app. That's exactly what we want to avoid. – pommy Oct 13 '21 at 14:23
  • @pommy You can add `volumeView.responds(to: "showsRouteButton")` before the value check but this will produce a warning. – spasbil Oct 14 '21 at 07:23
1

Safe and forward compatible

In iOS 15.0 the route button of MPVolumeView shows the route button as a subview. The safest way to get rid of it without a deprecation warning is to look for the button and hide it:

volume.subviews.first(where: { $0 is UIButton })?.isHidden = true

Unlike just ignoring the warning or setting the value with key-value coding (as suggested by @spasbil), this will not crash the app in a future iOS release where Apple may have removed the deprecated showsRouteButton. However, it has the disadvantage of leaving an empty space right of the slider.


Setting the value through key-value coding does lead to a visually more satisfying solution, where the slider extends over the full width of the MPVolumeView. Using a #keypath expression as suggested by @spasbil shows the same warning in Xcode 13.0. Using a String literal rather than a #keypath expression, as suggested in a comment, avoids this warning. It does not resolve the danger of crashing on future iOS releases where showsRouteButton will have disappeared.

Apple never tells us when they will remove a deprecated property, but they rarely do it in a minor release. So we can hope that showsRouteButton will stay around at least for iOS 15.* releases. A forward compatible way to get rid of the route button is therefore:

if #available(iOS 16, *) {
    volumeView.subviews.first(where: { $0 is UIButton })?.isHidden = true
} else {
    volumeView.setValue(false, forKey: "showsRouteButton")
}

We might need to get back to this during the next major release beta season, presumably next summer. In the meantime, use this at your own risk. In the unlikely case that Apple removes the property in a later release of iOS 15.*, your app will crash. In the more likely case that showsRouteButton stays available but deprecated in iOS 16, your MPVolumeView will leave some unwanted space until you bump up the availability check.

pommy
  • 3,717
  • 1
  • 15
  • 25
0

This works for me, not sure if it robust:

import MediaPlayer

extension MPVolumeView {
    
    /// Compiler warning 'showsRouteButton' was deprecated in iOS 13.0: Use AVRoutePickerView instead.
    /// But the route button will show by default if we don't set it false.
    /// We can set by key value to silent the warning. But also need prevent Apple remove the key in the future.
    func hideRouterButtonIfNecessary() {
        let bugKey = "showsRouteButton"
        var count: UInt32 = 0
        guard let properties = class_copyPropertyList(MPVolumeView.self, &count) else { return }
        for i in 0..<Int(count) {
            let property = properties[i]
            let name = property_getName(property)
            let str = String(cString: name)
            if str == bugKey {
                self.setValue(false, forKey: bugKey)
                break
            }
        }
        free(properties)
    }
}
grg
  • 5,023
  • 3
  • 34
  • 50
snakehnb
  • 63
  • 7