如何在Objective-C中提供Swift String枚举?

时间:2015-05-27 10:50:44

标签: objective-c swift enums interop

我有这个带有String值的枚举,它将用于告诉API方法,该方法会向服务器记录消息所具有的服务器类型。我正在使用Swift 1.2,因此枚举可以映射到Objective-C

@objc enum LogSeverity : String {
    case Debug = "DEBUG"
    case Info = "INFO"
    case Warn = "WARN"
    case Error = "ERROR"
}

我收到错误

  

@objc enum raw type String不是整数类型

我没有设法找到任何说只有整数可以从Swift转换为Objective-C的地方。是这样的吗?如果是这样,有没有人有关于如何在Objective-C中提供这样的东西的最佳实践建议?

10 个答案:

答案 0 :(得分:49)

其中一个解决方案是使用RawRepresentable协议。

编写init和rawValue方法并不理想,但允许您像往常一样在Swift和Objective-C中使用此枚举。

@objc public enum LogSeverity: Int, RawRepresentable {
    case Debug
    case Info
    case Warn
    case Error

    public typealias RawValue = String

    public var rawValue: RawValue {
        switch self {
            case .Debug:
                return "DEBUG"
            case .Info:
                return "INFO"
            case .Warn:
                return "WARN"
            case .Error:
                return "ERROR"
        }
    }

    public init?(rawValue: RawValue) {
        switch rawValue {
            case "DEBUG":
                self = .Debug
            case "INFO":
                self = .Info
            case "WARN":
                self = .Warn
            case "ERROR":
                self = .Error
            default:
                self = .Debug
        }
    }
}

答案 1 :(得分:46)

Xcode 6.3 release notes(强调添加):

  

快速语言增强

     

...
  现在可以使用@objc将Swift枚举导出到Objective-C   属性。 @objc枚举必须声明整数原始类型,而不能   通用或使用相关值。因为Objective-C枚举不是   namespaced,enum case被导入到Objective-C中   枚举名称和案例名称的连接。

答案 2 :(得分:21)

这是一个有效的解决方案。

@objc public enum ConnectivityStatus: Int {
    case Wifi
    case Mobile
    case Ethernet
    case Off

    func name() -> String {
        switch self {
        case .Wifi: return "wifi"
        case .Mobile: return "mobile"
        case .Ethernet: return "ethernet"
        case .Off: return "off"
        }
    }
}

答案 3 :(得分:6)

如果你真的想实现这个目标,那么这是解决方法。但是,您可以访问Objective C接受的对象中的枚举值,而不是实际的枚举值。

enum LogSeverity : String {

    case Debug = "DEBUG"
    case Info = "INFO"
    case Warn = "WARN"
    case Error = "ERROR"

    private func string() -> String {
        return self.rawValue
    }
}

@objc
class LogSeverityBridge: NSObject {

    class func Debug() -> NSString {
        return LogSeverity.Debug.string()
    }

    class func Info() -> NSString {
        return LogSeverity.Info.string()
    }

    class func Warn() -> NSString {
        return LogSeverity.Warn.string()
    }

    class func Error() -> NSString {
        return LogSeverity.Error.string()
    }
}

致电:

NSString *debugRawValue = [LogSeverityBridge Debug]

答案 4 :(得分:4)

如果您不介意在(Objective)C中定义值,可以使用NS_TYPED_ENUM宏在Swift中导入常量。

例如:

.h文件

typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;

FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;

.m文件

ProgrammingLanguage ProgrammingLanguageSwift = "Swift";
ProgrammingLanguage ProgrammingLanguageObjectiveC = "ObjectiveC";

在Swift中,这是以struct导入的:

struct ProgrammingLanguage: RawRepresentable, Equatable, Hashable {
    typealias RawValue = String

    init(rawValue: RawValue)
    var rawValue: RawValue { get }

    static var swift: ProgrammingLanguage { get }
    static var objectiveC: ProgrammingLanguage { get }
}

虽然类型没有被桥接为enum,但在Swift代码中使用它时感觉非常类似。

您可以在"与C API交互"中阅读有关此技术的更多信息。 Using Swift with Cocoa and Objective-C documentation

答案 5 :(得分:3)

Xcode 8的代码,使用Int有效但其他方法未向Objective-C公开的事实。这是非常可怕的,因为它... ...

class EnumSupport : NSObject {
    class func textFor(logSeverity severity: LogSeverity) -> String {
        return severity.text()
    }
}

@objc public enum LogSeverity: Int {
    case Debug
    case Info
    case Warn
    case Error

    func text() -> String {
        switch self {
            case .Debug: return "debug"
            case .Info: return "info"
            case .Warn: return "warn"
            case .Error: return "error"
        }
    }
}

答案 6 :(得分:1)

