Comment rendre une chaîne de caractères SWIFT enum disponible dans Objective-C?

j'ai cette enum String valeurs, qui seront utilisées pour indiquer à une méthode API qui se connecte à un serveur quel type de server un message a. J'utilise Swift 1.2, donc les énums peuvent être mappés à L'objectif-C

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

j'ai l'erreur

@objc enum crue de type String n'est pas un entier de type

Je n'ai pas réussi à trouver n'importe où qui dit que seuls les entiers peuvent être traduits à L'Objectif-C de Swift. Est-ce le cas? Si oui, n' quelqu'un a-t-il une suggestion de meilleure pratique sur la façon de rendre disponible quelque chose comme ça dans L'objectif-C?

40
demandé sur nhgrif 2015-05-27 13:50:43

10 réponses

Xcode 6.3 notes de mise à jour (non souligné dans l'original):

Améliorations De La Langue Swift

...

Les énums Swift peuvent maintenant être exportés vers L'Objectif-C en utilisant le @objc attribut. @objc enums doit déclarer un type brut entier, et ne peut pas être utiliser des valeurs génériques ou associées. Parce que Objective-C les énumérations ne sont pas les cas d'enum placés sous leur nom sont importés dans L'Objectif - C en tant que concaténation du nom enum et en cas de nom.

40
répondu Martin R 2017-11-06 20:51:20

une des solutions est d'utiliser le protocole RawRepresentable.

il n'est pas idéal d'avoir à écrire les méthodes init et rawValue mais cela vous permet d'utiliser cet enum comme d'habitude dans Swift et 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:
                self = .Debug
        }
    }
}
34
répondu Remy Cilia 2016-07-20 21:01:26

Voici une solution qui fonctionne.

@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
répondu David 2015-10-22 03:44:26

Voici contourner si vous voulez vraiment atteindre l'objectif. Cependant, vous pouvez accéder aux valeurs enum dans les objets que L'objectif c accepte, et non pas en tant que valeurs enum réelles.

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()
    }
}

l'appel De :

NSString *debugRawValue = [LogSeverityBridge Debug]
5
répondu BLC 2015-10-21 14:24:55

Code pour Xcode 8, en utilisant le fait que Int fonctionne mais d'autres méthodes ne sont pas exposées à L'objectif-C. C'est assez horrible en l'état...

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
répondu Dan Rosenstark 2017-01-25 22:27:06

si cela ne vous dérange pas de définir les valeurs dans (Objectif) C, vous pouvez utiliser le NS_TYPED_ENUM macro pour importer les constantes dans Swift.

Par exemple:

.h fichier

typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;

FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;

.m le fichier

ProgrammingLanguage ProgrammingLanguageSwift = "Swift";
ProgrammingLanguage ProgrammingLanguageObjectiveC = "ObjectiveC";

dans Swift, ceci est importé comme un struct en tant que tel:

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

bien que le type ne soit pas unenum, il se sent très similaire à un lors de son utilisation en code Swift.

Vous pouvez en lire plus à propos de cette technique dans le "Interaging with capis" de la utilisation de Swift avec la documentation sur le cacao et L'objectif-c

2
répondu RvdB 2018-04-25 07:05:59

Voici ce que j'ai trouvé. Dans mon cas, cet enum était dans le contexte de fournir des informations pour une classe spécifique,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"
        }
    }
}

à partir de Swift, vous pouvez utiliser .string sur un enum (similaire à .rawValue). De Objectif-C, vous pouvez utiliser [ServiceProvider FieldNameToString:enumValue];

1
répondu Chris Prince 2016-05-06 20:50:17

Vous pouvez créer un privé Inner enum. La mise en œuvre est un peu reproductible, mais clair et facile. 1 ligne init, qui se ressemblent toujours. Inner a une méthode retournant l'équivalent "extérieur", et vice-versa.

A l'avantage supplémentaire que vous pouvez mapper directement l'enum cas d'un String, contrairement aux autres réponses ici.

n'hésitez à construire sur cette réponse si vous savez comment résoudre le problème de répétabilité avec des modèles, Je n'ai pas de temps pour se mêler avec elle dès maintenant.

@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
répondu Pawel Jurczyk 2018-03-19 15:25:25

j'ai eu un cas d'utilisation où j'avais encore besoin d'associer une valeur de chaîne dans le côté Swift. J'ai écrit sur ma solution ici: https://medium.com/@oscarcortes/using-swift-string-enums-in-objective-c-f6683da5b92e Essentiellement, vous pouvez ajouter une méthode dans L'enum Swift qui retourne une chaîne basée sur la valeur actuelle de l'enum.

0
répondu Oscar Cortes 2015-06-02 15:51:14

C'est mon cas d'utilisation:

  • j'évite les chaînes de caractères codées dur chaque fois que je peux, de sorte que je reçois des avertissements de compilation quand je change quelque chose
  • j'ai une liste fixe de la Chaîne de valeurs provenant d'un back-end, qui peut aussi être nul

voici ma solution qui n'implique pas de chaînes codées en dur du tout, supporte les valeurs manquantes, et peut être utilisé avec élégance dans Swift et 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
        }
    }
}
0
répondu Chris Garrett 2018-05-18 18:29:32