68

Using only swift code I cant figure out how to take "(555) 555-5555" and return only the numeric values and get "5555555555". I need to remove all the parentheses, white spaces, and the dash. The only examples I can find are in objective-C and they seem to all use the .trim() method. It appears as though swift doesn't have this method but it does have the .stringByTrimmingCharacters method, but that only seems to trim the white spaces before and after the data.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
Jeff Dicket
  • 689
  • 1
  • 5
  • 3
  • 4
    None of the "trim" methods will work since they only remove character from the ends of the string. – rmaddy Apr 30 '15 at 15:20

12 Answers12

130

Swift 3 & 4

extension String {
    var digits: String {
        return components(separatedBy: CharacterSet.decimalDigits.inverted)
            .joined()
    }
}

Swift 5

You should be able to omit return

Also: Read the comment from @onmyway133 for a word of caution

retendo
  • 1,309
  • 2
  • 12
  • 18
  • 16
    `return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()` – Leo Dabus Dec 16 '16 at 20:08
  • 2
    Note that `decimalDigits` may contain more than just numbers https://ravron.com/2019/07/how-many-decimal-digits-are-there-anyways/ – onmyway133 Feb 25 '21 at 08:25
65

Split the string by non-digit characters to an array of digits and the join them back to a string:

Swift 1:

