Kotlin enum classes in Swift - swift

I would like to use this Kotiln code in Swift, but I don't know how to get the best and clean solution:
enum class ProType(val gCode: String, val cCode: String) {
FUND("FN", "PP"),
STOCK("VA", "")
}

Technically #esemusa answer is right. But if you have more than ~5 values in enum, you end up with difficult to maintain giant switch statements for every property.
So for cases like that I prefer to do this:
struct ProTypeItem {
var gCode: String
var cCode: String
}
struct ProType {
static let fund = ProTypeItem(gCode: "FN", cCode: "PP")
static let stock = ProTypeItem(gCode: "VA", cCode: "")
}
And you use it simply as ProType.stock, ProType.fund.gCode etc
You can also make ProTypeItem Comparable, Equatable etc.

should be like this:
enum ProType {
case fund
case stock
var gCode: String {
switch self {
case .fund:
return "FN"
case .stock:
return "VA"
}
}
var cCode: String {
switch self {
case .fund:
return "PP"
case .stock:
return ""
}
}
}

Related

SOLVED - Swift Enum - Casting Nested Enums to String Enum to allow .rawValue

SOLVED
Thank you #New Dev and #Joakim Danielson for your help. I used #Joakim Danielson's answer to improve my code.
I have an extension method to assign accessibilityIdentifiers to views based on a given String Enum. I updated the method to directly accept String Enum Cases as a parameter, thus COMPLETELY eliminating the need for the AccessibilityId enum class as shown below, awesome!
Changes
Before:
.accessibility(identifier: .home(.clickButton))
// Simplified for StackOverflow.
// Imagine 20 more cases..
enum AccessibilityId {
case home(HomeId)
var rawValue: String {
switch self {
case .home(let id):
return id.rawValue
}
}
}
extension View {
func accessibility(identifier: AccessibilityId) -> ModifiedContent<Self, AccessibilityAttachmentModifier> {
self.accessibility(identifier: identifier.rawValue)
}
}
After:
.accessibility(identifier: HomeId.clickButton)
extension View {
func accessibility<T: RawRepresentable>(identifier: T) -> ModifiedContent<Self, AccessibilityAttachmentModifier> where T.RawValue == String {
self.accessibility(identifier: identifier.rawValue)
}
}
---------------------------------------------------------------
Original Question
What I have
enum Item {
case animal(AnimalId)
case vehicle(VehicleId)
case food(FoodId)
var rawValue: String {
switch self {
case .animal(let id):
return id.rawValue
case .vehicle(let id):
return id.rawValue
case .food(let id):
return id.rawValue
}
}
}
enum AnimalId: String {
case cat
case dog
}
// etc.
// Imagine more cases and more enums.
What I want
enum Item {
case animal(AnimalId)
case vehicle(VehicleId)
case food(FoodId)
var rawValue: String {
switch self {
case self as StringEnum:
return id.rawValue
default:
return ""
}
}
}
Usage
func test() {
foo(.animal(.cat))
foo(.vehicle(.plane))
foo(.food(.tacos))
}
func foo(_ item: Item) {
print(item.rawValue)
}
I am happy with the usage, but I'd like to reduce the amount of duplicate cases in the given switch statement. Notice how they all have return id.rawValue. The above is just an example, in reality I have around 30 cases.
My Question
Is there a way for me to catch all Nested String Enums in a single switch or let case to reduce the duplicate code I have to write without losing the intended usage?
Thank you for your efforts, I hope to find an improvement for my code!
Here is a solution that is not based on Item being an enum but instead a generic struct
struct Item<T: RawRepresentable> where T.RawValue == String {
let thing: T
var rawValue: String {
thing.rawValue
}
}
With this solution you don't need to change your other enums.
Example
let item1 = Item(thing: AnimalId.cat)
let item2 = Item(thing: VehicleId.car)
print(item1.rawValue, item2.rawValue)
outputs
cat car
You need something common between all these associated values, like a conformance to a shared protocol, e.g. protocol RawStringValue:
protocol RawStringValue {
var rawValue: String { get }
}
// String enums already conform without any extra implementation
extension AnimalId: RawStringValue {}
extension VehicleId: RawStringValue {}
extension FoodId: RawStringValue {}
Then you could create a switch self inside like so:
var rawValue: String {
switch self {
case .animal (let id as RawStringValue),
.vehicle (let id as RawStringValue),
.food (let id as RawStringValue):
return id.rawValue
}
}
That being said, enum with associated values isn't the most convenient type to work with, so be sure that it's the right choice.

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

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

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

How should I implement Default Associated Values with Swift Enums?

Swift question is there a way of having an enum type with one case that can have an associated value.
I have an API that gives me available filters, it's unlikely but possible that the API will add additional filter types. So if the API sends an unknown filter type I want to keep that information associated with the enum.
Below are the different ideas I had about doing this.
My first two tries didn't compile. My third try just feels a bit clunky.
Does anyone have a better way of doing this? Do you think I just shouldn't use an enum for this problem?
typealias APIFilterIdentifier = String
/* Does Not Compile */
enum EnumTestAssociatedValeu: APIFilterIdentifier {
case Unknown(APIFilterIdentifier)
case Everyone = "everyone"
case Team = "myteam"
}
/* Does not compile */
enum EnumTestDefaultAssociatedValues: APIFilterIdentifier {
case Unknown(APIFilterIdentifier)
case Everyone(APIFilterIdentifier = "everyone")
case Team(APIFilterIdentifier = "myteam")
}
/* Compiles but is there a better way? */
enum EnumTestWithCustomInit {
case Unknown(APIFilterIdentifier)
case Everyone
case Team
init(filterIdentifier: APIFilterIdentifier) {
let everyone: APIFilterIdentifier = EnumTestWithCustomInit.everyoneFilterIdentifier
let team: APIFilterIdentifier = EnumTestWithCustomInit.teamFilterIdentifier
switch filterIdentifier {
case everyone:
self = .Everyone
case team:
self = .Team
default:
self = .Unknown(filterIdentifier)
}
}
func asIdentifer() -> APIFilterIdentifier {
switch self {
case .Everyone:
return EnumTestWithCustomInit.everyoneFilterIdentifier
case .Team:
return EnumTestWithCustomInit.teamFilterIdentifier
case .Unknown(let filterIdentifier):
return filterIdentifier
}
}
private static var everyoneFilterIdentifier: APIFilterIdentifier {
return "everyone"
}
private static var teamFilterIdentifier: APIFilterIdentifier {
return "myteam"
}
}
I know this is a bit old, but would this work for what you want?
typealias FilterIdentifier = String
enum DefaultAPIFilters: FilterIdentifier {
case Everyone = "everyone"
case Team = "team"
}
enum APIFilters {
case Default(DefaultAPIFilters)
case Custom(FilterIdentifier)
}
let everyoneFilter = APIFilters.Default(.Everyone)
let teamFilter = APIFilters.Default(.Team)
let clownFilter = APIFilters.Custom("clowns_only")
Extending Nathan Perry's response:
You can add a
var underlyingString: String {
return getUnderlyingString(self)
}
to the enum. Then define
func getUnderlyingString(apiFilter: APIFilters) -> String {
switch apiFilter {
case .Default(let defaultAPIFilter):
return defaultAPIFilter.rawValue
case .Custom(let custom):
return custom
}
}
According to this answer: Default value for Enum in Swift
I recommend using such an approach
public enum Result {
case passed(hint: String)
case failed(message: String)
static let passed: Self = .passed(hint: "")
}
let res: Result = Result.passed