Implementar protocolo parcialmente em Objective C e parcialmente em Swift

Nov 06 2020

Posso implementar um protocolo parcialmente em Objective C e parcialmente em Swift?

Eu tenho uma classe Objective C existente que é declarada assim no arquivo .h.

@interface SetupViewController : UIViewController <UITableViewDataSource> {
    // etc.
}

O arquivo .m correspondente implementa o protocolo UITableViewDataSource. Agora, quero reescrever alguns desses métodos em uma extensão do Swift. Mas eu esperava não ter que reescrever todos eles em Swift agora. (Este é apenas um trecho de exemplo, a aula real é muito mais longa.)

@implementation SetupViewController

// I want to rewrite this method in Swift
/*
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 2; 
}
*/

// I don't want to rewrite this method in Swift yet
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {    
    // lots of code
}

Então, movi um dos métodos do arquivo Objective C para minha extensão, assim:

@objc extension SetupViewController {
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // new, more involved code
    }
}

mas isso não vai compilar. Recebo um erro ao dizer Method 'tableView(_:numberOfRowsInSection:)' with Objective-C selector 'tableView:numberOfRowsInSection:' conflicts with previous declaration with the same Objective-C selector. Mas não tenho mais o numberOfRowsInSectionmétodo no arquivo Objective-C, tirei-o.

Parece que simplesmente colocar UITableViewDataSource no arquivo .h faz a extensão Swift pensar que já existem métodos implementados para cumprir o protocolo.

Também tentei declarar a extensão como @objc extension SetupViewController: UITableViewDataSourcee removê-la do arquivo .h, mas falhou porque a extensão não implementa o protocolo por si só.

Respostas

2 Cristik Nov 08 2020 at 10:49

Ao construir um projeto que combina código Swift + Objective-C, este é um breve resumo do que acontece:

  1. o cabeçalho de ponte é processado
  2. os arquivos Swift são compilados
  3. os arquivos Objective-C são compilados

O erro que você vê ocorre na etapa 2, neste ponto o compilador vê que a classe Objective-C está em conformidade UITableViewDataSource, portanto, ele assume que o código Objective-C implementa o tableView(_:numberOfRowsInSection:)método, portanto, ele vê um conflito, pois o código Swift também implementa método.

A fonte do problema é o fato de que você está tentando implementar alguns dos métodos de protocolo em Objective-C e alguns em Swift, e isso não é possível, você também precisa implementar todos os métodos não opcionais do protocolo em Swift ou Objective-C.

Se você realmente deseja seguir esse caminho, uma solução possível seria mover a conformidade do protocolo no lado do Swift e declarar todos os outros métodos no cabeçalho Objective-C como métodos públicos dessa classe. Mas isso pode causar outros problemas ao longo do caminho.