let stringArray = origString.componentsSeparatedByCharactersInSet(
    NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let newString = NSArray(array: stringArray).componentsJoinedByString("")

Swift 2:

let stringArray = origString.componentsSeparatedByCharactersInSet(
    NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let newString = stringArray.joinWithSeparator("")

Swift 3 & 4:

let newString = origString
    .components(separatedBy:CharacterSet.decimalDigits.inverted)
    .joined()
Axel Guilmin
  • 11,454
  • 9
  • 54
  • 64
Tapani
  • 3,191
  • 1
  • 25
  • 41
22

I like regular expressions:

var s = "(555) 555-5555"
s = s.stringByReplacingOccurrencesOfString(
    "\\D", withString: "", options: .RegularExpressionSearch, 
    range: s.startIndex..<s.endIndex)
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 6
    Thanks for your answer! One small addition: I found that a better regex to use is `[^\\d+]`, leaving the valid + character intact. – jeppeb May 08 '16 at 08:55
15

In Swift 4 the solution is more nice:

import Foundation

let sourceText = "+5 (555) 555-5555"

let allowedCharset = CharacterSet
    .decimalDigits
    .union(CharacterSet(charactersIn: "+"))

let filteredText = String(sourceText.unicodeScalars.filter(allowedCharset.contains))

print(filteredText) // +55555555555
werediver
  • 4,667
  • 1
  • 29
  • 49
10

Here is @Tapani's Swift 2.0 answer as a handy String extension, (length property is not part of solution but I left it in example because it is also handy):

import Foundation

extension String {

    var length : Int {
        return self.characters.count
    }

    func digitsOnly() -> String{
        let stringArray = self.componentsSeparatedByCharactersInSet(
            NSCharacterSet.decimalDigitCharacterSet().invertedSet)
        let newString = stringArray.joinWithSeparator("")

        return newString
    }

}

Usage:

let phone = "(123)-123 - 1234"
print(phone.digitsOnly())
Brian Ogden
  • 18,439
  • 10
  • 97
  • 176
7

I had a similar issue but I needed to retain the decimal points. I tweaked the top answer to this:

extension String {

    /// Returns a string with all non-numeric characters removed
    public var numericString: String {
        let characterSet = CharacterSet(charactersIn: "0123456789.").inverted
        return components(separatedBy: characterSet)
            .joined()
    }
}
Mark Bridges
  • 8,228
  • 4
  • 50
  • 65
6

Details

  • Xcode Version 10.2.1 (10E1001), Swift 5

Solution

import Foundation

extension String {

    private func filterCharacters(unicodeScalarsFilter closure: (UnicodeScalar) -> Bool) -> String {
        return String(String.UnicodeScalarView(unicodeScalars.filter { closure($0) }))
    }

    private func filterCharacters(definedIn charSets: [CharacterSet], unicodeScalarsFilter: (CharacterSet, UnicodeScalar) -> Bool) -> String {
        if charSets.isEmpty { return self }
        let charSet = charSets.reduce(CharacterSet()) { return $0.union($1) }
        return filterCharacters { unicodeScalarsFilter(charSet, $0) }
    }

    func removeCharacters(charSets: [CharacterSet]) -> String { return filterCharacters(definedIn: charSets) { !$0.contains($1) } }
    func removeCharacters(charSet: CharacterSet) -> String { return removeCharacters(charSets: [charSet]) }

    func onlyCharacters(charSets: [CharacterSet]) -> String { return filterCharacters(definedIn: charSets) { $0.contains($1) } }
    func onlyCharacters(charSet: CharacterSet) -> String { return onlyCharacters(charSets: [charSet]) }
}

Usage

let string = "23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E"
print("original string:                                 \(string)")
print("only .decimalDigits:                             \(string.onlyCharacters(charSet: .decimalDigits))")
print("only [.lowercaseLetters, .symbols]:              \(string.onlyCharacters(charSets: [.lowercaseLetters, .symbols]))")
print("remove .letters:                                 \(string.removeCharacters(charSet: .letters))")
print("remove [.decimalDigits, .lowercaseLetters]:      \(string.removeCharacters(charSets: [.decimalDigits, .lowercaseLetters]))")

Result

original string:                                 23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E
only .decimalDigits:                             2345425241309499238304
only [.lowercaseLetters, .symbols]:              fgdorivwer+wiuruwu$q+dgnkvljb`keflnwdlqsa`
remove .letters:                                 2345#@%#425  24 1+ 30949*()92_)$#)_ 38304+{ `; `,.
remove [.decimalDigits, .lowercaseLetters]:      #@%#   +DWEJ *()ER_)$I#Q)_ U+RFJO{ `; `WKFSA,.E

(Optional) String extension

extension String {
    var onlyDigits: String { return onlyCharacters(charSets: [.decimalDigits]) }
    var onlyLetters: String { return onlyCharacters(charSets: [.letters]) }
}

(Optional) String extension usage

let string = "23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E"
print("original string:     \(string)")
print(".onlyDigits:         \(string.onlyDigits)")
print(".onlyLetters:        \(string.onlyLetters)")

(Optional) String extension usage result

original string:     23f45gdor#@%#i425v wer 24 1+DWEJwi 3u09ru49w*()9uE2R_)$I#Q)_ U383q04+RFJO{dgnkvlj b`kefl;nwdl qsa`WKFSA,.E
.onlyDigits:         2345425241309499238304
.onlyLetters:        fgdorivwerDWEJwiuruwuERIQUqRFJOdgnkvljbkeflnwdlqsaWKFSAE
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
3

Try this:

let string = "(555) 555-5555"
let digitString = string.filter { ("0"..."9").contains($0) }
print(digitString) // 5555555555

Putting in extension:

extension String
{
    var digitString: String { filter { ("0"..."9").contains($0) } }
}

print("(555) 555-5555".digitString) // 5555555555
Nilanshu Jaiswal
  • 1,583
  • 3
  • 23
  • 34
2

You'll want to use NSCharacterSet:

Check out this NSHipster link for Swift and Obj-C implementations: http://nshipster.com/nscharacterset/

Similar example:

var string = "  Lorem    ipsum dolar   sit  amet. "

let components = string.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).filter({!isEmpty($0)})

string = join(" ", components)

See: punctuationCharacterSet

Description:

Returns a character set containing the characters in the category of Punctuation. Informally, this set is the set of all non-whitespace characters used to separate linguistic units in scripts, such as periods, dashes, parentheses, and so on.

@Tapani Makes a great suggestion: NSCharacterSet.decimalDigitCharacterSet().invertedSet

Community
  • 1
  • 1
NSWill
  • 659
  • 8
  • 15
1

Here is @Tapani Swift 3.2 solution

let phno = contact.phoneNumbers[0].phoneNumber
let strarr = phno.components(separatedBy: NSCharacterSet.decimalDigits.inverted)
let newString = NSArray(array: strarr).componentsJoined(by: "")
print(newString)
Sabrina Tuli
  • 160
  • 7
1

I found the best solution with filter function. Please have a look into it.

let string = "(555) 555-5555"

let onlyDigits = string.filter({ (char) -> Bool in

    if Int("\(char)") != nil {

        return true
    }
    else {

        return false
    }

})
Naresh
  • 869
  • 8
  • 17
  • `let onlyDigits = string.filter{ Int("\($0)") != nil }` – b m gevariya Nov 07 '19 at 07:33
  • let onlyDigits = string.filter{ch in "0123456789".contains(ch)} – altimes Aug 27 '21 at 03:57
  • @altimes this will increase the runtime complexity. – Naresh Aug 27 '21 at 06:47
  • Interesting thought. I was assuming that the parsing and constructing an Int would be more complex. But then, looking at it, it only has a single character to work on which probably has some simple manipulation of the of the ascii value under the covers. – altimes Aug 28 '21 at 04:42
1

Not exactly answered but it looks like a number. I used URLComponents to build the url because it strips out parenthesis and dashes automatically:

var telUrl: URL? {
    var component = URLComponents()
    component.scheme = "tel"
    component.path = "+49 (123) 1234 - 56789"
    return component.url
}

then

UIApplication.shared.open(telUrl, options: [:], completionHandler: nil)

calls +49 123 123456789

mbnz
  • 73
  • 8