How to use openURL for making a phone call in Swift? - swift

I have converted the code for making a phone call from Objective-C to Swift, but in Objective-C, we can set the type of the URL that we like to open (e.g. telephone, SMS, web) like this:
#"tel:xx"
#"mailto:info#example.es"
#"http://stackoverflow.com"
#"sms:768number"
The code in Swift is:
UIApplication.sharedApplication().openURL(NSURL(string : "9809088798")
I read that have not released any scheme parameter for tel:, but I don't know if Swift can detect if the string is for making a phone call, sending email, or opening a website. Or may I write:
(string : "tel//:9809088798")
?

I am pretty sure you want:
UIApplication.sharedApplication().openURL(NSURL(string: "tel://9809088798")!)
(note that in your question text, you put tel//:, not tel://).
NSURL's string init expects a well-formed URL. It will not turn a bunch of numbers into a telephone number. You sometimes see phone-number detection in UIWebView, but that's being done at a higher level than NSURL.
EDIT: Added ! per comment below

A self-contained solution in Swift:
private func callNumber(phoneNumber:String) {
if let phoneCallURL:NSURL = NSURL(string:"tel://\(phoneNumber)") {
let application:UIApplication = UIApplication.sharedApplication()
if (application.canOpenURL(phoneCallURL)) {
application.openURL(phoneCallURL);
}
}
}
Now, you should be able to use callNumber("7178881234") to make a call.

For Swift in iOS:
var url:NSURL? = NSURL(string: "tel://9809088798")
UIApplication.sharedApplication().openURL(url!)

You need to remember to remove the whitespaces or it won't work:
if let telephoneURL = NSURL(string: "telprompt://\(phoneNumber.stringByReplacingOccurrencesOfString(" ", withString: ""))") {
UIApplication.sharedApplication().openURL(telelphoneURL)
}
"telprompt://" will prompt the user to call or cancel while "tel://" will call directly.

# confile:
The problem is that your solution does not return to the app after the
phone call has been finished on iOS7. – Jun 19 at 13:50
&# Zorayr
Hm, curious if there is a solution that does do that.. might be a
restriction on iOS.
use
UIApplication.sharedApplication().openURL(NSURL(string: "telprompt://9809088798")!)
You will get a prompt to Call/Cancel but it returns to your application. AFAIK there is no way to return (without prompting)

You must insert "+"\ is another way
private func callNumber(phoneNumber:String) {
if let phoneCallURL:NSURL = NSURL(string:"tel://"+"\(phoneNumber)") {
let application:UIApplication = UIApplication.sharedApplication()
if (application.canOpenURL(phoneCallURL)) {
application.openURL(phoneCallURL);
}
}
}

Small update to Swift 3
UIApplication.shared.openURL(NSURL(string: "telprompt://9809088798")! as URL)

The following code snippet can tell if the SIM is there or not and if the device is capable of making the call and if ok then it'll make the call
var info = CTTelephonyNetworkInfo()
var carrier = info.subscriberCellularProvider
if carrier != nil && carrier.mobileNetworkCode == nil || carrier.mobileNetworkCode.isEqual("") {
//SIM is not there in the phone
}
else if UIApplication.sharedApplication().canopenURL(NSURL(string: "tel://9809088798")!)
{
UIApplication.sharedApplication().openURL(NSURL(string: "tel://9809088798")!)
}
else
{
//Device does not have call making capability
}

For making a call in swift 5.1, just use the following code: (I have tested it in Xcode 11)
let phone = "1234567890"
if let callUrl = URL(string: "tel://\(phone)"), UIApplication.shared.canOpenURL(callUrl) {
UIApplication.shared.open(callUrl)
}
Edit:
For Xcode 12.4, swift 5.3, just use the following:
UIApplication.shared.open(NSURL(string: "tel://555-123-1234")! as URL)
Make sure that you import UIKit, or it will say that it cannot find UIApplication in scope.

For swift 3
if let phoneCallURL:URL = URL(string:"tel://\(phoneNumber ?? "")") {
let application:UIApplication = UIApplication.shared
if (application.canOpenURL(phoneCallURL)) {
application.open(phoneCallURL, options: [:], completionHandler: nil);
}
}

For swift 4:
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)")
}
}
}
Then, use the function:
let phoneNumber = "tel://+132342424"
call(phoneNumber: phoneNumber)

