Enumeration on enums with associated values - Swift - swift

I have an enum with associated values. In addition, every value has a String description. How can I get description of all the cases?
enum MyEnum {
case caseA(data: [DataOfTypeA])
case caseB(data: [DataOfTypeB])
case caseC(data: [DataOfTypeC])
case caseD(data: [DataOfTypeD])
var typeDescription: String {
switch self {
case .caseA:
return "caseA"
case .caseB:
return "caseB"
case .caseC:
return "caseC"
case .caseD:
return "caseD"
}
}
}
The thing I am looking for is:
"caseA, caseB, caseC, caseD"

You can make your enum conform to CaseIterable, then simply iterate through allCases to create typeDescription.
enum MyEnum: CaseIterable {
case caseA(data: [Int])
case caseB(data: [String])
case caseC(data: [Date])
case caseD(data: [Data])
static var allCases: [MyEnum] = [.caseA(data: []), .caseB(data: []), .caseC(data: []), .caseD(data: [])]
var caseDescription: String {
switch self {
case .caseA:
return "caseA"
case .caseB:
return "caseB"
case .caseC:
return "caseC"
case .caseD:
return "caseD"
}
}
static var typeDescription: String {
return allCases.map {$0.caseDescription}.joined(separator: ", ")
}
}

Imagine you have this variable:
let myEnum = MyEnum.caseA(data: [])
for accessing associated values:
switch myEnum {
case .caseA(let data): print(data)
case .caseB(let data): print(data)
case .caseC(let data): print(data)
case .caseD(let data): print(data)
}
for accessing typeDescription:
print(myEnum.typeDescription)
Or for any case without having a variable:
print(MyEnum.caseA(data: []).typeDescription)

Related

Accessing Associated Values in an Array of Swift Enum in an Opaque Fashion

I haven't really found what I need in the associated questions. That may be because I'm a bit thick, and didn't see it, but here's my quandary:
I have a computed property that returns an Array of enums with associated values.
The values are all the same types (in my use case, the type is a "flag"), but the enums are different.
I'd like to see if there was some way to cycle through the Array, looking at only the associated values, regardless of the enum type. I am attaching a small playground that shows what I mean:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
print(String(describing: each))
}
print();
// This works:
for each in a {
if case let .valueA(value) = each {
print(value)
}
if case let .valueB(value) = each {
print(value)
}
if case let .valueC(value) = each {
print(value)
}
}
print();
// So does this:
for each in a {
var value: Int = 0
switch each {
case .valueA(let val):
value = val
case .valueB(let val):
value = val
case .valueC(let val):
value = val
}
print(value)
}
// What I want:
//for each in a {
// if case let A(value) = each {
// print(value)
// }
//}
I want to be able to treat each member of the collection, and extract its flag, and then make a decision, based on that flag.
I know that I could do it with a big ol' switch statement, with all the enum values (second go), but it would be nice if there were some generic way to just access all the values.
You would still have to test against the each case, but you could do
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
switch each {
case .valueA(let val), .valueB(let val), .valueC(let val):
print(val)
}
}
This would still apply if you have cases with associated values of different types:
enum Cases {
case one(Int)
case two(Int)
case three(Int, String)
case four(String)
case five(String)
}
let testArray = [Cases.one(1), .two(2), .three(3, "three"),
.four("four"), .five("five")]
// Matching associated Ints
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal), .three(let intVal, _):
print(intVal)
case .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching associated Strings
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal):
print(intVal)
case .three(_, let strVal), .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching any type, cast to Any
for value in testArray {
switch value {
case .one(let anyVal as Any), .five(let anyVal as Any):
print(anyVal)
default:
continue // skipping these cases
}
}
The important takeaway is that you are binding the same var names in every statement whose value you are trying to match, meaning you can't match values that aren't available in ever statement:
switch ... {
// This won't work, because myInt is not defined in case .two
// and myOtherInt isn't defined in case .one
case .one(let myInt), .two(let myOtherInt):
...
}
To add to #Kiril's answer, if you wanted computed values for cases with different types of associated values, you can define optional computed vars to return those values:
extension Cases {
var intValue: Int? {
switch self {
case .one(let myInt), .two(let myInt), .three(let myInt, _):
return myInt
default:
return nil
}
}
var strValue: String? {
switch self {
case .three(_, let myStr), .four(let myStr), .five(let myStr):
return myStr
default:
return nil
}
}
}
I'd suggest a slight modification on your enum:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
var flag: Int {
switch self {
case .valueA(let flag),
.valueB(let flag),
.valueC(let flag):
return flag
}
}
}
That makes any loop that just needs a flag trivial:
for each in a {
print(each.flag)
}

Is there a swift data structure which can contain a rawValue that can be any of its type and can be switchable

