Reference Swift enum member without its associated value - swift

I have the following enum:
enum State: Equatable {
case Loading
case Finished
case Error(message: String)
}
func ==(lhs: State, rhs: State) -> Bool {
//...
}
I want to be able to compare enum members. I have overloaded == operator, and it works, but there's a problem:
let state: State = .Loading
// works just fine
let thisWorks = state == .Finished
// this does as well
let thisWorks2 = state == .Error("Derp!")
// this, however, does not, compiler error: "Could not find member 'Error'"
let thisDoesnt = state == .Error
This seems to be a compiler limitation. I don't see why I should not be able to reference the enum member without its associated value. Apparently I don't care about the error message associated with .Error, I just need to know if an error has occurred. This is actually possible with switch, so I don't know why regular statements are limited.
I have to admit that I haven't looked at Swift 2 closely. Should I expect some improvements in the new version? Another question is, until it is released, is there any workaround?

Enums work really well with switch:
let state: State = .Error(message: "Foo")
switch state {
case .Loading:
print("Loading")
case .Finished:
print("Finished")
case .Error(message: "Foo"):
print("Foo!!!")
case .Error(message: _):
print("Some other error")
}
Swift 2.0 will bring another control flow syntax that probably you will appreciate:
Swift 2.0
if case .Error(message: _) = state {
print("An Error")
}
Hope this helps

