Swift: How to catch exception when parsing a numeric string?
In Swift only methods which can throw
can catch
. The Int
initializer does not throw
The Swift pattern are optionals
func getInt(_ data:String) -> Int?
{
return Int(data)
}
let a = "x123"
if let b = getInt(a) {
print("Result: \(b)")
} else {
print("No result")
}
Or you can return a non-optional and handle the optional error with the nil coalescing operator
func getInt(_ data:String) -> Int
{
return Int(data) ?? -1
}
let a = "x123"
let b = getInt(a)
print("Result: \(b)")
Or make your method can throw
enum MyError : Error {
case conversionError
}
func getInt(_ data:String) throws -> Int
{
guard let result = Int(data) else { throw MyError.conversionError }
return result
}
let a = "x123"
do {
let b = try getInt(a)
print("Result: \(b)")
} catch MyError.conversionError {
print("Could not convert the string '\(a)' to integer")
}
Please read the section about Optionals in the Swift Language Guide
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.
Handling errors from Swift 2.0 String object functions
So the problem String startIndex and characters functions do not throw and exception that can be caught by the swift do try catch block. See The Swift Programming Language (Swift 2.1) - Error Handling documentation for more info.
So I rewrote the code to deal with parsing of the integer without crashing
func parseBatteeryLevel(inputStr : String) -> Int {
if inputStr.hasPrefix("BATT") && inputStr.containsString("%") {
let start = inputStr.startIndex.advancedBy(5)
let indexOfPercent = inputStr.characters.indexOf("%")
if (indexOfPercent != nil) {
let end = indexOfPercent!.advancedBy(-1)
let batteryLevel = inputStr.substringWithRange(start...end)
if let level = Int(batteryLevel) {
return level
}
}
return 0
} else {
print("Return Value Parse Error: \"\(inputStr)\"")
return 0
}
}
Simplest way to throw an error/exception with a custom message in Swift?
The simplest approach is probably to define one custom enum
with just one case
that has a String
attached to it:
enum MyError: ErrorType {
case runtimeError(String)
}
Or, as of Swift 4:
enum MyError: Error {
case runtimeError(String)
}
Example usage would be something like:
func someFunction() throws {
throw MyError.runtimeError("some message")
}
do {
try someFunction()
} catch MyError.runtimeError(let errorMessage) {
print(errorMessage)
}
If you wish to use existing Error
types, the most general one would be an NSError
, and you could make a factory method to create and throw one with a custom message.
Converting String to Int with Swift
Basic Idea, note that this only works in Swift 1.x (check out ParaSara's answer to see how it works in Swift 2.x):
// toInt returns optional that's why we used a:Int?
let a:Int? = firstText.text.toInt() // firstText is UITextField
let b:Int? = secondText.text.toInt() // secondText is UITextField
// check a and b before unwrapping using !
if a && b {
var ans = a! + b!
answerLabel.text = "Answer is \(ans)" // answerLabel ie UILabel
} else {
answerLabel.text = "Input values are not numeric"
}
Update for Swift 4
...
let a:Int? = Int(firstText.text) // firstText is UITextField
let b:Int? = Int(secondText.text) // secondText is UITextField
...
Swift How to get integer from string and convert it into integer
First, we split the string so we can process the single items. Then we use NSCharacterSet
to select the numbers only.
import Foundation
let str = "I have to buy 3 apples, 7 bananas, 10eggs"
let strArr = str.split(separator: " ")
for item in strArr {
let part = item.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
if let intVal = Int(part) {
print("this is a number -> \(intVal)")
}
}
Swift 4:
let string = "I have to buy 3 apples, 7 bananas, 10eggs"
let stringArray = string.components(separatedBy: CharacterSet.decimalDigits.inverted)
for item in stringArray {
if let number = Int(item) {
print("number: \(number)")
}
}
Related Topics
User Already Authenticated in App and Firebase Realtime Database Returns Failed: Permission_Denied
Swift Struct Adopting Protocol with Static Read-Write Property Doesn't Conform
Accessing "Self" in Initializing Closure
Os X App Doesn't Launch New Window on Dock Icon Press in Swift
How to Delay a for Loop in Swift Without Interrupting The Main Thread
Querying Geohashes in Firestore Returns Nothing
Custom Markers Disappear on Zoomin The Map and Appear on Zoomout The Map with Clustering
Aurendercallbackstruct in Swift
Sharing Button Works Perfectly on iPhone But Crash on Ipad
Actions Assigned to Nsmenuitem Dont Seem to Work
List Is Not Conforming to Encodable
Swift: Check Which Value in Nsarray Is Closest to Another Given Value
Getting Reference to a Dictionary Value
iOS9 Filemanager File Permissions Change
Passing Data Between 2 View Controllers in Swift