100

I want my app to be able to call a certain number when a button is clicked. I've tried to google it but there doesn't seem to have one for iOS 10 so far (where openURL is gone). Can someone put an example for me on how to do so? For instance like:

@IBAction func callPoliceButton(_ sender: UIButton) {
    // Call the local Police department
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
user3175707
  • 1,123
  • 3
  • 11
  • 14
  • Thank! I tried the same exact code however, when I change the google.com to "tel://120390129391824812988238197", nothing happened when I clicked on the button. Is there something I am missing? – user3175707 Oct 18 '16 at 04:13
  • I was expecting it to have some pop-up and say "call 120390129391824812988238197 ?" – user3175707 Oct 18 '16 at 04:13

6 Answers6

184

You can call like this:

 if let url = URL(string: "tel://\(number)") {
     UIApplication.shared.openURL(url)
 }

For Swift 3+, you can use like

guard let number = URL(string: "tel://" + number) else { return }
UIApplication.shared.open(number)

OR

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

Make sure you've scrubbed your phone number string to remove any instances of (, ), -, or space.

Wimukthi Rajapaksha
  • 961
  • 1
  • 11
  • 23
Parth Adroja
  • 13,198
  • 5
  • 37
  • 71
89

Task

Make a call with phone number validation

Details

Tested on:

  • Swift 5.2, Xcode 11.4 (11E146)

Solution

// MARK: DataDetector

class DataDetector {

    private class func _find(all type: NSTextCheckingResult.CheckingType,
                             in string: String, iterationClosure: (String) -> Bool) {
        guard let detector = try? NSDataDetector(types: type.rawValue) else { return }
        let range = NSRange(string.startIndex ..< string.endIndex, in: string)
        let matches = detector.matches(in: string, options: [], range: range)
        loop: for match in matches {
            for i in 0 ..< match.numberOfRanges {
                let nsrange = match.range(at: i)
                let startIndex = string.index(string.startIndex, offsetBy: nsrange.lowerBound)
                let endIndex = string.index(string.startIndex, offsetBy: nsrange.upperBound)
                let range = startIndex..<endIndex
                guard iterationClosure(String(string[range])) else { break loop }
            }
        }
    }

    class func find(all type: NSTextCheckingResult.CheckingType, in string: String) -> [String] {
        var results = [String]()
        _find(all: type, in: string) {
            results.append($0)
            return true
        }
        return results
    }

    class func first(type: NSTextCheckingResult.CheckingType, in string: String) -> String? {
        var result: String?
        _find(all: type, in: string) {
            result = $0
            return false
        }
        return result
    }
}

// MARK: PhoneNumber

struct PhoneNumber {
    private(set) var number: String
    init?(extractFrom string: String) {
        guard let phoneNumber = PhoneNumber.first(in: string) else { return nil }
        self = phoneNumber
    }

    private init (string: String) { self.number = string }

    func makeACall() {
        guard let url = URL(string: "tel://\(number.onlyDigits())"),
            UIApplication.shared.canOpenURL(url) else { return }
        if #available(iOS 10, *) {
            UIApplication.shared.open(url)
        } else {
            UIApplication.shared.openURL(url)
        }
    }

    static func extractAll(from string: String) -> [PhoneNumber] {
        DataDetector.find(all: .phoneNumber, in: string)
            .compactMap {  PhoneNumber(string: $0) }
    }

    static func first(in string: String) -> PhoneNumber? {
        guard let phoneNumberString = DataDetector.first(type: .phoneNumber, in: string) else { return nil }
        return PhoneNumber(string: phoneNumberString)
    }
}

extension PhoneNumber: CustomStringConvertible { var description: String { number } }

// MARK: String extension

extension String {

    // MARK: Get remove all characters exept numbers

    func onlyDigits() -> String {
        let filtredUnicodeScalars = unicodeScalars.filter { CharacterSet.decimalDigits.contains($0) }
        return String(String.UnicodeScalarView(filtredUnicodeScalars))
    }

    var detectedPhoneNumbers: [PhoneNumber] { PhoneNumber.extractAll(from: self) }
    var detectedFirstPhoneNumber: PhoneNumber? { PhoneNumber.first(in: self) }
}

Usage

PhoneNumber(extractFrom: "+1-(800)-123-4567")?.makeACall()

PhoneNumber.extractAll(from: "+1-(800)-123-4567 bla bla 1(617)111-22-33").last?.makeACall()

PhoneNumber.first(in: "+1-(800)-123-4567 bla bla 1(617)111-22-33")?.makeACall()

"+1-(800)-123-4567 bla bla 1(617)111-22-33".detectedPhoneNumbers[1].makeACall()
"+1-(800)-123-4567 bla bla 1(617)111-22-33".detectedFirstPhoneNumber?.makeACall()

Full sample

do not forget to paste the solution code here

