Proper way to propagate exceptions in defer block - swift

In the following example, the function usingTemporaryDirectory() creates and deletes a temporary directory, calling a passed function body() in between. If an exception is thrown by createTemporaryDirectory() or the passed function body(), it is propagated to the caller. But the exception thrown by removeDirectory() cannot be passed to the caller because no exception may escape a defer block.
import Foundation
func createTemporaryDirectory() throws -> URL { ... }
func removeDirectory(_ url: URL) throws { ... }
func usingTemporaryDirectory(body: (URL) throws -> ()) throws {
let tempDir = try createTemporaryDirectory()
defer {
// Errors thrown from here are not handled.
try removeDirectory(tempDir)
}
try body(tempDir)
}
What is the proper way to handle such an exception? I see two options:
Just catch the exception and log a message.
Catch the exception, log a message and abort the process.
I don't want to use option 1 because that could lead in this example to an arbitrary number of temporary directories piling up. And I also don't want to use option 2 because that would prevent outer stack frames from completing their cleanup work (e.g. if multiple temporary directories have been created, removal for all of them should at least be attempted).
Java has a feature called suppressed exceptions. In this case the exception thrown in the defer block could be added as a suppressed exception to the exception thrown by body(), if any. Does Swift have a comparable feature?

In this case the exception thrown in the defer block could be added as a suppressed exception to the exception thrown by body(), if any. Does Swift have a comparable feature?
Not that I know of – you could however build something similar yourself. First, let's define an Error type that can store multiple underlying errors:
struct ErrorCollection : Error {
private var errors: [Error] = []
init() {}
init<S : Sequence>(_ sequence: S) where S.Element == Error {
for error in sequence {
append(error)
}
}
mutating func append(_ error: Error) {
switch error {
case let x as ErrorCollection: // ensure we flatten out any nested error collections.
errors.append(contentsOf: x.errors)
case let x:
errors.append(x)
}
}
}
extension ErrorCollection : RandomAccessCollection {
typealias Index = Int
typealias Element = Error
var startIndex: Index { return errors.startIndex }
var endIndex: Index { return errors.endIndex }
func index(_ i: Index, offsetBy n: Index) -> Index {
return errors.index(i, offsetBy: n)
}
subscript(index: Index) -> Element { return errors[index] }
}
Then we can define a Result<T> type which we can use in order to evaluate a closure, storing the return value if successful, otherwise the error it threw. We can then add a then(_:) method to allow us to catch any additional errors, potentially causing the success value to be invalidated:
enum Result<T> {
case success(T)
case failure(Error)
init(_ body: () throws -> T) {
do {
self = .success(try body())
} catch {
self = .failure(error)
}
}
func then(_ body: () throws -> Void) -> Result {
do {
try body()
return self
} catch let nextError {
switch self {
case .success: // invalidate the success value and store the error.
return .failure(nextError)
case .failure(let error): // concatenate the errors.
return .failure(ErrorCollection([error, nextError]))
}
}
}
func materialize() throws -> T {
switch self {
case .success(let value):
return value
case .failure(let error):
throw error
}
}
}
You can then use this like so:
func usingTemporaryDirectory<R>(body: (URL) throws -> R) throws -> R {
let tempDir = try createTemporaryDirectory()
return try Result { try body(tempDir) }
.then { try removeDirectory(tempDir) }
.materialize()
}

Related

How to write a template function that handle do catch

I want to write a template function that handle do catch. It may look like this
func handleMethod(methodNeedToHandle) -> result: notSureType{
var result
do {
let response = try handleMethod
result = response
return result
} catch let error as someObjectError{
result = error
return result
}
}
Then you can use it like
let catchResult = handleMethod(method(x: something, y: something))
Thank you guys help me a lot, I get working code below
func handleDoTryCatch<T>(closure:() throws -> T) -> Any {
do {
let result = try closure()
return result
} catch let error {
return error
}
}
You could use a generic function that takes a closure and returns a tuple.
Something like:
func handle<T>(closure:() throws -> T) -> (T?, Error?) {
do {
let result = try closure()
return (result, nil)
} catch let error {
return (nil, error)
}
}
This will define a function that takes a closure that calls the method that can throw. It the returns a tuple with the expected return type and something that conforms to the Error protocol.
You would use it like this:
let result: (Void?, Error?) = handle { try someFunc() }
let result2: (Int?, Error?) = handle { try someOtherFunc(2) }
someFunc and someOtherFunc are just examples and their signatures would be:
func someFunc() throws {}
func someOtherFunc(_ param: Int) throws -> Int {}
This is the function I managed to come up with:
// Your error cases
enum Errors: Error {
case someErrorCase
}
// Function to test another function
func doTryCatch<T>(for function: () throws -> T) {
do {
let returnValue = try function()
print("Success! The return value is \(returnValue)")
} catch {
print("Error! The error reason was \"\(String(describing: error))\"")
}
}
// Function to test
func failingFunction() throws -> Int {
throw Errors.someErrorCase // <-- Comment this to not recieve an error (for testing)
return 5 // Will return 5 if the error is not thrown
// Failure: Error! The error reason was "someErrorCase"
// Success: Success! The return value is 5
}
// Perform the test
doTryCatch(for: failingFunction) // <-- Very easy to test, no closures to write!
Hope this helps with your debugging! :)
The closest I could get to what you probably want is the following:
(Swift Playground code:)
func handleMethod(_ f: #autoclosure () throws -> Void) -> Error? {
do {
try f()
} catch let err {
return err
}
return nil
}
enum HelloError: Error {
case tooShort
}
func hello(_ what: String) throws {
guard what.count > 0 else { throw HelloError.tooShort }
print ("Hello \(what)!")
}
// use like this:
// let err = handleMethod(try hello("World")) // err == nil
// let err = handleMethod(try hello("")) // err == HelloError.tooShort
//
print ("* hello(\"World\") -> \(String(describing: handleMethod(try hello("World"))))")
print ("* hello(\"\") -> \(String(describing: handleMethod(try hello(""))))")
This will produce the following output:
Hello World!
* hello("World") -> nil
* hello("") -> Optional(__lldb_expr_3.HelloError.tooShort)
Consider just using do/catch as George_E recommends. It's a good advice. But if you need this function, than this snipped hopefully gives you a starting point.

