Can't construct URL for phone call in Swift - swift

I'm trying to ask for phone call programmatically but I'm not able to construct URL from my nine-digit phone number. When I try it with for example 999999999 phone number, it works, it asks for call
#IBAction func callButtonPressed(_ sender: UIButton) {
askForCall(to: "999999999")
}
func askForCall(to number: String) {
guard let url = URL(string: "tel:\(number)"), UIApplication.shared.canOpenURL(url) else { return }
UIApplication.shared.open(url)
}
but when I use real phone number 736XXXXXX it shows nothing.
Note: when I try it without canOpenUrl it doesn’t work so I guess problem is with constructing URL from my real number
Any ideas?

You should type "tel://" + number and not tel:\(number)
EDIT 2
Try something like this
func call(phoneNumber: String) {
if let url = URL(string: phoneNumber) {
if #available(iOS 10, *) {
UIApplication.shared.open(url, options: [:],
completionHandler: {
(success) in
print("Open \(phoneNumber): \(success)")
})
} else {
let success = UIApplication.shared.openURL(url)
print("Open \(phoneNumber): \(success)")
}
}
}
let number = "736XXXXXX"
let phoneNumber = "tel://\(number)"
call(phoneNumber: phoneNumber)
Try with that number to see if it's a bigger problem than the simple code :)

You need to add the scheme 'tel' into your info.plist
<key>LSApplicationQueriesSchemes</key>
<string>tel</string>
Then normal use:
guard let url = URL(string: "tel://\(phoneNumber)"), UIApplication.shared.canOpenURL(url) else {return}
if #available(iOS 10, *) {
UIApplication.shared.open(url)
} else {
UIApplication.shared.openURL(url)
}
Goodluck

Related

How to get the value of searchBar.text of one VC and use it in another file of the project in swift?

I've been looking for the answer everywhere and could't find any.. Is there any way I can access the value of searchBar.text in another file? I have the delegate set in my SearchVC but I also have a custom tableView cell in another file.
I need the value of the SearchBar of my SearchVC to use in FirstDefinitionVC for decoding the word from the searchBar and use it for finding the audio URL.
All works fine while I call the function inside the searchBarSearchButtonClicked method but I can find no way to pass that String into FirstDefintionVC.
The relevant searchVC code :
var word = ""
`
extension SearchVC: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
// { (data: [WordData], [Definitions])
word = searchBar.text!
wordManager.performRequest(word: word) { data in
self.wordData = data
self.searchButtonPressed = true
// print(data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
fetchAudio(word: word) { data in //this one works fine
DispatchQueue.main.async {
self.wordData = data
}
}
}
func fetchAudio(word: String, comp: #escaping ([WordData])-> Void) {
let wordURL = "https://api.dictionaryapi.dev/api/v2/entries/en/"
let urlString = "\(wordURL)\(word)"
if let url = URL(string: urlString) {
let dataTask = URLSession.shared.dataTask(with: url, completionHandler: {
(data,response,error) in
guard let data = data, error == nil else {
print("Error occured while accessing data with URL")
return
}
do {
let decoded = try JSONDecoder().decode([WordData].self, from: data)
comp(decoded)
if let sound = decoded[0].phonetics[0].audio,
let sound2 = decoded[0].phonetics[1].audio {
print("sound = \(sound)")
let nonEmpty = (sound != "") ? sound : sound2 //write switch cases or another ternary with more urls to choose from if both are empty
self.audioUrl = URL(string: nonEmpty)
// url = URL(string: sound2)
do {
try AVAudioSession.sharedInstance().setMode(.default)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
self.player = AVPlayer(url: self.audioUrl!)
guard let player = self.player else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}
//comp(decoded, entries.self)
} catch {
print("Error occured while decoding JSON into Swift structure \(error)")
}
})
dataTask.resume()
}
}
I need to call the searchBar.text value in another file inside this IBAction of class FirstDefinitionVC:
`
#IBAction func pronunciationButton(_ sender: UIButton) {
searchVC.fetchAudio(word: searchVC.word) { data in
self.wordData = data
}
}
This was one of my approaches to this, I tried to create a global model Word with an initializer also and it didn't work. Is there any way around it?

