How can I make a phone call programmatically on iPhone? I tried the following code but nothing happened:
NSString *phoneNumber = mymobileNO.titleLabel.text;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber]];
To go back to original app you can use telprompt:// instead of tel:// - The tell prompt will prompt the user first, but when the call is finished it will go back to your app:
NSString *phoneNumber = [#"telprompt://" stringByAppendingString:mymobileNO.titleLabel.text];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber]];
Probably the mymobileNO.titleLabel.text value doesn't include the scheme //
Your code should look like this:
ObjectiveC
NSString *phoneNumber = [#"tel://" stringByAppendingString:mymobileNO.titleLabel.text];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber]];
Swift
if let url = URL(string: "tel://\(mymobileNO.titleLabel.text))") {
UIApplication.shared.open(url)
}
Merging the answers of #Cristian Radu and #Craig Mellon, and the comment from #joel.d, you should do:
NSURL *urlOption1 = [NSURL URLWithString:[#"telprompt://" stringByAppendingString:phone]];
NSURL *urlOption2 = [NSURL URLWithString:[#"tel://" stringByAppendingString:phone]];
NSURL *targetURL = nil;
if ([UIApplication.sharedApplication canOpenURL:urlOption1]) {
targetURL = urlOption1;
} else if ([UIApplication.sharedApplication canOpenURL:urlOption2]) {
targetURL = urlOption2;
}
if (targetURL) {
if (#available(iOS 10.0, *)) {
[UIApplication.sharedApplication openURL:targetURL options:#{} completionHandler:nil];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[UIApplication.sharedApplication openURL:targetURL];
#pragma clang diagnostic pop
}
}
This will first try to use the "telprompt://" URL, and if that fails, it will use the "tel://" URL. If both fails, you're trying to place a phone call on an iPad or iPod Touch.
Swift Version :
let phone = mymobileNO.titleLabel.text
let phoneUrl = URL(string: "telprompt://\(phone)"
let phoneFallbackUrl = URL(string: "tel://\(phone)"
if(phoneUrl != nil && UIApplication.shared.canOpenUrl(phoneUrl!)) {
UIApplication.shared.open(phoneUrl!, options:[String:Any]()) { (success) in
if(!success) {
// Show an error message: Failed opening the url
}
}
} else if(phoneFallbackUrl != nil && UIApplication.shared.canOpenUrl(phoneFallbackUrl!)) {
UIApplication.shared.open(phoneFallbackUrl!, options:[String:Any]()) { (success) in
if(!success) {
// Show an error message: Failed opening the url
}
}
} else {
// Show an error message: Your device can not do phone calls.
}
The answers here are perfectly working. I am just converting Craig Mellon answer to Swift. If someone comes looking for swift answer, this will help them.
var phoneNumber: String = "telprompt://".stringByAppendingString(titleLabel.text!) // titleLabel.text has the phone number.
UIApplication.sharedApplication().openURL(NSURL(string:phoneNumber)!)
If you are using Xamarin to develop an iOS application, here is the C# equivalent to make a phone call within your application:
string phoneNumber = "1231231234";
NSUrl url = new NSUrl(string.Format(#"telprompt://{0}", phoneNumber));
UIApplication.SharedApplication.OpenUrl(url);
Swift 3
let phoneNumber: String = "tel://3124235234"
UIApplication.shared.openURL(URL(string: phoneNumber)!)
In Swift 3.0,
static func callToNumber(number:String) {
let phoneFallback = "telprompt://\(number)"
let fallbackURl = URL(string:phoneFallback)!
let phone = "tel://\(number)"
let url = URL(string:phone)!
let shared = UIApplication.shared
if(shared.canOpenURL(fallbackURl)){
shared.openURL(fallbackURl)
}else if (shared.canOpenURL(url)){
shared.openURL(url)
}else{
print("unable to open url for call")
}
}
The Java RoboVM equivalent:
public void dial(String number)
{
NSURL url = new NSURL("tel://" + number);
UIApplication.getSharedApplication().openURL(url);
}
Tried the Swift 3 option above, but it didnt work. I think you need the following if you are to run against iOS 10+ on Swift 3:
Swift 3 (iOS 10+):
let phoneNumber = mymobileNO.titleLabel.text
UIApplication.shared.open(URL(string: phoneNumber)!, options: [:], completionHandler: nil)
let phone = "tel://\("1234567890")";
let url:NSURL = NSURL(string:phone)!;
UIApplication.sharedApplication().openURL(url);
'openURL:' is deprecated: first deprecated in iOS 10.0 - Please use openURL:options:completionHandler: instead
in Objective-c iOS 10+ use :
NSString *phoneNumber = [#"tel://" stringByAppendingString:mymobileNO.titleLabel.text];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber] options:#{} completionHandler:nil];
Swift
if let url = NSURL(string: "tel://\(number)"),
UIApplication.sharedApplication().canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
Use openurl.
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.
Related
Is it possible to make same action like on Android, when we press Phone icon in application?
On Android we will see Phone Call screen with entered phone number.
On iOS it will call to a phone number immediately.
Example:
Android: Some app call button click -> Native Android Phone Call screen with pre-entered number -> Manual click call button
iOS: Same app call button click -> Immediately call action to phone number
I need to know, is it possible to add 2nd step into iOS like on Android.
yes it is possible by using openUrl as follows, i wrote in swift u can try same in objc
// swift
func makePhoneCall(_ phoneNumber: String?) {
if phoneNumber != nil && phoneNumber?.length ?? 0 > 0 {
let charSet: CharacterSet = CharacterSet(charactersIn: "0123456789-+()").inverted
let cleanedString: String? = (phoneNumber!.components(separatedBy: charSet) as NSArray).componentsJoined(by:"")
let escapedPhoneNumber: NSString? = cleanedString?.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) as NSString?
let phoneURLString: String = String(format:"telprompt:%#", escapedPhoneNumber!)
let phoneURL: URL? = URL(string: phoneURLString)
if phoneURL != nil {
if UIApplication.shared.canOpenURL(phoneURL!) == true {
UIApplication.shared.openURL(phoneURL!)
} else {
// show alert or something
}
}
}
}
// objc
-(void) makePhoneCall:(NSString *)phoneNumber {
if(phoneNumber.length > 0) {
NSCharacterSet *charSet = [[NSCharacterSet characterSetWithCharactersInString:#"0123456789-+()"] invertedSet];
NSString *cleanedString = [[phoneNumber componentsSeparatedByCharactersInSet:charSet] componentsJoinedByString:#""];
NSString *escapedPhoneNumber = [cleanedString stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLQueryAllowedCharacterSet]];
NSString *phoneURLString = [NSString stringWithFormat:#"telprompt:%#", escapedPhoneNumber];
NSURL *url = [NSURL URLWithString:phoneURLString];
if([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
} else {
// show alert // or something
}
}
}
I've been trying to get Tweetbot to open a user account when a table row is tapped by the user. However, although Tweetbot opens, it doesn't show the user account. I've been using the Tweetbot URL Scheme page as a reference.
Below is my code:
if (indexPath.row == 1) {
// Removed the actual username
self.destViewURL = #"http://twitter.com/dummyusername";
self.destViewTitle = #"Twitter";
// URLs to try
NSURL *twitterURL = [NSURL URLWithString:#"twitter://user?screen_name= dummyusername"];
NSURL *tweetbotURL = [NSURL URLWithString:#"tweetbot://dummyusername/timeline"];
// Check if Tweetbot is available to open it
if ([[UIApplication sharedApplication] canOpenURL:tweetbotURL]) {
[[UIApplication sharedApplication] openURL:tweetbotURL];
}
else {
// Check if Twitter is available to open it
if ([[UIApplication sharedApplication] canOpenURL:twitterURL]) {
[[UIApplication sharedApplication] openURL:twitterURL];
}
// Otherwise open it in the web view
else {
[self performSegueWithIdentifier:#"showWebView" sender:nil];
}
The URL schemes page for Tweetbot 3 is here
All of the supported URLs begin with tweetbot://<screenname>, which suggests that you need to know the user's existing twitter screen name to link to a profile.
However, my testing has shown that you could link directly to a profile by using the same value for tweetbot://<screenname>/user_profile/<profile_screenname>
Swift e.g.
/* Tweetbot app precedence */
if let tweetbotURL = NSURL(string: "tweetbot://dummyusername/user_profile/dummyusername") {
if UIApplication.sharedApplication().canOpenURL(tweetbotURL) {
UIApplication.sharedApplication().openURL(tweetbotURL)
return
}
}
/* Twitter app fallback */
if let twitterURL = NSURL(string: "twitter:///user?screen_name= dummyusername") {
if UIApplication.sharedApplication().canOpenURL(twitterURL) {
UIApplication.sharedApplication().openURL(twitterURL)
return
}
}
/* Safari fallback */
if let webURL = NSURL(string: "http://www.twitter.com/dummyusername") {
if UIApplication.sharedApplication().canOpenURL(webURL) {
UIApplication.sharedApplication().openURL(webURL)
}
}
Is there a way to start navigation automatically when launching Google Maps or Apple Maps with a URL Scheme on iOS?
I see several optional parameters for both but none to start navigation without user input.
Here's how I did it for your reference, but for apple, I haven't found a way to start the navigation through url scheme.
+ (void)navigateToLocation:(CLLocation*)_navLocation {
if ([[UIApplication sharedApplication] canOpenURL:
[NSURL URLWithString:#"comgooglemaps://"]]) {
NSString *string = [NSString stringWithFormat:#"comgooglemaps://?daddr=%f,%f&directionsmode=driving",_navLocation.coordinate.latitude,_navLocation.coordinate.longitude];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:string]];
} else {
NSString *string = [NSString stringWithFormat:#"http://maps.apple.com/?ll=%f,%f",_navLocation.coordinate.latitude,_navLocation.coordinate.longitude];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:string]];
}
}
You are correct, Google and Apple both require the user input - but only to hit the go button.
If you want to specify both the start and end location, use the following format:
Apple Maps:
http://maps.apple.com/maps?saddr=Current%20Location&daddr=<Your Location>
Google Maps:
comgooglemaps-x-callback://?saddr=&daddr=<Your Location>
Swift 3 helper class for starting Apple Maps or Google Maps navigation
struct LinksHelper {
static func startNavigation(coordinate: CLLocationCoordinate2D) {
struct Links {
static let kGoogleMapsSchema = "comgooglemaps://"
static let kGoogleMaps = "\(kGoogleMapsSchema)?daddr=%f,%f&directionsmode=driving"
static let kAppleMaps = "https://maps.apple.com/?saddr=Current Location&daddr=%f,%f&z=10&t=s"
}
var path: String!
if let googleMapsSchemaUrl = URL(string:Links.kGoogleMapsSchema), UIApplication.shared.canOpenURL(googleMapsSchemaUrl) {
path = Links.kGoogleMaps
} else {
path = Links.kAppleMaps
}
guard let str = String(format: path, coordinate.latitude, coordinate.longitude).addingPercentEncoding(
withAllowedCharacters: .urlQueryAllowed) else {
return
}
guard let url = URL(string: str) else {
return
}
UIApplication.shared.openURL(url)
}
}
How can I determine if the user of an iOS device has a specific application installed? If I know the name of the application can I use canOpenURL somehow?
If the application supports a custom url scheme you can check UIApplication -canOpenURL:. That will tell you only that an application able to open that url scheme is available, not necessarily which application that is. There's no publicly available mechanism to inspect what other apps a user has installed on their device.
If you control both apps you might also use a shared keychain or pasteboard to communicate between them in more detail.
You can check in this way as well:
BOOL temp = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"yourAppURL://"]];
if(!temp)
{
NSLog(#"INVALID URL"); //Or alert or anything you want to do here
}
for swift users
let urlPath: String = "fb://www.facebook.com"
let url: NSURL = NSURL(string: urlPath)!
let isInstalled = UIApplication.sharedApplication().canOpenURL(url)
if isInstalled {
print("Installed")
}else{
print("Not installed")
}
Facebook uses this https://github.com/facebook/facebook-ios-sdk/blob/master/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.m internally, you can do the same
#define FBSDK_CANOPENURL_FACEBOOK #"fbauth2"
+ (BOOL)isFacebookAppInstalled
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[FBSDKInternalUtility checkRegisteredCanOpenURLScheme:FBSDK_CANOPENURL_FACEBOOK];
});
NSURLComponents *components = [[NSURLComponents alloc] init];
components.scheme = FBSDK_CANOPENURL_FACEBOOK;
components.path = #"/";
return [[UIApplication sharedApplication]
canOpenURL:components.URL];
}
Code in Swift 3
static func isFacebookAppInstalled() -> Bool {
let schemes = ["fbauth2", "fbapi", "fb"]
let schemeUrls = schemes.flatMap({ URL(string: "\($0)://") })
return !schemeUrls.filter({ UIApplication.shared.canOpenURL($0) }).isEmpty
}
I have a commercial app that has a completely legitimate reason to see the SSID of the network it is connected to: If it is connected to a Adhoc network for a 3rd party hardware device it needs to be functioning in a different manner than if it is connected to the internet.
Everything I've seen about getting the SSID tells me I have to use Apple80211, which I understand is a private library. I also read that if I use a private library Apple will not approve the app.
Am I stuck between an Apple and a hard place, or is there something I'm missing here?
As of iOS 7 or 8, you can do this (need Entitlement for iOS 12+ as shown below):
#import SystemConfiguration.CaptiveNetwork;
/** Returns first non-empty SSID network info dictionary.
* #see CNCopyCurrentNetworkInfo */
- (NSDictionary *)fetchSSIDInfo {
NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
NSLog(#"%s: Supported interfaces: %#", __func__, interfaceNames);
NSDictionary *SSIDInfo;
for (NSString *interfaceName in interfaceNames) {
SSIDInfo = CFBridgingRelease(
CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
NSLog(#"%s: %# => %#", __func__, interfaceName, SSIDInfo);
BOOL isNotEmpty = (SSIDInfo.count > 0);
if (isNotEmpty) {
break;
}
}
return SSIDInfo;
}
Example output:
2011-03-04 15:32:00.669 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: Supported interfaces: (
en0
)
2011-03-04 15:32:00.693 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: en0 => {
BSSID = "ca:fe:ca:fe:ca:fe";
SSID = XXXX;
SSIDDATA = <01234567 01234567 01234567>;
}
Note that no ifs are supported on the simulator. Test on your device.
iOS 12
You must enable access wifi info from capabilities.
Important
To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID. Documentation link
Swift 4.2
func getConnectedWifiInfo() -> [AnyHashable: Any]? {
if let ifs = CFBridgingRetain( CNCopySupportedInterfaces()) as? [String],
let ifName = ifs.first as CFString?,
let info = CFBridgingRetain( CNCopyCurrentNetworkInfo((ifName))) as? [AnyHashable: Any] {
return info
}
return nil
}
UPDATE FOR iOS 10 and up
CNCopySupportedInterfaces is no longer deprecated in iOS 10. (API Reference)
You need to import SystemConfiguration/CaptiveNetwork.h and add SystemConfiguration.framework to your target's Linked Libraries (under build phases).
Here is a code snippet in swift (RikiRiocma's Answer):
import Foundation
import SystemConfiguration.CaptiveNetwork
public class SSID {
class func fetchSSIDInfo() -> String {
var currentSSID = ""
if let interfaces = CNCopySupportedInterfaces() {
for i in 0..<CFArrayGetCount(interfaces) {
let interfaceName: UnsafePointer<Void> = CFArrayGetValueAtIndex(interfaces, i)
let rec = unsafeBitCast(interfaceName, AnyObject.self)
let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)")
if unsafeInterfaceData != nil {
let interfaceData = unsafeInterfaceData! as Dictionary!
currentSSID = interfaceData["SSID"] as! String
}
}
}
return currentSSID
}
}
(Important: CNCopySupportedInterfaces returns nil on simulator.)
For Objective-c, see Esad's answer here and below
+ (NSString *)GetCurrentWifiHotSpotName {
NSString *wifiName = nil;
NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
for (NSString *ifnam in ifs) {
NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
if (info[#"SSID"]) {
wifiName = info[#"SSID"];
}
}
return wifiName;
}
UPDATE FOR iOS 9
As of iOS 9 Captive Network is deprecated*. (source)
*No longer deprecated in iOS 10, see above.
It's recommended you use NEHotspotHelper (source)
You will need to email apple at networkextension#apple.com and request entitlements. (source)
Sample Code (Not my code. See Pablo A's answer):
for(NEHotspotNetwork *hotspotNetwork in [NEHotspotHelper supportedNetworkInterfaces]) {
NSString *ssid = hotspotNetwork.SSID;
NSString *bssid = hotspotNetwork.BSSID;
BOOL secure = hotspotNetwork.secure;
BOOL autoJoined = hotspotNetwork.autoJoined;
double signalStrength = hotspotNetwork.signalStrength;
}
Side note: Yup, they deprecated CNCopySupportedInterfaces in iOS 9 and reversed their position in iOS 10. I spoke with an Apple networking engineer and the reversal came after so many people filed Radars and spoke out about the issue on the Apple Developer forums.
Here's the cleaned up ARC version, based on #elsurudo's code:
- (id)fetchSSIDInfo {
NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
NSLog(#"Supported interfaces: %#", ifs);
NSDictionary *info;
for (NSString *ifnam in ifs) {
info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
NSLog(#"%# => %#", ifnam, info);
if (info && [info count]) { break; }
}
return info;
}
This works for me on the device (not simulator). Make sure you add the systemconfiguration framework.
#import <SystemConfiguration/CaptiveNetwork.h>
+ (NSString *)currentWifiSSID {
// Does not work on the simulator.
NSString *ssid = nil;
NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
for (NSString *ifnam in ifs) {
NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
if (info[#"SSID"]) {
ssid = info[#"SSID"];
}
}
return ssid;
}
This code work well in order to get SSID.
#import <SystemConfiguration/CaptiveNetwork.h>
#implementation IODAppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(#"Connected at:%#",myDict);
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
NSString * BSSID = [myDictionary objectForKey:#"BSSID"];
NSLog(#"bssid is %#",BSSID);
// Override point for customization after application launch.
return YES;
}
And this is the results :
Connected at:{
BSSID = 0;
SSID = "Eqra'aOrange";
SSIDDATA = <45717261 27614f72 616e6765>;
}
If you are running iOS 12 you will need to do an extra step.
I've been struggling to make this code work and finally found this on Apple's site:
"Important
To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID."
https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo
See CNCopyCurrentNetworkInfo in CaptiveNetwork: http://developer.apple.com/library/ios/#documentation/SystemConfiguration/Reference/CaptiveNetworkRef/Reference/reference.html.
Here's the short & sweet Swift version.
Remember to link and import the Framework:
import UIKit
import SystemConfiguration.CaptiveNetwork
Define the method:
func fetchSSIDInfo() -> CFDictionary? {
if let
ifs = CNCopySupportedInterfaces().takeUnretainedValue() as? [String],
ifName = ifs.first,
info = CNCopyCurrentNetworkInfo((ifName as CFStringRef))
{
return info.takeUnretainedValue()
}
return nil
}
Call the method when you need it:
if let
ssidInfo = fetchSSIDInfo() as? [String:AnyObject],
ssID = ssidInfo["SSID"] as? String
{
println("SSID: \(ssID)")
} else {
println("SSID not found")
}
As mentioned elsewhere, this only works on your iDevice. When not on WiFi, the method will return nil – hence the optional.
For iOS 13
As from iOS 13 your app also needs Core Location access in order to use the CNCopyCurrentNetworkInfo function unless it configured the current network or has VPN configurations:
So this is what you need (see apple documentation):
- Link the CoreLocation.framework library
- Add location-services as a UIRequiredDeviceCapabilities Key/Value in Info.plist
- Add a NSLocationWhenInUseUsageDescription Key/Value in Info.plist describing why your app requires Core Location
- Add the "Access WiFi Information" entitlement for your app
Now as an Objective-C example, first check if location access has been accepted before reading the network info using CNCopyCurrentNetworkInfo:
- (void)fetchSSIDInfo {
NSString *ssid = NSLocalizedString(#"not_found", nil);
if (#available(iOS 13.0, *)) {
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
NSLog(#"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
} else {
CLLocationManager* cllocation = [[CLLocationManager alloc] init];
if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
[cllocation requestWhenInUseAuthorization];
usleep(500);
return [self fetchSSIDInfo];
}
}
}
NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
id info = nil;
for (NSString *ifnam in ifs) {
info = (__bridge_transfer id)CNCopyCurrentNetworkInfo(
(__bridge CFStringRef)ifnam);
NSDictionary *infoDict = (NSDictionary *)info;
for (NSString *key in infoDict.allKeys) {
if ([key isEqualToString:#"SSID"]) {
ssid = [infoDict objectForKey:key];
}
}
}
...
...
}