Visión general:
- Tengo un protocolo P1 que proporciona una implementación predeterminada de una de las funciones opcionales de Objective-C.
- Cuando proporciono una implementación predeterminada de la función opcional, hay una advertencia
Advertencia del compilador:
Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'
Versión:
- Rápido: 3
- Xcode: 8 (lanzamiento público)
Intentos realizados:
- Intenté agregar
@objc
pero no ayudó
Pregunta:
- ¿Cómo resolví esto?
- ¿Hay alguna solución?
Código:
@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
}
swift
swift3
swift-protocols
usuario1046037
fuente
fuente
@objc
Respuestas:
Si bien creo que puedo responder a su pregunta, no es una respuesta que le guste.
TL; DR: las
@objc
funciones pueden no estar actualmente en extensiones de protocolo. En su lugar, podría crear una clase base, aunque esa no es una solución ideal.Extensiones de protocolo y Objective-C
Primero, esta pregunta / respuesta ( Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c ) parece sugerir que debido a la forma en que se envían las extensiones de protocolo bajo el capó, los métodos declarados en las extensiones de protocolo no son visibles para la
objc_msgSend()
función, y por lo tanto, no son visibles para el código Objective-C. Dado que el método que está tratando de definir en su extensión debe ser visible para Objective-C (así queUIKit
puede usarlo), le grita por no incluirlo@objc
, pero una vez que lo incluye, le grita porque@objc
no está permitido en extensiones de protocolo. Esto probablemente se deba a que las extensiones de protocolo actualmente no pueden ser visibles para Objective-C.También podemos ver que el mensaje de error una vez que agregamos
@objc
dice "@objc solo se puede usar con miembros de clases, protocolos @objc y extensiones concretas de clases". Esta no es una clase; una extensión de un protocolo @objc no es lo mismo que estar en la propia definición del protocolo (es decir, en los requisitos), y la palabra "concreto" sugeriría que una extensión de protocolo no cuenta como una extensión de clase concreta.Solución alterna
Desafortunadamente, esto prácticamente le impide usar extensiones de protocolo cuando las implementaciones predeterminadas deben ser visibles para los marcos Objective-C. Al principio, pensé que tal vez
@objc
no estaba permitido en su extensión de protocolo porque Swift Compiler no podía garantizar que los tipos conformes fueran clases (aunque lo haya especificado específicamenteUIViewController
). Así que puse unclass
requisitoP1
. Esto no funcionó.Quizás la única solución es simplemente usar una clase base en lugar de un protocolo aquí, pero obviamente esto no es completamente ideal porque una clase puede tener una sola clase base pero cumplir con múltiples protocolos.
Si elige seguir esta ruta, tenga en cuenta esta pregunta ( Método de protocolo opcional de Swift 3 ObjC no llamado en subclase ). Parece que otro problema actual en Swift 3 es que las subclases no heredan automáticamente las implementaciones de requisitos de protocolo opcionales de su superclase. La respuesta a esas preguntas utiliza una adaptación especial de
@objc
para sortearla.Informar el problema
Creo que esto ya se está discutiendo entre los que trabajan en los proyectos de código abierto de Swift, pero puede estar seguro de que lo saben utilizando el Informe de errores de Apple , que probablemente llegará al Swift Core Team, o el informador de errores de Swift . Sin embargo, cualquiera de estos puede encontrar su error demasiado amplio o ya conocido. El equipo de Swift también puede considerar lo que está buscando como una nueva función de idioma, en cuyo caso primero debe consultar las listas de correo .
Actualizar
En diciembre de 2016, se informó de este problema a la comunidad de Swift. El problema aún está marcado como abierto con una prioridad media, pero se agregó el siguiente comentario:
Sin embargo, dado que su protocolo está en el mismo módulo que su extensión, es posible que pueda hacer esto en una versión futura de Swift.
Actualización 2
En febrero de 2017, uno de los miembros del Swift Core Team cerró oficialmente este problema como "No funciona" con el siguiente mensaje:
Extender
NSObject
o inclusoUIViewController
no logrará exactamente lo que desea, pero desafortunadamente no parece que sea posible.En el futuro (muy) a largo plazo, es posible que podamos eliminar por
@objc
completo la dependencia de los métodos, pero es probable que ese momento no llegue pronto, ya que los marcos de Cocoa no están escritos actualmente en Swift (y no pueden estarlo hasta que tenga un ABI estable) .Actualización 3
A partir del otoño de 2019, esto se está convirtiendo en un problema menor porque cada vez se escriben más marcos de Apple en Swift. Por ejemplo, si usa en
SwiftUI
lugar deUIKit
, elude el problema por completo porque@objc
nunca sería necesario al referirse a unSwiftUI
método.Los marcos de Apple escritos en Swift incluyen:
Uno esperaría que este patrón continúe con el tiempo ahora que Swift es oficialmente ABI y el módulo es estable a partir de Swift 5.0 y 5.1, respectivamente.
fuente
Swift 4
no hay otra alternativa a partir de ahora.Me encontré con esto después de habilitar la 'estabilidad del módulo' (activando 'Construir bibliotecas para distribución') en un marco rápido que uso.
Lo que tenía era algo como esto:
La función en la extensión tenía estos errores:
El método de instancia '@objc' en extensión de la subclase de 'LessAwesomeClass' requiere iOS 13.0.0
El método que no es '@ objc' 'niceDelegateFunc' no satisface el requisito del protocolo '@objc' 'GreatDelegate'
Mover las funciones a la clase en lugar de a una extensión resolvió el problema.
fuente
Aquí hay otra solución. También me encontré con este problema y todavía no puedo cambiar de UIKit a SwiftUI. Mover las implementaciones predeterminadas a una clase base común tampoco fue una opción para mí. Mis implementaciones predeterminadas eran bastante extensas, por lo que realmente no quería tener todo ese código duplicado. La solución alternativa que terminé usando fue usar funciones contenedoras en el protocolo y luego simplemente llamar a esas funciones desde cada clase. No es bonito, pero puede ser mejor que la alternativa, según la situación. Su código entonces se vería así:
fuente