Multiple Enums with same case - swift

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

Related

Dart Force enum values in switch outside of enum class

I'd like to use dart enums for more flow control outside of the enum class and allow the compiler to catch any missing enum cases in that switch.
I'm coming from Swift where enums can be defined and then later using the enum value coming in for force compiler flow control. So when developers add a new enum, they can update each location with the specific code flow.
I don't see any easy option to allow this type of programming.
This example is using MQTT topics with subscribes and I'd like to add code in all code locations where the enum is used.
enum SubscribeTopicType {
configData,
statusSensors,
}
So example above, I added "statusSensors" to the enum.
Inside the enum with an extension, the compiler catches any missing new enum types added.
extension SubscribeTopicTypeExtension on SubscribeTopicType {
String get endpointValue {
switch (this) {
case SubscribeTopicType.configData: return "data/configurationData/";
case SubscribeTopicType.statusSensors: return "data/sensorData/";
}
}
But when using the enum outside of the enum class or extension, the compiler doesn't find the missing case.
MqttManager class:
void _processSubscribePayload(SubscribeTopicType subscribeTopicType, String payload) {
switch (subscribeTopicType) {
case SubscribeTopicType.configData:
_subscribeConfigData(payload);
break;
// the code commented out will stop the compiler from requiring the new enum value.
// case SubscribeTopicType.statusSensors:
// _subscribeStatusSensors(payload);
// break;
}
}
Is this a gap or missing feature in dart? Or is there a way to force code flow control with enums?

Swift 5 Result type

In Swift 5 Apple introduced Result type. It's generic enum with two cases:
public enum Result<Success, Failure: Error> {
case success(Success), failure(Failure)
}
Personally I used to two separate completions in network calls success: Completion and failure: Completion, but from what I see now, Apple pushing us to use single completion with Result type and then inside perform switch. So what are advantages of this approach with Result? Because in a lot of cases I can just omit error handling and don't write this switch. Thanks.
You shouldn’t omit cases when Result is failure. You shouldn’t do it with Result and you shouldn’t do it with your closure for failure. You should handle errors.
Anyway, Result type was introduced for simplifing completion handlers. You can have single closure for handling success or failure (primary-opinion based if two separate closures are better or not). Also Result is designed for error handling. You can simply create your own enum conforming to Error and then you can create your own error cases.
Swift 5 introduced Result<Success, Failure> associated value enumeration[About] It means that your result can be either success or failure with additional info(success result or error object). Good practice is to manage error case and success case as atomic task.
Advantages:
Result is a single/general(generic)/full type which can be used for all yes/no cases
Not optional type
Forces consumers to check all cases
public enum Result<Success, Failure> {
case success(Success)
case failure(Failure)
}
To use it
//create
func foo() -> Result<String, Error> {
//logic
if ok {
return .success("Hello World")
} else {
return .failure(.someError)
}
}
//read
func bar() {
let result = foo()
switch result {
case .success(let someString):
//success logic
case .failure(let error):
//fail logic
}
}

Cannot put error with Generic Result enum in RxSwift

I'm trying to create a generic results enum in swift, this is what I have so far:
enum Result<T: Codable>: Error {
//Indicates that it was a success, and data is the returned result
case Success(data: T)
//Indicates that there was an unrecognizable error
case Failure(error: Error)
//Some cases for specific status codes
case Forbidden //Status code: 403
case NotAcceptable //Status code: 406
case Conflict //Status code: 409
case InternalServerError //Status code: 500
}
And then I try to create an Observable out of it, like this:
(The T is specified in the function's call, this is shortened for brevity)
Observable<Result<T>>.create { observer in
//Some code to go make an Http requests and return response....
switch response.result {
case .success(let value):
//This works fine
observer.onNext(Result.success(value))
observer.onCompleted()
case .failure(let error):
//This is where things go wrong.
observer.onError(Result.failure(error))
}
}
The problem is when I try to return in the .failure case, it always says Argument type 'Result<_>' does not conform to expected type 'Error' even though Result is an Error type
Am I doing something wrong?
Other than the fact that your capitalization is all over the place... When you do Result.failure(anError) the compiler can't infer the type of T. To fix just do: Result<T>.failure(anError)
... Or should it be Result<T>.Failure(error: anError)? You have it capitalized and use a named variable in the definition but you use lower-case and don't use a named variable where it's being used. :-(
What about this?
enum Result<T: Codable, E: Error> {
case Success(data: T)
case Failure(error: E)
// ...
}
I also removed the inheritance because it does not make sense when you actually wrap it by an enum case.
You might also want to put your networking code somewhere inside of Observable.create closure because the response is not available in a sequential manner. The closure instead is escaping the sequential flow like the network request/response does.

Reference Swift enum member without its associated value

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!");

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