Swift에서 문자열을 열거 형으로 변환 할 수 있습니까?


96

a, b, c, d 케이스가있는 열거 형이있는 경우 "a"문자열을 열거 형으로 캐스팅 할 수 있습니까?


3
이러한 '캐스트'를 리터럴 변환이라고합니다.
Vatsal Manot

답변:


139

확실한. 열거 형은 원시 값을 가질 수 있습니다. 문서를 인용하려면 :

원시 값은 문자열, 문자 또는 정수 또는 부동 소수점 숫자 유형일 수 있습니다.

— 발췌 : Apple Inc.“The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l ,

따라서 다음과 같은 코드를 사용할 수 있습니다.

enum StringEnum: String 
{
  case one = "one"
  case two = "two"
  case three = "three"
}

let anEnum = StringEnum(rawValue: "one")!

print("anEnum = \"\(anEnum.rawValue)\"")

참고 : 각 케이스 뒤에 = "one"등을 쓸 필요가 없습니다. 기본 문자열 값은 케이스 이름과 동일하므로 호출 .rawValue하면 문자열이 반환됩니다.

편집하다

케이스 값의 일부로 유효하지 않은 공백과 같은 것을 포함하기 위해 문자열 값이 필요한 경우 문자열을 명시 적으로 설정해야합니다. 그래서,

enum StringEnum: String 
{
  case one
  case two
  case three
}

let anEnum = StringEnum.one
print("anEnum = \"\(anEnum)\"")

준다

anEnum = "하나"

그러나 case one"값 1"을 표시하려면 문자열 값을 제공해야합니다.

enum StringEnum: String 
{
  case one   = "value one"
  case two   = "value two"
  case three = "value three"
}

원시 값은 리터럴로 변환 가능해야합니다. 어떤 Hashable유형도 사용할 수 없습니다 .
Vatsal Manot

1
좋아 ... 나는 열거 형 원시 값으로 사용할 수있는 값의 유형을 나열하는 Apple 문서를 인용했습니다. OP의 질문 인 문자열은 지원되는 유형 중 하나입니다.
Duncan C

1
흠, 상상해보세요 case one = "uno". 이제 "one"열거 형 값 으로 구문 분석하는 방법은 무엇입니까? (그들은 현지화에 사용하고 같은 RAWS를 사용할 수 없습니다)
Agent_L

현지화에 따라 초기화시 원시 문자열을 초기화하거나 단순히 다른 현지화에 대해 각각 다른 열거 형을 가질 수 있습니다. 어쨌든 열거 형을 갖는 모든 목적은 근본적인 원시, 즉 지역화를 추상화하는 것입니다. 좋은 코드 디자인은 "uno"를 매개 변수로 아무데도 전달하지 않고 StringEnum.one에 의존하는 것입니다
SkyWalker

5
= "one"각 경우마다 등 을 쓸 필요가 없습니다 . 기본 문자열 값은 케이스 이름과 동일합니다.
emlai 2016-07-05

38

필요한 것은 다음과 같습니다.

enum Foo: String {
   case a, b, c, d
}

let a = Foo(rawValue: "a")
assert(a == Foo.a)

let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)

이것은 원시 값을 확인하기 때문에 기술적으로 정답이 아닙니다. 주어진 예에서는 원시 값이 지정되지 않았으므로 암시 적으로 케이스 이름과 일치하지만 원시 값이있는 열거 형이있는 경우 중단됩니다.
Mark A. Donohoe

30

Swift 4.2에서 CaseIterable 프로토콜은 rawValues가있는 열거 형에 사용될 수 있지만 문자열은 열거 형 케이스 레이블과 일치해야합니다.

enum MyCode : String, CaseIterable {

    case one   = "uno"
    case two   = "dos"
    case three = "tres"

    static func withLabel(_ label: String) -> MyCode? {
        return self.allCases.first{ "\($0)" == label }
    }
}

용법:

print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)

2
이것은 훌륭한 대답입니다! 실제로 질문을 해결합니다.
Matt Rundle

3
이것은 실제 값이 아닌 케이스 이름 에 관한 OP 요청대로 실제로 작동하는 유일한 답변입니다 . 좋은 대답입니다!
Mark A. Donohoe

1
이것이 작동하는 동안 그것은 매우 어리석은 일입니다. 코드의 케이스 이름에 기반을 두지 마십시오.
Sulthan