How to make call form iOS swift with IVR call (i.e. #123#) from swift app on button click

I am trying to implement call on #123# in swift when user click on button. I am writing below code to make a call but it's not working.
let iVRPhoneNumber = "#123#"
guard let url = URL(string: "tel://\(iVRPhoneNumber)") else { return }
//It's alwasy returning from here. If I removed # from iVRPhoneNumber string then it's working.
if let phoneCallURL = URL(string: "tel://\(iVRPhoneNumber)") {
let application:UIApplication = UIApplication.shared
if (application.canOpenURL(phoneCallURL)) {
application.open(phoneCallURL, options: [:], completionHandler: nil)
}
}
Adding percentage encoding to the phone number string should work,
let iVRPhoneNumber = "#123#"
guard let phoneNumber = iVRPhoneNumber.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else { return }
guard let phoneCallURL = URL(string: "tel://\(phoneNumber)") else { return }
print(phoneCallURL)
let application:UIApplication = UIApplication.shared
if (application.canOpenURL(phoneCallURL)) {
application.open(phoneCallURL, options: [:], completionHandler: nil)
}

Return response as object in swift

I have a function that connects to an API to retrieve data. The API takes two parameters accessCode (provided by user in a text box) and then UDID (UDID of their device). I can parse the data from within the function, but only locally. I need to store the values that are returned but am unsure on how to return them properly. Essentially I need this to return the json object as a dictionary (I think...) so it can be parsed outside of the async task. I've read through the swift documentation and that's where I found out how to do the requests, but I can't find a way to store the returned values in memory for access outside of the function.
func getResponse(accessCode:String, UDID:String, _ completion: #escaping (NSDictionary) -> ()) {
let urlPath = "https://apihosthere.com/api/validate?accessCode=" + accessCode + "&UDID=" + UDID
guard let url = URL(string: urlPath) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
let results = jsonResult as? NSDictionary
print(results)
completion(results!)
}
} catch {
//Catch Error here...
}
}
task.resume()
}
First of all don't use NSDictionary in Swift, use native [String:Any] and declare the type as optional to return nil if an error occurs.
And never use .mutableContainers in Swift, the option is useless.
func getResponse(accessCode:String, UDID:String, completion: #escaping ([String:Any]?) -> Void)) {
let urlPath = "https://apihosthere.com/api/validate?accessCode=" + accessCode + "&UDID=" + UDID
guard let url = URL(string: urlPath) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error else {
print(error)
completion(nil)
return
}
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
print(jsonResult)
completion(jsonResult)
} else {
completion(nil)
}
} catch {
print(error)
completion(nil)
}
}
task.resume()
}
Your mistake is that you don't consider the closure, you have to execute the entire code inside the completion handler
#IBAction func StartWizard(_ sender: UIButton) {
//Store entered access code
let accessCode = AccessCodeField.text!
//Call API to validate Access Code
getResponse(accessCode:accessCode, UDID:myDeviceUDID) { [weak self] result in
if let accessCodeFound = result?["Found"] as? Bool {
print("Value of Found during function:")
//If access code is valid, go to License view
print(accessCodeFound)
if accessCodeFound {
//Load License View
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let licenseController = storyboard.instantiateViewController(identifier: "LicenseViewPanel")
self?.show(licenseController, sender: self)
}
}
}
}
}
Your completion closure should handle the obtained data. You would call the function like this:
getResponse(accessCode: "code", UDID: "udid", completion: { result in
// Do whatever you need to do with the dictionary result
}
Also, I'd recommend you to change your NSDictionary with a swift Dictionary.
This is what the API returns as a response
{
AccessCode = 00000000;
Client = "0000 - My Company Name";
EmailAddress = "brandon#brandonthomas.me";
FirstName = Brandon;
Found = 1;
LastName = Thomas;
Status = A;
UDIDregistered = 1;
}
And this is what calls the function. I am calling at after clicking a button after an access code is being entered in a text field.
#IBAction func StartWizard(_ sender: UIButton) {
//Store entered access code
let accessCode = AccessCodeField.text!
var accessCodeFound: Bool? = nil
//Call API to validate Access Code
getResponse(accessCode:accessCode, UDID:myDeviceUDID) { result in
accessCodeFound = result["Found"] as! Bool
print("Value of Found during function:")
print(accessCodeFound)
//accessCodeFound = true
}
//If access code is valid, go to License view
print("Value of Found after function:")
print(accessCodeFound)
//accessCodeFound = nil ???
//it seems the value is getting reset after the function completes
if accessCodeFound == true{
//Load License View
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let licenseController = storyboard.instantiateViewController(identifier: "LicenseViewPanel")
self.show(licenseController, sender: Any?.self)
}
}

