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
}
Related
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.
While reading swift forum about exceptions I found interesting issue. One of the examples about exceptions was something like this:
protocol Base {
func foo() throws -> Int
}
protocol Refined: Base {
func foo() -> Int
}
struct Test: Refined {
func foo() -> Int {
0
}
}
It's interesting, I thought it was typo that it would not compile, but it does. I am not sure how this does work behind the scenes. I mean when protocol adopts another protocol it adopts also its requirements. But in this case declaring same method without throws somehow satisfies also first protocol Base.
At the very least I expected Test to need to have 2 implementations of foo. What am I missing here?
This is because a non-throwing function is by definition a sub-type of a throwing function
From the Swift Programming Language book
The throws keyword is part of a function’s type, and nonthrowing functions are subtypes of throwing functions. As a result, you can use a nonthrowing function in the same places as a throwing one.
But you can't do it the other way around so the below code will generate an error
protocol Base {
func foo() -> Int
}
protocol Refined: Base {
func foo() throws -> Int //error: Cannot override non-throwing method with throwing method
}
Also note that this is not only for protocols, if you remove the func declaration from Refined you can still implement the function in Test as non throwing.
This is my code:
class GenericClass<T: UITableViewCell> {
let enumProperty = SomeEnum.myValue
enum SomeEnum {
case myValue
}
func callOtherClass() {
OtherClass.handle(property: enumProperty) // Compile error
}
}
class OtherClass {
static func handle(property: GenericClass<UITableViewCell>.SomeEnum) {}
}
Why do I get the compile error:
Cannot convert value of type 'GenericClass.SomeEnum' to expected
argument type 'GenericClass.SomeEnum'
Ofcourse, the fix would be adding the cast:
as! GenericClass<UITableViewCell>.SomeEnum
which results in this ugly code:
func callOtherClass() {
OtherClass.handle(property: enumProperty) as! GenericClass<UITableViewCell>.SomeEnum
}
But why do I need to cast? self is defined as GenericClass where T always is a UITableViewCell. The method handle expects that signature.
Is there any case this cast is needed because in some situations this will/can fail? I would not expect Swift just randomly asking me to insert a force cast. I expect Swift can just infer the types and sees it is safe, but somehow, Swift doesn't agree with me.
The problem here is that SomeEnum is actually GenericClass<T>.SomeEnum. There's no promise that T is exactly UITableViewCell, so it's not compatible with GenericClass<UITableViewCell> (generics are not covariant).
Typically in this case, what you want to do is move SomeEnum outside of GenericClass, since nothing about it is actually generic:
enum SomeEnum {
case myValue
}
class GenericClass<T: UITableViewCell> {
let enumProperty = SomeEnum.myValue
func callOtherClass() {
OtherClass.handle(property: enumProperty) // Compile error
}
}
class OtherClass {
static func handle(property: SomeEnum) {}
}
But if there's a reason for it to be generic, see Robert Dresler's answer, which is how you would specialize the function correctly:
class OtherClass {
static func handle<T: UITableViewCell>(property: GenericClass<T>.SomeEnum) {}
}
Make your static method also generic and create generic constrain for parameter inheriting from UITableViewCell. Then use this generic parameter in method parameter
class OtherClass {
static func handle<T: UITableViewCell>(property: GenericClass<T>.SomeEnum) {}
}
I'm trying to deserialise XML to a class using the example provided at the end of the readme, but it's raising the same compile time error as originally provoked this question.
method 'deserialize' in non-final class 'Element' must return Self to conform to protocol 'XMLElementDeserializable'
I have tried to use the example as verbatim as possible (although changing quite a bit as Date, a struct, is now mainly used over NSDate), but I am still getting the same issue.
This is the code I'm trying to use (essentially identical just stripped down for clarity
import Foundation
import SWXMLHash
class Element: XMLElementDeserializable {
public static func deserialize(element: SWXMLHash.XMLElement) throws -> Self {
return value(Element())
}
private static func value<T>(_ element: Element) -> T {
return element as! T
}
}
deserialize implementation does not implement the one which is blueprinted
Your own deserialize function does not implement the one blueprinted from XMLElementDeserializable, meaning a default implementation of deserialize (from XMLElementDeserializable) will become available to your class Element. This default implementation is a throwing erroneous one, which is the source of your somewhat obfuscated error message.
From the source code of the SWXMLHash framework
/// Provides XMLElement deserialization / type transformation support
public protocol XMLElementDeserializable {
/// Method for deserializing elements from XMLElement
static func deserialize(_ element: XMLElement) throws -> Self
/* ^^^^^^^^^- take note of the omitted external name
in this signature */
}
/// Provides XMLElement deserialization / type transformation support
public extension XMLElementDeserializable {
/**
A default implementation that will throw an error if it is called
- parameters:
- element: the XMLElement to be deserialized
- throws: an XMLDeserializationError.ImplementationIsMissing if no implementation is found
- returns: this won't ever return because of the error being thrown
*/
static func deserialize(_ element: XMLElement) throws -> Self {
throw XMLDeserializationError.ImplementationIsMissing(
method: "XMLElementDeserializable.deserialize(element: XMLElement)")
}
}
Note the mismatch between the signature of your deserialize method and the blueprinted one: the latter explicitly omits its external parameter name (_), whereas yours does not.
Analyzing the error case with a minimal example
We can construct an analogous, minimal example to achieve the same error message.
enum FooError : Error {
case error
}
protocol Foo {
static func bar(_ baz: Int) throws -> Self
}
extension Foo {
static func bar(_ baz: Int) throws -> Self {
throw FooError.error
}
}
// Bar implements its own bar(baz:) method, one which does
// NOT implement bar(_:) from the protocol Foo. This means
// that the throwing erroneous default implementation of
// bar(_:) becomes available to Bar, yielding the same error
// message as in your question
class Bar : Foo {
// does not match the blueprint!
static func bar(baz: Int) throws -> Self {
return value(Bar())
}
private static func value<T>(_ bar: Bar) -> T {
return bar as! T
}
}
// Error!
Which yields the following error message:
error: method 'bar' in non-final class 'Bar' must return Self to
conform to protocol 'Foo'
static func bar(_ baz: Int) throws -> Self { ...
If we fix the signature of the bar method in Bar to match that of the one blueprinted in Foo, we're no longer prompted with an error
/* ... FooError and Foo as above */
// Bar implements bar(_:) from protocol Foo, which
// means the throwing erroneous default implementation
// of bar(_:) is never in effect, OK
class Bar : Foo {
static func bar(_ baz: Int) throws -> Self {
return value(Bar())
}
private static func value<T>(_ bar: Bar) -> T {
return bar as! T
}
}
To avoid the type inference fix (inferring Bar() as a valid Self instance), mark Bar as final and explicitly annotate the type of Self
/* ... FooError and Foo as above */
final class Bar : Foo {
static func bar(_ baz: Int) throws -> Bar {
return Bar()
}
}
Applied to your use case
Taken the above into account, you need to modify your deserialize signature into
public static func deserialize(_ element: SWXMLHash.XMLElement) throws -> Self { /* ... */ }
Or, in case you do not plan on subclassing the Element class itself, marking it final allowing annotating a concrete return type Element rather than Self:
final class Element: XMLElementDeserializable {
public static func deserialize(_ element: SWXMLHash.XMLElement) throws -> Element { /* ... */ }
}
(Note that your current implementation of deserialized does not make much sense, as it does not make use of the object to be deserialized (internal parameter name element), but since you mention your example is stripped down, I'll presume this is intended for the example).
I have the following toy example
func identity<T>(a : T) -> T{
return a
}
func applyIdentity<T>(f : T->T, t:T) -> T{
return f(t)
}
applyIdentity(identity, t:1)
And this works without a hitch. However, once I try to throw an exception in identity like so:
enum MyError: ErrorType{
case MyErrorFoo
};
func identity<T>(a : T) throws -> T{
if (true) {
return a
} else {
throw MyError.MyErrorFoo
}
}
...
The type checker complains on the applyIdentity(identity, t:1) call with message:
Argument for generic parameter 'T' could not be inferred
Any idea why this may be happening?
Your (second) identity() method can throw an error, therefore it has the type
T throws -> T, not T -> T.
If applyIdentity() should just forward
an error thrown in f() to the caller then you can define it as
func applyIdentity<T>(f : T throws ->T , t:T) rethrows -> T {
return try f(t)
}
See also "Declarations" in the Swift book:
Rethrowing Functions and Methods
A function or method can be declared with the rethrows keyword to
indicate that it throws an error only if one of it’s function
parameters throws an error. These functions and methods are known as
rethrowing functions and rethrowing methods. Rethrowing functions and
methods must have at least one throwing function parameter.