I'm upgrading my code to Swift 2 using error handling with try-catch. I've stuck with closure (NSURLSession), I can't throw inside it.
Generally I'm using such code:
let request = NSURLRequest()
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
throw(ErrorType) // here some errortype enum
}
}
But I'm receiving the error: "Cannot invoke dataTaskWithRequest with an argument list of type …". How can I throw from closure?
You can't throw inside a closure because the closure can be called later, when the function is already executed.
In your example the closure is called asynchronously after the URLRequest got a response, at this time the calling function is already executed.
Related
I'm building an app that only consume a Web Service. For that, I use a method dataTask (URLSession.shared.dataTask).
I'm not waiting for information, only a process is triggered with the next code:
let endPoint = "http://host/service
let url = URL(string: endPoint)
let task = URLSession.shared.dataTask(with: url!) {_, _, _ in
}
task.resume()
When the method dataTask executes, Xcode show me the error:
"Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"
Is there a way to skip the return completionHandler (data, response, error)?
A completion handler is needed, but does not have to be specified when creating the data task object. In that case, you must define a URLSessionDataDelegate that will handle the response.
"A URLSession object need not have a delegate. If no delegate is assigned, when you create tasks in that session, you must provide a completion handler block to obtain the data.
Completion handler blocks are primarily intended as an alternative to using a custom delegate. If you create a task using a method that takes a completion handler block, the delegate methods for response and data delivery are not called."
(https://developer.apple.com/documentation/foundation/urlsessiondatadelegate).
As for the crash, it seems to be related to the force unwrapping (the ! symbol) used in the when declaring the task. You could use a guard condition to abort safely if this error is happening.
guard let url = URL(string: endPoint) else { return }
URLSession.shared.dataTask(with: url) {_, _, _ in
}.resume()
I have a doubt with rethrows. Following code is inside Dictionary extension:
init(_value: [(Key, Value)]) {
self.init(_value, uniquingKeysWith: { _, first in first }) //Call can throw, but it is not marked with 'try' and the error is not handled
}
here uniquingKeysWith will not throw error as I am specifying the sequence, still compiler is telling "Call can throw, but it is not marked with 'try' and the error is not handled"
Can any one explain me why I need to handle error for the init when parameter is not throwing?
As per rethrows concept if parameter uniquingKeysWith will not throw error then method does not need to handle errors.
I looked for this article, but it does not cover my case.
If i understand correct, we can use try either in do..catch.. statement or in a function that can throw.
But sometimes i see something like:
let jsonData = try jsonEncoder.encode(employee1)
Where jsonData is not optional. What is meaning of this? What if try sttement fail? Why value is not optional? Can someone explain? Thanks.
In addition to the cases you mentioned, you can call try at
top-level code. Here is a simple self-contained example:
// main.swift:
enum MyError : Error {
case failed
}
func foo() throws -> Int {
throw MyError.failed
}
defer { print("Good bye.") }
let x = try foo()
print(x)
You can compile and run this as a Xcode "Command Line Project"
or directly from the command line:
$ swiftc main.swift
$ ./main
Good bye.
Fatal error: Error raised at top level: main.MyError.failed: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift, line 187
Illegal instruction: 4
The failed try in the top-level code causes the program to
terminate with an error message. Deferred statement (if present) will be executed however.
This is slightly different from using a forced try! statement,
which causes the program to abort as well, but immediately, without executing deferred statements. (This can be relevant if deferred
statements are used to clean-up resources, e.g. remove temporary files).
The error message originates from ErrorType.swift, line 187:
/// Invoked by the compiler when code at top level throws an uncaught error.
#_inlineable // FIXME(sil-serialize-all)
#_silgen_name("swift_errorInMain")
public func _errorInMain(_ error: Error) {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
(also observed in Non Exhaustive List When Handling Errors Inside a Class Function in Swift).
Apparently the top-level code behaves as if embedded in a
do-catch block:
do {
func foo() throws -> Int {
throw NSError(domain: "foo", code: 0, userInfo: nil)
}
defer { print("Good bye.") }
let x = try foo()
} catch {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
A function call marked with try like the one you posted, as you stated, must be located inside a function marked with the keyword throws or a do/catch statement or it will cause a compile error.
On a side note if you want to call a throwing function outside a do/catch or another throwing function you can use try? before your function call to get an optional result that will be nil if something is thrown. There is also the try! variant that assumes nothing will be thrown returning a non-optional result, but if something is thrown the app will crash.
In my code I'm deserialising some XML using the SWXMLHash library. Its .value() method has the throws keyword in its declaration, as do any custom deserialise functions.
I have the following line of code:
let myValue : UInt8 = try? xml["Root"]["ValueNode"].value()
Since the library doesn't include a deserialiser for UInt8, I've defined my own:
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
return UInt8(element.text)!
}
}
This works when the node has a value. However, when the node doesn't exist or is nil, an error occurs on the following line:
return UInt8(element.text)! // Fatal error: Unexpectedly found nil while unwrapping an Optional value
This is supposed to happen, obviously. What I don't understand is why this error is not being caught by my try? statement and returning nil instead of throwing that error.
Can anyone help?
Not all errors can be caught in Swift. If you mark a function using the throws keyword it indicates that the function might throw a recoverable error. However, your custom implementation doesn't actually throw any errors. Only errors thrown from functions marked with the throws keyword and thrown using the code throw Error can be caught by a do-catch block.
try? is a way to convert a throwable function's return value to an optional. If the function would throw an error, the value after the try? will be nil, otherwise it will be an optional value.
When you use the !, you specifically tell the compiler that you know what you are doing and if the operation on which you used the ! fails, your app shouldn't fail gracefully.
You'll need to change your deserialize method to handle the optional unwrapping gracefully or throw and error.
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
if let val = UInt8(element.text) {
return val
} else {
throw NSError(domain: "Couldn't deserialize value to UInt8", code: 1)
}
}
}
return UInt8(element.text)!
There's no try in this line. Therefore, no errors are going to be thrown here. If UInt8 can't convert the string it's given, it just returns nil. And then, of course, your ! turns that nil into a crash.
Instead of that, do something like this instead:
guard let retVal = UInt8(element.text) else { throw SomeError }
return retVal
In general: When there's a way to do something using !, and another way to do the same thing without using !, go for the second one unless you've got a really good reason.
In Swift 2.0, Apple introduced a new way to handle errors (do-try-catch).
And few days ago in Beta 6 an even newer keyword was introduced (try?).
Also, knew that I can use try!.
What's the difference between the 3 keywords, and when to use each?
Updated for Swift 5.1
Assume the following throwing function:
enum ThrowableError: Error {
case badError(howBad: Int)
}
func doSomething(everythingIsFine: Bool = false) throws -> String {
if everythingIsFine {
return "Everything is ok"
} else {
throw ThrowableError.badError(howBad: 4)
}
}
try
You have 2 options when you try calling a function that may throw.
You can take responsibility of handling errors by surrounding your call within a do-catch block:
do {
let result = try doSomething()
}
catch ThrowableError.badError(let howBad) {
// Here you know about the error
// Feel free to handle or to re-throw
// 1. Handle
print("Bad Error (How Bad Level: \(howBad)")
// 2. Re-throw
throw ThrowableError.badError(howBad: howBad)
}
Or just try calling the function, and pass the error along to the next caller in the call chain:
func doSomeOtherThing() throws -> Void {
// Not within a do-catch block.
// Any errors will be re-thrown to callers.
let result = try doSomething()
}
try!
What happens when you try to access an implicitly unwrapped optional with a nil inside it? Yes, true, the app will CRASH!
Same goes with try! it basically ignores the error chain, and declares a “do or die” situation. If the called function didn’t throw any errors, everything goes fine. But if it failed and threw an error, your application will simply crash.
let result = try! doSomething() // if an error was thrown, CRASH!
try?
A new keyword that was introduced in Xcode 7 beta 6. It returns an optional that unwraps successful values, and catches error by returning nil.
if let result = try? doSomething() {
// doSomething succeeded, and result is unwrapped.
} else {
// Ouch, doSomething() threw an error.
}
Or we can use guard:
guard let result = try? doSomething() else {
// Ouch, doSomething() threw an error.
}
// doSomething succeeded, and result is unwrapped.
One final note here, by using try? note that you’re discarding the error that took place, as it’s translated to a nil.
Use try? when you’re focusing more on successes and failure, not on why things failed.
Using Coalescing Operator ??
You can use the coalescing operator ?? with try? to provide a default value incase of failure:
let result = (try? doSomething()) ?? "Default Value"
print(result) // Default Value