AVPlayer with downloaded content doesn't work but streaming content does

I am trying to (1) download a piece of audio from a link, (2) add that newly-downloaded audio to an AVPlayer and (3) play it. Something is going wrong at step (3) and I'm looking for any guidance. Here's the code, including my alamofire and download functions, as I fear something may be going wrong at that stage.
import AVFoundation
class SettingAlarmViewController: UIViewController {
var player:AVPlayer!
override func viewDidLoad() {
super.viewDidLoad()
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}
catch {
// report for an error
}
}
func getLatestPodcastURL(completion: #escaping (URL) -> ()) {
let RSSUrl: String = "https://www.npr.org/rss/podcast.php?id=510318"
Alamofire.request(RSSUrl).responseRSS() {response in
if let podcastURL: String = response.result.value?.items[0].enclosure!
{
let audioURL = URL(string: podcastURL)
completion(audioURL!)
}
else {
//error handling
}
}
}
func downloadSongAsynch(audioUrl: URL, completion: #escaping (URL) -> ()) {
let fileManager = FileManager.default
let documentsDirectoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("podcasts/")
do {
try fileManager.createDirectory(atPath: documentsDirectoryURL.path,
withIntermediateDirectories: true, attributes: nil)
} catch {
//error handling
}
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
try FileManager.default.moveItem(at: location, to: destinationUrl)
} catch {
//error handling
}
}).resume()
completion(destinationUrl)
}
#IBAction func SetUpBotton(_ sender: Any) {
getLatestPodcastURL() {response in
//Uses an asynchronous call to Alamofire to get the podcast URL
self.downloadPodcastAsynch(audioUrl: response){response2 in
self.player = AVPlayer(url: response2)
print(self.player.currentItem)
}
#IBAction func PlayButton(_ sender: Any) {
player.play()
print(player.currentItem)
}
The log consistently shows my current item: >
But nothing plays. I have checked that the audio is working by trying to use the URL to stream this content. That works fine. I am getting the following:
BoringSSL errors "[BoringSSL] Function boringssl_session_errorlog: line 2871 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert
but from what I've read, this is just a bug in the latest update and shouldn't be impacting the download. Any thoughts on this?

code to place call - cannot call value of non-functioning type "String"

I am trying to place a phone call with this code...
let phone = detail.value(forKey: "Phone") as? String
guard let number = URL(string: "telprompt://" (phone)) else { return }
UIApplication.shared.open(number, options: [:], completionHandler: nil)
I attempted to vary my code basically off this answer swift how to make phone call iOS 10?, but I am having difficulty creating a working/error-free function.
Originally my code went like
guard let number = URL(string: "telprompt://"\(phone))...
however, Xcode directed a space between the end quote and open paranthensis for the phone variable while simultaneously deleting "\". Unfortunately, now I am left with the error in the title. A tweak in my code would be appreciated :D
Update 1:
I have updated my code to
#IBAction func call(_ sender: Any)
{
let phone = detail.value(forKey: "Phone") as? NSURL
func makeCallToNumber(number: String){
if let url = URL(string: "TEL://\(phone)"){
UIApplication.shared.open(url , options: [:], completionHandler: nil)
}
else{
print("Error")
}
}
}
yet the code is still not bringing up the dialer.
update 2:
I have switched my code to
let phone = detail.value(forKey: "Phone") as? String
if let url = URL(string: "telprompt:\(String(describing: phone))") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
and while I have no errors, no call is being made and this appears in my console. . Unsure what it means.
Working example:
func callNumber(phoneNumber: String) {
if let phoneCallURL = NSURL(string: "tel://\(phoneNumber.phoneToString())") {
if UIApplication.shared.canOpenURL(phoneCallURL as URL) {
UIApplication.shared.openURL(phoneCallURL as URL)
}
}
}
//removes "(", ")", "-", " " etc. and adds "+" for region code format
extension String {
func phoneToString() -> String {
var value = "+"
for character in self.characters {
if Int(String(character)) != nil {
value = value + String(character)
}
}
return value
}
}
phoneToString would format "+000 (000) 000" into "+000000000"
Try this easy code:
func makeCallToNumber(number: String){
if let url = URL(string: "TEL://\(number)"){
UIApplication.shared.open(url , options: [:], completionHandler: nil)
}
else{
print("Error")
}
}
Using guard and telprompt
func makeCallToNumber2(number: String){
guard let url = URL(string: "telprompt://\(number)") else {
print("Error")
return
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
Regards