Доступ к собственности заранее объявленного enum от swift

Учитывая, что существует ObjC-совместимое перечисление, написанное на Swift:

// from MessageType.swift
@objc enum MessageType: Int {
    case one
    case two
}

и класс ObjC со свойством типа MessageType который должен быть заранее объявлен:

// from Message.h
typedef NS_ENUM(NSInteger, MessageType);

@interface Message: NSObject
@property (nonatomic, readonly) MessageType messageType;
@end

Для того, чтобы использовать Messageв остальной части кодовой базы Swift, Message.h был добавлен в заголовок моста:

// from App-Bridging-Header.h
#import "Message.h"

Теперь представьте, что есть класс Swift, который пытается прочитать messageType имущество:

// from MessageTypeReader.swift
class MessageTypeReader {
    static func readMessageType(of message: Message) -> MessageType {
        return message.messageType
    }
}

Компиляция завершится с ошибкой:

Value of type 'Message' has no member 'messageType'

Мой вопрос был бы: есть ли способ заранее объявить перечисление Swift для того, чтобы MessageTypeReader чтобы иметь доступ к собственности?

Примечание: я знаю о возможности переписать Сообщение в Swift или импортировать App-Bridging-Header.h в Message.h, но здесь это не вариант, я ищу решение, которое работало бы с текущей настройкой.

2 ответа

Решение

Я предполагаю, что одной из причин использования NS_ENUM на стороне Objective-C является проверка времени компиляции, является ли использование оператора switch исчерпывающим.

Если это так, можно использовать C союзы.

Заголовок Objective-C

typedef NS_ENUM(NSInteger, MessageType);

union MessageTypeU {
    MessageType objc;
    NSInteger swift;
};


@interface Message : NSObject

@property (nonatomic, readonly) union MessageTypeU messageType;

@end

Итак, основная идея такова:

Swift импортирует C союзы как структуры Swift. Хотя Swift не поддерживает изначально объявленные объединения, объединение C, импортированное как структура Swift, по-прежнему ведет себя как объединение C.

...

Поскольку объединения в C используют один и тот же базовый адрес памяти для всех своих полей, все вычисленные свойства в объединении, импортированном Swift, используют одну и ту же базовую память. В результате изменение значения свойства в экземпляре импортированной структуры приводит к изменению значения всех других свойств, определенных этой структурой.

см. здесь: https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift

Пример реализации Objective-C

@interface Message ()

@property (nonatomic, readwrite) union MessageTypeU messageType;

@end


@implementation Message


- (instancetype)init
{
    self = [super init];
    if (self) {
        _messageType.objc = MessageTypeTwo;
        [self testExhaustiveCompilerCheck];
    }
    return self;
}

- (void)testExhaustiveCompilerCheck {

    switch(self.messageType.objc) {
        case MessageTypeOne:
            NSLog(@"messageType.objc: one");
            break;
        case MessageTypeTwo:
            NSLog(@"messageType.objc: two");
            break;
    }

}

@end

Использование на Swift Side

Поскольку свойство messageType.swift изначально происходит со стороны Swift (см. Определение MessageType), мы можем безопасно использовать принудительное развертывание.

class MessageTypeReader {

    static func readMessageType(of message: Message) -> MessageType {
        return MessageType(rawValue: message.messageType.swift)!
    }

}

Вот обходной путь, предложенный Cristik (вся заслуга им):

  • В Message.h, объявить messageType как NSInteger:

    @interface Message : NSObject
    @property (nonatomic, readonly) NSInteger messageType;
    @end
    

    С помощью NS_REFINED_FOR_SWIFT рекомендуется Apple, но не обязательно здесь.

  • В Swift добавьте следующее Message расширение:

    extension Message {
        var messageType: MessageType {
            guard let type = MessageType(rawValue: self.__messageType) else {
                fatalError("Wrong type")
            }
            return type
        }
    }
    
Другие вопросы по тегам