Swift ENUM how to convert "rawValue" back to Enum case? - swift

I have an enum:
enum switch : String {
case on = "powerOn"
case off = "powerOff"
var japanswitch : String {
case .on : return "onpu"
case .off : return "offu"
}
}
In my code my function passed down "powerOn" as pure string parameter. The problem is some point of my function require to translate "powerOn" to japanswitch.
But problem is "powerOn" right now is nothing more than a mere String which has no relationship to japanswitch at all.
How do I translate "powerOn" to japanswitch? The desired result should be "onpu".

First you need to fix your code so that it compiles:
enum Switch : String {
case on = "powerOn"
case off = "powerOff"
var japanswitch : String {
switch self {
case .on : return "onpu"
case .off : return "offu"
}
}
}
Then you can achieve what you are after using:
let japanese = Switch(rawValue:"powerOn")?.japanswitch
Note that japanese will be an optional; you need to decide how you will handle an invalid raw value.

Keyword 'switch' cannot be used as an identifier so you need to add some another keyword like below:
enum SwitchType : String {
case on = "powerOn"
case off = "powerOff"
var japanswitch : String {
switch self {
case .on : return "onpu"
case .off : return "offu"
}
}
}

Try this one
enum ABC : String {
case on = "powerOn"
case off = "powerOff"
var japanswitch : String {
return self == .on ? "onpu" : "offu"
}
}
var a = ABC(rawValue: "powerOn")
print(a!.japanswitch)

Your question is not very clear, would be helpful if you show us the code you're trying to run using this enum, but I think you can do something like this:
import Foundation
enum SwitchType: String {
case on = "powerOn"
case off = "powerOff"
var japaneseRepresentation : String {
switch (self){
case Toggle.on : return "onpu"
case Toggle.off : return "offu"
default: return Toggle.on.japaneseRepresentation
}
}
init(japanString japanString: String) {
switch(japanString) {
case "onpu": self = Toggle.on
case "offu": self = Toggle.off
default: self = Toggle.on
}
}
}
let a = Toggle(rawValue: "powerOn")
print(a)
let b = Toggle(japanString: "onpu")
print(b)
print(a == b)
The benefit of doing this is that you can have a custom initializer to work with the same enum but initializing it from a Japaneses string.

enum Switch : String {
case on = "powerOn"
case off = "powerOff"
var japanswitch : String {
switch self {
case .on:
return "onpu"
case .off:
return "offu"
}
}}
You cannot use a case without a switch statement. So can try the above snippet. after defining enum as above, as per official Apple doc you can convert a string value to enum as follows.
If you define an enumeration with a raw-value type, the enumeration
automatically receives an initializer that takes a value of the raw
value’s type (as a parameter called rawValue) and returns either an
enumeration case or nil. You can use this initializer to try to create
a new instance of the enumeration.
This example identifies japanswitch from Switch's raw value of "powerOn":
let japanswitchValue = Switch(rawValue: "powerOn")?.japanswitch

Completely different solution, if you are only using the enum for translation then you can replace it with a dictionary
let japaneseTranslation = ["powerOn": "onpu", "powerOff": "offu"]
...
let offJapanese = japaneseTranslation["powerOff"]

Related

Accessing decodable enum after parsing in SwiftUI [duplicate]

