Importando um protocolo Swift na classe Objective-C

115

Tento importar um protocolo Swift nomeado AnalyticProtocolpara uma classe Objective-C chamada AnalyticFactory.

protocol AnalyticProtocol
{

}

Estou começando a partir de um projeto Objective-C existente (não criei um novo projeto Swift com xCode e não descobri como configurar meu projeto Objective-C para ser um projeto Swift no xCode 6 ).

No meu arquivo Swift, incluí o .harquivo chamado, MyProjectName-Swift.hmas o compilador retornou um erro dizendo que ele não existe . Então, criei um .harquivo chamado MyProjectName-Swift.hque está realmente vazio (não sei o que devo colocar dentro).

Na documentação da Apple eles disseram que eu tenho que incluir meu .harquivo nomeado MyProjectName-Swift.hem meu .marquivo. Mas preciso incluí-lo não no meu .marquivo, mas no meu .h. Isso pode ser problemático?

Quando tento compilar, recebo este erro:: 0: erro: xxxAnalyticFactory.h: 39: não é possível encontrar a declaração de protocolo para 'AnalyticProtocol'

E o código incriminado:

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

Acho que não entendo bem como posso importar um protocolo Swift para uma classe Objective-C.

Alguém vê um erro no que estou fazendo?

Jean Lebrument
fonte
Confira o vídeo Integrando Swift com Objective-C da WWDC 2014. Por volta das 30:40 do vídeo, eles descrevem como acessar os protocolos Swift nas classes Objective-C.
Jamie Forrest

Respostas:

224

Você precisa adicionar o @objcatributo ao seu protocolo Swift assim:

@objc protocol AnalyticProtocol {

}
Jamie Forrest
fonte
25
Obrigado pela sua resposta, mas o problema ainda persiste.
Jean Lebrument
Você poderia postar um projeto de amostra que reproduza o problema?
Jamie Forrest
Adicionar @objc me ajudou a importar classes Swift para Obj-C
serg
19
@objc nem sempre funciona. Quando em conformidade com um protocolo, por vezes, não funciona quando adicionando o protocolo sobre a @interfaceno .harquivo. entretanto, você pode adicionar o protocolo ao privado @interfaceno .marquivo e ele conserta as coisas (pelo menos para mim ocasionalmente). Assim acima do @implementationter @interface MyController() <AnalyticProtocol>.
Adam,
1
Às vezes, o Xcode 8 reclamará enquanto você estiver editando, mas quando você realmente o construir, seguindo esta resposta junto com os comentários, o erro irá embora.
Roger Pingleton,
76

Não é possível importar o cabeçalho Swift gerado pelo Xcode em arquivos de cabeçalho objC.

Portanto, como deseja usar o código Swift em um arquivo de cabeçalho objC, será necessário "declarar antecipadamente" as classes e protocolos que deseja usar no arquivo de cabeçalho objC, como este:

@protocol AnalyticProtocol;

Agora você pode usar o protocolo em sua declaração de classe objC:

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

Em seu arquivo de implementação (o arquivo objC .m), você pode importar o arquivo de cabeçalho Swift gerado pelo Xcode ("ProductModuleName-Swift.h") e a implementação correta AnalyticProtocolagora será conhecida pelo compilador.

Isso também é descrito na seção "Usando Swift de Objective-C" no Apple Docs

Observe que o XCode dará um aviso no arquivo de cabeçalho objC quando você usar o protocolo declarado de encaminhamento ("Não é possível encontrar a definição de protocolo para 'AnalyticProtocol'), mas isso pode ser ignorado - a implementação será encontrada no momento da compilação.

Pelle Stenild Coltau
fonte
19
Por que o Xcode mostra o aviso de protocolo ausente quando ele compila e funciona bem? Alguma maneira de remover o aviso?
Oren
Ainda não é possível chamar os métodos de protocolo nesta classe. Ele apresentará o erroNo visible @interface for <ClassName> declares the selector <protocolMethodName>
Elsa
ele produz o seguinte aviso: "Não é possível encontrar a definição de protocolo para xxxx"
JAHelia
51

Para quem simplesmente precisa adotar um protocolo - você pode fazer isso em duas etapas, sem gerar nenhum aviso ou erro:

  1. Em seu .swiftarquivo, adicione @objcantes do nome do protocolo:

    @objc protocol AnalyticProtocol {
    
    }
  2. Em seu .marquivo, importe o cabeçalho Swift gerado e adote o protocolo em uma categoria privada. (O arquivo de cabeçalho é nomeado automagicamente):

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

Esta é a abordagem recomendada pela Apple. Você pode aprender mais sobre como combinar Objective-C e Swift aqui: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

Chris Nolet
fonte
1
Ainda recebo um aviso, com declaração direta ou sem. Estou usando o protocolo em uma extensão.
Cristi Băluță
Não pode importar #import "AnalyticProtocol-Swift.h". Parece que WhateverProtocol-Swift.h não é gerado automaticamente como você disse.
Hlung de
2
Isso funcionou para mim; nenhuma outra sugestão ou resposta nesta página funcionou. Estou no Swift 4.
Poulsbo
1
Esta era a única maneira de remover o aviso (embora funcione quando os protocolos estão no arquivo de cabeçalho)
David P
1
O mesmo link está disponível via WebArchive: web.archive.org/web/20170310053717/https://developer.apple.com/…
Richard Topchii
3

Se você estiver criando uma estrutura, a importação necessária

#import "ProductModuleName-Swift.h"

muda para:

#import <ProductModuleName/ProductModuleName-Swift.h>

Nesse caso, você também deve tornar público o protocolo swift :

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

Podemos usar protocolos rápidos em Objective C com poucas alterações no código. Além disso, os protocolos declarados @objc permitem que você tenha métodos opcionais e obrigatórios sem implementações padrão. Ele vem com prós e contras.

Na verdade, poderíamos renomear o nome do protocolo para um nome mais descritivo ao usar no Objetivo C. Eu uso "@objc (alias_name)".

Aqui está o código, vamos ter um protocolo swift com atributo @objc e nome alternativo para usar no código ObjC.

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

Agora vamos farword declarar nosso protocolo no arquivo .h

@protocol ObjTableViewReloadable;

Agora você pode estar em conformidade com este protocolo no arquivo .m e adicionar a implementação dos métodos necessários.

#import "MyApp-Swift.h"
@interface MyObjcViewController () <ObjTableViewReloadable>
Vinay Hosamane
fonte
Obrigado por postar isso. Estou olhando para um código legado que é mais antigo do que a sujeira e, se essa abordagem funcionar, vou me poupar muito trabalho. :)
Adrian