如何在Objective-C中使Swift String枚举可用?


75

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

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

我得到了错误

@objc枚举原始类型字符串不是整数类型

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

Answers:


60

Xcode 6.3发行说明(添加了重点):

Swift语言增强

...
现在可以使用@objc属性将Swift枚举导出到Objective-C。@objc枚举必须声明一个整数原始类型,并且不能为泛型或使用关联的值。因为没有为Objective-C枚举命名空间,所以枚举个案将作为枚举名称和个案名称的串联导入到Objective-C中。


在链接上找不到页面
Zack Shapiro

77

解决方案之一是使用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:
                return nil
        }
    }
}

9
我真的很喜欢这种方法。为了使其完美,可以通过定义类型的字典来避免某些代码重复[LogSeverity: String],然后可以通过一行定义rawValueinit?方法。
Gobe

1
@Gobe您可以分享如何编写rawValue和init的示例吗?方法在一行中,好吗?
丹尼尔·桑切斯

1
@DanielSanchez,如果您有一个LogSeverityStrings原始表示的类型的枚举,并且一次定义了一个类型的字典[LogSeverity: String],那么rawValue就简单了myDictionary[self],它的初始化是self = myDictionary.first(where: { $0.value == rawValue })
Gobe

1
看来这是一个不错的答案,但尝试此方法后,我会感到奇怪的bad_access崩溃。
Jafar Khoshtabiat

1
@VladimirsMatusevics我将其修复并要求进行编辑,请改正
tnx

23

这是一个可行的解决方案。

@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"
        }
    }
}

12
以及如何name()在Objective-C中调用该函数?
斯蒂芬·保罗

@StephenPaul诸如ConnectivityStatusWifi之类的东西。如果在其中键入,应该会看到Xcode尝试自动完成。
戴维(David)

10
@David,但您无法致电name()objc
Cameron

我认为您应该将函数声明为公共
i89,2016年

@Chuck甚至public函数都不会公开该方法。
Priyal,

11

如果您确实想实现目标,可以在这里解决。但是,您可以访问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]

1
我看到的问题是您不能拥有variablesLogSeverity类型,但其他情况下还可以。
克里斯·普林斯

2
这对我来说做了一些小的改动。@objcMembers public class LogSeverityBridge: NSObject { static func debug() -> String { return TravelerProtectionLevel.premium.rawValue }
Hendrix

6

如果您不介意在(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代码中使用它时却感觉非常相似。

您可以在结合使用Swift和Cocoa和Objective-C文档的“与C API交互”中阅读有关此技术的更多信息。


这正是我一直在寻找的方法!
汤姆·克雷纳

5

使用有效的事实为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"
        }
    }
}

4

这是我的用例:

  • 我会尽可能避免使用硬编码的字符串,以便在更改某些内容时得到编译警告
  • 我有一个固定值的字符串列表,来自后端,也可以是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
        }
    }
}

1
我不能[InventoryItemType fromString:]从Objective-C使用:它给出了错误“接收器类型'InventoryItemType'不是Objective-C类”
agirault

1
这是因为在Objective-C中,InventoryItemType将为Int或NSInteger类型,而不是一个类。
克里斯·加勒特

嗨,@ ChrisGarrett,我得到了枚举: public enum TrackingValue { case constant(String) case customVariable(name: String) case defaultVariable(DefaultVariable) public enum DefaultVariable { case advertisingId case advertisingTrackingEnabled case appVersion case connectionType case interfaceOrientation case isFirstEventAfterAppUpdate case requestQueueSize case adClearId } }使其成为ObjC枚举的最佳方法是什么?提前致谢!
Dragisa Dragisic

@agirault,您可以将其包装在另一个类中,例如class InventoryItemTypeParser: NSObject { @objc static func fromString(_ string: String?) -> InventoryItemType { return InventoryItemType.fromString(string) } }
Krzysztof Skrzynecki

2

这是我想出的。就我而言,该枚举是在上下文中提供特定类的信息ServiceProvider

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"
        }
    }
}

在Swift中,您可以使用.string枚举(类似于.rawValue)。在Objective-C中,您可以使用[ServiceProvider FieldNameToString:enumValue];


1

您可以创建一个私人Inner枚举。该实现是可重复的,但清晰易行。1行rawValue,2行init,它们看起来总是一样的。所述Inner具有返回“外”等效的方法,并且反之亦然。

String与此处的其他答案不同,它具有附加的优点,您可以将枚举大小写直接映射到a 。

如果您知道如何解决模板的可重复性问题,欢迎您以此答案为基础,我现在没有时间讨论它。

@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
            }
        }
    }
}

1

我认为@Remi的答案在某些情况下会崩溃,因为我有这样的情况:

我的错误是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
        }
    }
}

有什么办法可以从Objc创建此枚举,当我尝试[LogSeverity rawValue:]时,找不到初始化器。
复古

如何从目标c访问字符串值?
OliverD
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.