Определяет, поддерживает ли устройство телефонные звонки?

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

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

9 ответов

Решение

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

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

canOpenURL: явно проверяет, есть ли приложение, способное открыть эту схему URL, а не то, что URL правильный. Поэтому не имеет значения, что номер телефона не указан. Метод возвращает BOOL, поэтому проверьте это для ДА или НЕТ.

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

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

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

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

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

import CoreTelephony

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

 - seealso: [Source](http://stackru.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 = 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 Simulator, iPhone 6S Plus и, похоже, работает надлежащим образом. Скоро определится на iPad с мобильными данными.

Это UIApplication.shared.openURL((URL(string: "tel://\(phoneNumber)")!)) не скажет, есть ли у него SIM-карта или нет, просто скажет, есть ли у устройства опции для совершения звонка. Например: iPhone с SIM-картой или без нее будет возвращать значение "истина", но в iPod Touch он всегда будет возвращать значение "ложь", как и в случае, если на ipad отсутствует опция sim, возвращается значение "ложь".

Вот код, который все проверяет всесторонне! (Использование 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()
        }

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

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

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

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

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

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

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

            }
        }

Основываясь на ответе @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
    }
Другие вопросы по тегам