Импорт протокола Swift в класс Objective-C

116

Я пытаюсь импортировать протокол Swift с именем AnalyticProtocolв класс Objective-C с именем AnalyticFactory.

protocol AnalyticProtocol
{

}

Я начинаю с существующего проекта Objective-C (я не создавал новый проект Swift с xCode, и я не нашел, как настроить мой проект Objective-C как проект Swift в xCode 6 ).

В свой файл Swift я включил .hфайл с именем, MyProjectName-Swift.hно компилятор вернул мне ошибку, сообщающую мне, что его не существует . Итак, я создал .hфайл с именем, MyProjectName-Swift.hкоторый на самом деле пуст (я не знаю, что мне поместить внутрь).

В документации Apple сказано, что я должен включить свой .hфайл с именем MyProjectName-Swift.hв свой .mфайл. Но мне нужно включить его не в свой .mфайл, а в свой .h. Это может быть проблематично?

Когда я пытаюсь скомпилировать, я получаю эту ошибку:: 0: error: xxxAnalyticFactory.h: 39: не удается найти объявление протокола для 'AnalyticProtocol'

И инкриминируемый код:

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

Думаю, я плохо понимаю, как мне импортировать протокол Swift в класс Objective-C.

Кто-нибудь видит ошибку в том, что делаю?

Жан Лебрюман
источник
Посмотрите видео « Интеграция Swift с Objective-C» с WWDC 2014. Примерно в 30:40 видео они описывают, как получить доступ к протоколам Swift в классах Objective-C.
Джейми Форрест

Ответы:

225

Вам нужно добавить @objcатрибут в свой протокол Swift следующим образом:

@objc protocol AnalyticProtocol {

}
Джейми Форрест
источник
25
Спасибо за ответ, но проблема не устранена.
Jean Lebrument 06
Не могли бы вы опубликовать образец проекта, в котором воспроизводится проблема?
Джейми Форрест
Добавление @objc помогло мне импортировать классы Swift в Obj-C
serg
19
@objc не всегда работает. При согласовании с протоколом иногда не работает при добавлении протокола @interfaceв .hфайл. Однако, вы можете добавить протокол к частному @interfaceв .mфайл и фиксируют вещи (по крайней мере , у него есть для меня по случаю). Так что прежде всего у вас @implementationесть @interface MyController() <AnalyticProtocol>.
Адам
1
Иногда Xcode 8 будет жаловаться, пока вы редактируете, но когда вы его действительно создаете, следуя этому ответу вместе с комментариями, ошибка исчезнет.
Роджер Пинглтон,
77

Невозможно импортировать созданный Xcode заголовок Swift в файлы заголовков objC.

Итак, поскольку вы хотите использовать код Swift в файле заголовка objC, вам нужно будет «пересылать объявление» классов и протоколов, которые вы хотите использовать в файле заголовка objC, например:

@protocol AnalyticProtocol;

Теперь вы можете использовать протокол в объявлении класса objC:

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

В вашем файле реализации (файл objC .m) вы можете импортировать созданный Xcode файл заголовка Swift ("ProductModuleName-Swift.h"), и AnalyticProtocolтеперь компилятору будет известна правильная реализация .

Это также описано в разделе «Использование Swift из Objective-C» в документации Apple.

Обратите внимание, что XCode выдаст предупреждение в файле заголовка objC, когда вы используете заявленный вперед протокол («Не удается найти определение протокола для 'AnalyticProtocol'), но это можно игнорировать - реализация будет найдена во время компиляции.

Пелле Стенильд Кольтау
источник
19
Почему Xcode показывает предупреждение об отсутствии протокола, когда он компилируется и работает нормально? Любой способ удалить предупреждение?
Oren
Однако вызвать методы протокола для этого класса по-прежнему невозможно. Выдаст ошибкуNo visible @interface for <ClassName> declares the selector <protocolMethodName>
Эльза
он выдает следующее предупреждение: «Не удается найти определение протокола для xxxx»
JAHelia
51

Для тех, кому просто нужно принять протокол - вы можете сделать это в два этапа, без каких-либо предупреждений или ошибок:

  1. В вашем .swiftфайле @objcперед названием протокола добавьте :

    @objc protocol AnalyticProtocol {
    
    }
  2. В своем .mфайле импортируйте сгенерированный заголовок Swift и перенесите протокол в частную категорию. (Заголовочный файл получает имя автоматически):

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

Это рекомендуемый подход Apple. Вы можете узнать больше о смешивании и сопоставлении Objective-C и Swift здесь: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

Крис Нолет
источник
1
Я все еще получаю предупреждение, с предварительным объявлением или без него. Я использую протокол в расширении.
Cristi Băluță
Невозможно импортировать #import "AnalyticProtocol-Swift.h". Похоже, что WhateverProtocol-Swift.h не создается автоматически, как вы сказали.
Hlung
2
Это сработало для меня; никакие другие предложения или ответы на этой странице не работали. Я на Swift 4.
Poulsbo
1
Это был единственный способ удалить предупреждение (даже если он работает, когда протоколы находятся в файле заголовка)
Дэвид П.
2
Та же ссылка доступна через WebArchive: web.archive.org/web/20170310053717/https://developer.apple.com/…
Ричард
3

Если вы создаете фреймворк, необходимый импорт

#import "ProductModuleName-Swift.h"

изменения к:

#import <ProductModuleName/ProductModuleName-Swift.h>

В этом случае вы также должны сделать быстрый протокол общедоступным :

@objc public protocol AnalyticProtocol {
  func something();
}
Michal
источник
3

Мы можем использовать быстрые протоколы в Objective C с небольшими изменениями кода. Кроме того, протоколы, объявленные @objc, позволяют использовать дополнительные и обязательные методы без реализаций по умолчанию. У него есть свои плюсы и минусы.

Мы могли бы фактически переименовать имя протокола в более наглядное при использовании в Objective C. Я использую «@objc (alias_name)».

Вот код: давайте получим быстрый протокол с атрибутом @objc и псевдонимом для использования в коде ObjC.

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

Теперь давайте объявим наш протокол в файле .h.

@protocol ObjTableViewReloadable;

Теперь вы можете согласовать этот протокол в файле .m и добавить реализацию необходимых методов.

#import "MyApp-Swift.h"
@interface MyObjcViewController () <ObjTableViewReloadable>
Винай Хосамане
источник
Спасибо за публикацию. Я смотрю на унаследованный код, который старше грязи, и если этот подход сработает, он избавит меня от тонны боли. :)
Адриан