我有这个带有String
值的enum,它将用于告诉记录到服务器的消息具有何种严重程度的API方法。我使用Swift 1.2,所以枚举可以映射到Objective-C
@objc enum LogSeverity : String {
case Debug = "DEBUG"
case Info = "INFO"
case Warn = "WARN"
case Error = "ERROR"
}
我得到错误
@objc enum原始类型字符串不是整数类型
我没有找到任何地方说只有整数可以从Swift翻译到Objective-C。是这样吗?如果是这样,有没有人对如何在Objective-C中提供这样的东西有任何最佳实践建议?
解决方案之一是使用RawRepresentable
协议。
必须编写init和rawValue方法并不理想,但这允许您在Swift和Objective-C中像往常一样使用此enum。
@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
}
}
}
来自Xcode 6.3发布说明(强调添加):
Swift语言增强
…Swift枚举现在可以使用@objc导出到Objective-C属性。@objc enums 必须声明一个整数原始类型,并且不能通用或使用关联值。因为Objective-C的枚举不是命名空间、枚举用例被导入到Objective-C中枚举名称和案例名称的连接。
这是一个有效的解决方案。
@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"
}
}
}
如果你真的想实现这个目标,这里有一些变通的方法。但是,你可以访问对象中的枚举值,而不是作为实际的枚举值。
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]
如果你不介意在(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代码中使用它时感觉非常相似。
你可以在对相关Objective-C常量进行分组
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"
}
}
}
这是我的用例:
- 我尽量避免硬编码字符串,所以当我改变一些东西时,我得到编译警告
- 我有一个固定的字符串值列表来自后端,这也可以是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
}
}
}
这是我想到的。在我的例子中,这个枚举在上下文中为特定的类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中,你可以在enum上使用.string
(类似于.rawValue
)。在Objective-C中,你可以使用[ServiceProvider FieldNameToString:enumValue];
可以创建私有的Inner
enum。实现有点可重复,但清晰而简单。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
}
}
}
}
我认为@Remi的回答在某些情况下会崩溃,因为我有这个:
我的错误截图。所以我贴出了@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
}
}
}