I need some kind of enum which can accept any StringLiteralType in which I don't need to create a lot of boilerplate code.
Here's an example of the boilerplate code I have.
enum Sample: RawRepresentable {
case foo
case bar
case unknown(String)
init?(rawValue: String) {
if let correspondingValue = Key(rawValue: rawValue)?.correspondingValue {
self = correspondingValue
} else {
self = .unknown(rawValue)
}
}
private enum Key: String {
case foo
case bar
var correspondingValue: Sample {
switch self {
case .foo: return .foo
case .bar: return .bar
}
}
init?(withSample sample: Sample) {
switch sample {
case .foo: self = .foo
case .bar: self = .bar
case .unknown: return nil
}
}
}
var rawValue: String {
switch self {
case let .unknown(value): return value
default: return Key(withSample: self)?.rawValue ?? ""
}
}
}
I want to have defined cases (foo, bar, etc) which have default values that I can use to switch against, and then I want to have an unkown(String) which can contain any value.
This can be easily done by just using String and some kind of Constants like this for example.
enum Constants {
static let foo = "foo"
static let bar = "bar"
}
// sample usage
let someString = "aaaa"
let sample = Sample(rawValue: someString)! // don't mind the implicit unwrapping
switch sample {
case Constants.foo:
// do something
case Constants.bar:
// do something
default:
// do something with unknown someString
}
The idea here is to be able to use sample like this.
let someString = "aaaa"
let sample = Sample(rawValue: someString)! // don't mind the implicit unwrapping
switch sample {
case .foo:
// do something
case .bar:
// do something
case .unknown(let value):
// do something
}
EDIT:
Why it needs to be an Enum
- Autocomplete in XCode using enums
- Adding functionality will be easy when adding new cases when using switch
Why it needs to be RawRepresentable
- This gets stored to persistence via it's RawValue.
- I can also use protocols under ExpressibleByXXXXXXLiteral by doing this.
Does it need to be RawRepresentable? The code below works per your requirement…
enum Sample {
case foo, bar, unknown(StringLiteralType)
init(_ string: StringLiteralType) {
switch string {
case "foo": self = .foo
case "bar": self = .bar
default: self = .unknown(string)
}
}
}
let sample = Sample("aaa")
switch sample {
case .foo:
print("foo")
case .bar:
print("bar")
case .unknown(let value):
print(value)
}
// aaa
Edit
enum Sample: RawRepresentable {
case foo, bar, unknown(StringLiteralType)
enum Keys: String {
case foo, bar
var sample: Sample {
switch self {
case .foo: return .foo
case .bar: return .bar
}
}
}
init(rawValue: StringLiteralType) {
self = Keys(rawValue: rawValue)?.sample ?? .unknown(rawValue)
}
var rawValue: String {
switch self {
case .foo: return Keys.foo.rawValue
case .bar: return Keys.bar.rawValue
case .unknown(let value): return value
}
}
}
print(Sample(rawValue: "aaa").rawValue) // aaa
print(Sample(rawValue: "foo").rawValue) // foo
print(Sample.foo.rawValue) // foo
print(Sample.bar.rawValue) // bar
This is the best solution I made so far.
I first created an enum which contains two cases with associated values, one is for a sub enum of known entities and the other is for unknown entities.
enum Foo {
case known(Bar)
case unknown(String)
enum Bar: String {
case fiz, baz
}
}
I then extended the said enum to have RawRepresentable capabilities
extension Foo: RawRepresentable {
init?(rawValue: String) {
if let bar = Bar(rawValue: rawValue) {
self = .known(bar)
} else {
self = .unknown(rawValue)
}
}
init(stringValue: String) {
self.init(rawValue: stringValue)!
}
var rawValue: String {
switch self {
case let .known(bar): return bar.rawValue
case let .unknown(string): return string
}
}
}
Here's a sample usage.
let foo: Foo = .known(.fiz)
switch foo {
case .known(.fiz):
... do something
case .known(.baz):
... do something
case .unknown(let str):
... do something
}
PS: This could possibly be a nice thing to propose in Swift Evolution, so there would be less boilerplate I guess.

Extract value from enum using guard or if case in Swift