ReactiveSwift SignalProducer : Argument 'failed' must precede argument 'value'

I am trying to update an upgrade an old piece of code using ReactSwift because I want ti run it with XCode 9. I have defined a class Network
final class Network: NSObject {
static func request(_ URLRequest: URLRequestConvertible) -> SignalProducer<Any, NetworkError> {
return SignalProducer { sink, disposable in
Alamofire.request(URLRequest)
.validate()
.responseJSON { response in
switch response.result {
case .success(let value):
sink.send(value: value)
sink.sendCompleted()
case .failure(let error):
// modification en as NSError
sink.send(error: NetworkError(error: error as NSError))
}
}
}
}
}
that is called in function using generic type:
private func fetch<T: Himotoki.Decodable>(URLRequest: URLRequestConvertible) -> SignalProducer<T, NetworkError> {
return Network.request(URLRequest)
.attemptMap { JSON in
do {
return .success(try decodeValue(JSON) as T)
} catch let error {
return .failure(.incorrectDataReturned)
}
}
}
The generic function is used to make network requests using Alamofire, like the following one.
// Get static UserData
static func fetchUserData() -> SignalProducer<UserData, NetworkError> {
return fetch(URLRequest: MoneyCupUsersBackEndRouter.getUserData)
}
So, I call :
API.fetchUserData()
.on(started: {
SVProgressHUD.show()
},
value: { cfg in
globalConfig = cfg
},
failed: { [weak self] error in
self?.view.window?.dodo.error("Impossible de récupérer la configuration globale")
},
terminated: {
SVProgressHUD.dismiss()
})
.start()
}
This scheme has worked well before and I have a large number of it across my code. But since I have upgraded to ReactiveCocoa, my code no longer compile. And I have a message stating that :
Argument 'failed' must precede argument 'value'
It used to be that for Swift functions where the arguments have default values you could reorder the arguments as desired at the call site. This feature was removed in Swift 3, which means you now must specify arguments in the same order as they are defined. So for the call to the on function, you must move the value: parameter to the end.

Nested do catch swift 3.0

I'd like to use consecutive try statements. If one returns an error I'd like to proceed to the next one, otherwise return the value.
The code below seems to work fine, however I'll end up with a big nested do catch pyramid. Is there a smarter/better way to do it in Swift 3.0?
do {
return try firstThing()
} catch {
do {
return try secondThing()
} catch {
return try thirdThing()
}
}
If the actual errors thrown from those function calls are not needed
then you can use try? to convert the result to an optional,
and chain the calls with the nil-coalescing operator ??.
For example:
if let result = (try? firstThing()) ?? (try? secondThing()) ?? (try? thirdThing()) {
return result
} else {
// everything failed ...
}
Or, if the error from the last method should be thrown if everything fails,
use try? for all but the last method call:
return (try? firstThing()) ?? (try? secondThing()) ?? (try thirdThing())
If Martin's answer is too terse for your taste you can just go with individual catch blocks.
do {
return try firstThing()
} catch {}
do {
return try secondThing()
} catch {}
do {
return try thirdThing()
} catch {}
return defaultThing()
As each throwing function's result is immediately returned no nesting is necessary.
Another way to do this is to write a function that takes all your throwing functions as an argument. It returns the first one that was successfully executed or nil.
func first<T>(_ values: (() throws -> T)...) -> T? {
return values.lazy.flatMap({ (throwingFunc) -> T? in
return try? throwingFunc()
}).first
}
The lazy ensures that the values are only called until it finds the first match. Doing it this way, you can also add a lot of cases very quickly.
You can use the function like this
return first(firstThing, secondThing, thirdThing) ?? "Default"
I also included the code I used to test this in playground:
enum ThingError: Error {
case zero
}
func firstThing() throws -> String {
print("0")
throw ThingError.zero
return "0"
}
func secondThing() throws -> String {
print("1")
return "1"
}
func thirdThing() throws -> String {
print("2")
return "B"
}
func first<T>(_ values: (() throws -> T)...) -> T? {
return values.lazy.flatMap({ (throwingFunc) -> T? in
return try? throwingFunc()
}).first
}
func tryThings() -> String {
return first(firstThing, secondThing, thirdThing) ?? "Default"
}
tryThings() // prints "0" and "1"