In this code I've written a really useless enum that defines a possible Number with Int or Float.
I can't understand how can I access the value that I set with the association. If I try to print it I get just (Enum Value)
enum Number {
case int (Int)
case float (Float)
}
let integer = Number.int(10)
let float = Number.float(10.5)
println("integer is \(integer)")
println("float is \(float)")
For sake of completeness, enum's association value could be accesed also using if statement with pattern matching. Here is solution for original code:
enum Number {
case int (Int)
case float (Float)
}
let integer = Number.int(10)
let float = Number.float(10.5)
if case let .int(i) = integer {
print("integer is \(i)")
}
if case let .float(f) = float {
print("float is \(f)")
}
This solution is described in detail in: https://appventure.me/2015/10/17/advanced-practical-enum-examples/
The value is associated to an instance of the enumeration. Therefore, to access it without a switch, you need to make a getter and make it available explicitly. Something like below:
enum Number {
case int(Int)
case float(Float)
func get() -> NSNumber {
switch self {
case .int(let num):
return num
case .float(let num):
return num
}
}
}
var vInteger = Number.int(10)
var vFloat = Number.float(10.5)
println(vInteger.get())
println(vFloat.get())
Maybe in the future something like that may be automatically created or a shorter convenience could be added to the language.
It surprises me that Swift 2 (as of beta 2) does not address this. Here's an example of a workaround approach for now:
enum TestAssociatedValue {
case One(Int)
case Two(String)
case Three(AnyObject)
func associatedValue() -> Any {
switch self {
case .One(let value):
return value
case .Two(let value):
return value
case .Three(let value):
return value
}
}
}
let one = TestAssociatedValue.One(1)
let oneValue = one.associatedValue() // 1
let two = TestAssociatedValue.Two("two")
let twoValue = two.associatedValue() // two
class ThreeClass {
let someValue = "Hello world!"
}
let three = TestMixed.Three(ThreeClass())
let threeValue = three. associatedValue() as! ThreeClass
print(threeValue.someValue)
If your enum mixes cases with and without associated values, you'll need to make the return type an optional. You could also return literals for some cases (that do not have associated values), mimicking raw-value typed enums. And you could even return the enum value itself for non-associated, non-raw-type cases. For example:
enum TestMixed {
case One(Int)
case Two(String)
case Three(AnyObject)
case Four
case Five
func value() -> Any? {
switch self {
case .One(let value):
return value
case .Two(let value):
return value
case .Three(let value):
return value
case .Four:
return 4
case .Five:
return TestMixed.Five
}
}
}
let one = TestMixed.One(1)
let oneValue = one.value() // 1
let two = TestMixed.Two("two")
let twoValue = two.value() // two
class ThreeClass {
let someValue = "Hello world!"
}
let three = TestMixed.Three(ThreeClass())
let threeValue = three.value() as! ThreeClass
print(threeValue.someValue)
let four = TestMixed.Four
let fourValue = four.value() // 4
let five = TestMixed.Five
let fiveValue = five.value() as! TestMixed
switch fiveValue {
case TestMixed.Five:
print("It is")
default:
print("It's not")
}
// Prints "It is"
like #iQ. answer, you can use property in enum also
enum Number {
case int (Int)
var value: Int {
switch self {
case .int(let value):
return value
}
}
}
let integer = Number.int(10)
println("integer is \(integer.value)")
I have used something like this:
switch number {
case .int(let n):
println("integer is \(n)")
case .float(let n):
println("float is \(n)")
}
If you're using guard, you can write like below:
enum Action {
case .moveTab(index: Int)
}
guard let case .moveTab(index) = someAction else { return }
You can access enum associated value not only through switch! Mirrors come to our aid
Let's create a protocol
protocol MirrorAssociated {
var associatedValues: [String: Any] { get }
}
extension MirrorAssociated {
var associatedValues: [String: Any] {
var values = [String: Any]()
if let associated = Mirror(reflecting: self).children.first {
let children = Mirror(reflecting: associated.value).children
for case let item in children {
if let label = item.label {
values[label] = item.value
}
}
}
return values
}
}
and use it like this:
enum Test: MirrorAssociated {
case test(value: String, anotherValue: Int)
}
Now we can access any associated value without using switch:
let test: Test = .test(value: "Test String", anotherValue: 1337)
if let value = test.associatedValues["value"] as? String {
print("\(value)") // "Test String"
}
if let intValue = test.associatedValues["anotherValue"] as? Int {
print("\(intValue)") // 1337
}
Swift 5
enum Directory {
case accountImages(URL)
case accountData(URL)
var url: URL {
switch self {
case .accountImages(let url):
return url
case .accountData(let url):
return url
}
}
}
func save(to directory: Directory) {
let dir = directory.url
}
Swift 4,
I have created a simple enum with associated values for handling firebase database reference paths
import Firebase
enum FirebaseDBConstants {
case UserLocation(database : DatabaseReference, userID :String)
case UserRequest(database : DatabaseReference, requestID :String)
func getDBPath() -> DatabaseReference {
switch self {
case .UserLocation(let database,let userID):
return database.root.child(FirebaseDBEnvironmentEnum.getCurrentEnvioronMent()).child("Location").child(userID).child("JSON")
case .UserRequest(let database,let requestID):
return database.root.child(FirebaseDBEnvironmentEnum.getCurrentEnvioronMent()).child("Request").child(requestID)
default:
break
}
}
}
Use it like as shown
//Pass Database refenence root as parameter with your request id
let dbPath = FirebaseDBConstants.UserRequest(database: database, requestID: requestId).getDBPath()

