J'ai cette énumération avec les valeurs String
qui seront utilisées pour indiquer à une méthode d'API qui enregistre sur un serveur le type de serveur d'un message. J'utilise Swift 1.2, donc les énumérations peuvent être mappées à Objective-C
@objc enum LogSeverity : String {
case Debug = "DEBUG"
case Info = "INFO"
case Warn = "WARN"
case Error = "ERROR"
}
Je reçois l'erreur
@objc enum raw type La chaîne n'est pas un type entier
Je n'ai réussi à trouver nulle part où il est indiqué que seuls les entiers peuvent être traduits en Objective-C à partir de Swift. Est-ce le cas? Si tel est le cas, quelqu'un a-t-il une suggestion de meilleure pratique sur la manière de rendre disponible un tel résultat dans Objective-C?
À partir des notes de version de Xcode 6.3 } (soulignement ajouté):
Améliorations du langage rapide
...
Les énumérations rapides peuvent maintenant être exportées vers Objective-C à l’aide de @objc attribut. @objc enums doit déclarer un type brut entier, et ne peut pas être générique ou utiliser des valeurs associées. Parce que les enums Objective-C ne sont pas namespaced, les requêtes enum sont importées dans Objective-C en tant que concaténation du nom enum et du nom de l’affaire.
L'une des solutions consiste à utiliser le protocole RawRepresentable.
Ce n'est pas idéal d'avoir à écrire les méthodes init et rawValue mais cela vous permet d'utiliser cette énumération 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
}
}
}
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"
}
}
}
Voici un travail autour si vous voulez vraiment atteindre l'objectif. Cependant, vous pouvez accéder aux valeurs enum dans les objets acceptés par Objective C, et non 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()
}
}
Appeler :
NSString *debugRawValue = [LogSeverityBridge Debug]
Code pour Xcode 8, en utilisant le fait que Int
fonctionne mais que les autres méthodes ne sont pas exposées à Objective-C. C'est assez horrible tel qu'il se présente ...
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"
}
}
}
Si cela ne vous dérange pas de définir les valeurs dans (Objective) C, vous pouvez utiliser la macro NS_TYPED_ENUM
pour importer des constantes dans Swift.
Par exemple:
fichier .h
typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;
fichier .m
ProgrammingLanguage ProgrammingLanguageSwift = "Swift";
ProgrammingLanguage ProgrammingLanguageObjectiveC = "ObjectiveC";
Dans Swift, ceci est importé en tant que 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 ponté en tant que enum
, il est très similaire à celui-ci lorsqu’il est utilisé dans le code Swift.
Pour en savoir plus sur cette technique, consultez la section "Interaction avec les API C" de Utilisation de Swift avec Cocoa et de la documentation Objective-C
Vous pouvez créer une énumération privée Inner
. L'implémentation est un peu répétable, mais claire et facile. 1 ligne rawValue
, 2 lignes init
, qui se ressemblent toujours. La variable Inner
a une méthode renvoyant l'équivalent "externe", et inversement.
L’avantage supplémentaire que vous pouvez mapper directement le cas d’énumération à un String
, contrairement à d’autres réponses ici.
N'hésitez pas à nous appuyer 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 le temps de le mélanger pour le moment.
@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
}
}
}
}
Voici ce que je suis venu avec. Dans mon cas, cette énumération était dans le contexte fournissant 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
) . À partir de Objective-C, vous pouvez utiliser [ServiceProvider FieldNameToString:enumValue];
Ceci est mon cas d'utilisation:
Voici ma solution qui n'implique aucune chaîne codée en dur, qui prend en charge les valeurs manquantes et peut être utilisée 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
}
}
}