webentwicklung-frage-antwort-db.com.de

Wie kann ich eine schnelle String-Enumeration in Objective-C verfügbar machen?

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?

46
bogen

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.

45
Martin R

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
        }
    }
}
44
Remy Cilia

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

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]
5
BLC

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"
        }
    }
}
3
Dan Rosenstark

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

2
RvdB

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
            }
        }
    }
}
1
Pawel Jurczyk

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.

1
Chris Prince

Dies ist mein Anwendungsfall:

  • Ich vermeide hartcodierte Strings, wann immer ich kann, und bekomme Warnungen, wenn ich etwas ändere
  • Ich habe eine feste Liste von String-Werten, die von einem Back-End kommen, das auch Null sein kann

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
        }
    }
}
0
Chris Garrett