Return templet string from the selected country code - swift

I would like to get the pre-define temple name based on the country selection. Here I'm trying some code, but unable to get that from code. How do I get back messages based on the country code input?
enum Descriptor: String, CaseIterable, CustomStringConvertible {
case fr = "FR"
case jp = "JP"
var description: String {
get {
return self.rawValue
}
}
var mesage : String {
let templet = "Welcome to "
switch self {
case .fr:
return templet + "France"
case .jp:
return templet + "Japan"
}
}
}
extension Descriptor {
static func hasCountry(code: String) -> String? {
return Descriptor.allCases
.map({$0.rawValue})
.first(where: {$0.description == code})
}
}
let x = Descriptor.hasCountry(code: "JP")
print(x)
// Expected output is like
// Welcome to Japan
// or
// Welcome to France

extension Descriptor {
static func hasCountry(code: String) -> String? {
return Descriptor.allCases
.first(where: {$0.description == code})?
.mesage
}
}
You have a tiny bit of mistake in your extension.
So when you do .map({$0.rawValue}), you actually transform all your enum cases to a strings array ["FR", "JP"].
What you actually want to be doing is find your first enum case, and call .mesage on that one.

Related

Refactoring two similar swift switch statements

I have a couple of switch statements where I initially convert a some JSON values to a user-friendly name (convertCategoryValueToName).
Later on, in a different part of the app, I need to undertake some checks based on user-friendly name, and I have written a corresponding switch to convert the name back to it's original value (convertCategoryNameToValue).
The switch statements are long, and I'm not happy with the repetition. Is there a way to refactor this work into one Switch?
Shortened switch examples...
func convertCategoryValueToName(category: String) -> String? {
var categoryName: String?
switch category {
case "dessert":
categoryName = "Desserts"
case "drink":
categoryName = "Drinks"
default:
break
}
return categoryName
}
func convertCategoryNameToValue(category: String) -> String? {
var categoryValue: String?
switch category {
case "Desserts":
categoryValue = "dessert"
case "Drinks":
categoryValue = "drink"
default:
break
}
return categoryValue
}
You should simply use an enum.
enum Category: String {
case dessert = "Desserts"
case drink = "Drinks"
}
Then use Category.dessert.rawValue to display the "user-friendly name" on the UI and use the enum cases for everything else in your code.
This will do:
let dict = ["dessert": "Desserts",
"drink" : "Drinks"]
func convertCategoryValueToName(category: String) -> String? {
return dict[category]
}
func convertCategoryNameToValue(category: String) -> String? {
return dict.keys.first(where: { $0 == category})
}

Swift: assign to variable in switch case-let-where

