检测设备是否支持电话通话?

49

下面的代码可靠吗,用于确定设备是否支持电话呼叫?我的担忧是如果苹果将 iPhone 字符串更改为其他任何内容,比如"iPhone 3G"、"iPhone 4"等。

[[UIDevice currentDevice].model isEqualToString:@"iPhone"]
9个回答

100

iPhone支持tel:// URI方案。因此,您可以使用:

[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]];

canOpenURL: 明确检查是否有能够打开该URL方案的应用程序,而不是URL是否正确。因此,未指定任何电话号码并不重要。该方法返回一个BOOL值,因此请检查是否为YES或NO。

这应该确切地回答了是否存在能够进行电话呼叫的应用程序。因此,它应该对设备分割的任何未来更改都可以使用。


21
我想指出这种方法可能被人们忽视的一些事情。如果设备处于飞行模式,或SIM卡已拆除,或SIM卡的服务已停用,则“技术上”该设备支持电话通话,但这并不意味着它此刻“能够”打电话。虽然听起来很罕见,但有些人会使用没有服务的旧iPhone作为iPod Touch。根据您想要实现的目标,考虑一下这个问题。 - AlBeebe
3
请注意,该功能在IOS8测试版中无效。但这可能是测试版中的一个错误。如果不是这样的话,那么将需要进行CTTelephonyNetworkInfo检查。 - kasuku
3
在iOS8GM上也无法工作,因此看起来不是测试版中的错误。 - Diogo T
5
在我的 iPad2 上运行 iOS 8.1 时,这个返回TRUE。然而,在模拟器 iPad2 8.1 上它会返回 FALSE(正确的行为)。所以很遗憾它在重要的地方无法工作... - FranticRock
1
...但这是可能的,因为自从iOS 8以来,iPad可以打电话了?https://support.apple.com/zh-cn/HT204681 — “通过连续性功能,您可以使用iPad进行蜂窝电话的拨打和接听。” - Tommy
显示剩余7条评论

65

仅仅检查设备是否“支持”电话可能不是最好的方法,这取决于你想要实现的目标。信不信由你,有些人使用没有服务的旧iPhone作为iPod Touch。有时候人们没有在他们的iPhone上安装SIM卡。在我的应用程序中,我希望如果用户的设备能够拨打电话,则拨打电话号码;否则,我希望显示电话号码并提示用户拿起电话拨打它。这是我想出的一个解决方案,迄今为止它已经运行良好。欢迎评论和改进。

// You must add the CoreTelephony.framework
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>

-(bool)canDevicePlaceAPhoneCall {
    /*

     Returns YES if the device can place a phone call

     */

    // Check if the device can place a phone call
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]]) {
        // Device supports phone calls, lets confirm it can place one right now
        CTTelephonyNetworkInfo *netInfo = [[[CTTelephonyNetworkInfo alloc] init] autorelease];
        CTCarrier *carrier = [netInfo subscriberCellularProvider];
        NSString *mnc = [carrier mobileNetworkCode]; 
        if (([mnc length] == 0) || ([mnc isEqualToString:@"65535"])) {
            // Device cannot place a call at this time.  SIM might be removed.
            return NO;
        } else {
            // Device can place a phone call
            return YES;
        }
    } else {
        // Device does not support phone calls
        return  NO;
    }
}

你可能会注意到我检查mobileNetworkCode是否为65535。在我的测试中,当你拿掉了SIM卡后,mobileNetworkCode就会被设置为65535。不确定为什么会这样。


2
这在我的iOS 8.1.3上没有起作用,无论是否处于飞行模式,mnc始终等于“02”。但我找到了一个适用于iOS 7及更高版本的解决方案,请参见下面的答案。 - flo von der uni
我知道这是一个老话题,但我想指出,在南非,65535 MNC 是绝对有效的 MNC。因此,根据您的市场,您需要小心采用这种方法。 - Gabor Furedi
1
我认为你是错误的@GaborFuredi,因为在南非MCC是655(移动国家代码),而35将是MNC(移动网络代码)。两个不同的事情。在我的例子中,我正在检查MNC是否为65536,并且根据http://en.wikipedia.org/wiki/Mobile_country_code,没有大于1,000的MNC。 - AlBeebe
1
这个方法很好,但还不完美。我正在3G iPad上测试一些东西,它将接受tel:url并返回有效的mmc。当您在应用程序中单击相关按钮时,它将处理url,但在选择弹出窗口中的呼叫选项后,什么也不会发生。实际上,我已经将您的检测与设备名称检查相结合,因此所有iPad都被排除在外。它可能不是100%未来的保证,但它已经提供了所需的功能。 - Martin Cassidy
1
注意,MCC是一个10位值[1],显然有人将其塞入了一个16位有符号整数中,因此65535是某人将其设置为FF以指示该值未设置[1]https://standards.ieee.org/content/dam/ieee-standards/standards/web/documents/tutorials/bopid.pdf - alfwatt
显示剩余3条评论

11

我需要确保客户录制时来电不会打断录音。因此,我提示他们进入飞行模式,但仍打开wifi。AlBeebe上述方法在iOS 8.1.3上对我无效,但我发现了这个解决方案,适用于iOS 7及更高版本:

您必须添加并导入CoreTelephony.framework。

#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>

