web-dev-qa-db-ja.com

Objective-CでSwift String enumを使用可能にする方法は?

この列挙型には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は整数型ではありません

SwiftからObjective-Cに変換できるのは整数のみであると言う場所を見つけることができませんでした。これは事実ですか?もしそうなら、Objective-Cでこのようなものを利用可能にする方法についてのベストプラクティスの提案はありますか?

55
bogen

Xcode 6.3リリースノート (強調を追加)から:

Swift言語の機能強化

...
Swift列挙型は、@ objc属性を使用してObjective-Cにエクスポートできるようになりました。 @objc列挙は整数の生の型を宣言する必要があり、ジェネリックまたは関連する値を使用することはできません。 Objective-C列挙型は名前空間ではないため、列挙型ケースは、列挙名とケース名の連結としてObjective-Cにインポートされます。

48
Martin R

解決策の1つは、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:
                self = .Debug
        }
    }
}
54
Remy Cilia

これが有効なソリューションです。

@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"
        }
    }
}
21
David

目標を本当に達成したい場合の回避策を次に示します。ただし、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]
7
BLC

(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コードで使用する場合と非常によく似ています。

CocoaおよびObjective-CのドキュメントでSwiftを使用

4
RvdB

Intは機能するが、他のメソッドはObjective-Cに公開されないという事実を使用したXcode 8のコード。これはかなり恐ろしいことです...

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

これは私のユースケースです:

  • 可能な場合は常にハードコードされた文字列を避けるため、何かを変更するとコンパイル警告が表示されます
  • バックエンドからの文字列値の固定リストがありますが、これも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
        }
    }
}
2
Chris Garrett

プライベートInner enumを作成できます。実装は少し繰り返し可能ですが、明確で簡単です。 1行rawValue、2行init、これらは常に同じように見えます。 Innerには、同等の「外部」を返すメソッドがあり、逆も同様です。

ここにある他の回答とは異なり、enumケースを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
            }
        }
    }
}
1
Pawel Jurczyk

これが私が思いついたものです。私の場合、この列挙型は特定のクラス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];

1
Chris Prince