How to create Enum for hardcoded string in swift?

I have some hardcoded string, based on that i have created switch case. But instead of string in switch case i am trying to create Enum for same. but i am not sure how to do that. I dont want to use default case.
Do i need to access with raw value or any other better way to do ?
enum screens: String {
case faq = "faq", contactus = "contactus", termncondi = "termncondi", dashoboard = "dashoboard"
}
func deepLink(text: String) -> String {
switch text {
case "faq":
return (FAQ.localized())
case "contactus":
return (Contactus.localized())
case "termncondi":
return (Term.localized())
case "dashoboard":
return (Dashboard.localized())
default:
return ""
}
}
You can simply define the enum and override the rawValue property like so:
enum Link {
case faq
case contact
...
var rawValue: String {
switch self {
case .faq:
return FAQ.localized()
case .contact:
return Contact.localized()
default:
return "Unknown case"
}
}
}
And get its rawValue.
let faqLink = Link.faq.rawValue
You can also perform a switch on an enum instance just like you do with a string.
In order to get hardcoded values do something like:
public extension String {
static func getScreens(name: DeepLink) -> String {
return name.rawValue
}
public enum DeepLink: String {
case faq = "faq"
case contactus = "contactus"
// and so on
}
}

Swift enum getting value of Int Enum using String value

