С тех пор, как для прохождения модерации на Mac Store стала требоваться поддержка Sandbox, прошло уже 5 лет. Хотя возможности MacOS и Sandbox постепенно расширяются, разработчики, желающие публиковаться в официальном магазине Apple, по-прежнему ограничены в возможностях работы c этой ОС. Особенно остро эта проблема стоит для утилит и системных приложений.
Sandbox призван сделать macOS безопасней и уберечь пользователя от вредоносных и потенциально опасных приложений — все действия приложений, которые могут гипотетически принести вред, должны сопровождаться запросом к пользователю на предоставление доступа. В теории это разумная мера предосторожности, однако в действительности под санкции попадают многие приложения, которым пользовательские данные необходимы для осуществления базовых функций. В данной статье мы расскажем, как внедряли подобные функции в приложение, поддерживая совместимость с Sandbox, — возможно, этот опыт будет полезен для других разработчиков, работающих с официальным маркетом.
Мы столкнулись с подобной необходимостью при работе с утилитой для мониторинга системы MaCleaner X. Вся линейка продуктов MaCleaner распространяется исключительно через Mac App Store.
Отличительными особенностями MaCleaner X являются поддержка тачбара и кастомизированный дизайн: приложение автоматически определяет модель Mac и предлагает особый вариант интерфейса для каждого девайса. Однако в дополнение к этому мы в данный момент работаем над тем, чтобы реализовать в расширенной версии некоторые функции, которые пользователи хотели бы видеть в программе и которые для утилит, распространяемых вне Mac Store, считаются привычными.
В первую очередь это:
Способ решения
Для начала в общем виде наметим, по какой схеме будет происходить весь процесс:
Для выполнения этой операции вне песочницы воспользуемся следующей возможностью macOS: приложения, которые поддерживают расширение своего функционала при помощи Apple Script, могут запускать эти скрипты вне песочницы, если они находятся в папке ~/Library/Application Scripts. Приложение, поддерживающее Sandbox, не может самостоятельно копировать в эту папку скрипты — это пресекается модераторами Apple.
Таким образом, последовательность действий у нас вырисовывается следующая:
Пример
В качестве примера рассмотрим возможность получения информации о текущих носителях при помощи bash-команды
Подготовка Extension-приложения
Все, что требуется от этой компоненты — это копирование скрипта в определенную папку. Поэтому в его интерфейсе мы отобразим единственную возможность: включение или отключение отображения информации о носителях (чтобы не грузить пользователя подробностями, это может быть представлено как включение/отключение плагинов в master-приложении).
Apple Sript — bash
Содержимое файла extract_storage_info.scpt, который копируется ectention'ом:
Подготовка Master-приложения
Для корректной работы приложения с точки зрения пользователя необходимо осуществлять проверку доступности дополнительного функционала и скрывать соответствующие части UI:
Если скрипт доступен, его вызов осуществляется следующим образом:
В нашем упрощенном примере, мы выводим полученную от выполненного скрипта строку как есть. Для стилизации под интерфейс приложения необходимо будет парсить результат и отображать только необходимую информацию.
Совместная работа приложений
В результате мы получаем следующую схему работы.
Master-приложение
Extension-приложение
Выводы
Плюсы подхода:
Минусы:
Данная статья предлагает решение проблемы с ограничениями функционала продукта, диктуемыми Sandbox, для разработчиков. В будущем мы расскажем вам о том, как ситуация выглядит со стороны пользователей. Если вы находили другие обходные пути, будем рады, если вы поделитесь ими в комментариях.
Sandbox призван сделать macOS безопасней и уберечь пользователя от вредоносных и потенциально опасных приложений — все действия приложений, которые могут гипотетически принести вред, должны сопровождаться запросом к пользователю на предоставление доступа. В теории это разумная мера предосторожности, однако в действительности под санкции попадают многие приложения, которым пользовательские данные необходимы для осуществления базовых функций. В данной статье мы расскажем, как внедряли подобные функции в приложение, поддерживая совместимость с Sandbox, — возможно, этот опыт будет полезен для других разработчиков, работающих с официальным маркетом.
Мы столкнулись с подобной необходимостью при работе с утилитой для мониторинга системы MaCleaner X. Вся линейка продуктов MaCleaner распространяется исключительно через Mac App Store.
Отличительными особенностями MaCleaner X являются поддержка тачбара и кастомизированный дизайн: приложение автоматически определяет модель Mac и предлагает особый вариант интерфейса для каждого девайса. Однако в дополнение к этому мы в данный момент работаем над тем, чтобы реализовать в расширенной версии некоторые функции, которые пользователи хотели бы видеть в программе и которые для утилит, распространяемых вне Mac Store, считаются привычными.
В первую очередь это:
- удаление приложений и всей связанной с ними информации;
- ?удаление неиспользуемых служебных файлов из macOS (логи, локализации, temp);
- получение системной информации о железе (например, о жестком диске);
- мониторинг температуры и других показателей системы.
Способ решения
Для начала в общем виде наметим, по какой схеме будет происходить весь процесс:
- Пользователь должен дать согласие на доступ для расширения функционала (фактически ему нужно будет осуществить несколько действий вне приложения);
- Приложение всегда работает в песочнице, но в случае необходимости перед каждым действием, требующим администраторские права доступа, запрашивает у пользователя разрешение.
Для выполнения этой операции вне песочницы воспользуемся следующей возможностью macOS: приложения, которые поддерживают расширение своего функционала при помощи Apple Script, могут запускать эти скрипты вне песочницы, если они находятся в папке ~/Library/Application Scripts. Приложение, поддерживающее Sandbox, не может самостоятельно копировать в эту папку скрипты — это пресекается модераторами Apple.
Таким образом, последовательность действий у нас вырисовывается следующая:
- Реализация master-приложения, поддерживающего расширения при помощи Apple Script из папки Application Scripts.
- Прохождение модерации и публикация приложения в Mac Store
- Подготовка extension-приложения. В его задачи будет входить копирование нужных скриптов в общую папку c master-приложением. Для этого они должны иметь общую AppGroup, при помощи которой будет происходить обмен.
- Публикация extension-приложения на собственном сайте.
Пример
В качестве примера рассмотрим возможность получения информации о текущих носителях при помощи bash-команды
system_profiler SPStorageDataType
.Подготовка Extension-приложения
Все, что требуется от этой компоненты — это копирование скрипта в определенную папку. Поэтому в его интерфейсе мы отобразим единственную возможность: включение или отключение отображения информации о носителях (чтобы не грузить пользователя подробностями, это может быть представлено как включение/отключение плагинов в master-приложении).
Apple Sript — bash
Содержимое файла extract_storage_info.scpt, который копируется ectention'ом:
do shell script "system_profiler SPStorageDataType"
Подготовка Master-приложения
Для корректной работы приложения с точки зрения пользователя необходимо осуществлять проверку доступности дополнительного функционала и скрывать соответствующие части UI:
- (BOOL) isStorageInfoAvailable {
return [self _storageInfoScriptTask] != nil;
}
#pragma mark - Private
- (NSUserAppleScriptTask *)_storageInfoScriptTask {
NSUserAppleScriptTask *result = nil;
NSError *error;
NSURL *directoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationScriptsDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
if (directoryURL) {
NSURL *scriptURL = [directoryURL URLByAppendingPathComponent: StorageInfoScriptFileName];
result = [[NSUserAppleScriptTask alloc] initWithURL:scriptURL error:&error];
if (error != nil) {
NSLog(@"No AppleScript \"%@\" task error: %@", StorageInfoScriptFileName, error);
}
} else {
// Возникнет в случае, если приложение запущено не в Sandbox
NSLog(@"No Application Scripts folder error: %@", error);
}
return result;
}
Если скрипт доступен, его вызов осуществляется следующим образом:
NSUserAppleScriptTask * automationScriptTask = [self _storageInfoScriptTask];
if (automationScriptTask != nil) {
[automationScriptTask executeWithAppleEvent:nil completionHandler:^(NSAppleEventDescriptor *resultEventDescriptor, NSError *error) {
if (error != nil) {
NSLog(@"AppleScript \"%@\" executing error: %@", StorageInfoScriptFileName, error);
//TODO: Обработка ошибки в GUI
}
else {
NSString* resultString = [resultEventDescriptor stringValue];
NSLog(@"Result string:\n%@", resultString);
// Парсинг и вывод информации
}
}];
} else {
//TODO: Обработка ошибки в GUI
}
В нашем упрощенном примере, мы выводим полученную от выполненного скрипта строку как есть. Для стилизации под интерфейс приложения необходимо будет парсить результат и отображать только необходимую информацию.
Совместная работа приложений
В результате мы получаем следующую схему работы.
Master-приложение
- В случае недоступности дополнительного функционала скрывает элементы UI.
- В случае наличия расширяющих скриптов, Apple Script (по определенному формату) активирует UI и выполняет их.
- Если функции, которые выполняет Apple Script, требуют ввода данных администраторской учетной записи, пользователь увидит стандартный диалог авторизации — эту работу выполнит за нас Apple Script.
Extension-приложение
- Должно быть скачано пользователем с сайта разработчика. Мы говорим о специальном сайте, потому что необходимо разъяснить пользователю принцип функционирования системы и действия, которые ему необходимо осуществить для корректной работы. Отчасти это можно сделать через GUI extension-а.
- Несмотря на то, что работаем вне песочницы, мы предлагаем пользоваться общим AppGroup для компонент-решения.
- Этот компонент может быть представлен даже более широко — как Менеджер Расширений. Благодаря согласованному интерфейсу, master-приложение может расширяться по мере появления расширений в общей папке: они могут быть добавлены вместе с файлами описаний и конфигурациями.
- Копируемый Apple Script может даже содержать общую функцию task(bash_script), принимающую на вход произвольную bash-команду и выполняющую ее. Но этот подход открывает большую дыру в безопасности, а также обязует master-приложение содержать расширение перед публикацией в App Store.
Выводы
Плюсы подхода:
- Расширение функционала приложения
- Конфигурируемость расширения
Минусы:
- Нерационально с точки зрения юзабилити. От пользователя требуется дополнительное действие — скачивание стороннего приложения.
- Потенциальная дыра в безопасности и необходимость дополнительных проверок, согласованности форматов.
Данная статья предлагает решение проблемы с ограничениями функционала продукта, диктуемыми Sandbox, для разработчиков. В будущем мы расскажем вам о том, как ситуация выглядит со стороны пользователей. Если вы находили другие обходные пути, будем рады, если вы поделитесь ими в комментариях.
Поделиться с друзьями
awoland
Полагаю, что проще распространять и продавать приложение напрямую с сайта. Если это приложение действительно полезное и востребованное, потенциальные пользователи его найдут. Сам всегда (и принципиально) при наличии возможности выбора отдаю предпочтение версии, распространяющейся непосредственно и напрямую от разработчика, а не через AppStore. А вот функционал, который можно реализовать исключительно через AppStore (InApp P., iCloud Drive, Game Center и т.п.) имеет смысл реализовывать через дополнения (приложения-расширения) и распространять их через AppStore.
Механизм подобных расширений использует сам Apple (расширения для последнего Xcode, например).
EverydayTools
Дело в том, что наша задача изначально заключалась именно в том, чтобы создать продукт, который распространяется через Mac Store, поэтому мы исходили из этих реалий.
Наш опыт говорит о большей лояльности пользователей к продуктам, размещенным в официальном маркете, нежели распространяющихся через собственные сайты. Собственно, статья написана как раз для разработчиков, которые, как мы, работая с Mac Store ищут варианты расширения функционала своих продуктов, не отходя от идеи работы с официальным стором.