15

In Swift, how can one check if a string is a valid double value? I have been using the following extension from this question (but as a float) but if the value cannot be converted, it simply returns "0":

extension String {
    var doubleValue:Double? {
        return (self as NSString).doubleValue
    }
}

Ideally, I would like it to return nil so it can be caught in an if-let, like so:

if let i = str.doubleValue {
    object.price = i
} else {
    // Tell user the value is invalid
}
Community
  • 1
  • 1
Michael Voccola
  • 1,827
  • 6
  • 20
  • 46
  • if (number - floor(number) > 0.000001) { // 0.000001 can be changed depending on the level of precision you need // double value } Try this out? It came from here http://stackoverflow.com/questions/25552648/check-if-number-is-decimal-with-swift – Wraithseeker May 19 '15 at 02:30

4 Answers4

29

edit/update: Xcode 11 or later • Swift 5.1 or later

You can use Double initializer init?<S>(_ text: S) where S : StringProtocol to create an instance property on StringProtocol and use it to check if a String or Substring is a valid Double:

extension StringProtocol {
    var double: Double? { Double(self) }
    var float: Float? { Float(self) }
    var integer: Int? { Int(self) }
}

Testing

let str = "2.9"
if let value = str.double  {
    print(value)           // "2.9\n"
} else {
    print("invalid input")
}

str.prefix(1).integer  // 2
str.suffix(1).integer  // 9
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 3
    I would suggest you cache NSNumberFormatter, if you use this method a lot (for example on a tableViewCell) you would benefit from caching it. – EmilioPelaez May 19 '15 at 03:44
  • @EmilioPelaez How would you go about caching it? – Fengson Sep 27 '17 at 13:42
  • @Fengson his question was done when the answer was in Swift 1 and at the time there was no Double(_ string:) initializer. this answer shows how to do it in Swift 3.x and later https://stackoverflow.com/a/27705739/2303865 – Leo Dabus Sep 27 '17 at 15:41
16

It is indeed more efficient not to create a number formatter every time we do a conversion:

extension String {
     struct NumFormatter {
         static let instance = NumberFormatter()
     }

     var doubleValue: Double? {
         return NumFormatter.instance.number(from: self)?.doubleValue
     }

     var integerValue: Int? {
         return NumFormatter.instance.number(from: self)?.intValue
     }
}
Satyam
  • 15,493
  • 31
  • 131
  • 244
andriys
  • 2,202
  • 2
  • 19
  • 24
  • 2
    If going this route, any reason to not make the structure a first-class member of the entire class extension? Then you can easily extend string to also support `integerValue`, `boolValue`, etc. and share the same `Formatter`. – Blake Merryman May 19 '15 at 13:59
  • 1
    Note for others: NSNumberFormatter() has been renamed to NumberFormatter in Swift 3 so the struct name will give you errors. All you have to do is change it's name. – Hedylove Nov 06 '16 at 02:08
  • Note that this is no pure Swift. It requires you to import Foundation. – Leo Dabus Jan 20 '20 at 03:17
4

Why not let it return false? Or true of course.

extension String {

    func isInt() -> Bool {

        if let intValue = Int(self) {
            return true
        }

        return false
    }

    func isFloat() -> Bool {

        if let floatValue = Float(self) {
            return true
        }

        return false
    }

    func isDouble() -> Bool {

        if let doubleValue = Double(self) {
            return true
        }

        return false
    }

    func numberOfCharacters() -> Int {
        return self.characters.count
    }
}

Or even better, as suggested by @LeoDabus:

extension String {
    var isInteger: Bool { return Int(self) != nil }
    var isFloat: Bool { return Float(self) != nil }
    var isDouble: Bool { return Double(self) != nil }
} 
asdf
  • 952
  • 14
  • 13
  • That is definitely cleaner! – Michael Voccola Apr 26 '18 at 13:01
  • Why would youl return FALSE for all negative numbers? – Leo Dabus Jun 22 '19 at 11:13
  • 2
    You can simply return not equal to nil and there is no need to make it a method. If there is no need to pass any value to it. You can make them all computed properties with a getter but no setter `extension StringProtocol { var isInteger: Bool { return Int(self) != nil } var isFloat: Bool { return Float(self) != nil } var isDouble: Bool { return Double(self) != nil } }` – Leo Dabus Jun 23 '19 at 15:10
-9

Here is my function:

func je_numerik(text:Character)->Bool //en znak
{
    if((text=="0")||(text=="1")||(text=="2")||(text=="3")||(text=="4")||(text=="5")||(text=="6")||(text=="7")||(text=="8")||(text=="9")){
        return true
    }
    else
    {
        return false
    }
}

func je_stevilka(text: String)->Bool
{
    var pika:Character
    pika="."

    var je_stevilka=true
    //var znaki = Character(text)
    var znaki=Array(text)
    var stevilo_znakov=znaki.count

    if(stevilo_znakov==0)
    {
        je_stevilka=false
    }
    else
    {
        if(stevilo_znakov==1)
        {
            if(je_numerik(znaki[0]))
            {
                je_stevilka=true
            }
            else
            {
                je_stevilka=false
            }
        }
        else
        {

            if((je_numerik(znaki[0])) && (!((znaki[0]=="0")&&((znaki[1]=="0"))))&&(!((znaki[0]=="0")&&((je_numerik(znaki[1]))))))
            {

                var je_pika=false
                for var i = 0; i < stevilo_znakov; i++
                {
                    if(je_numerik(znaki[i])||(znaki[i]==pika))
                    {
                        if(znaki[i]==pika)
                        {
                            if(je_pika)
                            {
                                je_stevilka=false
                            }
                            else
                            {
                                je_pika=true
                                if(stevilo_znakov==(i+1))
                                {
                                    je_stevilka=false
                                }
                            }
                        }
                    }
                    else
                    {
                        je_stevilka=false
                    }
                }
            }
            else
            {
                je_stevilka=false
            }
        }
    }

    return je_stevilka
}

Just call it like this:

var check_if_is_number=je_stevilka(numberText)
Gregor
  • 275
  • 1
  • 7
  • 19