Swift `Failure` keyword meaning in a Swift Combine chain error - swift

Let's say that I've this simple chain that from an HTTP request creates a publisher for <T, APIManagerError>
func run<T:Decodable>(request:URLRequest)->AnyPublisher<T, APIManagerError>{
return URLSession.shared.dataTaskPublisher(for: request)
.map{$0.data}
.decode(type: T.self, decoder: JSONDecoder())
.eraseToAnyPublisher()// it should run mapError before this point
}
This code produces this error since I'm returning Error instead of APIManagerError.
Cannot convert return expression of type
'AnyPublisher<T, Publishers.Decode<Upstream, Output, Coder>.Failure>'
(aka 'AnyPublisher<T, Error>')
to return type 'AnyPublisher<T, RestManagerError>'
I know that to fix the issue I need to add a mapError after .decode.
.mapError{error in
APIManagerError.error("Decode Fail")
}
but I can't really understand what is reported with the error message before the "aka" part that is quite clear instead
How do you read the error Publishers.Decode<Upstream, Output, Coder>.Failure? specifically what does it mean the .Failure part? where can I find the Failure in the Swift Doc?

Since we are talking about two different kinds of errors here lets denote a Compilation Error as CE and the Failure of a Publisher (conforming to Swift.Error) as PF (Publisher Failure).
Your question is about the interpretation of the CE message.
Cannot convert return expression of type
'AnyPublisher<T, Publishers.Decode<Upstream, Output, Coder>.Failure>'
Writes out the resulting returned type of your implementation of func run - without the mapError call. The compiler acknowledges your call to eraseToAnyPublisher() in the end of the function, and also your generic Output of type T. So that covers the Cannot convert return expression of type 'AnyPublisher<T,. As to Publishers.Decode<Upstream, Output, Coder>.Failure>' outputs the derived type of the Failure. This is somewhat a symbolic breakdown of the derived Failure type. Your upstream Publisher is initially of type URLSession.DataTaskPublisher, as a result of your URLSession.shared.dataTaskPublisher call, which you then transform with every Combine operator you call: map and then decode. Resulting in the publisher Publishers.Decode. And the Failure type of cannot be properly "desymbolised" (I lack the proper compiler knowledge to use the correct terminology).
Which Xcode version do you use? The New Diagnostic Architecture might be able to show a better error message. This is in fact the reason why I later in my response use .assertMapError(is: DecodingError.self)
Your code in mapError does the job, but it completely tossed away information about the actual error. So I would not do that. At least print (log) the error. But still I would do something like:
Declare custom Error type
Intuitively we have at least two different kinds of errors, either networking or decoding. But potentially more...
public enum HTTPError: Swift.Error {
indirect case networkingError(NetworkingError)
indirect case decodingError(DecodingError)
}
public extension HTTPError {
enum NetworkingError: Swift.Error {
case urlError(URLError)
case invalidServerResponse(URLResponse)
case invalidServerStatusCode(Int)
}
}
You might need to tell combine that the error type is indeed DecodingError, thus I've declared some fatalError macros useful for this information. It is somewhat simular to Combine's setFailureType (but which only works when the upstream publisher has Failure type Never, thus we cannot use it here).
castOrKill
func typeErasureExpected<T>(
instance incorrectTypeOfThisInstance: Any,
toBe expectedType: T.Type,
_ file: String = #file,
_ line: Int = #line
) -> Never {
let incorrectTypeString = String(describing: Mirror(reflecting: incorrectTypeOfThisInstance).subjectType)
fatalError(
"Incorrect implementation: Expected variable '\(incorrectTypeOfThisInstance)' (type: '\(incorrectTypeString)') to be of type `\(expectedType)`",
file, line
)
}
func castOrKill<T>(
instance anyInstance: Any,
toType: T.Type,
_ file: String = #file,
_ line: Int = #line
) -> T {
guard let instance = anyInstance as? T else {
typeErasureExpected(instance: anyInstance, toBe: T.self, file, line)
}
return instance
}
And then create a convenience method on Publisher, similar to setFailureType:
extension Publisher {
func assertMapError<NewFailure>(is newFailureType: NewFailure.Type) -> AnyPublisher<Output, NewFailure> where NewFailure: Swift.Error {
return self.mapError { castOrKill(instance: $0, toType: NewFailure.self) }.eraseToAnyPublisher()
}
}
Usage:
I took the liberty of catching some more errors in your examples. Asserting e.g. that the server responds with a non failure HTTP status code etc.
func run<Model>(request: URLRequest) -> AnyPublisher<Model, HTTPError> where Model: Decodable {
URLSession.shared
.dataTaskPublisher(for: request)
.mapError { HTTPError.NetworkingError.urlError($0) }
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse else {
throw HTTPError.NetworkingError.invalidServerResponse(response)
}
guard case 200...299 = httpResponse.statusCode else {
throw HTTPError.NetworkingError.invalidServerStatusCode(httpResponse.statusCode)
}
return data
}
.decode(type: Model.self, decoder: JSONDecoder())
// It's unfortunate that Combine does not pick up that failure type is `DecodingError`
// thus we have to manually tell the Publisher this.
.assertMapError(is: DecodingError.self)
.mapError { HTTPError.decodingError($0) }
.eraseToAnyPublisher()
}
Bonus - equality check for HTTPError
It is indeed very advantageous if our error types are Equatable, it makes writing unit tests so much easier. Either we go the Equatable route, or we can do some reflection magic. I will present both solutions, but the Equatable solution is more robust for sure.
Equatable
In order to make HTTPError conform to Equatable we only need to manually make DecodingError equatable. I've done this with this code:
extension DecodingError: Equatable {
public static func == (lhs: DecodingError, rhs: DecodingError) -> Bool {
switch (lhs, rhs) {
/// `typeMismatch` is an indication that a value of the given type could not
/// be decoded because it did not match the type of what was found in the
/// encoded payload. As associated values, this case contains the attempted
/// type and context for debugging.
case (
.typeMismatch(let lhsType, let lhsContext),
.typeMismatch(let rhsType, let rhsContext)):
return lhsType == rhsType && lhsContext == rhsContext
/// `valueNotFound` is an indication that a non-optional value of the given
/// type was expected, but a null value was found. As associated values,
/// this case contains the attempted type and context for debugging.
case (
.valueNotFound(let lhsType, let lhsContext),
.valueNotFound(let rhsType, let rhsContext)):
return lhsType == rhsType && lhsContext == rhsContext
/// `keyNotFound` is an indication that a keyed decoding container was asked
/// for an entry for the given key, but did not contain one. As associated values,
/// this case contains the attempted key and context for debugging.
case (
.keyNotFound(let lhsKey, let lhsContext),
.keyNotFound(let rhsKey, let rhsContext)):
return lhsKey.stringValue == rhsKey.stringValue && lhsContext == rhsContext
/// `dataCorrupted` is an indication that the data is corrupted or otherwise
/// invalid. As an associated value, this case contains the context for debugging.
case (
.dataCorrupted(let lhsContext),
.dataCorrupted(let rhsContext)):
return lhsContext == rhsContext
default: return false
}
}
}
extension DecodingError.Context: Equatable {
public static func == (lhs: DecodingError.Context, rhs: DecodingError.Context) -> Bool {
return lhs.debugDescription == rhs.debugDescription
}
}
Which, as you can see, also has to make DecodingError.Context Equatable.
Then you can declare these XCTest helpers:
func XCTAssertThrowsSpecificError<ReturnValue, ExpectedError>(
file: StaticString = #file,
line: UInt = #line,
_ codeThatThrows: #autoclosure () throws -> ReturnValue,
_ error: ExpectedError,
_ message: String = ""
) where ExpectedError: Swift.Error & Equatable {
XCTAssertThrowsError(try codeThatThrows(), message, file: file, line: line) { someError in
guard let expectedErrorType = someError as? ExpectedError else {
XCTFail("Expected code to throw error of type: <\(ExpectedError.self)>, but got error: <\(someError)>, of type: <\(type(of: someError))>")
return
}
XCTAssertEqual(expectedErrorType, error, line: line)
}
}
func XCTAssertThrowsSpecificError<ExpectedError>(
_ codeThatThrows: #autoclosure () throws -> Void,
_ error: ExpectedError,
_ message: String = ""
) where ExpectedError: Swift.Error & Equatable {
XCTAssertThrowsError(try codeThatThrows(), message) { someError in
guard let expectedErrorType = someError as? ExpectedError else {
XCTFail("Expected code to throw error of type: <\(ExpectedError.self)>, but got error: <\(someError)>, of type: <\(type(of: someError))>")
return
}
XCTAssertEqual(expectedErrorType, error)
}
}
func XCTAssertThrowsSpecificErrorType<Error>(
_ codeThatThrows: #autoclosure () throws -> Void,
_ errorType: Error.Type,
_ message: String = ""
) where Error: Swift.Error & Equatable {
XCTAssertThrowsError(try codeThatThrows(), message) { someError in
XCTAssertTrue(someError is Error, "Expected code to throw error of type: <\(Error.self)>, but got error: <\(someError)>, of type: <\(type(of: someError))>")
}
}
Reflection magic
Or you can take a look at my Gist here which does not make use of Equatable at all, but can "compare" any enums errors which are not conforming to Equatable.
Usage
Together with CombineExpectation you can now write unit tests of your Combine code and compare errors more easily!

