Comprobar si una function está disponible en Swift?

Me gustaría detectar si el usuario ha habilitado Reducir transparencia. Es simple que acaba de llamar a la function UIAccessibilityIsReduceMotionEnabled() y devuelve un Bool . Pero mi aplicación se dirige a iOS 7 y 8 y esta function no está disponible en iOS 7.

En Objective-C, así es como lo comprobé para ver si esa function existe:

 if (UIAccessibilityIsReduceMotionEnabled != NULL) { } 

En Swift, no puedo entender cómo verificar si existe o no. De acuerdo con esta respuesta , simplemente puede usar el encadenamiento opcional y, si es nil entonces no existe, pero está restringido a los protocolos Obj-C aparentemente. A Xcode 6.1 no le gusta esto:

 let networkinguceMotionDetectionIsAvailable = UIAccessibilityIsReduceMotionEnabled?() 

¿Quiere que elimines el? Y, por supuesto, si lo hace, se bloqueará en iOS 7 porque esa function no existe.

¿Cuál es la forma correcta de verificar si existen estos types de funciones?

En Swift 2 se agregó una verificación adecuada de la disponibilidad. Esto se recomienda con respecto a otras opciones mencionadas aquí.

 var shouldApplyMotionEffects = true if #available(iOS 8.0, *) { shouldApplyMotionEffects = !UIAccessibilityIsReduceMotionEnabled() } 

Si está bien con ser un poco descarado, siempre puede abrir el binary UIKit usando el cargador de la biblioteca y ver si puede resolver el símbolo:

 let uikitbundle = NSBundle(forClass: UIView.self) let uikit = dlopen(uikitbundle.executablePath!, RTLD_LAZY) let handle = dlsym(uikit, "UIAccessibilityIsReduceMotionEnabled") if handle == nil { println("Not available!") } else { println("Available!") } 

Las llamadas dlopen y dlsym pueden ser algo costosas, por lo que recomendaría mantener abierto el identificador de dlopen durante la vida útil de la aplicación y almacenar en algún lugar el resultado de intentar dlsym . Si no lo haces, asegúrate de dlclose .

Por lo que sé, esto es seguro para AppStore, ya que UIAccessibilityIsReduceMotionEnabled es una API pública.

Puede comprobar si se está ejecutando en iOS 8 o superior:

 var networkinguceMotionEnabled = false if NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion: 8, minorVersion: 0, patchVersion: 0)) { networkinguceMotionEnabled = UIAccessibilityIsReduceMotionEnabled() } 

No creo que haya otra forma de contar. Así que, en teoría, si pudieras comprobar, intentar acceder al nombre de la function sin el () te daría nil en iOS 7 y la function () -> Bool en iOS 8. Sin embargo, para que esto ocurra, UIAccessibilityIsReduceMotionEnabled tendría que definirse como (() -> Bool)? , que no es. Probándolo produce una instancia de function en ambas versiones de iOS que falla si se llama en iOS 7:

 let networkinguceMotionDetectionIsAvailable = UIAccessibilityIsReduceMotionEnabled // networkinguceMotionDetectionIsAvailable is now a () -> Bool networkinguceMotionDetectionIsAvailable() // crashes in iOS7, fine in iOS8 

La única manera que puedo ver para hacerlo sin probar la versión es simplemente definir su propia function C para verificar su file de encabezado de puenteo, y llamar a eso:

 // ObjC static inline BOOL networkinguceMotionDetectionIsAvailable() { return (UIAccessibilityIsReduceMotionEnabled != NULL); } // Swift var networkinguceMotionEnabled = false if networkinguceMotionDetectionIsAvailable() { networkinguceMotionEnabled = UIAccessibilityIsReduceMotionEnabled() } 

De los documentos de Apple Developer ( Uso de Swift con Cocoa y Objective-C (Swift 3)> Interoperabilidad> Adopción de patrones de layout de cocoa> Disponibilidad de API ):

El código Swift puede usar la disponibilidad de API como condición en time de ejecución. Las verificaciones de disponibilidad se pueden usar en lugar de una condición en una instrucción de flujo de control, como una instrucción if , guard o while .

Tomando el ejemplo anterior, puede verificar la disponibilidad en una instrucción if para llamar a requestWhenInUseAuthorization() solo si el método está disponible en time de ejecución:

 let locationManager = CLLocationManager() if #available(iOS 8.0, macOS 10.10, *) { locationManager.requestWhenInUseAuthorization() } 

Alternativamente, puede verificar la disponibilidad en una statement de guard , que sale fuera del scope a less que el objective actual satisfaga los requisitos especificados. Este enfoque simplifica la lógica de manejar diferentes capacidades de la plataforma.

 let locationManager = CLLocationManager() guard #available(iOS 8.0, macOS 10.10, *) else { return } locationManager.requestWhenInUseAuthorization() 

Cada argumento de la plataforma consiste en uno de los nombres de plataforms que se enumeran a continuación, seguido del número de versión correspondiente. El último argumento es un asterisco ( * ), que se usa para manejar futuras plataforms potenciales.

Nombres de la plataforma:

  • iOS
  • iOSApplicationExtension
  • macOS
  • macOSApplicationExtension
  • watchOS
  • watchOSApplicationExtension
  • tvOS
  • tvOSApplicationExtension