Catching an error from a non-throwing method
You can use the init(exactly:)
constructor, it will not throw an error but it will return nil if the value is to large
guard let value = Int(exactly: pow(Double(1000000000), Double(10))) else {
//error handling
}
Best practice for Swift methods that can return or error
To add an answer to this question (five years later), there’s a dedicated Result
type for this exact scenario. It can return the type you want on success, or type an error on failure.
It does mean re-factoring some code to instead accept a completion handler, and then enumerating over the result in that callback:
class SecurityService {
static func loginWith(email: String, password: String, completionHandler: @escaping (Result<User, SecurityError>) -> Void) {
// Body
}
}
Then in a handler:
securityService.loginWith(email: email, password: password) { result in
switch result {
case .success(let user):
// Do something with user
print("Authenticated as \(user.name)")
case .failure(let error):
// Do something with error
print(error.localizedDescription)
}
}
An elegant way to ignore any errors thrown by a method
If you don't care about success or not then you can call
let fm = NSFileManager.defaultManager()
_ = try? fm.removeItemAtURL(fileURL)
From "Error Handling" in the Swift documentation:
You use
try?
to handle an error by converting it to an optional value.
If an error is thrown while evaluating thetry?
expression, the value
of the expression isnil
.
The removeItemAtURL()
returns "nothing" (aka Void
), therefore the return value of the try?
expression is Optional<Void>
.
Assigning this return value to _
avoids a "result of 'try?' is unused" warning.
If you are only interested in the outcome of the call but not in
the particular error which was thrown then you can test the
return value of try?
against nil
:
if (try? fm.removeItemAtURL(fileURL)) == nil {
print("failed")
}
Update: As of Swift 3 (Xcode 8), you don't need the dummy assignment, at least not in this particular case:
let fileURL = URL(fileURLWithPath: "/path/to/file")
let fm = FileManager.default
try? fm.removeItem(at: fileURL)
compiles without warnings.
How to handle errors/exceptions in swift
The Swift do
-try
-catch
syntax is designed to handle the errors analogous to the NSError
patterns in Objective-C, not for handling fatal errors/exceptions. Do not be confused by the similarity of Swift's error handling do
-try
-catch
with the completely different Objective-C exception handling pattern of @try
-@catch
.
One should only use forced unwrapping (!
) and forced type casting (as!
) when one knows with certainty that they cannot possibly fail. If they could fail (as in this case), you should gracefully detect this scenario and handle it accordingly.
You could, for example, use Swift error handling to communicate a failure converting a string to a date (and then use do
-try
-catch
pattern when you call it in order to detect and handle that error):
enum DateError: Error {
case badDate
case dateNotFound
}
func getDateFromTextfield() throws -> Date {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
guard let dateString = dictOfLabelsAndText["Birthdate"] as? String else {
throw DateError.dateNotFound
}
guard let date = formatter.date(from: dateString) else {
throw DateError.badDate
}
return date
}
Then you could do:
do {
child.birthdateAT = try getDateFromTextfield()
} catch {
child.birthdateAT = formatter.date(from: "01.01.2000")
}
Or, more concisely, use try?
and nil
coalescing operator:
child.birthdateAT = try? getDateFromTextfield() ?? formatter.date(from: "01.01.2000")
Alternatively, you might just change the method to return an optional and use nil
as a way of detecting a failure:
func getDateFromTextfield() -> Date? {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
guard let dateString = dictOfLabelsAndText["Birthdate"] as? String else {
return nil
}
return formatter.date(from: dateString)
}
And then do:
if let birthDate = getDateFromTextfield() {
child.birthdateAT = birthDate
} else {
child.birthdateAT = formatter.date(from: "01.01.2000")
}
Or, again, use the nil
coalescing operator:
child.birthdateAT = getDateFromTextfield() ?? formatter.date(from: "01.01.2000")
But, bottom line, do not use !
to unwrap an optional unless you know it can never be nil
. Otherwise, use optional binding and/or guard
statements to gracefully detect and report the failure.
This has been updated for contemporary versions of Swift. For original Swift 2 answer, see previous revision of this answer.
Swift 2.1 do-try-catch not catching error
The do - catch
combination is fine. This issue is simply one that cannot be caught - and therefore never makes it to the catch
block.
If the issue were catchable (defined and handled via Swift's throws
functionality), the catch
block would've been executed.
Some semantics: there is a long-standing argument about the differences between the terms error and exception.
Some developers consider the two terms to be distinct. In this case, the term error represents an issue that was designed to be handled. Swift's throws
action would fit here. In this case, a do - catch
combination would allow the issue to be caught.
An exception, for these developers, represents an unexpected, usually fatal, issue that cannot be caught and handled. (Generally, even if you could catch it, you would not be able to handle it.)
Others consider the two terms to be equivalent and interchangeable, regardless of whether the issue in question can be caught or not. (Apple's documentation seems to follow this philosophy.)
(Updated to focus on the answer rather than the semantics.)
Error handling on native methods swift
This method doesn't throw, so you can't catch the error. It seems the only way to be sure the function call will not fail is to manually check that the range is valid.
You could for example get the range from the string itself with something like rangeOfString(:)
(attributedString.string as NSString).rangeOfString("foo")
Or simply validate your NSRange
bounds against the string.
if (range.location != NSNotFound && range.location + range.length <= attributedString.length) { /* ok */ }
Related Topics
Convert Character to Integer in Swift
Mutable Binding in Swiftui Live Preview
How to Convert Between Related Types Through a Common Initializer
How to Install Xcode on an External Hard Drive Along with the iPhone Simulator.App
Obj-C Cocoapods + Swift Framework
How to Open a Nspopover at a Distance from the System Bar
Swift: How to Call Cckeyderivationpbkdf from Swift
Why to Avoid Forced Unwrapping
Can't Use Storyboard Custom Instantiated Window Controller
Can an Enum Contain Another Enum Values in Swift
Change Uibarbuttonitem from Uisearchbar
What Are 'Get' and 'Set' in Swift
iOS 10 Imessage App Extension: How to Calculate the Height of the Extra Tall Navbar
Dynamically Change Text of Realitykit Entity
Differencebetween Swift 2.0 Do-Try-Catch and Regular Java/C#/C++ Exceptions