Обнаружение того, поддерживают ли телефонные звонки?

Является ли приведенный ниже код надежным для определения того, может ли устройство поддерживать телефонные звонки или нет? Меня беспокоит, что яблоко меняет строку iphone на что-то еще, пусть говорят, что они решили иметь "iphone 3g", "iphone 4" и т.д.

[[UIDevice currentDevice].model isEqualToString:@"iPhone"]

Ответ 1

iPhone поддерживает схему tel://URI. Таким образом, вы можете использовать:

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

canOpenURL: явно проверяет, может ли приложение открывать эту схему URL, а не то, что URL-адрес правильный. Поэтому не имеет значения, что номер телефона не указан. Метод возвращает BOOL, поэтому проверьте, что для YES или NO.

Это должно буквально ответить, есть ли какое-либо приложение, способное совершить телефонный звонок. Поэтому все должно быть в порядке с любыми будущими изменениями сегментации устройства.

Ответ 2

Просто проверка того, что устройство "поддерживает" телефонные звонки, может быть не лучшим способом обойти вещи в зависимости от того, что вы пытаетесь выполнить. Верьте или нет, некоторые люди используют старые iPhone без обслуживания, как если бы они были iPod Touch. Иногда у людей нет SIM-карт, установленных в их iPhone. В моем приложении я хотел набрать номер телефона, если пользовательское устройство было в состоянии, в противном случае я хотел отобразить номер телефона и попросить пользователя взять телефон и набрать его. Вот решение, с которым я столкнулся, до сих пор работал. Не стесняйтесь комментировать и улучшать его.

// 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. Не 100% уверены, почему это так.

Ответ 3

Мне нужно убедиться, что входящие телефонные вызовы не могут прервать записи, которые делают мои клиенты, поэтому я предлагаю им перейти в режим самолета, но все же включить Wi-Fi. Метод выше от 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, и вы вернете нуль/ноль, если соединение с ячейкой отсутствует.

Вот где я нашел его: http://www.raywenderlich.com/48001/easily-overlooked-new-features-ios-7

Ответ 4

Основываясь на ответе @the-guardian, я придумал следующее (быстро):

import CoreTelephony

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

 - seealso: [Source](http://stackoverflow.com/a/11595365/3643020)

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

    let mobileNetworkCode = CTTelephonyNetworkInfo().subscriberCellularProvider?.mobileNetworkCode

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

    return UIApplication.sharedApplication().canOpenURL(URL)
        && !isInvalidNetworkCode
}

Этот код был протестирован на iPad Air 2 Wifi, iPad Air Simulator, iPhone 6S Plus и, похоже, работает соответствующим образом. Определит на iPad с мобильными данными в ближайшее время.

Ответ 5

Этот 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()
        }

Таким образом, мы можем убедиться, что устройство поддерживает вызов или нет.

Ответ 6

Я не думаю, что ваш метод надежный, поскольку имена устройств могут измениться в будущем. Если ваша проблема заключается в том, чтобы запретить запуск приложения на устройствах, отличных от iPhone, вы можете добавить "телефонию" в словарь UIRequiredDeviceCapabilities в своей Info. PLIST. Это отключит устройства, отличные от iPhone, для загрузки вашего приложения из App Store.

В качестве альтернативы, если вам нужно проверить подключение 3G в определенный момент, вы можете использовать утилиту Apple Reachability, чтобы узнать о текущий статус соединения 3G/WIFI.

Ответ 7

Я думаю, что вообще это так. Я бы пошел на более общее сравнение строк (просто чтобы быть более безопасным в случае будущего обновления). Я использовал его без проблем (до сих пор...).

Если вы хотите быть более уверенным в том, действительно ли устройство может совершать звонки, вы также должны воспользоваться преимуществами Core Telephony API. Класс CTCarrier может рассказать вам, действительно ли вы можете позвонить в любой момент.

Ответ 8

Если вы запрашиваете номер телефона и показываете ошибка на устройствах, которые не имеют телефонии:

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

            }
        }

Ответ 9

Основываясь на ответе @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
    }