这是我想出的。就我而言,这个枚举是在上下文中提供特定类private bool TryGetData(string query, out DataSet dataSet) { try { dataSet = ...; //do some stuff to populate dataset return true; } catch (SqlException ex) { MessageBox.Show("There was a database error. Please contact administrator.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); LogExceptionToFile(ex); //to log whole exception stack trace, etc. return false; } finally { //cleanup } } //calling methods: DataSet ds; if (TryGetData(query, out ds)) { OtherMethod1(); } else { //error } 的信息。

ServiceProvider

从Swift中,您可以在枚举上使用class ServiceProvider { @objc enum FieldName : Int { case CITY case LATITUDE case LONGITUDE case NAME case GRADE case POSTAL_CODE case STATE case REVIEW_COUNT case COORDINATES var string: String { return ServiceProvider.FieldNameToString(self) } } class func FieldNameToString(fieldName:FieldName) -> String { switch fieldName { case .CITY: return "city" case .LATITUDE: return "latitude" case .LONGITUDE: return "longitude" case .NAME: return "name" case .GRADE: return "overallGrade" case .POSTAL_CODE: return "postalCode" case .STATE: return "state" case .REVIEW_COUNT: return "reviewCount" case .COORDINATES: return "coordinates" } } } (类似于.string)。 从Objective-C,您可以使用.rawValue

答案 7 :(得分:1)

您可以创建私有Inner枚举。实现有点可重复,但清晰简单。 1行rawValue,2行init,总是看起来一样。 Inner有一个方法返回"外部"等价的,反之亦然。

与其他答案不同,您可以直接将枚举案例映射到String,这有额外的好处。

如果您知道如何使用模板解决重复性问题,请立即建立此答案,我现在没有时间与它混在一起。

@objc enum MyEnum: NSInteger, RawRepresentable, Equatable {
    case
    option1,
    option2,
    option3

    // MARK: RawRepresentable

    var rawValue: String {
        return toInner().rawValue
    }

    init?(rawValue: String) {
        guard let value = Inner(rawValue: rawValue)?.toOuter() else { return nil }
        self = value
    }

    // MARK: Obj-C support

    private func toInner() -> Inner {
        switch self {
        case .option1: return .option1
        case .option3: return .option3
        case .option2: return .option2
        }
    }

    private enum Inner: String {
        case
        option1 = "option_1",
        option2 = "option_2",
        option3 = "option_3"

        func toOuter() -> MyEnum {
            switch self {
            case .option1: return .option1
            case .option3: return .option3
            case .option2: return .option2
            }
        }
    }
}

答案 8 :(得分:1)

这是我的用例:

  • 我尽可能避免使用硬编码的字符串,以便在我改变某些内容时收到编译警告
  • 我有一个来自后端的String值的固定列表,也可以是nil

这是我的解决方案,根本不涉及硬编码字符串,支持缺失值,并且可以在Swift和Obj-C中优雅地使用:

@objc enum InventoryItemType: Int {
    private enum StringInventoryItemType: String {
        case vial
        case syringe
        case crystalloid
        case bloodProduct
        case supplies
    }

    case vial
    case syringe
    case crystalloid
    case bloodProduct
    case supplies
    case unknown

    static func fromString(_ string: String?) -> InventoryItemType {
        guard let string = string else {
            return .unknown
        }
        guard let stringType = StringInventoryItemType(rawValue: string) else {
            return .unknown
        }
        switch stringType {
        case .vial:
            return .vial
        case .syringe:
            return .syringe
        case .crystalloid:
            return .crystalloid
        case .bloodProduct:
            return .bloodProduct
        case .supplies:
            return .supplies
        }
    }

    var stringValue: String? {
        switch self {
        case .vial:
            return StringInventoryItemType.vial.rawValue
        case .syringe:
            return StringInventoryItemType.syringe.rawValue
        case .crystalloid:
            return StringInventoryItemType.crystalloid.rawValue
        case .bloodProduct:
            return StringInventoryItemType.bloodProduct.rawValue
        case .supplies:
            return StringInventoryItemType.supplies.rawValue
        case .unknown:
            return nil
        }
    }
}

答案 9 :(得分:1)

我认为@Remi的答案在某些情况下会因为我的出现而崩溃:

My error's screesshot。所以我发布我的版本以获取@Remi的答案:

@objc public enum LogSeverity: Int, RawRepresentable {
    case debug
    case info
    case warn
    case error

    public typealias RawValue = String

    public var rawValue: RawValue {
        switch self {
            case .debug:
                return "DEBUG"
            case .info:
                return "INFO"
            case .warn:
                return "WARN"
            case .error:
                return "ERROR"
        }
    }

    public init?(rawValue: RawValue) {
        switch rawValue {
            case "DEBUG":
                self = .debug
            case "INFO":
                self = .info
            case "WARN":
                self = .warn
            case "ERROR":
                self = .error
            default:
                return nil
        }
    }
}