I have an enum thats raw value is Int. I know How to get the raw value. But I am accessing the data through webservice and there data for Enum is coming in String value.
Let me show you my Enum, then we will discuss the data that need to be convert into Enum and then getting the raw value.
My Enum:
enum ReportStatus : Int {
case None = 0
case Received = 2
case Forward = 9
case Reporting = 14
case Completed = 50
}
and the data I am getting from webservice is sending me value in
string like so
var valueToCompareWithEnum = "Forward"
what I want:
I want to match that string value and to get the resultant int value.
For example as I am receiving "Forward" So I must convert it in enum
and getting the 9 Int value.
I know I can make switch case for it but I wanted to know if it can be handled with more clean way.....
Thanks in advance please help.
You can let your enum implement CaseIterable and then compare your string to the string representation of each enum case using Array.first(where:)
enum ReportStatus : Int, CaseIterable {
case None = 0
case Received = 2
case Forward = 9
case Reporting = 14
case Completed = 50
static func translate(_ string: String) -> Int? {
return ReportStatus.allCases.first(where: {string == "\($0)"})?.rawValue
}
}
As a variant here is a global generic function that does the same but it returns the enum case rather than the raw value.
func translate<T: CaseIterable>(_ string: String, forEnum: T.Type) -> T? {
return forEnum.allCases.first(where: {string == "\($0)"})
}
Usage
if let item = translate(valueToCompareWithEnum, forEnum: ReportStatus.self) {
print(item.rawValue)
}
You can code like this;
enum ReportStatus : Int {
case None = 0
case Received = 2
case Forward = 9
case Reporting = 14
case Completed = 50
static func getRawValue(from value : String) -> Int {
switch value {
case "Forward":
return self.Forward.rawValue
case "Received":
return self.Received.rawValue
case "Reporting":
return self.Reporting.rawValue
case "Completed":
return self.Completed.rawValue
default:
return self.None.rawValue
}
}
}
var valueToCompareWithEnum = "Forward"
let myVal = ReportStatus.getRawValue(from: valueToCompareWithEnum)
print(myVal) // 9
Since you're receiving these strings from a server, I assume you mean you're receiving them encoded in JSON. If so, then you likely want them to be Decodable. In that case, you can implement a custom decoder.
Before I show that, I'm going to rename your enum cases to match Swift style, which is a lowercase letter. I'm doing this to make the problem slightly harder and show more techniques you can use.
enum ReportStatus : Int {
case none = 0
case received = 2
case forward = 9
case reporting = 14
case completed = 50
}
Here's the most straightforward approach:
extension ReportStatus: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let stringValue = try container.decode(String.self)
switch stringValue {
case "None": self = .none
case "Received": self = .received
case "Forward": self = .forward
case "Reporting": self = .reporting
case "Completed": self = .completed
default:
throw DecodingError
.valueNotFound(Self.self,
.init(codingPath: decoder.codingPath,
debugDescription: "Unknown value for report status: \(stringValue)"))
}
}
}
This is a very good technique, but since our case names do match the server's keys closely (just not perfectly), we could fix that up by lowercasing the key:
enum ReportStatus : Int, CaseIterable { // Note CaseIterable
case none = 0
case received = 2
case forward = 9
case reporting = 14
case completed = 50
}
extension ReportStatus: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let stringValue = try container.decode(String.self)
guard let value = Self.allCases.first(where: { String(describing: $0) == stringValue.lowercased() })
else { throw DecodingError
.valueNotFound(Self.self,
.init(codingPath: decoder.codingPath,
debugDescription: "Unknown value for report status: \(stringValue)"))
}
self = value
}
}
I recommend you switch Int enum to String to make it easer to parse and add one more field to get int value for each case:
enum ReportStatus: String {
case none = "None"
case received = "Received"
case forward = "Forward"
case reporting = "Reporting"
case completed = "Completed"
var intValue: Int {
switch self {
case .none : return 0
case .received : return 2
case .forward : return 9
case .reporting : return 14
case .completed : return 50
}
}
}
let valueToCompareWithEnum = "Forward"
if let myVal = ReportStatus(rawValue: valueToCompareWithEnum) {
print(myVal.intValue) // 9
}
Because that data from web in String format it's kindly recommended parse it to String enum case and then use you'r own Int value where you need.

Decode string or int value in structure will return string("1") in Swift 4

I have a structure that some of the values will get Integer or string so I used this structure in my structure to parse JSON in the correct way but the problem is that when I want to print the value it will print string("1") instead of 1.
public struct player1_result_sheet : Decodable {
let ans_1 : QuantumValue?
let ans_2 : QuantumValue?
let ans_3 : QuantumValue?
let ans_4 : QuantumValue?
}
enum QuantumValue: Decodable {
case int(Int), string(String)
init(from decoder: Decoder) throws {
if let int = try? decoder.singleValueContainer().decode(Int.self) {
self = .int(int)
return
}
if let string = try? decoder.singleValueContainer().decode(String.self) {
self = .string(string)
return
}
throw QuantumError.missingValue
}
enum QuantumError:Error {
case missingValue
}
}
Here is the printing after decode:
(self.res?.response?.detailData?[indexPath.row].player1_result_sheet?.ans_1!)!
If you want to print the value without the enum wrapper, just implement description:
extension QuantumValue: CustomStringConvertible {
public var description: String {
switch self {
case let .string(string):
return string
case let .int(number):
return "\(number)"
}
}
}
Your enum case is string(String) so it prints case(value)
string("1")
You can solve it by creating varible inside enum which returns you a value depends on case of QuantumValue
var value: Any {
switch self {
case .int(let value):
return value
case .string(let value):
return value
}
}
then you can use it like this:
...ans_1!.value)!
1
Also note that type of value is Any so if you want to work with it as String, you have to downcast it
if let string = ...ans_1.value)! as? String {
...
}
QuantumValue is declared as enum, both good cases have associated values.
So printing a value prints both, the case and the associated value.
You could add two properties intValue and stringValue inside QuantumValue
var intValue : Int? {
guard case .int(let num) = self else { return nil }
return num
}
var stringValue : String? {
guard case .string(let string) = self else { return nil }
return string
}
Then you can print
player1_result_sheet?.ans_1?.intValue
By the way the name player1_result_sheet is pretty php-ish.
Please conform to the naming convention.
Structs, classes and enums are UpperCamelCased → Player1ResultSheet
Variables and functions are lowerCamelCased → ans1
And please consolidate your optionals, 6 (six) question and exclamation marks in one line is pretty weird
(self.res?.response?.detailData?[indexPath.row].player1_result_sheet?.ans_1!)!