如果您想跟踪更改,请在您的类上定义该属性。

@property (strong, nonatomic) CTTelephonyNetworkInfo* networkInfo;  

初始化CTTelephonyNetworkInfo:

self.networkInfo = [[CTTelephonyNetworkInfo alloc] init];
NSLog(@"Initial cell connection: %@", self.networkInfo.currentRadioAccessTechnology);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(radioAccessChanged) name:CTRadioAccessTechnologyDidChangeNotification object:nil];

然后当它改变时,您将收到回调:

- (void)radioAccessChanged {
    NSLog(@"Now you're connected via %@", self.networkInfo.currentRadioAccessTechnology);
}
currentRadioAccessTechnology 的值在 CTTelephonyNetworkInfo.h 中定义,当没有与蜂塔的连接时,返回 null / nil。
我在这里找到它:http://www.raywenderlich.com/48001/easily-overlooked-new-features-ios-7

10
根据 @the-guardian 的回应,我已经用 Swift 编写了以下代码:
import CoreTelephony

/**
 Indicates if the device can make a phone call.

 - seealso: [Source](https://dev59.com/LW445IYBdhLWcg3wAFcC#11595365)

 - returns: `true` if the device can make a phone call. `false` if not.
 */
final class func canMakePhoneCall() -> Bool {
    guard let url = URL(string: "tel://") else {
        return false
    }

    let mobileNetworkCode = CTTelephonyNetworkInfo().subscriberCellularProvider?.mobileNetworkCode

    let isInvalidNetworkCode = mobileNetworkCode == nil
        || mobileNetworkCode?.count == 0
        || mobileNetworkCode == "65535"

    return UIApplication.shared.canOpenURL(url)
        && !isInvalidNetworkCode
}

这段代码已经在iPad Air 2 Wifi、iPad Air 2模拟器和iPhone 6S Plus上进行过测试,看起来运行正常。很快将在具有移动数据的iPad上进行测试。


这个已经过时了。 - Jacob

2
我认为您的方法不可靠,因为设备名称可能会在未来更改。如果您的关注点是防止应用在非 iPhone 设备上运行,您可以将“telephony”添加到 Info.plist 中的 UIRequiredDeviceCapabilities 字典中。这将禁止除 iPhone 之外的设备从 App Store 下载您的应用程序。
或者,如果您需要检查特定时刻的 3G 连接状态,您可以使用苹果的 Reachability 实用程序类来查询当前的 3G/WIFI 连接状态。

2

我认为通常是这样的。我会选择更通用的字符串比较(以防未来更新时更安全)。我已经使用它并且没有遇到任何问题(至少目前还没有...)。

如果你想更确定设备是否能够实际拨打电话,你还应该利用核心电信API。 CTCarrier类可以告诉您在任何特定时刻是否可以实际拨打电话。


2

这个UIApplication.shared.openURL((URL(string: "tel://\(phoneNumber)")!))只会判断设备是否有拨打电话的选项,而不是判断设备是否有SIM卡。例如:无论iPhone是否插入SIM卡,它都会返回true,但iPod Touch则始终返回false,同样地,如果iPad没有SIM卡选项,它也会返回false。

以下是检查所有内容的代码!(使用Swift 3.0)

if UIApplication.shared.canOpenURL(URL(string: "tel://\(phoneNumber)")!) {
            var networkInfo = CTTelephonyNetworkInfo()
            var carrier: CTCarrier? = networkInfo.subscriberCellularProvider
            var code: String? = carrier?.mobileNetworkCode
            if (code != nil) {
                UIApplication.shared.openURL((URL(string: "tel://\(phoneNumber)")!))
            }
            else {
                var alert = UIAlertView(title: "Alert", message: "No SIM Inserted", delegate: nil, cancelButtonTitle: "ok", otherButtonTitles: "")
                alert.show()
            }
        }
        else {
            var alert = UIAlertView(title: "Alert", message: "Device does not support phone calls.", delegate: nil, cancelButtonTitle: "ok", otherButtonTitles: "")
            alert.show()
        }

通过这种方式,我们可以确保设备是否支持呼叫功能。


0
如果您是因为想要拨打电话号码并在没有电话功能的设备上显示错误而询问:
void openURL(NSURL *url, void (^ __nullable completionHandler). (BOOL success))
{
    if (@available(iOS 10.0, *)) {
        [application openURL:url options:@{} completionHandler:^(BOOL success) {
            completionHandler(success);
        }];
    } else
    {
        if([application openURL:url]) {
            completionHandler(YES);
        } else {
            completionHandler(NO);
        }
    }
}

用法

        p97openURL(phoneURL, ^(BOOL success) {
            if(!success) {
                  show message saying there is no telephony on device

            }
        }

-1

根据@TheGuardian的回答,我认为这可能是一个更简单的方法:

   private final func canMakePhoneCall() -> Bool {
       guard UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Phone else {
        return false
       }

       let mobileNetworkCode = CTTelephonyNetworkInfo().subscriberCellularProvider?.mobileNetworkCode
       let isInvalidNetworkCode = mobileNetworkCode == nil || mobileNetworkCode?.characters.count <= 0
                                                        || mobileNetworkCode == "65535"
                                                        //When sim card is removed, the Code is 65535

       return !isInvalidNetworkCode
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接