Is it possible to make an assignment to the artist variable before it is used in the where subclause?
var artist
switch fullline {
case let path where path.hasPrefix("Monet"):
artist = "Monet"
case let path where path.hasPrefix("Cezanne"):
artist = "Cezanne"
default: ()
}
Closure:
case let path where { () -> Bool in let artist = "Monet"; return path.hasPrefix(artist) }:
Error:
() -> Bool' is not convertible to 'Bool'
Context:
I have lines of freeform text with artist name as the prefix that requires
massaging to output consistent humanly readable text. e.g.
Monet : Snow at Argenteuil 02, 1874
Monet - Snow at Argenteuil, 1874, 3rd Floor Collections
Monet, Claude - 1875, Snow in Argenteuil
Cezzane - Vase of Flowers, 1880-81, print
Cezzane, Paul 1900-1903 Vase of Flowers
Cezzane - Vase with Flowers, 1895-1896
There will be a code fragments that performs detailed processing/categorizing
for each artist. Hence the processing logic is artist dependent.
I would like to define similar to the following construct
switch fullline
hasPrefix(artist = "Monet")
-> code logic 1
get_birthday(artist)
hasPrefix(artist = "Cezzane")
-> code logic 2
get_birthday(artist)
With a little modification to the Alexander's struct, you can write something like this:
struct PrefixMatcherWithHandler {
var handler: (String)->Void
var string: String
init(_ string: String, handler: #escaping (String)->Void) {
self.string = string
self.handler = handler
}
static func ~= (prefix: String, matcher: PrefixMatcherWithHandler) -> Bool {
if matcher.string.hasPrefix(prefix) {
matcher.handler(prefix)
return true
} else {
return false
}
}
}
var fullline: String = "Monet, Claude"
var artist: String? = nil
let matcher = PrefixMatcherWithHandler(fullline) {str in
artist = str
}
switch matcher {
case "Monet":
break
case "Cezanne":
break
default: break
}
print(artist ?? "") //->Monet
But having some side-effect in boolean operators like ~= makes your code less readable and can easily make unexpected result.
If you just want to reduce some redundant reference to a same thing, switch-statement may not be a good tool for it.
For example, you can get the same result without defining specific matcher types:
var fullline: String = "Monet, Claude"
var artist: String? = nil
if let match = ["Monet", "Cezanne"].first(where: {fullline.hasPrefix($0)}) {
artist = match
}
print(artist ?? "") //->Monet
ADDED for updated parts of the question
The following code behaves slightly different than prefix-matching, but I believe you do not want to match "Mon" to the line Monet, Claude - 1875, Snow in Argenteuil.
extension String {
var firstWord: String? {
var result: String? = nil
enumerateSubstrings(in: startIndex..<endIndex, options: .byWords) {str, _, _, stop in
result = str
stop = true
}
return result
}
}
func get_birthday(_ artist: String) {
//What do you want to do?
print(artist)
}
var fullline: String = "Monet, Claude - 1875, Snow in Argenteuil"
switch fullline.firstWord {
case let artist? where artist == "Monet":
//code dedicated for "Monet"
get_birthday(artist)
case let artist? where artist == "Cezanne":
//code dedicated for "Cezanne"
get_birthday(artist)
default:
break
}
When you can retrieve data suitable for switch-statement, the code would be far more intuitive and readable.
You're giving that closure where a boolean is expected. Not sure why you would want to do this, but you could make it work by using () to invoke the closure.
var artist
switch fullline {
case let path where { () -> Bool in let artist = "Monet"; return path.hasPrefix(artist) }():
artist = "Monet"
case let path where path.hasPrefix("Cezanne"):
artist = "Cezanne"
default: ()
}
Here is how I would do this:
import Foundation
struct PrefixMatcher {
let string: String
init(_ string: String) { self.string = string }
static func ~= (prefix: String, matcher: PrefixMatcher) -> Bool {
return matcher.string.hasPrefix(prefix)
}
}
extension String {
var prefix: PrefixMatcher { return PrefixMatcher(self) }
}
let fullline = "Monet 123456789"
let artist: String?
switch fullline.prefix {
case "Monet": artist = "Monet"
case "Cezanne": artist = "Cezanne"
default: artist = nil
}
print(artist as Any)
More general solution:
import Foundation
struct PredicateMatcher<Pattern> {
typealias Predicate = (Pattern) -> Bool
let predicate: Predicate
static func ~=(pattern: Pattern,
matcher: PredicateMatcher<Pattern>) -> Bool {
return matcher.predicate(pattern)
}
}
extension String {
var prefix: PredicateMatcher<String> {
return PredicateMatcher(predicate: self.hasPrefix)
}
}
You can achieve this by switching over a tuple of your enum and your optional.
Optional is an enum too, so you can switch both of them
enum SomeSnum {
case a, b, c
}
let someString: String? = "something"
let esomeEnum = SomeSnum.b
switch(esomeEnum, someString) {
case (.b, .some(let unwrappedSomething)) where unwrappedSomething.hasPrefix("so"):
print("case .b, \(unwrappedSomething) is unwrapped, and it has `so` prefix")
case (.a, .none):
print("case .a, and optional is nil")
default:
print("Something else")
}
You can also do an if statement
if case let (.b, .some(unwrappedSomething)) = (esomeEnum, someString), unwrappedSomething.hasPrefix("so") {
} else if case (.a, .none) = (esomeEnum, someString) {
} else {
}

