Как получить доступ к внутреннему классу Swift в Objective-C в рамках одной и той же структуры?

Работа с смешанной структурой. импортированный внутри файла Obj-C, но внутренние классы не видны, только публичные.

В документации четко указано, что внутренние кланы должны быть доступны между Swift и Obj-C:

Импорт Swift в Objective-C
Чтобы импортировать набор файлов Swift в тот же целевой объект инфраструктуры, что и ваш Objective-C, вы не используете необходимо импортировать что-либо в заголовок зонтика для фреймворка. Вместо этого импортируйте заголовочный файл сгенерированный Xcode для вашего кода Swift в любой файл Objective-C.m, с которого вы хотите использовать код Swift. Поскольку сгенерированный заголовок для целевой среды является частью frameworks public interface, только декларации, помеченные общественностью модификатор появляется в сгенерированном заголовке для целевой среды. Вы все еще могут использовать методы и свойства Swift, отмеченные внутренний модификатор из Objective-C части вашей структуры, пока они объявлены в классе, который наследуется от Objective-C класс. Для получения дополнительной информации о модификаторах уровня доступа см. Контроль доступа в Быстрый язык программирования (Swift 2).

Пример кода (создайте новый проект с каркасом)

// SwiftObject.swift

public class SwiftObject: NSObject {
    public class func doSomething() {}
}

internal class YetAnotherSwiftObject: NSObject {
    internal class func doSomething() {}
}

// SomeObject.m file

@implementation SomeObject

- (void)someMethod {
    [SwiftObject doSomething];
}

- (void)someOtherMethod {
    [YetAnotherSwiftObject doSomething]; // Use of undeclared identifier
}

@end

Ответ 1

Как указано в документах, объявления, помеченные модификатором internal, не отображаются в сгенерированном заголовке, поэтому компилятор не знает о них и, следовательно, жалобах. Конечно, вы можете отправлять сообщения с использованием подхода performSelector, но это не удобно и подвержено ошибкам. Нам просто нужно помочь компилятору узнать, что эти объявления есть.

Во-первых, нам нужно использовать вариант атрибута @objc, который позволяет указать имя для вашего символа в Objective-C:

// SwiftObject.swift

@objc(SWIFTYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
    internal class func doSomething() {}
}

И тогда вам просто нужно создать объявление @interface с помощью методов, которые вы хотите использовать в своем коде, - поэтому компилятор будет счастлив, а также примените макрос SWIFT_CLASS с указанным ранее символом - так компоновщик выберет фактическую реализацию:

// SomeObject.m file

SWIFT_CLASS("SWIFTYetAnotherSwiftObject")
@interface YetAnotherSwiftObject : NSObject

+ (void)doSomething;

@end


@implementation SomeObject

- (void)someOtherMethod {
    [YetAnotherSwiftObject doSomething]; // Should work now !!!
}

@end
  • Я использовал декларацию интерфейса в формате .m только для ясности, лучшим вариантом было бы объединить такие объявления в файле .h и включить его.
  • Объявляя методы в этом интерфейсе, мы обещаем компилятору, и он не будет жаловаться, если вы поместите туда метод, который не существует (или с неправильной подписью и т.д.). Очевидно, вы будете в этом случае сбой во время выполнения - так что будьте осторожны.

Ответ 2

Для меня это просто работало, проверяя: "Разрешить только API расширения приложения". Вы найдете его, перейдя к настройке проекта, выберите свою цель, а затем на вкладке "Общие" в разделе "Сведения о развертывании".

Может кто-нибудь объяснить мне, почему это решает проблему?