Importando un protocolo Swift en clase Objective-C

116

Intento importar un protocolo Swift nombrado AnalyticProtocolen una clase Objective-C llamada AnalyticFactory.

protocol AnalyticProtocol
{

}

Estoy comenzando desde un proyecto Objective-C existente (no creé un nuevo proyecto Swift con xCode y no encontré cómo configurar mi proyecto Objective-C para que sea un proyecto Swift en xCode 6 ).

En mi archivo Swift incluí el .harchivo nombrado MyProjectName-Swift.hpero el compilador me devolvió un error diciéndome que no existe . Entonces, creé un .harchivo llamado MyProjectName-Swift.hque en realidad está vacío (no sé qué debo poner dentro).

En la documentación de Apple dijeron que tengo que incluir mi .harchivo nombrado MyProjectName-Swift.hen mi .marchivo. Pero necesito incluirlo no en mi .marchivo sino en mi .h. ¿Esto puede resultar problemático?

Cuando intento compilar, obtengo este error:: 0: error: xxxAnalyticFactory.h: 39: no puedo encontrar la declaración de protocolo para 'AnalyticProtocol'

Y el código incriminado:

@interface AnalyticFactory : NSObject
{
    Class<AnalyticProtocol> _analyticProtocolClass; // The type of the analytic class currently used.
}

Creo que no entiendo bien cómo puedo importar un protocolo Swift a una clase Objective-C.

¿Alguien ve un error en lo que estoy haciendo?

Jean Lebrument
fuente
Vea el video Integrating Swift with Objective-C de WWDC 2014. Alrededor de las 30:40 en el video, describen cómo acceder a los protocolos Swift en las clases Objective-C.
Jamie Forrest

Respuestas:

224

Debe agregar el @objcatributo a su protocolo Swift de esta manera:

@objc protocol AnalyticProtocol {

}
Jamie Forrest
fuente
25
Gracias por tu respuesta, pero el problema persiste.
Jean Lebrument
¿Podría publicar un proyecto de muestra que reproduzca el problema?
Jamie Forrest
Agregar @objc me ayudó a importar clases de Swift a Obj-C
serg
19
@objc no siempre funciona. Cuando se ajusta a un protocolo, a veces no funciona cuando se agrega el protocolo @interfaceen el .harchivo. sin embargo, puede agregar el protocolo al privado @interfaceen el .marchivo y arregla las cosas (al menos lo ha hecho para mí en ocasiones). Así que por encima de tu @implementationtienes @interface MyController() <AnalyticProtocol>.
Adam
1
A veces, Xcode 8 se quejará mientras edita, pero cuando realmente lo construya, siguiendo esta respuesta junto con los comentarios, el error desaparecerá.
Roger Pingleton
77

No es posible importar el encabezado Swift generado por Xcode en archivos de encabezado objC.

Entonces, dado que desea usar el código Swift en un archivo de encabezado objC, deberá "declarar hacia adelante" las clases y protocolos que desea usar en el archivo de encabezado objC, así:

@protocol AnalyticProtocol;

Ahora puede usar el protocolo en su declaración de clase objC:

@interface AnalyticFactory : NSObject
{
    Class<AnalyticProtocol> _analyticProtocolClass; // The type of the analytic class currently used.
}

En su archivo de implementación (el archivo objC .m), puede importar el archivo de encabezado Swift generado por Xcode ("ProductModuleName-Swift.h") y AnalyticProtocolahora el compilador conocerá la implementación correcta .

Esto también se describe en la sección "Uso de Swift desde Objective-C" en Apple Docs

Tenga en cuenta que XCode dará una advertencia en el archivo de encabezado objC cuando use el protocolo declarado hacia adelante ("No se puede encontrar la definición de protocolo para 'AnalyticProtocol'), pero esto se puede ignorar: la implementación se encontrará en tiempo de compilación.

Pelle Stenild Coltau
fuente
19
¿Por qué Xcode muestra la advertencia por falta de protocolo cuando se compila y funciona bien? ¿Alguna forma de eliminar la advertencia?
Oren
Sin embargo, todavía no es posible llamar a los métodos de protocolo en esta clase. Dará el errorNo visible @interface for <ClassName> declares the selector <protocolMethodName>
Elsa
produce la siguiente advertencia: "No se puede encontrar la definición de protocolo para xxxx"
JAHelia
51

Para cualquiera que simplemente necesite adoptar un protocolo, puede hacerlo en dos pasos, sin generar advertencias ni errores:

  1. En su .swiftarchivo, agregue @objcantes del nombre del protocolo:

    @objc protocol AnalyticProtocol {
    
    }
  2. En su .marchivo, importe el encabezado Swift generado y adopte el protocolo en una categoría privada. (El archivo de encabezado se nombra automáticamente):

    #import "ProductModuleName-Swift.h"
    
    @interface AnalyticFactory () <AnalyticProtocol>
    
    @end

Este es el enfoque recomendado por Apple. Puede obtener más información sobre cómo mezclar y combinar Objective-C y Swift aquí: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

Chris Nolet
fuente
1
Todavía recibo una advertencia, con declaración anticipada o sin ella. Estoy usando el protocolo en una extensión.
Cristi Băluță
No se puede importar #import "AnalyticProtocol-Swift.h". Parece que WhateverProtocol-Swift.h no se genera automáticamente como dijiste.
Hlung
2
Esto funcionó para mí; ninguna otra sugerencia o respuesta en esta página funcionó. Estoy en Swift 4.
Poulsbo
1
Esta fue la única forma de eliminar la advertencia (aunque funciona cuando los protocolos están en el archivo de encabezado)
David P
1
El mismo enlace está disponible a través de WebArchive: web.archive.org/web/20170310053717/https://developer.apple.com/…
Richard Topchii
3

Si está creando un marco, la importación requerida

#import "ProductModuleName-Swift.h"

cambios a:

#import <ProductModuleName/ProductModuleName-Swift.h>

En este caso también debes hacer público el protocolo rápido :

@objc public protocol AnalyticProtocol {
  func something();
}
Michal
fuente
3

Podemos usar protocolos rápidos en Objective C con pocos cambios en el código. Además, los protocolos declarados @objc le permiten tener métodos opcionales y obligatorios sin implementaciones predeterminadas. Viene con pros y contras.

De hecho, podríamos cambiar el nombre del protocolo a uno más descriptivo cuando lo usemos en Objective C. Hago uso de "@objc (alias_name)".

Aquí está el código. Tengamos un protocolo rápido con el atributo @objc y el nombre de alias para usar en el código de ObjC.

@objc(ObjTableViewReloadable) protocol TableViewReloadable: class {
   func reloadRow(at index: IndexPath)
   func reloadSection(at index: Int)
   func reloadTable()
}

Ahora dejemos que farword declare nuestro protocolo en un archivo .h

@protocol ObjTableViewReloadable;

Ahora puede ajustarse a este protocolo en el archivo .m y agregar la implementación de los métodos necesarios.

#import "MyApp-Swift.h"
@interface MyObjcViewController () <ObjTableViewReloadable>
Vinay Hosamane
fuente
Gracias por publicar esto. Estoy viendo un código heredado que es más antiguo que la suciedad y si este enfoque funciona, me ahorrará un montón de dolor. :)
Adrian