Get index of enum with extension of String,

I have an Enum that looks like this:
enum Status: String {
case online = "online"
case offline = "offline"
case na = "na"
}
I need the String value and I know how to get it, but my question is if it´s possible to get the index value too for each case in the enum.
0 for online, 1 for offline and 2 for na.
I will add more statues in the future.
-------- UPDATE -------
Since swift 4.2 you can do following:
enum Status: String, CaseIterable {
case online
case offline
case na
}
extension CaseIterable where Self: Equatable {
var index: Self.AllCases.Index? {
return Self.allCases.index { self == $0 }
}
}
or as I wrote earlier:
enum Status: Int {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var value: String {
return String(describing: self)
}
}
-------- ORIGIN ANSWER -------
I'm using this extension:
protocol EnumIterable: RawRepresentable {
static var allValues: [Self] { get }
var index: Int? { get }
}
extension EnumIterable {
static var count: Int {
return allValues.count
}
}
extension EnumIterable where Self.RawValue: Equatable {
var next: Self? {
if let index = Self.allValues.index(where: { rawValue == $0.rawValue }) {
return Self.allValues[safe: index + 1]
}
return nil
}
var index: Int? {
return Self.allValues.index { rawValue == $0.rawValue }
}
}
But you would define allValues variable:
enum Status: String, EnumIterable {
case online = "online"
case offline = "offline"
case na = "na"
static var allValues: [Status] {
return [
.online,
.offline,
.na,
]
}
}
Something similar was solved here (count of enumerations):
How do I get the count of a Swift enum?
Next possibility is to define enum like this:
enum Status: Int {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var value: String {
return String(describing: self)
}
}
print (Status.online.value) // online
print (Status.online.index) // 0
or
enum Status: Int {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var value: String {
switch self {
case .online:
return "online"
case .offline:
return "offline"
case .na:
return "na"
}
}
}
print (Status.online.value) // online
print (Status.online.index) // 0
Note: for defining string value, you can use CustomStringConvertible protocol.
Eg:
enum Status: Int, CustomStringConvertible {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var description: String {
switch self {
case .online:
return "online"
case .offline:
return "offline"
case .na:
return "na"
}
}
}
Great answer of #JMI in Swift 5.
enum MyEnum: CaseIterable {
case one
case two
}
extension CaseIterable where Self: Equatable {
var index: Self.AllCases.Index? {
return Self.allCases.firstIndex { self == $0 }
}
}
How to use:
guard let enumCaseIndex = MyEnum.one.index else { return }
print("Enum case index: ", \(enumCaseIndex)) // prints: 0
As enum in Swift does not have index of its values (please read the post in Martin R's comment), you have to create your self some 'index' function or to map all values to an Array to have the index.
You can implement as in this post or another way to do:
enum Status: String {
case online = "online"
case offline = "offline"
case na = "na"
static func index(of aStatus: Status) -> Int {
let elements = [Status.online, Status.offline, Status.na]
return elements.index(of: aStatus)!
}
static func element(at index: Int) -> Status? {
let elements = [Status.online, Status.offline, Status.na]
if index >= 0 && index < elements.count {
return elements[index]
} else {
return nil
}
}
}
let a = Status.na
//return 2
let index = Status.index(of: a)
//return Status.offline
let element2 = Status.element(at: 1)
//return nil
let element3 = Status.element(at: 3)
I did use a solution witch is almost the same than santhosh-shettigar:
func toInt() -> Int {
let allValues: NSArray = MyEnum.allCases as NSArray
let result: Int = allValues.index(of: self)
return result
}
Simple! I use the Swift built-in MyEnum.allCases, ...but I'm not sure that in Swift Specification, we have the guaranty that the Array return by MyEnum.allCases is always in the same order, the one used at the MyEnum definition???
How about this.
enum Status: Int {
case online = 0
case offline = 1
case na = 2
}
You can get the string value and the integer index both.
// enum as string
let enumName = "\(Status.online)" // `online`
// enum as int value
let enumValue = Status.online.rawValue // 0
// enum from int
let enumm = Status.init(rawValue: 1)
Hope it helps. Thanks.
Possible work around may to associate custom functions with enum
enum ToolbarType : String{
case Case = "Case", View="View", Information="Information"
static let allValues = [Case, View, Information]
func ordinal() -> Int{
return ToolbarType.allValues.index(of: self)!
}
}
Can be used as
for item in ToolbarType.allValues {
print("\(item.rawValue): \(item.ordinal())")
}
Output
Case: 0
View: 1
Information: 2

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+.

Accessing Class Variables by enum

I have following code which basically defines enum for different companies and different methods for each company.
protocol Fetcher {
func getDetail()
static var idRegex:String {get}
}
class FooFetcher:Fetcher {
static var idRegex = "(^\\d{5}$)"
var company = Company.Foo
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Foo Get detail")
}
}
class BarFetcher:Fetcher {
static var idRegex = "(^\\d{11}$)"
var company = Company.Bar
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Bar Get detail")
}
}
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> AnyObject {
switch self {
case Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
var compList = [Company.Foo , Company.Bar]
How can I get idRegex by using compList array only? I don't want to create another array like [FooFetcher.idRegex,BarFetcher.idRegex] because it will bloat the file. I want to use enum because it is easy to store their rawValue with Core Data.
I also wonder how to write better fetcher function for Company enum. Currently I use fetcher function like below
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id") as! Fetcher
strategy.getDetail()
AnyObject does not support Fetcher, in the company.fetcher function instead of returning AnyObject is -> Fetcher
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher {
switch self {
case .Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
FOO also there corresponds almost .foo
Along with eliasRuizHz's answer, add a new regex method to the Company enum.
func regex() -> String {
switch self {
case .Foo:
return FooFetcher.idRegex
case .Bar:
return BarFetcher.idRegex
}
}
The two responses both provide good contributions. For example, returning a Fetcher rather than AnyObject makes complete sense, as does a regex() function. The thing that I query, however, is whether you could use enums with associated values rather than this enum + class approach, but putting that to one side and answering the question in hand if you really want to directly access the .idRegex from an instance then it needs to be a regular instance variable not a class variable. One way to have it both ways is to return the type variable through an instance variable like so:
protocol Fetcher {
func getDetail()
var idRegex:String {get}
static var regex:String {get}
}
class FooFetcher:Fetcher {
static var regex = "(^\\d{5}$)"
var idRegex:String { return FooFetcher.regex }
var company = Company.Foo
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Foo Get detail")
}
}
class BarFetcher:Fetcher {
static var regex = "(^\\d{11}$)"
var idRegex:String { return BarFetcher.regex }
var company = Company.Bar
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Bar Get detail")
}
}
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher {
switch self {
case Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
var compList = [Company.Foo, Company.Bar]
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id")
strategy.getDetail()
strategy.idRegex
Edit Responding to Query
If you want the fetcher to not necessarily initialize an instance then set whatever requirement there is to determine whether a value or nil is returned.
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher? {
switch self {
case Foo:
if FooFetcher.regex == "(^\\d{11}$)" {
return FooFetcher(name: name, id: id)}
else {return nil}
case .Bar:
if BarFetcher.regex == "(^\\d{11}$)" {
return BarFetcher(name: name, id: id)
}
else {return nil}
}
}
}
var compList = [Company.Foo, Company.Bar]
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id")
strategy?.getDetail()
strategy?.idRegex