Cannot put error with Generic Result enum in RxSwift - swift

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.

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: use inOut parameter in escaping closure

I have the following method:
private func retrieveFromAPIAndMapToCD<T: Decodable & CoreDataMappable, R: NSManagedObject>(endpoint: Endpoint, object: T.Type, cdObjectType: R.Type, storedObjectList: inout [R]) {
API.client.call(endpoint, expecting: [T].self) { (result, objects) in
switch result {
case .failure:
print("Unable to retrieve objects")
case .success:
guard let objectResponse = objects else { return }
DispatchQueue.main.async {
for object in objectResponse {
object.mapToCoreData()
}
self.appDelegate.saveContext()
self.updateLastSavedDate(object: T.self)
self.fetchObjectsFromCD(object: cdObjectType.self, objectList: &storedObjectList)
}
}
}
}
Without pasting too much unnecessary code, my issue is that within this API call (which comes from a library helper method we use to handle API responses) I need to set storedObjectList which is a variable defined within the scope of the current class.
However, when I do this I receive an error stating:
Escaping closure captures 'inout' parameter 'storedObjectList'
I'm trying to find a way around this so that I can still pass in storedObjectList here. I hit this method for 3 different objects, hence why I am trying to make it generic to avoid code repetition. This is the last part that I cannot get to work. I would like to avoid having some kind of a switch statement based on the object type passed in as R because this will limit the reusability of this method in the future.

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

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