What's the equivalent of finally in Swift
If you are thinking about the SWIFT 2.0 error handling to be the same thing as exception you are missunderstanding.
This is not exception, this is an error that conforms to a protocol called ErrorType
.
The purpose of the block is to intercept the error thrown by a throwing function or method.
Basically there is no finally
, what you can do is wrap your code in a defer
block, this is guaranteed to be execute and the end of the scope.
Here a sample from SWIFT 2 programming guide
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
/* Work with the file. */
}
// close(file) is called here, at the end of the scope.
}
}
You use a defer statement to execute a set of statements just before
code execution leaves the current block of code. This lets you do any
necessary cleanup that should be performed regardless of whether an
error occurred. Examples include closing any open file descriptors and
freeing any manually allocated memory.
Swift do-try-catch syntax
There are two important points to the Swift 2 error handling model: exhaustiveness and resiliency. Together, they boil down to your do
/catch
statement needing to catch every possible error, not just the ones you know you can throw.
Notice that you don't declare what types of errors a function can throw, only whether it throws at all. It's a zero-one-infinity sort of problem: as someone defining a function for others (including your future self) to use, you don't want to have to make every client of your function adapt to every change in the implementation of your function, including what errors it can throw. You want code that calls your function to be resilient to such change.
Because your function can't say what kind of errors it throws (or might throw in the future), the catch
blocks that catch it errors don't know what types of errors it might throw. So, in addition to handling the error types you know about, you need to handle the ones you don't with a universal catch
statement -- that way if your function changes the set of errors it throws in the future, callers will still catch its errors.
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch let error {
print(error.localizedDescription)
}
But let's not stop there. Think about this resilience idea some more. The way you've designed your sandwich, you have to describe errors in every place where you use them. That means that whenever you change the set of error cases, you have to change every place that uses them... not very fun.
The idea behind defining your own error types is to let you centralize things like that. You could define a description
method for your errors:
extension SandwichError: CustomStringConvertible {
var description: String {
switch self {
case NotMe: return "Not me error"
case DoItYourself: return "Try sudo"
}
}
}
And then your error handling code can ask your error type to describe itself -- now every place where you handle errors can use the same code, and handle possible future error cases, too.
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch let error as SandwichError {
print(error.description)
} catch {
print("i dunno")
}
This also paves the way for error types (or extensions on them) to support other ways of reporting errors -- for example, you could have an extension on your error type that knows how to present a UIAlertController
for reporting the error to an iOS user.
What's the proper way of using Swift's defer
The proper use of the defer
keyword is within a swift do
, try
, catch
block. Procedures within a defer
statement will always execute prior to exiting the scope of a do
, try
, catch
block. Typically this is used for cleanup, like closing IO.
do {
// will always execute before exiting scope
defer {
// some cleanup operation
}
// Try a operation that throws
let myVar = try someThrowableOperation()
} catch {
// error handling
}
Error-Handling in Swift-Language
Swift 2 & 3
Things have changed a bit in Swift 2, as there is a new error-handling mechanism, that is somewhat more similar to exceptions but different in detail.
1. Indicating error possibility
If function/method wants to indicate that it may throw an error, it should contain throws
keyword like this
func summonDefaultDragon() throws -> Dragon
Note: there is no specification for type of error the function actually can throw. This declaration simply states that the function can throw an instance of any type implementing ErrorType or is not throwing at all.
2. Invoking function that may throw errors
In order to invoke function you need to use try keyword, like this
try summonDefaultDragon()
this line should normally be present do-catch block like this
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}
Note: catch clause use all the powerful features of Swift pattern matching so you are very flexible here.
You may decided to propagate the error, if your are calling a throwing function from a function that is itself marked with throws
keyword:
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
Alternatively, you can call throwing function using try?
:
let dragonOrNil = try? summonDefaultDragon()
This way you either get the return value or nil, if any error occurred. Using this way you do not get the error object.
Which means that you can also combine try?
with useful statements like:
if let dragon = try? summonDefaultDragon()
or
guard let dragon = try? summonDefaultDragon() else { ... }
Finally, you can decide that you know that error will not actually occur (e.g. because you have already checked are prerequisites) and use try!
keyword:
let dragon = try! summonDefaultDragon()
If the function actually throws an error, then you'll get a runtime error in your application and the application will terminate.
3. Throwing an error
In order to throw an error you use throw keyword like this
throw DragonError.dragonIsMissing
You can throw anything that conforms to ErrorType
protocol. For starters NSError
conforms to this protocol but you probably would like to go with enum-based ErrorType
which enables you to group multiple related errors, potentially with additional pieces of data, like this
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
Main differences between new Swift 2 & 3 error mechanism and Java/C#/C++ style exceptions are follows:
- Syntax is a bit different:
do-catch
+try
+defer
vs traditionaltry-catch-finally
syntax. - Exception handling usually incurs much higher execution time in exception path than in success path. This is not the case with Swift 2.0 errors, where success path and error path cost roughly the same.
- All error throwing code must be declared, while exceptions might have been thrown from anywhere. All errors are "checked exceptions" in Java nomenclature. However, in contrast to Java, you do not specify potentially thrown errors.
- Swift exceptions are not compatible with ObjC exceptions. Your
do-catch
block will not catch any NSException, and vice versa, for that you must use ObjC. - Swift exceptions are compatible with Cocoa
NSError
method conventions of returning eitherfalse
(forBool
returning functions) ornil
(forAnyObject
returning functions) and passingNSErrorPointer
with error details.
As an extra syntatic-sugar to ease error handling, there are two more concepts
- deferred actions (using
defer
keyword) which let you achieve the same effect as finally blocks in Java/C#/etc - guard statement (using
guard
keyword) which let you write little less if/else code than in normal error checking/signaling code.
Swift 1
Runtime errors:
As Leandros suggests for handling runtime errors (like network connectivity problems, parsing data, opening file, etc) you should use NSError
like you did in ObjC, because the Foundation, AppKit, UIKit, etc report their errors in this way. So it's more framework thing than language thing.
Another frequent pattern that is being used are separator success/failure blocks like in AFNetworking:
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})
Still the failure block frequently received NSError
instance, describing the error.
Programmer errors:
For programmer errors (like out of bounds access of array element, invalid arguments passed to a function call, etc) you used exceptions in ObjC. Swift language does not seem to have any language support for exceptions (like throw
, catch
, etc keyword). However, as documentation suggests it is running on the same runtime as ObjC, and therefore you are still able to throw NSExceptions
like this:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
You just cannot catch them in pure Swift, although you may opt for catching exceptions in ObjC code.
The questions is whether you should throw exceptions for programmer errors, or rather use assertions as Apple suggests in the language guide.
I'm confused about value type () in Swift. What is it, and how am I meant to use it?
()
is shorthand for void
. It means "no value is possible here".
In this example, it means that the .round()
method does not return anything - it is a mutating function called on its receiver. So assigning its void return to a var
causes that var's type to be inferred to be void
. Void vars can be useful, sometimes, rarely*, but not in this case.
Methods on value types often come in pairs: a verb like round
, and a passive verb e.g. rounded
. The first operates directly on, and modifies, its target; the second returns a modified version of its target. For another example, see sort()
and sorted()
on collections, or append(_)
and appending(_)
on strings, etc.
(* note: filter
is an annoying exception; it means "filtered", and there is no handy "filter in place".)
To get the effect you were going for in the first example, rounded()
is what you want.
--
(* To answer the tangential question in your title: how would one actually use a void variable? Well, here's a way I use them sometimes:
In an object with some setup that I would like to happen sometime after init
, but guaranteed at most once per instance, I used to use Objective-C's dispatch_once
. That's not available in Swift, so now I'll make a lazy void member like so:
class Foo {
lazy var setup: () = {
// do some complicated stuff I couldn't do in `init` for some reason
// this will only run once
}()
func doSomethingUseful() {
_ = setup // runs the setup initializer just the first time we get here
// do useful things that depend on setup having happened
}
}
I'll leave it to the comments to judge whether we're "meant to" use such a technique. :)
Swift - How to know when a loop has ended?
To produce the same result as what others have posted but with basic closure syntax:
func printFunction() {
println("loop has finished")
}
func loopWithCompletion(closure: () -> ()) {
for var i=0; i<5; i++ {
println(i)
}
closure()
}
This is how you call it:
loopWithCompletion(printFunction)
Swift 3 Update:
func printFunction() {
print("loop has finished")
}
// New for loop syntax and naming convention
func loop(withCompletion completion: () -> Void ) {
for i in 0 ..< 5 {
print(i)
}
completion()
}
Then call it like this:
loop(withCompletion: printFunction)
Or
loop {
print("this does the same thing")
}
What's the difference between a defer statement and a statement right just before return?
What's the difference between a defer statement and a statement right just before return?
All the difference in the world. The defer
statement is executed after the return! This allows you to accomplish things that can be accomplished in no other way.
For example, you can return a value and then change the value. Apple makes use of this trick quite regularly; here, for example, is code from the Sequence documentation showing how to write a custom Sequence:
struct Countdown: Sequence, IteratorProtocol {
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}
If you wrote that as
count -= 1
return count
... it would break; we don't want to decrement count
and then return it, we want to return count
and then decrement it.
Also, as has been already pointed out, the defer
statement is executed no matter how you exit. And it works no matter you exit the current scope, which might not involve return
at all; defer
works for a function body, a while block, an if construct, a do block, and so on. A single return
is not the only way to exit such a scope! There might be more than one return
in your method, and/or you might throw an error, and/or you might have a break
, etc. etc., or you might just reach the last line of the scope naturally; the defer
is executed in every possible case. Writing the same code "by hand", so as to cover every possible exit, can be very error-prone.
Related Topics
Format Realtime Stopwatch Timer to the Hundredth Using Swift
Function Throws and Returns Optional.. Possible to Conditionally Unwrap in One Line
Could Not Find an Overload for '/' That Accepts the Supplied Arguments
How to Create an Array of Functions
Nstextfield, Change Text in Swift
Is There a Writetofile Equivalent for Swift 3's 'Data' Type
Swift, How to Play Sound When Press a Button
Distinction Between Private and Fileprivate Top-Level Classes
Multiple Bottom Sheets - the Content Doesn't Load Swiftui
Create Codable Struct with Generic Type
Implementing a Drag-And-Drop Zone in Swift
Check If Variable Is an Optional, and What Type It Wraps
Binary Operator Cannot Be Applied to Operands of Type Int and Int? Swift 3