7
그는 또 무엇을해야합니까? 그가 데이터베이스에 열거 형을 작성하고 다시 캐스팅해야한다면 어떨까요?
Joe

16

Int 유형이있는 열거 형의 경우 다음과 같이 할 수 있습니다.

enum MenuItem: Int {
    case One = 0, Two, Three, Four, Five //... as much as needs

    static func enumFromString(string:String) -> MenuItem? {
        var i = 0
        while let item = MenuItem(rawValue: i) {
            if String(item) == string { return item }
            i += 1
        }
        return nil
    }
}

그리고 사용 :

let string = "Two"
if let item = MenuItem.enumFromString(string) {
    //in this case item = 1 
    //your code
} 

2
언어에 내장 된 유사한 기능을 사용할 수 없다는 것은 미친 짓입니다. 예를 들어 열거 형 이름으로 JSON에 값을 저장 한 다음 파싱 할 때 값을 다시 변환해야한다고 상상할 수 있습니다. enumFromString사용하는 각 열거 형에 대한 메서드를 작성하는 것은 미친 것 같습니다.
Peterdk 2017 년

1
@Peterdk, 가능한 최선의 대안을 제안하십시오. Igor 솔루션은 실제로 저에게 효과적이었습니다.
Hemang

@Hemang 잘 작동하지만 더 나은 솔루션은 자동으로 수행하는 Swift 지원입니다. 각 열거 형에 대해이 작업을 수동으로 수행하는 것은 미친 짓입니다. 그러나 네, 작동합니다.
Peterdk

@Peterdk, 동일한 답변을 별도로 추가해 주시겠습니까? 여기있는 모든 사람들에게 분명히 도움이 될 것입니다.
Hemang

1
Swift가 기본적으로 지원하지 않는다는 것은 미친 것이 아닙니다. 미친 것은 기능이 유형의 이름에 의존한다는 것입니다. 값이 변경되면 모든 사용을 리팩토링하고 이름을 바꿔야합니다. 이것은 이것을 해결하는 올바른 방법이 아닙니다.
Sulthan

2

Duncan C의 답변 확장

extension StringEnum: StringLiteralConvertible {

    init(stringLiteral value: String){
        self.init(rawValue: value)!
    }

    init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }

    init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }
}

2

Swift 4.2 :

public enum PaymentPlatform: String, CaseIterable {
    case visa = "Visa card"
    case masterCard = "Master card"
    case cod = "Cod"

    var nameEnum: String {
        return Mirror(reflecting: self).children.first?.label ?? String(describing: self)
    }

    func byName(name: String) -> PaymentPlatform {
        return PaymentPlatform.allCases.first(where: {$0.nameEnum.elementsEqual(name)}) ?? .cod
    }
}

2

Int enum 및 해당 문자열 표현의 경우 다음과 같이 enum을 선언합니다.

enum OrderState: Int16, CustomStringConvertible {

    case waiting = 1
    case inKitchen = 2
    case ready = 3

    var description: String {
        switch self {
        case .waiting:
            return "Waiting"
        case .inKitchen:
            return "InKitchen"
        case .ready:
            return "Ready"
        }
    }

    static func initialize(stringValue: String)-> OrderState? {
        switch stringValue {
        case OrderState.waiting.description:
            return OrderState.waiting
        case OrderState.inKitchen.description:
            return OrderState.inKitchen
        case OrderState.ready.description:
            return OrderState.ready

        default:
            return nil
        }
    }
}

용법:

order.orderState = OrderState.waiting.rawValue

let orderState = OrderState.init(rawValue: order.orderState)
let orderStateStr = orderState?.description ?? ""
print("orderStateStr = \(orderStateStr)")

0

djruss70의 답변을 리핑하여 매우 일반화 된 솔루션을 만듭니다.

extension CaseIterable {
    static func from(string: String) -> Self? {
        return Self.allCases.first { string == "\($0)" }
    }
    func toString() -> String { "\(self)" }
}

용법:

enum Chassis: CaseIterable {
    case pieridae, oovidae
}

let chassis: Chassis = Chassis.from(string: "oovidae")!
let string: String = chassis.toString()

참고 : enum이 @objc로 선언되면 안타깝게도 작동하지 않습니다. 내가 Swift 5.3에서 아는 한 무차별 대입 솔루션 (switch 문)을 제외하고 @objc enum과 함께 작동하도록 할 방법이 없습니다.

누군가 @objc 열거 형에 대해이 작업을 수행하는 방법을 알고 있다면 대답에 매우 관심이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.