Swift: How to Catch Exception When Parsing a Numeric String

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



Leave a reply



Submit