func test() {
    isPhone("blabla")
    isPhone("+1(222)333-44-55")
    isPhone("+42 555.123.4567")
    isPhone("+1-(800)-123-4567")
    isPhone("+7 555 1234567")
    isPhone("+7(926)1234567")
    isPhone("(926) 1234567")
    isPhone("+79261234567")
    isPhone("926 1234567")
    isPhone("9261234567")
    isPhone("1234567")
    isPhone("123-4567")
    isPhone("123-89-01")
    isPhone("495 1234567")
    isPhone("469 123 45 67")
    isPhone("8 (926) 1234567")
    isPhone("89261234567")
    isPhone("926.123.4567")
    isPhone("415-555-1234")
    isPhone("650-555-2345")
    isPhone("(416)555-3456")
    isPhone("202 555 4567")
    isPhone("4035555678")
    isPhone(" 1 416 555 9292")
    isPhone("1(617)111-22-33!")
    isPhone("+44 1838 300284")
    isPhone("+44 1838 300284, 1 416 555 9292")
    isPhone("+44 1838 3d0384, 1 416 555 9292!")
}

private func isPhone(_ string: String) {
    let phoneNumbers = PhoneNumber.extractAll(from: string)
    let result = !phoneNumbers.isEmpty
    print("\(result ? "✅" : "❌") \(string) | detected phones: \(phoneNumbers)")
}

Result

❌ blabla | detected phones: []
✅ +1(222)333-44-55 | detected phones: [+1(222)333-44-55]
✅ +42 555.123.4567 | detected phones: [555.123.4567]
✅ +1-(800)-123-4567 | detected phones: [+1-(800)-123-4567]
✅ +7 555 1234567 | detected phones: [+7 555 1234567]
✅ +7(926)1234567 | detected phones: [+7(926)1234567]
✅ (926) 1234567 | detected phones: [(926) 1234567]
✅ +79261234567 | detected phones: [+79261234567]
✅ 926 1234567 | detected phones: [926 1234567]
✅ 9261234567 | detected phones: [9261234567]
✅ 1234567 | detected phones: [1234567]
✅ 123-4567 | detected phones: [123-4567]
✅ 123-89-01 | detected phones: [123-89-01]
✅ 495 1234567 | detected phones: [495 1234567]
✅ 469 123 45 67 | detected phones: [469 123 45 67]
✅ 8 (926) 1234567 | detected phones: [8 (926) 1234567]
✅ 89261234567 | detected phones: [89261234567]
✅ 926.123.4567 | detected phones: [926.123.4567]
✅ 415-555-1234 | detected phones: [415-555-1234]
✅ 650-555-2345 | detected phones: [650-555-2345]
✅ (416)555-3456 | detected phones: [(416)555-3456]
✅ 202 555 4567 | detected phones: [202 555 4567]
✅ 4035555678 | detected phones: [4035555678]
✅  1 416 555 9292 | detected phones: [1 416 555 9292]
✅ 1(617)111-22-33! | detected phones: [1(617)111-22-33]
✅ +44 1838 300284 | detected phones: [+44 1838 300284]
✅ +44 1838 300284, 1 416 555 9292 | detected phones: [+44 1838 300284, 1 416 555 9292]
✅ +44 1838 3d0384, 1 416 555 9292! | detected phones: [1 416 555 9292]
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
23

In Swift 4.2

func dialNumber(number : String) {

 if let url = URL(string: "tel://\(number)"),
   UIApplication.shared.canOpenURL(url) {
      if #available(iOS 10, *) {
        UIApplication.shared.open(url, options: [:], completionHandler:nil)
       } else {
           UIApplication.shared.openURL(url)
       }
   } else {
            // add error message here 
   }
}

Call this like below

dialNumber(number: "+921111111222")

Hope this help.

Abdul Rehman
  • 2,386
  • 1
  • 20
  • 34
18

Updated for Swift 3:

used below simple lines of code, if you want to make a phone call:

// function defination:

func makeAPhoneCall()  {
    let url: NSURL = URL(string: "TEL://1234567890")! as NSURL
    UIApplication.shared.open(url as URL, options: [:], completionHandler: nil)
}

// function call: [Used anywhere in your code]

self.makeAPhoneCall()

Note: Please run the app on a real device because it won't work on the simulator.

Kiran Jadhav
  • 3,209
  • 26
  • 29
7

By mistake my answer was misplaced, please checkout this one: You can use this:

guard let url = URL(string: "tel://\(yourNumber)") else {
return //be safe
}

if #available(iOS 10.0, *) {
UIApplication.shared.open(url)
} else {
UIApplication.shared.openURL(url)
}

We need to check whether we're on iOS 10 or later As 'openURL' was deprecated in iOS 10.0

Anjali jariwala
  • 410
  • 5
  • 15
6
if let phoneCallURL:URL = URL(string: "tel:\(strPhoneNumber)") {
        let application:UIApplication = UIApplication.shared
        if (application.canOpenURL(phoneCallURL)) {
            let alertController = UIAlertController(title: "MyApp", message: "Are you sure you want to call \n\(self.strPhoneNumber)?", preferredStyle: .alert)
            let yesPressed = UIAlertAction(title: "Yes", style: .default, handler: { (action) in
                application.openURL(phoneCallURL)
            })
            let noPressed = UIAlertAction(title: "No", style: .default, handler: { (action) in

            })
            alertController.addAction(yesPressed)
            alertController.addAction(noPressed)
            present(alertController, animated: true, completion: nil)
        }
    }
Pratik Patel
  • 1,393
  • 12
  • 18