如何使 Swift 字符串枚举在 Objective-C 中可用?
How to make a Swift String enum available in Objective-C?
我有这个带有 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 is not an integer type
我还没找到任何地方说只能将整数从 Swift 转换为 Objective-C。是这样吗?如果是这样,有人对如何在 Objective-C 中提供此类内容有任何最佳实践建议吗?
来自Xcode 6.3 release notes(强调已添加):
Swift Language Enhancements
...
Swift enums can now be exported to Objective-C using the @objc
attribute. @objc enums must declare an integer raw type, and cannot be
generic or use associated values. Because Objective-C enums are not
namespaced, enum cases are imported into Objective-C as the
concatenation of the enum name and case name.
如果您真的想实现目标,这里有变通办法。但是,您可以访问 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]
这是一个有效的解决方案。
@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"
}
}
}
这是我想出的。在我的例子中,这个枚举是在上下文中为特定的 class、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];
解决方案之一是使用 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
}
}
}
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"
}
}
}
如果您不介意在 (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 代码中使用它时感觉非常相似。
的 "Interacting with C APIs" 中阅读有关此技术的更多信息
您可以创建私有 Inner
枚举。实现有点可重复,但清晰易行。 1 行 rawValue
、2 行 init
,它们看起来总是一样的。 Inner
有一个返回 "outer" 等效项和 vice-versa 的方法。
与此处的其他答案不同,还有一个额外的好处是您可以将枚举大小写直接映射到 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
}
}
}
}
这是我的用例:
- 我尽可能避免硬编码字符串,这样当我更改某些内容时会收到编译警告
- 我有一个来自后端的固定字符串值列表,它也可以是 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
}
}
}
我认为@Remi 的回答在某些情况下会崩溃,因为我有这个:
My error's screesshot。所以我 post 我对 @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
}
}
}
我有这个带有 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 is not an integer type
我还没找到任何地方说只能将整数从 Swift 转换为 Objective-C。是这样吗?如果是这样,有人对如何在 Objective-C 中提供此类内容有任何最佳实践建议吗?
来自Xcode 6.3 release notes(强调已添加):
Swift Language Enhancements
...
Swift enums can now be exported to Objective-C using the @objc attribute. @objc enums must declare an integer raw type, and cannot be generic or use associated values. Because Objective-C enums are not namespaced, enum cases are imported into Objective-C as the concatenation of the enum name and case name.
如果您真的想实现目标,这里有变通办法。但是,您可以访问 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]
这是一个有效的解决方案。
@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"
}
}
}
这是我想出的。在我的例子中,这个枚举是在上下文中为特定的 class、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];
解决方案之一是使用 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
}
}
}
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"
}
}
}
如果您不介意在 (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 代码中使用它时感觉非常相似。
您可以创建私有 Inner
枚举。实现有点可重复,但清晰易行。 1 行 rawValue
、2 行 init
,它们看起来总是一样的。 Inner
有一个返回 "outer" 等效项和 vice-versa 的方法。
与此处的其他答案不同,还有一个额外的好处是您可以将枚举大小写直接映射到 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
}
}
}
}
这是我的用例:
- 我尽可能避免硬编码字符串,这样当我更改某些内容时会收到编译警告
- 我有一个来自后端的固定字符串值列表,它也可以是 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
}
}
}
我认为@Remi 的回答在某些情况下会崩溃,因为我有这个:
My error's screesshot。所以我 post 我对 @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
}
}
}