Can't convert throwing to non-throwing completion block error - swift

I have an #objc protocol defined like this:
#objc public protocol MyDelegate: AnyObject {
#objc func foo(
completion: #escaping (FooResult) -> Void
) throws
}
where FooResult is:
#objc public enum FooResult: Int {
case succeeded
case delayed
}
I then have a service MyService whose function bar gets called with a completion block. This completion block is of type MyServiceResult:
enum MyServiceResult {
case succeeded
case delayed
case failed(Error)
}
I need to be able to throw that error back from my delegate like this:
func foo(
completion: #escaping (FooResult) -> Void
) throws {
MyService.shared.bar(foo) { result in
switch result {
case .succeeded:
completion(.succeeded)
case .delayed:
completion(.delayed)
case .failed(let error):
throw error // Can't do this!
}
}
}
However, it doesn't work because it complains:
Invalid conversion from throwing function of type '(MyService.MyServiceResult) throws -> Void' to non-throwing function type '(MyService.MyServiceResult) -> Void'
How can I do this? Is this possible?
Because the delegate has to conform to #objc, I can't use an associated value with a failure case which is why I've decided to throw instead.

Related

Constraining a generic initilizer parameter to class generic type

I'm trying to implement a type erasing wrapper for a protocol. I have a protocol with an associatedtype constrainted to CaseIterable and a method using that type.
Given the following definitions:
protocol Foo {
associatedtype A: CaseIterable
func doSomething(input: A)
}
final class AnyFoo<A: CaseIterable>: Foo {
private let _doSomething: (A) -> Void
init<Other: Foo>(wrappedFoo: Other) where Other.A == A {
// "Cannot assign value of type '(Other.A) -> ()' to type '(A) -> Void'"
_doSomething = wrappedFoo.doSomething(input:)
}
func doSomething(input: A) {
_doSomething(input)
}
}
I'm getting the error Cannot assign value of type '(Other.A) -> ()' to type '(A) -> Void' in the initializer of the class. It seems that the compiler interprets A and Other.A as different types, and I can't figure out why, because the initializer has constrained Other.A and A to be the same. If I replace CaseIterable with Hashable, there's no problem.
Can somebody explain why this is happening?
Of course you can't. The _doSomething in class AnyFoo is type of Block which is an Anonymous function. So you can assign wrappedFoo.doSomething(input:) to it. When you call _doSomething(input), it actually call wrappedFoo.doSomething(input: input).
In another way, you can define a Block like:
let completion: (bool) -> void = { finished in
}
but you can't define it like:
let completion: (true) -> void = { finished in
}

Disambiguate a complex closure return type (foo -> _)

I'm using Alamofire's Result class. I've boiled Result down to a simple subset for presentation here.
public enum Result<Value> {
case success(Value)
case failure(Error)
}
extension Result {
/// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
public func mapError<T: Error>(_ transform: (Error) -> T) -> Result {
switch self {
case .failure(let error):
return .failure(transform(error))
case .success:
return self
}
}
}
I can't get a call to mapError to compile.
I make a simple Error class and a couple of results:
class MyError: Error { }
let s: Result<Bool> = .success(true)
let f: Result<Bool> = .failure(MyError())
Now I have something to call mapError on! Maybe I'll print any error before passing it along unchanged:
f.mapError() {
print($0)
return $0
}
Here Swift tells me "error: unable to infer complex closure return type; add explicit type to disambiguate". It doesn't seem that complex to me; mapError passes an Error into the closure, and expects one (or T:Error) returned, but I try placating Swift anyway:
f.mapError() { (e: Error)->Error in
print(e)
return e
}
Now Swift says, "error: cannot convert value of type '(Error) -> Error' to expected argument type '(Error) -> _'".
What is this _ return type? And how should I be writing the closure?
(I realize I can avoid calling mapError, but whatever, I can’t seem to call it. If I really wanted to, how would I even do so?)
You cannot pass a closure of type (Error) -> Error to mapError
because a protocol does not conform to itself, i.e. Error is not
a valid T for the constraint <T: Error>.
Making mapError non-generic
public func mapError(_ transform: (Error) -> Error) -> Result
makes both of your usage examples compile.
What is this _ return type [in the error]?
This means the compiler cannot determine a type, which might give a further clue as to what's going on...

