Статьи

Swift 2: проверка доступности

В этом коротком руководстве я бы хотел остановиться на новом синтаксисе Swift для проверки доступности. Если вы уже занимались разработкой для iOS или OS X, то я уверен, что вы знаете, насколько утомительно проверять, доступен ли определенный API на устройстве, на котором работает ваше приложение. В Swift 2 это стало намного меньше боли для разработчиков.

Представьте себе следующий сценарий. Вы разрабатываете приложение для iOS, предназначенное для iOS 7 и выше. Во время прошлогодней WWDC Apple представила новый API для регистрации уведомлений.

1
registerUserNotificationSettings(_:)

Означает ли это, что вам нужно поднять цель развертывания приложения с iOS 7 на iOS 8? Вы могли бы сделать это, но это оставило бы значительную часть пользовательской базы вашего приложения в холодном состоянии, только чтобы соответствовать новой политике Apple для локальных и удаленных уведомлений. Ваши пользователи не будут вам благодарны за это.

Альтернативой является использование нового API только на устройствах с iOS 8 и выше. Это имеет больше смысла. Правильно? Реализация будет выглядеть примерно так.

1
2
3
4
5
6
7
8
9
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    if UIApplication.instancesRespondToSelector(«registerUserNotificationSettings:») {
        let types = UIUserNotificationType.Alert |
        let settings = UIUserNotificationSettings(forTypes: types, categories: nil)
        application.registerUserNotificationSettings(settings)
    }
     
    return true
}

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

1
2
3
if ([UIUserNotificationSettings class]) {
    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge) categories:nil]];
}

Хотя оба подхода будут работать в большинстве ситуаций, есть ситуации, в которых вы столкнетесь с проблемами. Например, некоторые API начинают свою жизнь как частные API и становятся общедоступными на более позднем этапе. В этом случае вы можете столкнуться с частными API на устройствах с операционной системой, в которой эти API еще не опубликованы. И я уверен, что вы знаете, что это значит.

Благодаря работе команды Swift решение нашей проблемы в Swift 2 простое и понятное. Взгляните на следующий пример. Обратите внимание, что цель развертывания проекта установлена ​​на iOS 7 с использованием Swift 2 и Xcode 7 .

Компилятор выдает ошибку.

В этом примере мы используем API, которые были представлены в iOS 8. Поскольку компилятор знает, что цель развертывания проекта установлена ​​на iOS 7, он выдает ошибку, сообщая нам, что API, которые мы хотим использовать, доступны только в iOS 8 и выше. Он знает это, проверяя SDK на наличие информации. Если вы нажмете Command и выберите метод registerUserNotificationSettings(_:) , вы должны увидеть что-то вроде этого.

1
2
@available(iOS 8.0, *)
func registerUserNotificationSettings(notificationSettings: UIUserNotificationSettings)

К счастью, Xcode дает нам решение для решения этой проблемы. Он предлагает использовать проверку версии, чтобы избежать вызова API исключительно для iOS 8 и выше, если наши пользователи запускают приложение на более старой версии iOS.

Обратите внимание, что эта функция была введена в Swift 2. Компилятор не выдаст ошибку, если вы используете Swift 1.2. Добавление проверки версии также облегчает понимание примера. Взгляните на обновленный пример ниже, в котором мы следуем совету, который дал нам XCode.

1
2
3
4
5
6
7
8
9
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    if #available(iOS 8.0, *) {
        let types = UIUserNotificationType([UIUserNotificationType.Alert, UIUserNotificationType.Sound, UIUserNotificationType.Badge])
        let settings = UIUserNotificationSettings(forTypes: types, categories: nil)
        application.registerUserNotificationSettings(settings)
    }
     
    return true
}

Синтаксис ясен и понятен. Используя синтаксис доступности, мы проверяем, запущено ли приложение на устройстве с iOS 8 и выше. Если это не так, предложение if пропускается, в противном случае приложение вызывает новый API для регистрации уведомлений.

Синтаксис прост. Мы запускаем условие доступности с #available и #available условие в скобки. Мы можем добавить столько платформ, сколько необходимо, разделяя список платформ запятыми.

1
2
3
if #available(iOS 8.0, OSX 10.10, watchOS 2, *) {
    …
}

Обратите внимание, что мы заканчиваем список платформ звездочкой. Эта звездочка обязательна и указывает на то, что условие if выполняется для цели минимального развертывания для любой платформы, не включенной в список платформ.

Как мы видели ранее, мы можем использовать атрибут @available для добавления информации о доступности в функции, методы и классы. В следующем примере мы сообщаем компилятору, что useFancyNewAPI должен useFancyNewAPI только на устройствах с iOS 9 и выше.

1
2
3
4
@available(iOS 9.0, *)
func useFancyNewAPI() {
    …
}

Имейте в виду, что синтаксис доступности не является альтернативой для двух примеров, которые я показал вам в начале этого урока. Эти примеры ошибочны и должны использоваться, только если вы используете Objective-C или более раннюю версию Swift.

Доступный синтаксис — еще одна причина для миграции ваших проектов Swift на Swift 2. Он избавляется от подверженных ошибкам решений для проверки доступности API. С Swift 2 мир выглядит немного дружелюбнее.