Di Swift, apakah mungkin untuk mengonversi string menjadi enum?

96

Jika saya memiliki enum dengan case a, b, c, d apakah mungkin bagi saya untuk mentransmisikan string "a" sebagai enum?

rmaddy
sumber
3
Para 'pemeran' ini disebut konversi literal.
Vatsal Manot

Jawaban:

141

Tentu. Enum bisa memiliki nilai mentah. Mengutip dokumen:

Nilai mentah dapat berupa string, karakter, atau salah satu jenis bilangan bulat atau floating-point

- Kutipan Dari: Apple Inc. "The Swift Programming Language." iBooks. https://itun.es/us/jEUH0.l ,

Jadi Anda bisa menggunakan kode seperti ini:

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

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

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

Catatan: Anda tidak perlu menulis = "one" dll setelah setiap kasus. Nilai string default sama dengan nama kasus sehingga pemanggilan .rawValuehanya akan mengembalikan string

EDIT

Jika Anda memerlukan nilai string untuk memuat hal-hal seperti spasi yang tidak valid sebagai bagian dari nilai kasus, Anda perlu menyetel string secara eksplisit. Begitu,

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

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

memberi

anEnum = "satu"

Tetapi jika Anda ingin case onemenampilkan "nilai satu", Anda perlu memberikan nilai string:

enum StringEnum: String 
{
  case one   = "value one"
  case two   = "value two"
  case three = "value three"
}
Duncan C
sumber
Nilai mentah harus dapat dikonversi secara literal. Anda tidak dapat menggunakan sembarang Hashablejenis.
Vatsal Manot
1
Oke ... Saya mengutip dokumen Apple, yang mencantumkan jenis nilai yang dapat digunakan sebagai nilai mentah enum. String, pertanyaan OP, adalah salah satu jenis yang didukung.
Duncan C
1
Hmm, bayangkan case one = "uno". Sekarang, bagaimana cara mengurai "one"ke nilai enum? (tidak dapat menggunakan bahan mentah, karena digunakan untuk pelokalan)
Agent_L
Mungkin Anda dapat menginisialisasi String mentah saat inisialisasi bergantung pada pelokalannya ... atau cukup memiliki enum yang berbeda untuk pelokalan yang berbeda. Dalam kasus apapun, seluruh tujuan memiliki enum adalah untuk mengabstraksi raw yang mendasari yaitu lokalisasi. Desain kode yang baik tidak akan meneruskan "uno" sebagai parameter di mana pun, tetapi mengandalkan StringEnum.one
SkyWalker
5
Anda tidak perlu menulis = "one"dll setelah setiap kasus. Nilai string default sama dengan nama kasus.
emlai
38

Yang kamu butuhkan adalah:

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

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

let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)
emlai
sumber
Ini bukanlah jawaban yang benar secara teknis karena ini memeriksa nilai mentahnya. Dalam contoh di sini seperti yang diberikan, tidak ada nilai mentah yang ditentukan, sehingga secara implisit dicocokkan dengan nama kasus, tetapi jika Anda memiliki enum dengan nilai mentah, ini akan rusak.
Mark A. Donohoe
30

Di Swift 4.2, protokol CaseIterable dapat digunakan untuk enum dengan rawValues, tetapi stringnya harus cocok dengan label enum case:

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

pemakaian:

print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)
djruss70.dll
sumber
2
Ini jawaban yang bagus! Ini benar-benar menjawab pertanyaan itu.
Matt Rundle
3
Ini adalah satu-satunya jawaban yang benar-benar berfungsi seperti yang diminta OP, yaitu tentang nama kasus, bukan nilai mentah. Jawaban yang bagus!
Mark A. Donohoe
1
Sementara ini berhasil, itu hal yang sangat konyol untuk dilakukan. Tolong jangan mendasarkan fungsionalitas pada nama kasus dalam kode.
Sulthan
7
Apa lagi yang harus dia lakukan? Bagaimana jika dia menulis enum ke database dan kemudian perlu membuangnya kembali?
Joe
16

Dalam kasus dengan enum dengan tipe Int Anda dapat melakukannya:

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

Dan gunakan:

let string = "Two"
if let item = MenuItem.enumFromString(string) {
    //in this case item = 1 
    //your code
} 
Igor
sumber
2
Sungguh gila Anda tidak bisa hanya menggunakan fungsionalitas serupa yang ada di dalam bahasa. Saya dapat membayangkan Anda menyimpan nilai di JSON misalnya dengan nama enum, dan kemudian mengurai perlu mengubahnya kembali. Menulis enumFromStringmetode untuk setiap enum yang Anda gunakan sepertinya gila.
Peterdk
1
@Peterdk, mohon sarankan alternatif terbaik. Solusi Igor sebenarnya hanya berhasil untuk saya.
Hemang
@Hemang Ini berfungsi dengan baik, baiklah, tetapi solusi yang lebih baik adalah dukungan Swift untuk melakukan ini secara otomatis. Sangat gila untuk melakukan ini secara manual untuk setiap enum. Tapi ya, ini berhasil.
Peterdk
@Peterdk, dapatkah Anda menambahkan jawaban terpisah untuk hal yang sama? Itu pasti akan membantu semua orang di sini.
Hemang
1
Tidaklah gila bahwa Swift tidak mendukungnya secara asli. Hal yang gila adalah fungsionalitasnya bergantung pada nama suatu tipe. Ketika nilainya berubah, Anda harus memfaktor ulang dan mengganti nama semua penggunaan. Ini bukanlah cara yang benar untuk menyelesaikan masalah ini.
Sulthan
2

Memperluas jawaban 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)
    }
}
gujci
sumber
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
    }
}
Tuan Boyfox
sumber
2

Untuk Int enum dan representasi Stringnya, saya mendeklarasikan enum sebagai berikut:

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

Pemakaian:

order.orderState = OrderState.waiting.rawValue

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

Mengolah jawaban djruss70 untuk membuat solusi yang sangat umum:

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

Pemakaian:

enum Chassis: CaseIterable {
    case pieridae, oovidae
}

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

Catatan: sayangnya ini tidak akan berfungsi jika enum dideklarasikan @objc. Sejauh yang saya tahu tentang Swift 5.3, tidak ada cara untuk membuat ini bekerja dengan @objc enum kecuali solusi brute force (pernyataan switch).

Jika seseorang mengetahui cara untuk membuat ini bekerja untuk @objc enum, saya akan sangat tertarik dengan jawabannya.

aepryus
sumber