I have a service class that offers several methods that make calls to the backend, and a common theme for those methods is that I can pass callbacks for the success and error cases:
func makeCall(onSuccess: #escaping APIResponse, onError: #escaping APIError)
The type APIError is defined like so:
typealias APIError = (Error) -> Void
Furthermore I have an enum APICallError like this:
enum APICallError: Error {
case insufficientCredentials
case malformedResponse
case forbidden
}
So in my service methods I can call onError(response.result.error!) if the result contained an error object (the force unwrap is just for brevity, I'm not really doing that) and also pass my own enum value if the HTTP result is not specific enough, like onError(APICallError.insufficientCredentials).
This works fine.
The problem is, I can't figure out how to evaluate in my client code whether the error parameter that's coming in is of type APICallError and which one of those specifically. If I do:
if let callError = error as? APICallError {
if callError == .forbidden {
// ...
}
}
execution doesn't make it past the typecast. if error is APICallError also does not seem to work.
How can I cast the error parameter to my APICallError enum value that I know is in there, because when I print(error) it gives me myapp.APICallError.forbidden?
I tried to simulate what you have posted in your question in Playground, and what you are already doing should work fine for you.
if error is APICallError is also working. One possibility why the if let condition fails might be due to the error being nil. Check if that is the case by using breakpoints.
typealias APIError = (Error) -> Void
//The error type used in your question
enum APICallError: Error {
case insufficientCredentials
case malformedResponse
case forbidden
}
//A different error type for comparison
enum AnotherError: Error {
case error1
case error2
case error3
}
let apiCallError: Error = APICallError.insufficientCredentials
let anotherError: AnotherError = AnotherError.error1
//Closure definition
var onError: APIError? = { inputError in
if let apiError = inputError as? APICallError {
print("APICallError")
}
if let anotherError = inputError as? AnotherError {
print("AnotherError")
}
}
//Above defined closure is called here below...
onError?(apiCallError)
onError?(anotherError)
Console Output (Works as expected):
APICallError
AnotherError
You need to use the enum rawValue constructor.
if let callError = APICallError(rawValue: error) {
if callError == .forbidden {
...
}
} else {
// was not an APICallError value
}
Related
I am using an enum that inherits from Error (or ErrorType in Swift 2) and I am trying to use it in such a way that I can catch the error and use something like print(error.description) to print a description of the error.
This is what my Error enum looks like:
enum UpdateError: Error {
case NoResults
case UpdateInProgress
case NoSubredditsEnabled
case SetWallpaperError
var description: String {
switch self {
case .NoResults:
return "No results were found with the current size & aspect ratio constraints."
case .UpdateInProgress:
return "A wallpaper update was already in progress."
case .NoSubredditsEnabled:
return "No subreddits are enabled."
case .SetWallpaperError:
return "There was an error setting the wallpaper."
}
}
// One of many nested enums
enum JsonDownloadError: Error {
case TimedOut
case Offline
case Unknown
var description: String {
switch self {
case .TimedOut:
return "The request for Reddit JSON data timed out."
case .Offline:
return "The request for Reddit JSON data failed because the network is offline."
case .Unknown:
return "The request for Reddit JSON data failed for an unknown reason."
}
}
}
// ...
}
An important thing to note is that there are a few nested enums within UpdateError so something like this won't work because the nested enums aren't of the UpdateError type themselves:
do {
try functionThatThrowsUpdateError()
} catch {
NSLog((error as! UpdateError).description)
}
Is there a better way of printing a description of the error without having to check every type of UpdateError that occurred in the catch statement?
You could define another (possibly empty) protocol, and conform your errors to it.
protocol DescriptiveError {
var description : String { get }
}
// specify the DescriptiveError protocol in each enum
You could then pattern match against the protocol type.
do {
try functionThatThrowsUpdateError()
} catch let error as DescriptiveError {
print(error.description)
}
How can one write unit tests in Swift that communicate useful information when calling functions that can throw?
What I'd really like to be able to do is something like this:
class TestTestsTests: XCTestCase {
func doFoo() throws -> String {
// A complex operation that might throw in various places
return "foo"
}
func doBar() throws -> String {
// A complex operation that might throw in various places
return "bar"
}
func testExample() throws {
let foo = try doFoo()
let bar = try doBar()
XCTAssertNotEqual(foo, bar)
}
}
Ideally the unit test runner would stop on the line where an unhandled exception occurred, and let the user explore the stack and error message. Unfortunately, adding throws to the test function causes it to be silently skipped over (and there's a UI bug that makes it look as though the test is still being run, and getting the same result as before adding throws).
Of course it is also possible to do this:
func testExample() {
let foo = try! doFoo()
let bar = try! doBar()
XCTAssertNotEqual(foo, bar)
}
But now a failure doesn't really provide the context we need. Add a throw to doFoo, and we get a message like fatal error: 'try!' expression unexpectedly raised an error: TestTestsTests.Error(): file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-700.1.101.15/src/swift/stdlib/public/core/ErrorType.swift, line 50, which only gives us the line number within testExample, and not within doFoo where the error occurred. This also seems to get the debugger 'stuck' (clicking continue just returns us to the same line with the same error message) on that line, regardless of whether breakpoints are enabled, and prevents other tests from running.
So maybe we could try something like this?
func testExample() {
do {
let foo = try doFoo()
let bar = try doBar()
XCTAssertNotEqual(foo, bar)
} catch {
XCTFail("\(error)")
}
}
This runs as expected, however we can't determine which of doFoo or doBar threw the error, and also no line number information. At least we can get the error message and not prevent other tests from running.
I could go on, but the short of it is that I can't find a way to simultaneously not break the unit test running (like with try!), figure out which function threw the error, and get the error information -- Unless I do something ridiculous like:
func testExample() {
var foo: String? = nil
var bar: String? = nil
do {
foo = try doFoo()
} catch {
XCTFail("\(error)")
return
}
do {
bar = try doBar()
} catch {
XCTFail("\(error)")
return
}
if let foo = foo, bar = bar {
XCTAssertNotEqual(foo, bar)
}
}
And I still don't get to find out where in doFoo or doBar the error occurred.
Is this just the sad state of unit testing in Swift, or am I missing something?
This runs as expected, however we can't determine which of doFoo or doBar threw the error, and also no line number information.
Maybe this is just an old problem or I'm not understanding your problem, or maybe you're just kind of making a statement rather than asking a question. But with respect to the quoted statement above, you can add any information you like to the messages in XCTest Assertions.
Swift also provides the following literals:
#file String The name of the file in which it appears.
#line Int The line number on which it appears.
#column Int The column number in which it begins.
#function String The name of the declaration in which it appears.
func testExample() {
var foo: String? = nil
var bar: String? = nil
do {
foo = try doFoo()
} catch {
XCTFail("try doFoo() failed on line: \(#line) in file: \(#file) with error: \(error)")
return
}
do {
bar = try doBar()
} catch {
XCTFail("try doBar() failed on line: \(#line) in file: \(#file) with error: \(error)")
return
}
if let foo = foo, bar = bar {
XCTAssertNotEqual(foo, bar)
}
}
If you really want to go crazy with it you can add error handling to your doBar() method and that error can contain any internal information you'd like.
In fact... by implementing your own errors in your methods you might not even need to separate the methods into two blocks in your tests, just printing the error should be enough. You can put any information you like in the error message.
Anyway, I think this is an outdated issue, you can get all the information you need from the test logs - they list out all the methods that failed and even have little arrows that let you jump right to the test that failed. They then highlight the specific assertion that failed... from there it's quite easy to tell what is happening in most cases. Worst case scenario you have to set a breakpoint or two and run the test again.
You can do your own errors, using Error or LocalizedError protocols
enum Errors: Error, CustomStringConvertible {
case foo_param_is_null
case bar_param_is_null(paramIndex: Int)
var description: String {
switch self {
case .foo_param_is_null:
return "Param is null in foo"
case .bar_param_is_null(let paramIndex):
return "Param at index \(paramIndex) is null in bar"
}
}
}
func foo(_ param: Int) throws {
guard param != 0 else {
throw Errors.foo_param_is_null
}
print("foo = \(param)")
}
func bar(_ params: [Int]) throws {
if let index = params.firstIndex(where: {$0 == 0}) {
throw Errors.bar_param_is_null(paramIndex: index)
}
print("bar = \(params)")
}
do {
try foo(1)
try foo(0)
} catch {
print("\(error)")
}
do {
try bar([1,2,3])
try bar([1,0,3])
} catch {
print("\(error)")
}
Result:
foo = 1
Param is null in foo
bar = [1, 2, 3]
Param at index 1 is null in bar
And if you need even more information, you can use structures to define errors and error domains. Something like :
struct FooBarError: Error, CustomStringConvertible {
var string: String
var context: Any?
static func fooError() {
FooBarError(string: "Foo Error")
}
static func barError(context: BarErrorContext) { FooBarError(string: "Bar Error", context: context)
}
var description: String {
if let cox = context as? BarErrorContext {
return "\(string) - paramIndex: \(ctx.paramIndex) - \(ctx.insidiousReason)"
}
return string
}
}
Note:
As #ibrust proposed, you can pass #function, #line and other special parameters to your errors initialisers to provide this information
do {
try foo()
} catch {
throw(BarFooError.foo(line: #line))
}
You can also propagate the original error
do {
try bar()
} catch {
throw(BarFooError.bar(exception: error))
}
Edited:
Finally , you can also use print(Thread.callStackSymbols) in your error description, but at this point, there is a risk of confusion between debugging and testing. Just a personal thought.
I have a function assignName(name:) which throws an error. When that function is called from a do block with multiple catch, It shows the error as:
Errors thrown from here are not handled because the enclosing catch is not exhaustive
My Code is:
enum PersonError: ErrorType {
case IsNotAPerson
case IsNotAGoodPerson
case IsNotAValidPerson
}
func assignName(name: String?) throws {
guard name != nil else {
throw PersonError.IsNotAPerson
}
personName = name
}
func catchingSpecificError() {
do {
try assignName(nil) // Compiler Error displays at this line.
}catch PersonError.IsNotAPerson {
print("Propagated error is caught in catch on case .NotAPerson")
}
}
Thanks in Advance!
You need to include a default catch block (much like when using a switch case) to make your error handling exhaustive; in case the error thrown is not one of the ones you specify.
func catchingSpecificError() {
do {
try assignName(nil) // Compiler Error displays at this line.
}catch PersonError.IsNotAPerson {
print("Propagated error is caught in catch on case .NotAPerson")
}catch {
print("default..")
}
}
Slightly off-topic, but I assume personName = name refers to a class property personName that we cannot see in your example above.
With the default catch block added, you mention in the comments below that function catchingSpecificError() does not cast the error you expect; or rather, the default catch block catches your error.
Now, since I don't know the context of your code, I cannot infer what is actually going wrong in your case. I'll post a working example for your below where---in the context of this question---the throw and catch work as expected. Note however that your use of the guard block is somewhat out of by convention. Usually you make use of guard just like if let blocks, i.e., guard let name = name else { .., which will enter the guard block if name contains nil.
Anyway, consider the following fully working example:
enum PersonError: ErrorType {
case IsNotAPerson
case IsNotAGoodPerson
case IsNotAValidPerson
}
class Person {
var personName : String? = ""
func assignName(name: String?) throws {
guard name != nil else {
throw PersonError.IsNotAPerson
}
personName = name
}
func catchingSpecificError() {
do {
try assignName(nil)
}catch PersonError.IsNotAPerson {
print("Propagated error is caught in catch on case .NotAPerson")
}catch {
print("default..")
}
}
}
var myPerson = Person()
var myName : String? = nil
myPerson.catchingSpecificError()
/* Prints: "Propagated error is caught in catch on case .NotAPerson" */
As expected, we catch PersonError.IsNotAPerson thrown by function assignName. Hopefully you can make use of this example your get your own code (the parts that you haven't shown us in your question) working.
Can someone please help me with this.
I have the following public enum
public enum OfferViewRow {
case Candidates
case Expiration
case Description
case Timing
case Money
case Payment
}
And the following mutableProperty:
private let rows = MutableProperty<[OfferViewRow]>([OfferViewRow]())
In my init file I use some reactiveCocoa to set my MutableProperty:
rows <~ application.producer
.map { response in
if response?.application.status == .Applied {
return [.Candidates, .Description, .Timing, .Money, .Payment]
} else {
return [.Candidates, .Expiration, .Description, .Timing, .Money, .Payment]
}
}
But now the problem, when I try to get the value of my enum inside my rows it throws errors. Please look at the code below.
func cellViewModelForRowAtIndexPath(indexPath: NSIndexPath) -> ViewModel {
guard
let row = rows.value[indexPath.row],
let response = self.application.value
else {
fatalError("")
}
switch row {
case .Candidates:
// Do something
case .Expiration:
// Do something
case .Description:
// Do something
case .Timing:
// Do something
case .Money:
// Do something
case .Payment:
// Do something
}
}
It throws an error: Enum case 'some' not found in type 'OfferViewRow on the line let row = rows.value[indexPath.row]
And on every switch statements it throws: Enum case 'Candidates' not found in type '<<Error type>>
Can someone help me with this?
The guard statement wants an optional, as hinted by "Enum case 'some'" in the error message.
But rows.value[indexPath.row] is not Optional<OfferViewRow>, it is a raw OfferViewRow. So it won't enter a guard statement.
Move let row = rows.value[indexPath.row] one line up: Swift takes care of bounds checking, and will crash if indexPath.row is out of bounds.
I would like to pass a function as a parameter because I am dealing with web services, and I have noticed that the code is repetitive.
Snippet 1
Service.getAllVouchersUsingCallback() { (response, data, error) -> Void in
guard let statusCode = response?.statusCode else {
Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
return
}
switch statusCode {
case 200:
self.loadVouchersWithData(data!)
case 503:
Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
default:
Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
}
}
Snippet 2
Service.getAllCategoriesUsingCallback { (response, data, error) -> Void in
guard let statusCode = response?.statusCode else {
Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
return
}
switch statusCode {
case 200:
self.loadAndGetCategories(data!, withInialText: "Category ")
case 503:
Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
default:
Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
}
}
The part that is repetitive is what happens when the status code is nil, and the action I have to perform when the response is 200. I guess the function signature should be like this:
func dealWithWebServiceResponse(response: NSURLResponse?, withData data: NSData?, whichActionIs action: whateverFunctionType)
So, I would like to know how I can pass whatever function, i.e., whatever number of parameters or whatever number of return values because in this case I am passing just data, but probably in the future I will need another kind of function.
Thanks in advance.
This is a great problem to explore functions that return functions. So we have this block of code:
guard let statusCode = response?.statusCode else {
Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
return
}
switch statusCode {
case 200:
// <<================ Right here, we want to do "something different"
case 503:
Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
default:
Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
}
So how do we do "something different?" We pass a function. That function needs to take "data" because that's the only thing we have. You might be thinking that the function takes "other things" (like "Category "), but it really doesn't. This code doesn't know anything about "Category ". Something else, earlier in the program, has to have dealt with that part. The only thing that varies here is the data. So let's pretend we have that function for a second:
let success: (NSData) -> Void = ...
...
case 200:
success(data!)
...
We just want to figure out what success is in this case. Well, in your first example, it's:
{ self.loadVouchersWithData($0) }
and in your second example it's:
{ self.loadAndGetCategories($0, withInialText: "Category ") }
Those are both functions that take an NSData and return nothing, just like we want.
So we want a way to take that first block of code and plug-in this thing that changes. We want a function that takes an "on success" function and returns a "handle all the stuff" function. Let's write that out the long way:
func successHandler(success: (NSData) -> Void) -> (NSHTTPURLResponse?, NSData?, NSError?) -> Void {
return { (response: NSHTTPURLResponse?, data: NSData?, error: NSError?) in
guard let statusCode = response?.statusCode else {
Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
return
}
switch statusCode {
case 200:
success(data!) // <==== Here's the part that changes!
case 503:
Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
default:
Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
}
}
}
Whoa, that first line is a doozy. Let's look at it:
func successHandler(success: (NSData) -> Void) -> (NSHTTPURLResponse?, NSData?, NSError?) -> Void {
This is a function, that takes a function that takes an NSData and returns nothing, and that whole function returns a function that takes a response,data,error tuple, and returns nothing. Meditate on that for a moment. You really want that to sink in because it's really, really powerful.
OK, hopefully that's starting to sink in a little bit, so I'm going to move on. The syntax is pretty enormous, so Swift gives us a nice trick to simplify it, called currying:
func successHandler(success: (NSData) -> Void)(response: NSHTTPURLResponse?, data: NSData?, error: NSError?) {
guard let statusCode = response?.statusCode else {
Util.showToastWithMessage(Messages.NO_INTERNET_CONNECTION)
return
}
switch statusCode {
case 200:
success(data!) // <==== Here's the part that changes!
case 503:
Util.showToastWithMessage(Messages.SERVICES_NOT_AVAILABLE)
default:
Util.showToastWithMessage(Messages.UNEXPECTED_RESPONSE)
}
}
The declaration is now:
func successHandler(success: (NSData) -> Void)(response: NSHTTPURLResponse?, data: NSData?, error: NSError?) {
(I know that probably doesn't seem much simpler, but it really is, and it definitely makes the rest of the function simpler.)
That is (almost) exactly identical to the previous declaration. Mediate on that line for a moment. Note the f(x: T)(y: U) double-parentheses syntax. Note where I could drop -> Void at the end.
Currying is like passing some parameters now, and being able to pass the rest later.
OK, so how do we use that?
Service.getAllVouchersUsingCallback(successHandler{ self.loadVouchersWithData($0) })
Service.getAllCategoriesUsingCallback(successHandler{ self.loadAndGetCategories($0, withInialText: "Category ") })
We call our thing that wants a (response,data,error) and pass it the result of calling successHandler with a function that takes a data.
And that should remove all the duplication you were talking about. This is a particularly complicated version of currying because there are a lot of levels of functions. But it also shows how powerful the technique is.
You may want to put this aside for a moment and go back to a simpler introduction such as Introduction to Function Currying in Swift. Then, when that makes sense, come back to this.