Issue with generic conversion method using RxSwift

I have this result datatype called πŸ’ which wraps a type and a progress
public enum πŸ’<T> {
case Success(T)
case Progress(CGFloat)
}
class func rx_request(router: Router) -> Observable<πŸ’<AnyObject>>
This AnyObject needs to be transformed into a Mappable structure (ObjectMapper), so because I have several of these Mappable structs, I wanted to achieve something like this, a generic map that I can use:
class func rx_convert<T : Mappable>(value: πŸ’<AnyObject>) -> Observable<πŸ’<T>> {
return Observable.create { observer in
switch value {
case .Success(let object):
guard let convertedObject = Mapper<T>().map(object) else {
observer.onError(NetworkError.IncorrectDataReturned)
return NopDisposable.instance
}
observer.on(.Next(πŸ’<T>.Success(convertedObject)))
observer.on(.Completed)
case .Progress(let progress):
observer.on(.Next(πŸ’<T>.Progress(progress)))
break
}
return NopDisposable.instance
}
}
It seems the above definition is wrong because this cannot be compiled: Cannot explicitly specialize a generic function
.flatMapLatest({ _ in
🐯.rx_request(Router.Pipeline)
})
.flatMapLatest({ result -> Observable<πŸ’<ResponsePipeline>> in
🐯.rx_convert<ResponsePipeline>(result)
// Cannot explicitly specialize a generic function
})
What am I doing wrong here?
.flatMapLatest({ result -> Observable<πŸ’<ResponsePipeline>> in
return 🐯.rx_convert(result)
})
Because the closure passed to flatMapLatest is already typed to return an Observable<πŸ’<ResponsePipeline>>, compiler can infer the type for rx_convert to be πŸ’<AnyObject> -> Observable<πŸ’<ResponsePipeline>> so you can drop <ResponsePipeline> from the call.

Throwing an exception from an inherited function without throws

I'm trying to do the following:
protocol X{
func foo()
}
enum XError{
case BAR
}
class Y:X{
func foo(){
throw XError.BAR
}
}
I can't add a throws declaration to the protocol and it complains that
the error is not handled because the enclosing function is not
declared 'throws'.
How can I achieve this?
You need to explicitly add throw in the signature of any function that throws.
So
func foo() throws {
throw XError.BAR
}
This also applies to the protocol definition.
protocol X {
func foo() throws
}
Errors in Swift should conform to the Error protocol.
enum XError: Error {
case BAR
}

Declare a function in Swift that returns a closure with a generic T.Type

we are trying to make a function in Swift , that returns a closure. The return type is as follows:
func completionBlock() -> (Response<T, NSError>) -> ()
where Response itself is a struct with 2 generics. The second will be always NSError, but the first depends on a switch.
Declaration of Response:
public struct Response<Value, Error: ErrorType> { ...
What we want to achieve is a function that will return a closure with a type that depends on a value of variable.
Depending on this value, and with a switch, it specifies one type or another, the only requirement for the generic parameter is that it must conform to a specific protocol, but we can't find a solution.
What we got so far is:
class BaseClass {
var endpoint: Int
...
func completionBlock() -> (Response<T, NSError>) -> () {
switch endpoint
{
case 1:
return getHandleResponseClosure(Car.self)
case 2:
return getHandleResponseClosure(Truck.self)
}
}
func getHandleResponseClosure<T: Mappable>(_: T.Type) -> (Response<T, NSError>) -> () {
let closure = { (_: Response<T, NSError>) -> () in
// ...
}
return closure
}
}
But this don't compile: "Use of undeclared Type T" at func completionBlock(), the type at this point is generic, it only requires to conform to protocol Mappable, but we don't know how to specify this.
Thanks!!, Greetings
While there are other problems with your code, the fundamental one is that generic types in method signatures must be declared directly after the name of the method. Take a look at completionBlock below. This code compiles and works.
struct Response<T, E: Error> {
}
struct Car {}
struct Truck {}
class BaseClass {
func completionBlock<T>() -> ((Response<T, NSError>) -> ()) {
return { response in /* What goes here? */ }
}
}
let c = BaseClass()
let r: (Response<Car, NSError>) -> () = c.completionBlock()
On a side note, I would replace struct Response<T, E: Error> with…
enum Response<T> {
case success(T)
case error(Error)
}