Using Do/Catch in Swift

I am working on an app and want to get data back from a function. However sometimes data is missing or is different from the kind of that I want to retrieve. I am new to Swift and I can't find a way to write a function that does a little bit of processing and returns this data. When this data is missing, the function should give back a string "Not Found". Like this:
func processData(data:String) {
do {
//processing
var result = processedData
} catch {
var result = "Not Found"
}
return result
}
It would be very nice if somebody could help me.
You should check if the result is nil.
func processData(data: String?) -> String {
guard let result = data else {
return "Not Found"
  }
return result
}
The most concise way of doing it would be using the guard-let construct:
func processData(data: String?) -> String {
// Assuming getProcessedData(:) returns your processed data
guard let result = getProcessedData(data) else {
return "Not found"
}
return result
}
Also, your function is missing a return type. You must specify the return type like -> TYPE in all functions that return some value.
Those answer were written till mine are right. There is one way: with handler check get result and use by your point.
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?, completionHandler: #escaping (_ result: String? , _ error: Error?) -> Void ) {
guard let data = data else {
// Data is missing
throw nil, Errors.noData
}
// Do other things, and throw if necessary
result = data
return result, nil
}
// example of calling this function
process(data: "A data to process"){(result, error) -> Void in
//do any stuff
/*if error == nil {
}*/
}
A good practice in swift would be to use correctly the throws errors
This is an example inspired from yours :
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?) throws -> String {
guard let data = data else {
// Data is missing
throw Errors.noData
}
// Do other things, and throw if necessary
result = data
return result
}
do {
try process(data: "A data to process")
} catch {
print("An error occurred: \(error)")
}
You can try this code as is in a Swift Playgound
Your function needs to be explicit about returning something with e.g. -> String Also do-catch is for methods that can throw an error. It seems like you need to take a look at how to use optionals. Optionals can have a value or they can have no value.
fun processData(data: String) -> String {
var result: String?
// Do some processing and assign the result to result variable
guard let result = result else { return "Not Found" }
return result
}

Why does iterating over closures cause a bus error in swift?

I'm getting a strange Bus Error when running what appears to be perfectly safe swift code. I've tried to reduce it down to a minimal test case, as follows:
Apple Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 0ddf238ad7)
Target: x86_64-apple-macosx10.9
This code:
public enum MyError: ErrorType {
case SomeError(code: Int)
}
public typealias MyType = () throws -> Bool
public class Foo {
var a:MyType = { () throws -> Bool in
print("A")
return true
}
var b:MyType = { () throws -> Bool in
print("B")
return true
}
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
}
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
for child in mirror.children {
if let callable = child.value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
}
let foo = Foo()
handle(foo)
Generates this output:
Foo
A
B
C
Bus error: 10
Running it in the debugger works fine, so I assume it has something to do with a timing issue at runtime.
Am I doing something illegal or unsafe in this code?
Are exceptions somehow illegal in closures?
What's causing this error?
Edit:
I've created a bug on the swift issue tracker for this now here: https://bugs.swift.org/browse/SR-324
What's causing this error?
The error doesn't happen until you get to the last closure:
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
Obviously, you're throwing an exception here, and I suspect that the problem has less to do with iterating over the children and more to do with throwing an exception while you're doing that iterating. I tried calling c without iterating:
public func trythis() {
let foo = Foo()
do {
try (foo.c)()
}
catch MyError.SomeError(let id) {
print(id)
}
catch { print("unknown") }
}
trythis()
and found that it worked fine. I also tried removing the throw from c:
var c:MyType = { () throws -> Bool in
print("C")
// throw MyError.SomeError(code: 0)
return true
}
and found that the code works fine in that case. So it's the combination of throwing while iterating over the list that's the problem, and that makes me suspect that it's just a compiler bug or maybe some problem with the Mirror class.
I think you should file a bug report with Apple for this one.
I agree with Caleb that this must be a bug.
But just to be clear, it is not the combination of throwing while iterating. It is the combination of reflecting and throwing.
This is a modified version of your handle function:
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
// Extract only the last function, no iteration...
if let callable = mirror.children[AnyForwardIndex(2)].value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
This function will cause the same error as your function.
You simply can not call a function that throws, if found using reflection.
Bug I would say.