In Swift, is it possible to convert a string to an enum?

If I have an enum with the cases a,b,c,d is it possible for me to cast the string "a" as the enum?
Sure. Enums can have a raw value. To quote the docs:
Raw values can be strings, characters, or any of the integer or
floating-point number types
— Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l,
So you can use code like this:
enum StringEnum: String
{
case one = "value one"
case two = "value two"
case three = "value three"
}
let anEnum = StringEnum(rawValue: "value one")!
print("anEnum = \"\(anEnum.rawValue)\"")
Note: You don't need to write = "one" etc. after each case. The default string values are the same as the case names so calling .rawValue will just return a string
EDIT
If you need the string value to contain things like spaces that are not valid as part of a case value then you need to explicitly set the string. So,
enum StringEnum: String
{
case one
case two
case three
}
let anEnum = StringEnum.one
print("anEnum = \"\(anEnum)\"")
gives
anEnum = "one"
But if you want case one to display "value one" you will need to provide the string values:
enum StringEnum: String
{
case one = "value one"
case two = "value two"
case three = "value three"
}
All you need is:
enum Foo: String {
case a, b, c, d
}
let a = Foo(rawValue: "a")
assert(a == Foo.a)
let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)
In Swift 4.2, the CaseIterable protocol can be used for an enum with rawValues, but the string should match against the enum case labels:
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 }
}
}
usage:
print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno")) // Optional(MyCode.one)
In case with an enum with Int type you can do it so:
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
}
}
And use:
let string = "Two"
if let item = MenuItem.enumFromString(string) {
//in this case item = 1
//your code
}
Riffing on djruss70's answer to create highly generalized solution:
extension CaseIterable {
static func from(string: String) -> Self? {
return Self.allCases.first { string == "\($0)" }
}
func toString() -> String { "\(self)" }
}
Usage:
enum Chassis: CaseIterable {
case pieridae, oovidae
}
let chassis: Chassis = Chassis.from(string: "oovidae")!
let string: String = chassis.toString()
Note: this will unfortunately not work if the enum is declared #objc. As far as I know as of Swift 5.3 there is no way to get this to work with #objc enum's except brute force solutions (a switch statement).
If someone happens to know of a way to make this work for #objc enums, I'd be very interested in the answer.
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
}
}
Extending Duncan C's answer
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)
}
}
For Int enum and their String representation, I declare enum as follows:
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
}
}
}
Usage:
order.orderState = OrderState.waiting.rawValue
let orderState = OrderState.init(rawValue: order.orderState)
let orderStateStr = orderState?.description ?? ""
print("orderStateStr = \(orderStateStr)")
I used this:
public enum Currency: CaseIterable, Codable {
case AFN = 971 // Afghani (minor=2)
case DZD = 012 // Algerian Dinar (minor=2)
...
private static var cachedLookup: [String: Currency] = [:]
init?(string: String) {
if Self.cachedLookup.isEmpty {
Self.cachedLookup = Dictionary(uniqueKeysWithValues: Self.allCases.map { ("\($0)", $0) })
}
if let currency = Self.cachedLookup[string] {
self = currency
return
} else {
return nil
}
}
}
I found the other answers make this way more complicated then it needs to be. Here is a quick and concise example.
enum Gender: String {
case male, female, unspecified
}
Simple enum, note that I added ": String" to the enum itself to declare the type as string.
Now all you have to do is:
let example: Gender = Gender(rawValue: "male")
And thats it, 'example' is now an enum of type Gender with the value of .male
There is literally nothing else you need to do in Swift 4+.