Why is 'throws' not type safe in Swift?
The choice is a deliberate design decision.
They did not want the situation where you don't need to declare exception throwing as in Objective-C, C++ and C# because that makes callers have to either assume all functions throw exceptions and include boilerplate to handle exceptions that might not happen, or to just ignore the possibility of exceptions. Neither of these are ideal and the second makes exceptions unusable except for the case when you want to terminate the program because you can't guarantee that every function in the call stack has correctly deallocated resources when the stack is unwound.
The other extreme is the idea you have advocated and that each type of exception thrown can be declared. Unfortunately, people seem to object to the consequence of this which is that you have large numbers of catch blocks so you can handle each type of exception. So, for instance, in Java, they will throw Exception
reducing the situation to the same as we have in Swift or worse, they use unchecked exceptions so you can ignore the problem altogether. The GSON library is an example of the latter approach.
We chose to use unchecked exceptions to indicate a parsing failure. This is primarily done because usually the client can not recover from bad input, and hence forcing them to catch a checked exception results in sloppy code in the catch() block.
https://github.com/google/gson/blob/master/GsonDesignDocument.md
That is an egregiously bad decision. "Hi, you can't be trusted to do your own error handling, so your application should crash instead".
Personally, I think Swift gets the balance about right. You have to handle errors, but you don't have to write reams of catch statements to do it. If they went any further, people would find ways to subvert the mechanism.
The full rationale for the design decision is at https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst
EDIT
There seems to be some people having problems with some of the things I have said. So here is an explanation.
There are two broad categories of reasons why a program might throw an exception.
- unexpected conditions in the environment external to the program such as an IO error on a file or malformed data. These are errors that the application can usually handle, for example by reporting the error to the user and allowing them to choose a different course of action.
- Errors in programming such as null pointer or array bound errors. The proper way to fix these is for the programmer to make a code change.
The second type of error should not, in general be caught, because they indicate a false assumption about the environment that could mean the program's data is corrupt. There my be no way to continue safely, so you have to abort.
The first type of error usually can be recovered, but in order to recover safely, every stack frame has to be unwound correctly which means that the function corresponding to each stack frame must be aware that the functions it calls may throw an exception and take steps to ensure that everything gets cleaned up consistently if an exception is thrown, with, for example, a finally block or equivalent. If the compiler doesn't provide support for telling the programmer they have forgotten to plan for exceptions, the programmer won't always plan for exceptions and will write code that leaks resources or leaves data in an inconsistent state.
The reason why the gson attitude is so appalling is because they are saying you can't recover from a parse error (actually, worse, they are telling you that you lack the skills to recover from a parse error). That is a ridiculous thing to assert, people attempt to parse invalid JSON files all the time. Is it a good thing that my program crashes if somebody selects an XML file by mistake? No isn't. It should report the problem and ask them to select a different file.
And the gson thing was, by the way, just an example of why using unchecked exceptions for errors you can recover from is bad. If I do want to recover from somebody selecting an XML file, I need to catch Java runtime exceptions, but which ones? Well I could look in the Gson docs to find out, assuming they are correct and up to date. If they had gone with checked exceptions, the API would tell me which exceptions to expect and the compiler would tell me if I don't handle them.
Swift - can a function accept a throwing function that returns a tuple as a parameter?
There is no problem calling a throwing function inside the call to another function, if the first function throws the catch
clause will be entered and the second function never gets called. You can simply redefine the function to take a tuple instead
func updateUserMemberships(userInfo: (UserProfile, [GroupMembership])) throws {
...
}
And call the other function inside the call to `updateUserMemberships
do {
try updateUserMemberships(userInfo: try sendNewInvitation(user: user, group: group))
} catch {
print(error.localizedDescription)
return
}
You can create a type alias for your tuple if it helps improve readability
typealias UserInfo = (UserProfile, [GroupMembership])
func sendNewInvitation(user: String, group: String) throws -> UserInfo {
...
}
func updateUserMemberships(userInfo: UserInfo) throws {}
But if this is a type you are going to use in many places then I suggest you wrap it in a struct instead
struct UserInfo {
var userProfile: UserProfile
var groupMembership: [GroupMembership])
}
Invalid conversion from throwing function of type '(_) throws - ()' to non-throwing function type '(DataSnapshot) - Void'
You are throwing an error in the completion block. This is not possible and causes the error.
The return value of the closure is not related to the return value of the enclosing function – strictly spoken throws
is not a return value but is affected, too.
To be able to return something from the closure you have to implement a completion block rather than throws
func getUserList(completion : (Error?) -> ())
and use it
completion(value.isEmpty ? UserError.Empty : nil)
Side-note: You are using too many question and exclamation marks. Use optional binding to unwrap optionals for example (and use Swift native collection types)
if let value = snapshot.value as? [String:Any] {
for key in value.keys { ...
Swift throw from closure nested in a function
When you define closure that throws:
enum MyError: ErrorType {
case Failed
}
let closure = {
throw MyError.Failed
}
then type of this closure is () throws -> ()
and function that takes this closure as parameter must have the same parameter type:
func myFunction(completion: () throws -> ()) {
}
It this function you can call completion
closure synchronous:
func myFunction(completion: () throws -> ()) throws {
completion()
}
and you have to add throws
keyword to function signature or call completion with try!
:
func myFunction(completion: () throws -> ()) {
try! completion()
}
or asynchronous:
func myFunction(completion: () throws -> ()) {
dispatch_async(dispatch_get_main_queue(), { try! completion() })
}
In last case you will not be able to catch error.
So if completion
closure in eventStore.requestAccessToEntityType
method and the method itself does not have throws
in its signature or if completion
is called asynchronously then you can not throw
from this closure.
I suggest you the following implementation of your function that passes error to callback instead of throwing it:
func insertEventToDefaultCalendar(event: EKEvent, completion: CalendarEventError? -> ()) {
let eventStore = EKEventStore()
switch EKEventStore.authorizationStatusForEntityType(.Event) {
case .Authorized:
do {
try insertEvent(eventStore, event: event)
} catch {
completion(CalendarEventError.Failed)
}
case .Denied:
completion(CalendarEventError.AccessDenied)
case .NotDetermined:
eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in
if granted {
//insertEvent(eventStore)
} else {
completion(CalendarEventError.AccessDenied)
}
})
default:
}
}
How to use throw from within do block?
The most common way is to add an error with / without new Result
func getWeatherBy(city: String, completion: ((WeatherRecord? , Error?) -> ()))
you can't throw from an asynchronous method immediately
Why can a public type throw an error that is a private type?
You can easily return something of a private type
private enum MyError : Error {
case unexpectedError
}
public struct MyStruct {
static func myError() -> Error {
return MyError.unexpectedError
}
}
...by hiding it as a value of a not-so-private type. It's just subtyping. Or: unexpectedError
is of type MyError
, but it is also an Error
. You can't expose the type MyError
, but you can do whatever you want with its values, as long as you don't tell anyone anything more specific than "these are Error
s".
Similarly, you can easily throw something of a private type
public struct MyStruct {
static func throwError() throws {
throw MyError.unexpectedError
}
}
because throw
wants an Error
, you are giving it a MyError
, and MyError : Error
. The throws
annotation itself says nothing about throwError
throwing MyError
s; it just says it may throw Error
s in general.
Related Topics
Blue Highlighting/Focus Ring on Catalyst App
How to Get Data from Observedobject with Onreceive in Swiftui
Sub-Classing Nstextstorage Causes Significant Memory Issues
Swift 4 - Notification Center Addobserver Issue
How to Use Storyboards with Spritekit Using Swift
Enum with Identical Cases Names with Associated Values of Different Types
How to Build a Workout App on Watchos with Audio Feedback
Swiftui - Foreach Deletion Transition Always Applied to Last Item Only
Why Does Swift Provide Both a Cgrect Initializer and a Cgrectmake Function
Multi-Component Picker (Uipickerview) in Swiftui
Changing Color of Button Text and State
How to Make a Https Request to a Server in Swift
Swift Error: Missing Argument Label 'Name:' in Call
How Is a Type-Erased Generic Wrapper Implemented
How to Take Screen Shot Programmatically (Swift, Spritekit)
When Should I Use Optionals and When Should I Use Non-Optionals with Default Values