I have enum with cases:
case one(value: myClassOne)
case two(value: myClassTwo)
I want to check value of that enum. Now i ended with:
switch self.model! {
case .one:
// Great, my case here
default:
break
}
But i rather want to do something like:
if case self.model(let value) is myClassOne { // do smth } // Not compiling
Is there is easy way to simply extract enum value and check it for some condition or class equality?
This is the correct syntax:
if case .one(value: let value) = self.model {
// do something with `value`
}
guard case .one(value: let value) = self.model else {
//handle case where self.model != .one
}
// do something with `value`

How can I add raw values for an enum that doesn't have it?

Given an external enum that I can't change:
enum MyEnum {
case first
case second
}
How would I best make this RawRepresentable, or at least convertible to an Int (or String) ?
I could write an extension to mimic rawValue, but this feels rather clumsy:
extension MyEnum {
enum EnumError: Error {
case invalidValue
}
init (rawValue: Int) throws {
switch rawValue {
case 0:
self = .first
case 1:
self = .second
default:
throw EnumError.invalidValue
}
}
var rawValue: Int {
switch self {
case .first:
return 0
case .second:
return 1
}
}
}
What is a better way?
This works:
enum MyEnum {
case first
case second
}
extension MyEnum {
enum MyExtendedEnum:Int {
case first
case second
}
}
Its a bit more cleaner code anyways, and your call is now:
let myVar = MyEnum.MyExtendedEnum.RawValue()

Enum of tuples in Swift

Is this possible to create a enum of Tuples in Swift?
I'd like to build something like:
enum ErrorCode: (Int, String) {
case Generic_Error = (0, "Unknown")
case DB_Error = (909, "Database")
}
But it doesn't compile... Is there a way to obtain a similar result?
Swift enumerations cannot have Tuples as a raw value type.
Alternative approaches include storing the code and deriving a description from that:
enum ErrorCode: Int, CustomStringConvertible {
case Generic = 0
case DB = 909
var description: String {
switch self {
case .Generic:
return "Unknown"
case .DB:
return "Database"
}
}
}
...or storing associated values for code and description in the enumeration cases themselves:
enum Error {
case Generic(Int, String)
case DB(Int, String)
}
If you're just looking for constant values, #matt's suggestion of organizing them within a struct would work, too.
It depends what you mean by "similar". What I do is use a Struct with static constant properties:
struct Trouble {
static let Generic_Error = (0, "Unknown")
static let DB_Error = (909, "Database")
}
Now things like Trouble.Generic_Error are usable throughout your code.
You can conform to RawRepresentable and use (Int, String) as the Self.RawValue associated type.
enum ErrorCode: RawRepresentable {
case Generic_Error
case DB_Error
var rawValue: (Int, String) {
switch self {
case .Generic_Error: return (0, "Unknown")
case .DB_Error: return (909, "Database")
}
}
init?(rawValue: (Int, String)) {
switch rawValue {
case (0, "Unknown"): self = .Generic_Error
case (909, "Database"): self = .DB_Error
default: return nil
}
}
}
you can do such thing, maybe:
enum ErrorCode {
case Generic_Error
case DB_Error
func values() -> (code: Int!, description: String?)! {
switch self {
case .Generic_Error:
return (0, "Unknown")
case .DB_Error:
return (909, "Database")
}
}
}
and you can do such thing later:
let errorCode: ErrorCode = ErrorCode.Generic_Error;
if (errorCode.values().code == 0) {
// do the business here...
}
Create your enum and add a var of tuple type (String, String).
enum SomeType {
case type1
case type2
case type3
var typeNameAndDescription: (name: String, description: String) {
switch self {
case .type1:
return ("type1 name", "type1 description")
case .type2:
return ("type2 name", "type2 description")
case .type3:
return ("type3 name", "type3 description")
}
}
}
and later:
let myType = SomeType.type1
let typeName = myType.typeNameAndDescription.name
let typeDescription = myType.typeNameAndDescription.description
My solution to keep the enum, was to create the get method for the rawValue var:
enum LoadingType {
case poster
case movie
case refresh
var rawValue: (file: String, anim: String) {
get {
switch self {
case .movie:
return ("poster_loading", "LoadingView")
case .poster:
return ("loading", "LoadingView")
case .refresh:
return ("loading", "RefreshView")
}
}
}
}
With this code, you can even call each of your Tuple elements by a name:
self.type.rawValue.file
I think I would change your code to something like this:
enum ErrorCode {
case generic, db
var message: String {
switch self {
case .generic:
return "Unknown"
case .db:
return "Database"
}
}
var code: Int {
switch self {
case .generic:
return 0
case .db:
return 909
}
}
}
I feel this would make it much more easier to use like so:
let error = ErrorCode.generic
print("Error Code: \(error.code), Message: \(error.message)")
enum ErrorCode {
case Generic_Error
case DB_Error
public var code:Int{
switch self {
case .Generic_Error: return 0
case .DB_Error: return 909
}
}
public var name:String{
switch self {
case .Generic_Error: return "Unknown"
case .DB_Error: return "Database"
}
}
}
using:
let err:ErrorCode = .Generic_Error
print(err.code) //0
print(err.name) //Unknown
This is what I did in a similar situation. Use an associated data enum, with default values (you can even name the values in the tuple if you like). Then add a simple function that returns the tuple based on self.
enum ErrorCode {
case Generic_Error( code: Int = 0, desc: String = "Unknown")
case DB_Error( code: Int = 909, desc: String = "Database")
func getCodeDescription() -> (Int, String) {
switch self {
case let .Generic_Error(code, desc): return (code, desc)
case let .DB_Error(code, desc): return(code, desc)
}
}
}
Later
var errorCode: Int
var errorDesc: String
let myError: ErrorCode = .DB_Error() // let it take the default values
(errorCode, errorDesc) = myError.getCodeDescription()
Create an Enumeration as Int.
For example:
https://github.com/rhodgkins/SwiftHTTPStatusCodes/blob/master/Sources/HTTPStatusCodes.swift