Swift 4 and above
let dialer = URL(string: "tel://5028493750")
if let dialerURL = dialer {
UIApplication.shared.open(dialerURL)
}

I prefer deferring the URL creation to the built-ins like:
var components = URLComponents()
components.scheme = "tel"
components.path = "1234567890"
print(components.url!) //Prints out: "tel:1234567890"
Which can then be used in UIApplication.shared.openURL

Related

Select Text in Webkit applications via macOS accessibility API

I need to select text in a WebKit view of another application (Apple Mail) using accessibility APIs.
For regular text fields, I do something like this:
func selectText(withRange range: CFRange) throws {
var range = range
guard let newValue: AXValue = AXValueCreate(AXValueType.cfRange, &range) else { return }
AXUIElementSetAttributeValue(self, kAXSelectedTextRangeAttribute as CFString, newValue)
}
However, in the composing window of Apple Mail every text seems to be of type Static Text which doesn't come with the necessary AXSelectedTextRange
It has AXSelectedTextMarkerRange, though, which requires an AXTextMarker. I just don't get how to create one of these. I have no trouble reading the text from a user created selection using this here, but I'm unable to select text via the accessibility APIs.
Thanks to the hint from Willeke I was able to figure it out. It is indeed possible to do it using AXTextMarkerForIndex. Knowing that it's actually pretty straightforward.
Here's my code:
func getTextMarker(forIndex index: CFIndex) throws -> AXTextMarker? {
var textMarker: AnyObject?
guard AXUIElementCopyParameterizedAttributeValue(self,"AXTextMarkerForIndex" as CFString, index as AnyObject, &textMarker) == .success else { return nil }
return textMarker as! AXTextMarker
}
func selectStaticText(withRange range: CFRange) throws {
guard let textMarkerStart = try? getTextMarker(forIndex: range.location) else { return }
guard let textMarkerEnd = try? getTextMarker(forIndex: range.location + range.length) else { return }
let textMarkerRange = AXTextMarkerRangeCreate(kCFAllocatorDefault, textMarkerStart, textMarkerEnd)
AXUIElementSetAttributeValue(self, "AXSelectedTextMarkerRange" as CFString, textMarkerRange)
}

Implementing "Open file with" in Swift Cocoa App

I'm working on a macOS cocoa-app in Swift where I import several different file types into the app for the user to interact with.
I'm currently trying to determine if it's possible to implement the "Open file with" feature, so that the user could open those files in a different program if they wanted to:
I've found a few different SO questions that seem tangentially related to what I'm trying to do:
Swift: How to open file with associated application?
Launch OSX Finder window with specific files selected
...but so far nothing to indicate if it's possible to implement right-click Finder/file (?) access in the way I had in mind.
Apologies if this is too vague of a question; any help / guidance appreciated!
Without going into details, it's pretty straight forward:
Get the list of all known applications that can open a specific file type (see LSCopyApplicationURLsForURL, a Core Foundation C function).
Build the menu. You can use NSWorkspace (and probably URL) to get the application icons.
Use NSWorkspace.openFile(_:withApplication:) to tell the application to open the given document.
2022, Swift 5
Get app list associated with local file:
func getAppsAssociatedWith(_ url: URL?) {
guard let url = localFileURL,
let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
let listOfRelatedApps = retainedArr as? Array<URL>
else {
return []
}
return listOfRelatedApps
}
Getting thumbnail for app:
let singleAppIcon = NSWorkspace.shared
.icon(forFile: appUrl.path)
.scaledCopy(sizeOfLargerSide: 17)
Open url with app:
#available(macOS 10.15, iOS 9.0, *)
public class func openUrlWithApp(_ urls: [URL], appUrl: URL) {
NSWorkspace.shared.open(urls, withApplicationAt: appUrl, configuration: NSWorkspace.OpenConfiguration())
}
In my app I'm cashing all apps icons in dictionary.
[someFile localURL : app icon]
If I have already got icon earlier - no need to get it once more
var relatedAppsThumbnails: [URL: Image] = [:]
func updateRelatedApps() {
guard let url = currImgUrl, // file url to get icons from related apps
let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
let listOfRelatedApps = retainedArr as? Array<URL>
else {
relatedApps = []
return
}
self.relatedApps = listOfRelatedApps
// add app icon in case of it wasn't added yet
for appUrl in listOfRelatedApps {
if relatedAppsThumbnails[appUrl] == nil {
let nsImg = NSWorkspace.shared.icon(forFile: appUrl.path)
.scaledCopy(sizeOfLargerSide: 17)
relatedAppsThumbnails[appUrl] = Image(nsImage: nsImg)
}
}
}
LSCopyApplicationURLsForURL is deprecated. You can use this alternative:
func getListOfExternalApps(forURL url: URL) -> [(URL, Image)] {
let listOfExternalApps = NSWorkspace.shared.urlsForApplications(toOpen: url)
let icons = listOfExternalApps.map {
let nsimage = NSWorkspace.shared.icon(forFile: $0.path())
nsimage.size = CGSize(width: .s16, height: .s16)
return Image(nsImage: nsimage)
}
return Array(zip(listOfExternalApps, icons))
}