I've easily found the answer actually but I keep the question open in case someone else needed it.
The Failure keyword in this case is just the associatedtype that comes with the Publisher protocol.
In this case I'm just getting the Failure type for the Publishers.Decode, that by default is just Error.

Related

How to differentiate what generic value is what in Swift?

How can I differentiate what generic value is for what in Swift?
For example, what does the value 'T' do and what does the value 'E' do?
func ??<T, E>(result: Result<T, E>, handleError: (E) -> T) -> T {
switch result {
case let .success(value):
return value
case let .failure(error):
return handleError(error)
}
}
what does the value 'T' do and what does the value 'E' do?
They're not "values", they're the names of types — on a par with a term like String or Int. In fact, T could be String or Int. But E has to be some type of Error.
So the phrase <T, E>, which appears twice, just refers to the fact that T and E are generic placeholders for their real types. When someone actually calls this ?? function, the caller will make clear what T and E really are. This is called resolving the generic.
So let's imagine that we call ?? in such a way as to resolve T to String and E to Error. Then in the mind of the compiler, we'll have this:
func ??(result: Result<String, Error>, handleError: (Error) -> String) -> String {
switch result {
case let .success(value):
return value
case let .failure(error):
return handleError(error)
}
}
So now we can read the function declaration. It says: "You hand me two parameters. One, result:, must be a Result enum whose success type is String and whose failure type is Error. The other, handleError:, must be a function that takes an Error and returns a String. And I will return a String to you."
Except, of course, that that is only one way out of an infinite number of ways to resolve T and E. They stand in for the real types that will be resolved a compiled time, depending on how ?? is actually called. So there's your answer; that is "what they do". They are placeholders standing for the real types that will be resolved at compile time.
And in fact, to demonstrate, I will call your function in a way that resolves T to String and E to Error (though I will rename your function myFunc to make the name legal):
func myFunc<T, E>(result: Result<T, E>, handleError: (E) -> T) -> T {
switch result {
case let .success(value):
return value
case let .failure(error):
return handleError(error)
}
}
enum MyError : Error { case oops }
let r = Result<String, Error> { throw MyError.oops }
let output = myFunc(result:r) { err in "Ooops" }
print(output) // Ooops
Footnote: Note that your function cannot really be called ??, as that name is taken. It might have been better to call it foo (the usual nonsense name in these situations).
#matt's answer nails it, so I thought I'd add a like bit of higher level commentary.
Type parameter naming
The use of single characters for these generic type parameters is bit of a legacy a convention from Java and C#, but it need not be so concise. In this example, if you look at the main constraint on the types (Result), note that they use Success and Failure. Using these here would provide a clearer idea about the intent of this function:
func ??<Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success
Thus, a function that takes:
a Result that can either contain a Success or a Failure, and
a closure that takes a Failure and returns a Success
and returns:
a Success
Implementations bourne out of type system constraints
Note that as none of these types are wrapped in Optional the implementation of this function is almost entirely constrained (side-effects notwithstanding).
Take, for example, the simplest function that could seemingly match:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
return Success()
}
Attempting to compile this gives the following error:
main.swift:2:12: error: type 'Success' has no member 'init'
return Success()
^~~~~~~
due to the fact that the Success type is entirely unconstrained in Result, so we don't actually know how to create one inside the function.
We know that Result can contain a Success, so what if we try and forcibly get that?
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
return result.get()
}
This now fails with the following compiler error:
main.swift:2:12: error: call can throw, but it is not marked with 'try' and the error is not handled
return result.get()
^
due to the fact that this function has explicitly denoted that it will not throw, and Result.get() will throw the contained Failure if it does not contain a Success.
The other way to get the Success out of a Result is pattern matching, let's see how that works out matching the enumeration case pattern for a single if:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
if case let .success(success) = result {
return success
}
}
When attempting this, the compilation error is (quite sensibly):
main.swift:5:1: error: missing return in a function expected to return 'Success'
}
^
So we need to also handle the case when a Result contains a Failure. Let's try with another pattern match, using the provided handleError closure:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
if case let .success(success) = result {
return success
}
if case let .failure(failure) = result {
return handleError(failure)
}
}
This still gives the same error as the previous attempt, as the logic could still fall through in this case (this seems unlikely, but this could be subject to a kind of time-of-check to time-of-use bug).
Let's attempt it again, but matching both patterns in a switch:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
switch result {
case let .success(success):
return success
case let .failure(failure):
return handleError(failure)
}
}
There we go, compiling without error.
As is obvious, this is the original function, as provided in your question.
Now that we've arrived back here, can we trim down this implementation? We could try something like:
func ?? <Success, Failure>(result: Result<Success, Failure>, handleError: (Failure) -> Success) -> Success {
switch result {
case let .success(success):
return success
}
}
but this provides this compilation error:
main.swift:2:5: error: switch must be exhaustive
switch result {
^
main.swift:2:5: note: add missing case: '.failure(_)'
switch result {
^
which indicates that we need that .failure(_) case to match all possible outcomes of the result.

How to fix cannot convert value of type Result<SuccessType<A>, Error> to closure result type Result<Success, Failure> Swift

I'm trying to map over Swift's Result type to produce a new Result of a different type but it isn't type checking, and is throwing the error:
error: cannot convert value of type Result<ParseSuccess<B>, ParseError> to closure result type Result<Success, Failure>
I've tried using a switch on the result and matching on the .success and .failure but I get a similar type checking error. I've also tried specifying the input and return types explicitly with no luck.
I've replicated the code in other languages I'm a little more familiar with (F# and Kotlin) and it seems to type check there.
The swift compiler shows a sqiggly under this line:
return self.apply(input).map { result in ParseSuccess(transform(result.data), result.input) }
Where am I going wrong with this code?
import Foundation
class ParseError : Error {
let err: String
init(_ err: String) {
self.err = err
}
}
class ParseSuccess<A> {
let data: A
let input: Substring
init(_ data: A, _ input: Substring) {
self.data = data
self.input = input
}
}
class Parser<A> {
let fn: (Substring) -> Result<ParseSuccess<A>, ParseError>
init(_ fn: #escaping (Substring) -> Result<ParseSuccess<A>, ParseError>) {
self.fn = fn
}
func apply(_ input: Substring) -> Result<ParseSuccess<A>, ParseError> {
return self.fn(input)
}
func map<B>(_ transform: #escaping (A) -> B) -> Parser<B> {
return Parser { (input) in
return self.apply(input).map { result in
ParseSuccess(transform(result.data), result.input) }
}
}
}
For what it's worth, I'm sure this is probably not the best way to start writing a parser combinator, but I've gotten caught up in trying to figure out where I went wrong here!
Thank you
It's a weird behaviour of the unqualified used of Paraser in the line return Parser {. When a generic type is mentioned in its unqualified form, within the body of the generic type's declaration, the type parameters are inferred to be unchanged. For example, a function on Array<T> can return simply Array, and the T is implied to be the same as the value of T for self. Needless to say, I'm not a huge fan of this behaviour.
Changing it to return Parser<B> { ... solves the issue.
Here's how I found the issue. When working with generics errors, I always try to insert a ton of type annotations to "pin down" the data types, to confirm that the compiler and me are on the same page.
I first wrote:
func map<B>(_ transform: #escaping (A) -> B) -> Parser<B> {
return Parser { (input) in
return self.apply(input).map { result in
let newData: ParseSuccess<B> = transform(result.data)
return ParseSuccess(newData, result.input)
}
}
}
At which point the error changed to:
Untitled 2.swift:32:10: error: cannot convert return expression of type 'Parser<A>' to return type 'Parser<B>'
return Parser { (input) in
^~~~~~~~~~~~~~~~~~~~

How do you properly use a generic type in Swift's new Result type?

I'm trying to use Swift's new Result type in such a way that the type of the .success associated value is a generic. The rather contrived sample code below works, but is there a way to simplify the type casting so that the compiler can infer the correct type for T?
enum FetchError : Error {
case unknownKey
}
enum FetchKey {
case getWidth
case getName
}
func fetchValue<T>(_ key:FetchKey) -> Result<T, FetchError> {
switch key {
case .getName:
// Ideally I would like to just use: return .success("Johnny Appleseed")
return Result<String, FetchError>.success("Johnny Appleseed") as! Result<T, FetchError>
case .getWidth:
return Result<Double, FetchError>.success(25.0) as! Result<T, FetchError>
#unknown default:
return .failure(.unknownKey)
}
}
// This explicit type declaration is also required.
let r:Result<String, FetchError> = fetchValue(.getName)
print(r)
To your question about the type-casting, you can definitely simplify it:
case .getName:
return .success("Johnny Appleseed" as! T)
This is ok if asking for the wrong type should be considered a programming error (and so should crash), and the results will never come from external sources. If the data could ever come from an external source, then you should never crash in response to it being wrong.
In that case, we should model that kind of error:
enum FetchError : Error {
case unknownKey
case invalidType
}
Then you can have a syntax very close to what you like by adding a function to do the (possibly failing) type conversion:
func fetchValue<Value>(_ key:FetchKey) -> Result<Value, FetchError> {
func checkType(_ value: Any) -> Result<Value, FetchError> {
guard let value = value as? Value else { return .failure(.invalidType) }
return .success(value)
}
switch key {
case .getName: return checkType("Johnny Appleseed")
case .getWidth: return checkType(25.0)
#unknown default: return .failure(.unknownKey)
}
}
That said, I'd do it this way to avoid the ugliness of required type annotations:
func fetch<Value>(_: Value.Type, forKey key: FetchKey) -> Result<Value, FetchError> { ... }
let r = fetch(String.self, forKey: .getName)
This follows the pattern of Codable.
Here's the whole solution together in one place in a few different ways:
Returning Result
enum FetchError : Error {
case unknownKey
case invalidType
}
enum FetchKey {
case width
case name
}
func fetch<Value>(_: Value.Type, forKey key: FetchKey) -> Result<Value, FetchError> {
func checkType(_ value: Any) -> Result<Value, FetchError> {
guard let value = value as? Value else { return .failure(.invalidType) }
return .success(value)
}
switch key {
case .name: return checkType("Johnny Appleseed")
case .width: return checkType(25.0)
#unknown default: return .failure(.unknownKey)
}
}
With throws
I think this gets a little nicer if you throw rather than wrapping things into Result. It means you can more easily lift checkType into one place and gets very close to the syntax you said you wanted.
func fetch<Value>(_: Value.Type, forKey key: FetchKey) throws -> Value {
func checkType(value: () throws -> Any) throws -> Value {
guard let value = try value() as? Value else { throw FetchError.invalidType }
return value
}
return try checkType {
switch key {
case .name: return "Johnny Appleseed"
case .width: return 25.0
#unknown default: throw FetchError.unknownKey
}
}
}
With Optionals
This gets a little simpler with Optional if you don't really care about the errors.
func fetch<Value>(_: Value.Type, forKey key: FetchKey) -> Value? {
func _fetch() -> Any? {
switch key {
case .name: return "Johnny Appleseed"
case .width: return 25.0
#unknown default: return nil
}
}
return _fetch() as? Value
}
The function you are trying to create has a dynamically typed generic. This isn't possible as the Swift compiler needs to know the types for each method/variable at compile time.
Suppose you have
func whatToFetch() -> FetchKey {
// Randomly returns one of the FetchKey cases
}
let r = fetchValue(whatToFetch())
There is no way for the compiler to know the type of r before the call is made. Your example code works because your force-casts are perfectly lined up. But in a correct implementation you should not have to specify what type T is inside your generic function
In your case, it seems generics are not really needed and you could do away with protocols:
enum FetchKey {
case someStringValueKey
case someDoubleValueKey
case someModelValueKey
}
protocol FetchResult {}
extension String: FetchResult {}
extension Double: FetchResult {}
extension SomeModel: FetchResult {}
func fetch(_ key: FetchKey) -> Result<FetchResult,Error> {
switch key {
case .someStringValueKey:
return .success("")
case .someDoubleValueKey:
return .success(1.0)
case .someModelValueKey:
return .success(SomeModel())
}
}
let wtf = whatToFetch()
let r = fetch(wtf)
switch r {
case .success(let value):
switch wtf {
case .someStringValueKey:
guard let stringValue = value as? String else { return }
case .someDoubleValueKey:
guard let doubleValue = value as? Double else { return }
case .someModelValueKey:
guard let someModelValue = value as? SomeModel else { return }
}
case .failure(let error):
print("Better do more than just print on production code")
}
Trying to use an unqualified generic fetchValue<T> to do what you want probably isn’t going to work. The reason is that in a generic function, the T is specified by the caller, not the function. You’re essentially saying, “Ask fetchValue for any T you want, and it will give you a result.”
There’s missing link in the way you’ve set up your types. The red flag that’s your primary clue to this is the use of as!. That’s a sign that you are making assumptions about type relationships that you’re not telling the compiler about.
What does that mean? Well, note that with your code, let r: Result<URLConnection, FetchError> = fetchValue(.getName) also compiles — but then crashes at runtime!
One solution is to have an umbrella type that gathers all the possible result types, as Emil’s solution does. In this approach, you erase the type of the result, and ask callers to dynamically extract it. Callers have to deal with the possibility that the result might be of any type, not necessarily the one they expected. This is a common pattern when dealing with dynamically structured data such as JSON documents.
A second approach is to do what Rob suggests: let the caller specify the type, but translate an incorrect type into an error.
A third approach to solving this is to find a way to associate keys with result types in the type system, i.e. to tell the compiler getName → String, getWidth → Double. Unfortunately, AFAIK, there’s no way to do that with individual enum cases, so you’ll need to encode the keys as something other than an enum.
Here’s one way to do it:
enum FetchError : Error {
case unknownKey
}
protocol FetchKey {
associatedtype ValueType
func get() -> ValueType
}
struct GetNameKey: FetchKey {
func get() -> String { // This line establishes the getName → String mapping
return "Johnny Appleseed"
}
}
struct GetWidthKey: FetchKey {
func get() -> Double { // This line establishes the getWidth → Double mapping
return 25.0
}
}
// This function signature means: “If you give fetchValue a key, it
// will give you a result _with the appropriate type_ for that key”
//
func fetchValue<K: FetchKey>(_ key: K) -> Result<K.ValueType, FetchError> {
// The return type here is Result<K.ValueType, FetchError>, but
// now Swift has enough into to infer it!
return Result.success(key.get())
}
// This now works without type inference:
let r0 = fetchValue(GetNameKey())
print(r0)
// And this now (correctly) fails to compile:
// let r1: Result<Double, FetchError> = fetchValue(GetNameKey())
Which approach should you use?
Does each key always return a single, consistent type that you know beforehand (e.g. name is always a string)?
Yes: Use my approach above
No: If a fetch succeeds, but the type that came back isn’t the type the caller asked for, how do you want it reported?
As a Result.failure, and the caller can’t see the value: Use Rob’s approach
As a Result.success, and the caller has to figure out what type the value is: Use Emil’s approach

Swift How to returning a tuple from a do catch where conditional binding must have optional type?

I am wanting to put a swift 3 do-catch inside a function rather than constantly writing it everywhere I need it; inside this function I wish to return a tuple with a boolean, and an optional error.
I am trying to return a tuple from the function and handle the result in my XCTest
However, I get an error saying:
Initializer for conditional binding must have Optional type, not '(Bool, Error?)' (aka '(Bool, Optional)')
My function is as follows;
public static func isValidPurchase(train: Train, player: Player) -> (Bool, Error?) {
do {
let result = try train.canBePurchased(by: player)
return (result, nil)
} catch let error {
return (false, error)
}
}
My canBePurchased code is a bit long, but it goes like this:
func canBePurchased(by player: Player) throws -> Bool {
if (!self.isUnlocked) {
throw ErrorCode.trainIsNotUnlocked(train: self)
}
// other if-statements and throws go here
}
And in my XCTest I call it as such:
if let result = TrainAPI.isValidPurchase(train: firstTrain, player: firstPlayer) as! (Bool, Error?) {
}
I've tried to force cast:
if let result: (Bool, Error?) ...
but this only demotes the compiler error to a warning.
The complier displays the error as noted above.
What am I doing wrong in terms of Initializer for conditional binding must have Optional type and how do I avoid it?
Thanks
The return type from isValidPurchase(train:player) is (Bool, Error?), which is not an optional (it is a tuple where the 2nd member happens to be an optional). Hence, there is no use for optional binding when capturing the return from a call to isValidPurchase(train:player). You simply assign the return value and study it's content (possible error etc) from there:
// e.g. using explicitly separate tuple members
let (result, error) = TrainAPI
.isValidPurchase(train: firstTrain, player: firstPlayer)
if let error = error { /* you have an error */ }
else { /* no error, proceed with 'result' */ }
Or, studying the return using a switch statement:
// result is a tuple of type (Bool, Error?)
let result = TrainAPI
.isValidPurchase(train: firstTrain, player: firstPlayer)
switch result {
case (_, let error?): print("An error occured!")
case (let result, _): print("Result = \(result)")
}
Just use optional casting instead of force casting. Using force casting result would have a non-optional value even if used without the if let statement.
if let result = TrainAPI.isValidPurchase(train: firstTrain, player: firstPlayer) as? (Bool, Error?) {
}

Asynchronous error handling in swift 2

So I tried error handling thing in swift 2. But one thing that I am not sure about is how to get it work for asynchronous callback functions. Suppose I am loading a resource from backend. I defined my error type like the following:
enum NetworkError: ErrorType {
case NoConnection
case InvalidJSON
case NoSuccessCode(code: Int)
}
I am planning to throw one of these cases when something wrong. Here is the function that makes network call:
func loadRequest<T: Decodable>(request: NSURLRequest, callback:T -> Void) throws {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
// Other code that parses json and at somewhere it throws
throw NetworkError.NoConnection
}
}
But here compiler gives error:
Cannot invoke dataTaskWithRequest with an argument list of type
(NSURLRequest, (_,_,_) throws) -> Void)
From here it is obvious that same closure type is considered as a different type when it is declared with throws.
So how is this do-try-catch thing works in these situations?
An error cannot be thrown asynchronously because the the function will already have returned when the error occurs, you have to handle the error within the closure by calling back to some function with an ErrorType parameter to decide what you want to do with it. Example:
import Foundation
enum NetworkError: ErrorType {
case NoConnection
case InvalidJSON
case NoSuccessCode(code: Int)
}
func getTask() -> NSURLSessionDataTask? {
let session = NSURLSession.sharedSession()
let urlRequest = NSURLRequest(URL: NSURL(string: "www.google.com")!)
return session.dataTaskWithRequest(urlRequest) { data, response, error in
if let error = error {
asyncError(error)
} else {
// Do your stuff while calling asyncError when an error occurs
}
}
}
func asyncError(error: ErrorType) {
switch error {
case NetworkError.NoConnection:
// Do something
break
default:
break
}
}
Nothing in NSURLSession.h seems to throw exceptions. So I wonder if that class has been converted to use this new functionality yet.