I've got an enumeration with a few different cases which are different types, e.g.
enum X {
case AsInt(Int)
case AsDouble(Double)
}
I can switch on these just fine to get the underlying value back out. However, the switch statement is highly annoying with trying to make me execute some code for the other cases that I simply don't care about. For example, right now I have something like
func AsInt(x: X) -> Int? {
switch x {
case AsInt(let num):
return num;
default:
return nil;
}
}
This works but it's pretty tedious always having to reference this method and having to write a new one for each case of each enumeration. What I'm looking for is how to simply attempt to cast a value of type X to one of the cases, like
var object: X = func();
let value = obj as? Int;
if value {
// do shit
}
How can I simply check for a case without having to enumerate all of the cases about which I don't care and execute some non-statement for them?
Bonus points for any solution that can declare value as part of the conditional instead of polluting the scope.
There are actually multiple ways to do it.
Let's do it by extending your enum with a computed property:
enum X {
case asInt(Int)
case asDouble(Double)
var asInt: Int? {
// ... see below
}
}
Solutions with if case
By having let outside:
var asInt: Int? {
if case let .asInt(value) = self {
return value
}
return nil
}
By having let inside:
var asInt: Int? {
if case .asInt(let value) = self {
return value
}
return nil
}
Solutions with guard case
By having let outside:
var asInt: Int? {
guard case let .asInt(value) = self else {
return nil
}
return value
}
By having let inside:
var asInt: Int? {
guard case .asInt(let value) = self else {
return nil
}
return value
}
The last one is my personal favorite syntax of the four solutions.
As of Swift 2 (Xcode 7) this is possible with if/case and
pattern matching:
let x : X = ...
if case let .AsInt(num) = x {
print(num)
}
The scope of num is restricted to the if-statement.
Related
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()
Is it possible to do a getter and setter for an attribute that has a type of 'Any'
Here is my thought:
private var _valueObject: Any?
public var valueObject: Any? {
set {
if newValue is String {
self._valueObject = newValue as? String
} else if newValue is BFSignature {
self._valueObject = newValue as? BFSignature
}
}
get {
if self._valueObject is String {
return self._valueObject as? String
} else if self._valueObject is BFSignature {
return self._valueObject as? BFSignature
} else {
return self._valueObject
}
}
}
When I try to use it through out my code though I get errors stating:
Cannot compare String to type Any
Is there a way to use something like this without casting the 'valueObject' to a string whenever I need it. A way to use it and it already knows its a 'String' or 'BFSignature' instead of 'Any'.
Here is an example of the error:
I would rather it just know that cellValue is a 'String.' Instead of casting it each time I use it.
You shouldn't use Any
In my opinion, you should make a common representation of the API call result instead of using Any. You know exactly what the API is going to return, don't you? It's either a String or something that you turn into your custom object BFSignature.
Therefore, you can make an enum to represent your API call result:
enum APIResult {
case signature(BFASignature)
case justString(String)
}
and use it like
private var _valueObject: APIResult?
if let stringValue = newValue as? String {
self._valueObject = .justString(stringValue)
}
if let signatureValue = newValue as? BFSignature {
self._valueObject = .signature(signatureValue)
}
If there are a fixed number of types that you need to use here, you can use an enum:
struct BFSignature {
var a: Int
}
enum Either {
case bfSig(BFSignature)
case string(String)
}
var a: Either
var b: Either
a = .bfSig(BFSignature(a: 7))
b = .string("Stack Overflow")
a = b
Usage:
switch (b) {
case Either.bfSig(let signature):
print(signature.a) // Output integeral value
case Either.string(let str):
print(str) //Output string value
}
Consider this enumeration (note its type is Int)
enum MyTestEnum : Int{
case one = 1
case eight = 8
case unknown = -1
}
You can easily initialize a version of this based on the raw value, like so...
let value = MyTestEnum(rawValue:-1)
I'm trying to find out if it can be initialized with a string representation of the case name itself (again, not the raw value, but the word after 'case') like so...
let value = MyTestEnum(caseName:"eight")
Note: I want this to work with any enum if possible, regardless of its raw value type. For instance, this one...
enum MyOtherEnum{
case xxx
case yyy
case zzz
}
let value = MyOtherEnum(caseName:"xxx")
So can this be done?
Thoughts:
I think Swift has a way to instantiate a class given a string representing the fully-qualified class-name. Perhaps something similar can be used here.
In Swift 4.2, this is quite easy to do now using CaseIterable.
enum MyOtherEnum: CaseIterable {
case xxx
case yyy
case zzz
init?(caseName: String) {
for value in MyOtherEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
enum MyTestEnum: Int, CaseIterable{
case one = 1
case eight = 8
case unknown = -1
init?(caseName: String) {
for value in MyTestEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
What I am doing here is creating a failable initializer which iterates through all potential cases, testing to see if "\(value)" (which returns the name for that potential case) matches the caseName argument passed in to the initializer.
When a match is found, self is set and the loop ends. Otherwise, nil is returned for the call.
Below, are two working and two failing examples:
let myOtherEnum = MyOtherEnum(caseName:"xxx")
print(myOtherEnum) // MyOtherEnum.xxx
let myTestEnum = MyTestEnum(caseName:"eight")
print(myTestEnum?.rawValue) // 8
let myOtherEnumFail = MyOtherEnum(caseName:"aaa")
print(myOtherEnumFail) // nil
let myTestEnumFail = MyTestEnum(caseName:"ten")
print(myTestEnumFail) // nil
Based on CodeBender's solution here is a nice extension for this case
extension CaseIterable {
///Note: case value and name can be different
init?(caseName: String) {
for v in Self.allCases where "\(v)" == caseName {
self = v
return
}
return nil
}
}
You can go with custom initializer
extension MyTestEnum {
public static func allValues() -> [MyTestEnum] {
let retVal = AnySequence { () -> AnyIterator<MyTestEnum> in
var raw = 0
return AnyIterator {
let current = withUnsafePointer(to: &raw) {
$0.withMemoryRebound(to: MyTestEnum.self, capacity: 1) { $0.pointee }
}
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
return [MyTestEnum](retVal)
}
init?(caseName: String){
for v in MyTestEnum.allValues() {
if "\(v)" == caseName {
self = v
return
}
}
self = MyTestEnum.unknown
}
}
let test = MyTestEnum(caseName: "eight")
or simple manually all your case :)
extension MyTestEnum {
init?(caseName: String){
switch caseName {
case "eight": self.init(rawValue: 8)
case "one": self.init(rawValue: 1)
default: self.init(rawValue: -1)
}
}
}
let test1 = MyTestEnum(caseName: "eight")
let test2 = MyTestEnum(rawValue: 1)
Hope this helps !!
It sounds like your want your enum cases to have raw Int values and raw String values. It's not strictly possible to do this, but perhaps this comes close:
enum MyTestEnum : String {
case one
case eight
case unknown
var intValue: Int {
switch self {
case one: return 1
case eight: return 8
case unknown: return -1
}
}
}
Now you can initialize from case names, but you can also retrieve an intValue when needed. (If needed, you could easily add a failable initializer to allow initialization from integers as well.)
I've got an enumeration with a few different cases which are different types, e.g.
enum X {
case AsInt(Int)
case AsDouble(Double)
}
I can switch on these just fine to get the underlying value back out. However, the switch statement is highly annoying with trying to make me execute some code for the other cases that I simply don't care about. For example, right now I have something like
func AsInt(x: X) -> Int? {
switch x {
case AsInt(let num):
return num;
default:
return nil;
}
}
This works but it's pretty tedious always having to reference this method and having to write a new one for each case of each enumeration. What I'm looking for is how to simply attempt to cast a value of type X to one of the cases, like
var object: X = func();
let value = obj as? Int;
if value {
// do shit
}
How can I simply check for a case without having to enumerate all of the cases about which I don't care and execute some non-statement for them?
Bonus points for any solution that can declare value as part of the conditional instead of polluting the scope.
There are actually multiple ways to do it.
Let's do it by extending your enum with a computed property:
enum X {
case asInt(Int)
case asDouble(Double)
var asInt: Int? {
// ... see below
}
}
Solutions with if case
By having let outside:
var asInt: Int? {
if case let .asInt(value) = self {
return value
}
return nil
}
By having let inside:
var asInt: Int? {
if case .asInt(let value) = self {
return value
}
return nil
}
Solutions with guard case
By having let outside:
var asInt: Int? {
guard case let .asInt(value) = self else {
return nil
}
return value
}
By having let inside:
var asInt: Int? {
guard case .asInt(let value) = self else {
return nil
}
return value
}
The last one is my personal favorite syntax of the four solutions.
As of Swift 2 (Xcode 7) this is possible with if/case and
pattern matching:
let x : X = ...
if case let .AsInt(num) = x {
print(num)
}
The scope of num is restricted to the if-statement.
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()