Open containing app from custom keyboard in Swift 3?

Before iOS 10 i used the method written by Valentin Sherwin here.
Any workarounds to open the containing app from the custom keyboard with Swift 3?
please try to use below method.
it worked well on xcode 8.2, swift 3.0
func openURL(_ url: URL) {
return
}
func openApp(_ urlstring:String) {
var responder: UIResponder? = self as UIResponder
let selector = #selector(openURL(_:))
while responder != nil {
if responder!.responds(to: selector) && responder != self {
responder!.perform(selector, with: URL(string: urlstring)!)
return
}
responder = responder?.next
}
}
// Usage
//call the method like below
// self.openApp(urlString)
// URL string need to included custom scheme.
// for example, if you created scheme name = customApp
// urlString will be "customApp://?[name]=[value]"
// self.openApp("customApp://?category=1")

How Save UILocalNotifications in CoreData

Answer is below, image is here:
I was searching how to do this for a couple of days and was only able to find people who stored UILocalNotificaations in NSUserDefaults. Saving these in NSUserDefaults seemed wrong to me because it is supposed to be used for small flags. I just now finally figured out how to store notifications in CoreData. This is Using Xcode 7.3.1 and Swift 2.2
First off you need to create a new entity in your CoreDataModel
and then add a single attribute to it. the attribute should be of type Binary Data I named my table/entity "ManagedFiredNotifications" and my attribute "notification". it should look like this:
Image linked in Question above.
Next you need to add an extension to UILocalNotification it should go like this:
extension UILocalNotification {
func save() -> Bool {
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
let firedNotificationEntity = NSEntityDescription.insertNewObjectForEntityForName("ManagedFiredNotifications", inManagedObjectContext: appDelegate!.managedObjectContext)
guard appDelegate != nil else {
return false
}
let data = NSKeyedArchiver.archivedDataWithRootObject(self)
firedNotificationEntity.setValue(data, forKey: "notification")
do {
try appDelegate!.managedObjectContext.save()
return true
} catch {
return false
}
}
}
Now for saving a notification all you need to do is call
UILocalNotification.save()
On the notification you would like to save. my notifications were named 'notification' so I would call notification.save()
To retrieve a notification you need a method like this
func getLocalFiredNotifications() -> [UILocalNotification]? {
let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)!.managedObjectContext
let firedNotificationFetchRequest = NSFetchRequest(entityName: "ManagedFiredNotifications")
firedNotificationFetchRequest.includesPendingChanges = false
do {
let fetchedFiredNotifications = try managedObjectContext.executeFetchRequest(firedNotificationFetchRequest)
guard fetchedFiredNotifications.count > 0 else {
return nil
}
var firedNotificationsToReturn = [UILocalNotification]()
for managedFiredNotification in fetchedFiredNotifications {
let notificationData = managedFiredNotification.valueForKey("notification") as! NSData
let notificationToAdd = NSKeyedUnarchiver.unarchiveObjectWithData(notificationData) as! UILocalNotification
firedNotificationsToReturn.append(notificationToAdd)
}
return firedNotificationsToReturn
} catch {
return nil
}
}
Note that this returns an array of UILocalNotifications.
When retrieving these if you plan on removing a few of them and then storing the list again you should remove them when you get them something like this works:
func loadFiredNotifications() {
let notifications = StudyHelper().getLocalFiredNotifications()
if notifications != nil {
firedNotifications = notifications!
} else {
// throw an error or log it
}
classThatRemoveMethodIsIn().removeFiredLocalNotifications()
}
I hope this helps someone who had the same problems that I did trying to implement this.