You are creating a new instance of the enum. You're error enum has a required associated value. It can't be nil. Thus when you create an enum, you must give it a value.
How about if you make your error state's associated value optional?
enum State: Equatable {
case Loading
case Finished
case Error(message: String?)
Then you could use code like this:
let thisDoesnt = state == .Error(nil)

Try it.
let thisWorks2 = state == .Error(message: "Derp!");

Related

Multiple Enums with same case

Is there a better approach to tackle this situation.
I am following MVVM for my project and we have ViewModels for every ViewController, which feed them the required data. The data is passed back through enums with associatedValues.
Suppose, we have
enum HomeViewModelState {
case failure(String)
case anotherState(SomeModel)
}
enum ListingViewModelState {
case failure(String)
case anotherState(SomeModel)
}
As we see we have a repetitive case i.e. failure(String) that pass back the error message incase of an api failure. I want to avoid this repetitive task. Is there a better approach ?
Consider using the Result instead of multiple enums, you will have a state stored as Result<SomeModel, Error> in every controller (and each controller can have a different model. The state assignment is convenient, you are no longer passing strings as errors, and will use proper Error protocol instead:
var state: Result<SomeModel1, Error>
...
state = .failure(MyCustomErrors.badThingHappened)
state = .success(SomeModel)
enum MyCustomErrors: Error {
case badThingHappened
It's still an enum and can be used in a switch statement:
switch state {
case .success(let model):
...
case .failure(let error):
...
}
You could create one state enum that uses generics:
enum ViewModelState<T> {
case failure(String)
case anotherState(T)
}

Swift Switch case on enum with Classes

This is the first time I use this kind of enums, enum with associated value type, I need to make a switch statement depending on the object's type, I cannot managed to do it, this is the enum:
enum TypeEnum {
case foo(FooClass)
case doo(DooClass)
case roo(RooClass)
}
My object has a variable of type TypeEnum, now I need to check which kind of object is in the enum:
if let anObject = object as? TypeEnum {
switch anObject {
case .foo(???):
return true
...
default:
return false
}
}
I have no idea what to put instead of ???. Xcode tells me to put something, but I just want to switch on .foo.
Any ideas?
You can use let to capture the associated values for that:
switch anObject {
case .foo(let fooObj):
...
}
or nothing at all if you just don't care about them:
switch anObject {
case .foo:
...
}
Please be sure to check the Swift Programming Language book for further details.
You could use the underscore to indicate that you aren't interested in the associated type:
case .foo(_):
...

Swift. RawRepresentable init with optional RawValue

I am trying to make a generic failable initializer with optional parameter for RawRepresentable, basically this https://www.natashatherobot.com/swift-failable-enums-with-optionals/
There were a couple of methods proposed one of which is this (EDIT: fixed let in the second clause):
extension RawRepresentable {
init?(rawValue optionalRawValue: RawValue?) {
guard let rawValue = optionalRawValue, let value = Self(rawValue: rawValue) else { return nil }
self = value
}
}
from here https://gist.github.com/okla/e5dd8fbb4e604dabcdc3
I have no idea if it was ever working on Swift 2 but I can't compile it on Swift 3. I get:
Command failed due to signal: Segmentation fault: 11
Is there a way to make it work?
P.S. I am aware of other approaches from the article and its comments.
EDIT: Fixed broken copy/pasted code.
I just copied and pasted your code in a Playground, and the only error I get is that it's missing a let before assigning value in the guard statement.
Are you sure the segmentation fault is because of this extension?
This is the code that works for me (Xcode 8.2.1, Swift 3.0.2):
extension RawRepresentable {
init?(rawValue optionalRawValue: RawValue?) {
guard let rawValue = optionalRawValue, let value = Self(rawValue: rawValue) else { return nil }
self = value
}
}
-- EDIT --
After defining an enum with raw values I do get an error.
Adding this makes the Playground to crash:
enum Counter: Int {
case one = 1, two, three, four, five
}
Fixed it by renaming the parameter in init? to be init?(optRawValue optionalRawValue: RawValue?). I guess the problem is that you are calling Self(rawValue: rawValue) inside init?, and the compiler does not know which one to use...
Regardless of the problems with compiling this code or making it work as you'd like it to, I'd argue that you're trying to solve the underlying problem in the wrong way.
Trying to design an initializer like this is an anti-pattern: the design for Optionals in Swift encourages handling and resolving nullability questions at the earliest opportunity, not cascading failures so far that it's hard to recover their origins. If you have a function/initializer that returns nil if and only if it's passed nil, it just shouldn't accept nil in the first place and never return nil.
In Swift 3, you can keep the default rawValue initializer for your enum and resolve the issues from that original NatashaTheRobot post in either of a couple of different ways.
Use a compound if (or guard) statement.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let string = segue.identifier,
let identifier = SegueIdentifier(rawValue: string)
else { return /* or fatalError or whatever for catching problems */ }
switch identifier {
// handle all the enum cases here
}
}
Do it functional style with flatMap (twice), which executes a closure iff its input is not nil:
segue.identifier.flatMap(SegueIdentifier.init).flatMap { identifier in
switch identifier {
// handle all cases
}
}
Either way, you're unwrapping both levels of optionality (whether the segue has an identifier, and whether that string is a valid raw value for your SegueIdentifier enum) before the switch, which means that the switch must handle all valid SegueIdentifier cases. In turn, that means you catch yourself if you later add a new SegueIdentifier case and don't handle it. (As opposed to switching on something that might be a valid SegueIdentifier or might be nil, which means you need a default case, which means you'll silently fail if there are new SegueIdentifiers you ought to be handling.)
had to add public to init and it compiles and passes tests
Apple Swift version 4.1.2 (swiftlang-902.0.54 clang-902.0.39.2)
Target: x86_64-apple-darwin17.6.0
extension RawRepresentable {
public init?(rawValue optionalRawValue: RawValue?) {
guard let rawValue = optionalRawValue, let value = Self(rawValue: rawValue) else { return nil }
self = value
}
}

In Swift, is there a way of determining whether an enum is based on a certain type (eg. String)?

In order to write generic code for an NSValueTransformer, I need to be able to check that an enum is of type String for example. Ie.:
enum TestEnum: String {
case Tall
case Short
}
I am expecially interested in a test that can be used with the guard statement. Something allong the line of:
guard let e = myEnum as <string based enum test> else {
// throw an error
}
Please note that not all enums have raw values. For eample:
enum Test2Enum {
case Fat
case Slim
}
Hence a check on the raw value type can not be used alone for this purpose.
EDIT
After some further investigation it has become clear that NSValueTransformer can not be used to transform Swift enums. Please see my second comment from matt's answer.
First, it's your enums, so you can't not know what type they are. Second, you're not going to receive an enum type, but an enum instance. Third, even if you insist on pretending not to know what type this enum is, it's easy to make a function that can be called only with an enum that has a raw value and check what type that raw value is:
enum E1 {
case One
case Two
}
enum E2 : String {
case One
case Two
}
enum E3 : Int {
case One
case Two
}
func f<T:RawRepresentable>(t:T) -> Bool {
return T.RawValue.self == String.self
}
f(E3.One) // false
f(E2.One) // true
f(E1.One) // compile error
Generics to the rescue :
func enumRawType<T>(of v:T)-> Any?
{ return nil }
func enumRawType<T:RawRepresentable>(of v:T)-> Any?
{
return type(of:v.rawValue)
}
enumRawType(of:E1.One) // nil
enumRawType(of:E2.One) // String.Type
enumRawType(of:E3.One) // Int.Type

Swift compiler crashes on switch statement, need workaround

I've got code that basically looks like this, although I'm not sure that this distilled version exactly reproduces the compiler crash:
enum Response<T> {
case Success(T)
case Failure(String)
}
struct ResponseData {
let someData = "some data"
}
func evaluate() {
let response = Response.Success(ResponseData())
switch response {
case let .Success(data):
println("Got response with \(data)")
case let .Failure(reason):
println("Got failure: \(reason)")
default: ()
}
}
The Xcode editor doesn't detect any problems, but when I build, the compiler crashes with this error:
Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1
When I comment out the .Success case, I can build just fine. I'm guessing the Swift compiler doesn't like generics as associated values with the enum. However, that pattern is extremely useful for me, and it makes the code much more readable. Any idea how I can work around this? As far as I can tell, there's no way to access associated values except with a switch statement.
I should also mention that I have found the question here, but have not managed to make much use of the solutions presented.
Edit Actually the following already throws a seg-fault:
enum Response<T> {
case Success(T)
case Failure(String)
}
struct ResponseData {
let someData = "some data"
}
func evaluate() {
let response = Response.Success(ResponseData())
}
unimplemented IR generation feature non-fixed multi-payload enum layout
enum Response {
Credit to the solution really goes to everyone who commented above.
The problem seems to be that the Swift compiler wants to know exactly how large the enum is in memory. Generics make that impossible, thus the compiler does not behave correctly.
The solution I went with was taken from Rob Rix's library, which is to box the generic in another type. Note that it must be a class, since a reference has a known size but a struct with the generic does not.
The #autoclosure solution seemed interesting also, but it does not work with the latest version of Xcode. This was by design; as I understand, the developers don't want closures to run each time the enum is evaluated.
Thanks everyone!
Looks like you can use only objects that inherit from NSObject
Like this
enum Result<T: NSObject>: Printable {
case Success(T)
case Error(NSError)
var description: String {
switch self{
case .Success(let obj):
return obj.description
case .Error(let error):
return error.description
}
}
And then
let r = Result.Success("Xcode")
r.description
UPD:
You can do something like this
#objc
protocol ResultObjectProtocol {
var description: String { get }
}
extension NSDictionary: ResultObjectProtocol {}
enum Result<T: ResultObjectProtocol>: Printable {
case Success(T)
case Failure(NSError)
var description: String {
switch self{
case .Success(let obj):
return obj.description
case .Failure(let error):
return error.description
}
}
}
let r = Result.Success(["d" : 132]).description
Note: var description - only for example
Upd. (Swift 4)
You are free to use generic type in enum:
enum Result<T> {
case success(T)
case error(Error)
}