Ich habe dieses Enum mit String
-Werten, das verwendet wird, um einer API-Methode mitzuteilen, die auf einem Server protokolliert, welche Art von Serverity eine Nachricht hat. Ich verwende Swift 1.2, so dass Enumerationen auf Objective-C abgebildet werden können
@objc enum LogSeverity : String {
case Debug = "DEBUG"
case Info = "INFO"
case Warn = "WARN"
case Error = "ERROR"
}
Ich bekomme den Fehler
@objc Enum Raw-Typ String ist kein Integer-Typ
Ich habe es nicht geschafft, irgendwo einen Ort zu finden, der besagt, dass nur ganze Zahlen von Swift in Objective-C übersetzt werden können. Ist das der Fall? Wenn ja, hat jemand einen Best-Practice-Vorschlag, wie man so etwas in Objective-C verfügbar macht?
Aus den Versionshinweisen zu Xcode 6.3 (Hervorhebung hinzugefügt):
Schnelle Sprachverbesserungen
...
Swift-Enums können jetzt mit @objc .__ in Objective-C exportiert werden. Attribut. @objc Aufzählungszeichen muss einen Integer-Raw-Typ deklarieren und darf nicht .__ sein. generisch oder verwandte Werte verwenden. Weil Objective-C-Enums nicht .__ sind. Namensraum, Enumerationsfälle werden als .__ in Objective-C importiert. Verkettung des Aufzählungsnamens und des Fallnamens.
Eine der Lösungen ist die Verwendung des RawRepresentable-Protokolls.
Es ist nicht ideal, die init- und rawValue-Methoden schreiben zu müssen, aber damit können Sie dieses Enum wie gewohnt in Swift und Objective-C verwenden.
@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
}
}
}
Hier ist eine Lösung, die funktioniert.
@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"
}
}
}
Hier ist eine Problemumgehung, wenn Sie das Ziel wirklich erreichen wollen. Sie können jedoch auf die Aufzählungswerte in Objekten zugreifen, die von Objective C akzeptiert werden, nicht als tatsächliche Aufzählungswerte.
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()
}
}
Anrufen :
NSString *debugRawValue = [LogSeverityBridge Debug]
Code für Xcode 8 unter Verwendung der Tatsache, dass Int
funktioniert, andere Methoden jedoch nicht für Objective-C verfügbar sind Das ist ziemlich schrecklich, wie es steht ...
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"
}
}
}
Wenn Sie die Werte in (Ziel) C nicht definieren möchten, können Sie mit dem NS_TYPED_ENUM
-Makro Konstanten in Swift importieren.
Zum Beispiel:
.h Datei
typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;
.m Datei
ProgrammingLanguage ProgrammingLanguageSwift = "Swift";
ProgrammingLanguage ProgrammingLanguageObjectiveC = "ObjectiveC";
In Swift wird dies als struct
als solche importiert:
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 }
}
Obwohl der Typ nicht als enum
überbrückt ist, fühlt er sich einem sehr ähnlich, wenn er im Swift-Code verwendet wird.
Weitere Informationen zu dieser Technik finden Sie in der "Interaktion mit C-APIs" der Swift mit Cocoa und Objective-C-Dokumentation verwenden
Sie können eine private Inner
-Enumeration erstellen. Die Implementierung ist ein bisschen wiederholbar, aber klar und einfach. 1 Zeile rawValue
, 2 Zeilen init
, die immer gleich aussehen. Die Variable Inner
hat eine Methode, die das "äußere" Äquivalent zurückgibt, und umgekehrt.
Hat den zusätzlichen Vorteil, dass Sie den Enumerationsfall im Gegensatz zu anderen Antworten hier direkt einer String
zuordnen können.
Bitte zögern Sie nicht, auf dieser Antwort aufzubauen, wenn Sie wissen, wie Sie das Wiederholbarkeitsproblem mit Vorlagen lösen.
@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
}
}
}
}
Hier ist was ich mir ausgedacht habe. In meinem Fall war diese Enumeration im Zusammenhang mit Informationen für eine bestimmte Klasse, 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"
}
}
}
In Swift können Sie .string
für ein Enum verwenden (ähnlich wie .rawValue
) . In Objective-C können Sie [ServiceProvider FieldNameToString:enumValue];
verwenden.
Dies ist mein Anwendungsfall:
Hier ist meine Lösung, die keine hartcodierten Strings enthält, fehlende Werte unterstützt und elegant sowohl in Swift als auch in Obj-C verwendet werden kann:
@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
}
}
}