How to make phone calls in swift

First of all I know there are some similar topics as this one but because of my reputation I couldn't comment on those for help and stack overflow warned me not to ask for help from the answers section.. none of the similar posts have answered my question so here I go.
As can be understood from the topic, I want make a phone call on click,
I'm making an app for my business and I want to put in a call button so that people can call me over the app.
here are the attempts I've tried as read from the similar topics:
let phoneNumber = "1234567890"
if let phoneCallURL = NSURL(string: "tel:\(phoneNumber)") {
let application = UIApplication.sharedApplication()
if application.canOpenURL(phoneCallURL) {
application.openURL(phoneCallURL)
}
else{
println("failed")
}
}
so when I run the above code with a phone number it prints out the failed message on the console seems like i fails opening the URL
The other code I've tried is a very similar one
var url:NSURL = NSURL(string: "tel://phoneNumber")!
UIApplication.sharedApplication().openURL(url)
one other question is that: What is the correct syntax for the NSURL?
this
NSURL(string: "tel://\(phoneNumber)")
or this
NSURL(string: "tel//:\(phoneNumber)")
My last question is: If the app manages to make a call, does it appear on the simulator like a calling screen? I'm very new to swift programming and I apologise if the questions seem stupid..
The simple format for a tel: URL is tel:######. / is not a number. You probably mean this to be:
NSURL(string: "tel:\(phoneNumber)")
assuming phoneNumber is a string containing the phone number.
The tel: scheme is defined in RFC2806. You can look there for details on all its expected features.
Note that phone calls are not possible in the simulator, so you'll receive an error if you try to open a tel: URL there (unless you handle the URL yourself by registering an NSURLProtocol).
If you want to return to your app after your call has been ended (which you should do) then you need to use telprompt:// instead of tel://. The tel:// will take you to the home screen after the call.
Better use this:
var url:NSURL = NSURL(string: "telprompt://1234567891")!
UIApplication.sharedApplication().openURL(url)
let phoneNumber = "0507712323"
if let callNumber = phoneNumber {
let aURL = NSURL(string: "telprompt://\(callNumber)")
if UIApplication.sharedApplication().canOpenURL(aURL) {
UIApplication.sharedApplication().openURL(aURL)
} else {
print("error")
}
}
else {
print("error")}
I have this issue for different reasons and I would like share with you .
First Dont try in simulator must try on real device
Second make sure the passed number dont contain space
here is example
private func callNumber(phoneNumber:String) {
// I add this line to make sure passed number wihthout space
let CleanphoneNumber = phoneNumber.stringByReplacingOccurrencesOfString(" ", withString: "")
if let phoneCallURL:NSURL = NSURL(string: "tel://\(CleanphoneNumber)") {
let application:UIApplication = UIApplication.sharedApplication()
if (application.canOpenURL(phoneCallURL)) {
application.openURL(phoneCallURL);
}
}
}
Your code looks correct.It seems that it would always fail if you test it in simulator.
Try to use your iPhone to run it,and it would go to dialer interface as you want.
i added some additional validation in the code
func makeCall(constactNumber : NSString)
{
if(constactNumber.length == 0)
{
print("Contact number in not valid")
}
else
{
let CleanconstactNumber = constactNumber.stringByReplacingOccurrencesOfString(" ", withString: "")
if let phoneCallURL:NSURL = NSURL(string: "tel://\(CleanconstactNumber)")
{
if (UIDevice.currentDevice().model.rangeOfString("iPad") != nil)
{
print("Your device doesn't support this feature.")
} else
{
let application:UIApplication = UIApplication.sharedApplication()
if (application.canOpenURL(phoneCallURL))
{
let mobileNetworkCode = CTTelephonyNetworkInfo().subscriberCellularProvider?.mobileNetworkCode
if( mobileNetworkCode == nil)
{
print(" No sim present Or No cellular coverage or phone is on airplane mode.")
}
else
{
application.openURL(phoneCallURL);
}
}
}
}
}
}