Information

Founded
Location
Россия
Website
productivityinside.com
Employees
101–200 employees
Registered

Habr blog

Pull to refresh
176.91
Rating
Productivity Inside
Для старательного нет ничего невозможного

Как расширить функционал приложения, размещенного на Mac Store, при помощи Apple Script

Productivity Inside corporate blogObjective CDevelopment for MacOS
С тех пор, как для прохождения модерации на Mac Store стала требоваться поддержка Sandbox, прошло уже 5 лет. Хотя возможности MacOS и Sandbox постепенно расширяются, разработчики, желающие публиковаться в официальном магазине Apple, по-прежнему ограничены в возможностях работы c этой ОС. Особенно остро эта проблема стоит для утилит и системных приложений.


Sandbox призван сделать macOS безопасней и уберечь пользователя от вредоносных и потенциально опасных приложений — все действия приложений, которые могут гипотетически принести вред, должны сопровождаться запросом к пользователю на предоставление доступа. В теории это разумная мера предосторожности, однако в действительности под санкции попадают многие приложения, которым пользовательские данные необходимы для осуществления базовых функций. В данной статье мы расскажем, как внедряли подобные функции в приложение, поддерживая совместимость с Sandbox, — возможно, этот опыт будет полезен для других разработчиков, работающих с официальным маркетом.

Наши партнеры столкнулись с подобной необходимостью при работе с утилитой для мониторинга системы MaCleaner X. Вся линейка продуктов MaCleaner распространяется исключительно через Mac App Store.

Отличительными особенностями MaCleaner X являются поддержка тачбара и кастомизированный дизайн: приложение автоматически определяет модель Mac и предлагает особый вариант интерфейса для каждого девайса. Однако в дополнение к этому мы в данный момент работаем над тем, чтобы реализовать в расширенной версии некоторые функции, которые пользователи хотели бы видеть в программе и которые для утилит, распространяемых вне Mac Store, считаются привычными.

В первую очередь это:

  • удаление приложений и всей связанной с ними информации;
  • ​удаление неиспользуемых служебных файлов из macOS (логи, локализации, temp);
  • получение системной информации о железе (например, о жестком диске);
  • мониторинг температуры и других показателей системы.

Способ решения

Для начала в общем виде наметим, по какой схеме будет происходить весь процесс:

  • Пользователь должен дать согласие на доступ для расширения функционала (фактически ему нужно будет осуществить несколько действий вне приложения);
  • Приложение всегда работает в песочнице, но в случае необходимости перед каждым действием, требующим администраторские права доступа, запрашивает у пользователя разрешение.

Для выполнения этой операции вне песочницы воспользуемся следующей возможностью macOS: приложения, которые поддерживают расширение своего функционала при помощи Apple Script, могут запускать эти скрипты вне песочницы, если они находятся в папке ~/Library/Application Scripts. Приложение, поддерживающее Sandbox, не может самостоятельно копировать в эту папку скрипты — это пресекается модераторами Apple.

Таким образом, последовательность действий у нас вырисовывается следующая:

  1. Реализация master-приложения, поддерживающего расширения при помощи Apple Script из папки Application Scripts.
  2. Прохождение модерации и публикация приложения в Mac Store
  3. Подготовка extension-приложения. В его задачи будет входить копирование нужных скриптов в общую папку c master-приложением. Для этого они должны иметь общую AppGroup, при помощи которой будет происходить обмен.
  4. Публикация 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-приложение

  1. В случае недоступности дополнительного функционала скрывает элементы UI.
  2. В случае наличия расширяющих скриптов, Apple Script (по определенному формату) активирует UI и выполняет их.
  3. Если функции, которые выполняет Apple Script, требуют ввода данных администраторской учетной записи, пользователь увидит стандартный диалог авторизации — эту работу выполнит за нас Apple Script.

Extension-приложение

  1. Должно быть скачано пользователем с сайта разработчика. Мы говорим о специальном сайте, потому что необходимо разъяснить пользователю принцип функционирования системы и действия, которые ему необходимо осуществить для корректной работы. Отчасти это можно сделать через GUI extension-а.
  2. Несмотря на то, что работаем вне песочницы, мы предлагаем пользоваться общим AppGroup для компонент-решения.
  3. Этот компонент может быть представлен даже более широко — как Менеджер Расширений. Благодаря согласованному интерфейсу, master-приложение может расширяться по мере появления расширений в общей папке: они могут быть добавлены вместе с файлами описаний и конфигурациями.
  4. Копируемый Apple Script может даже содержать общую функцию task(bash_script), принимающую на вход произвольную bash-команду и выполняющую ее. Но этот подход открывает большую дыру в безопасности, а также обязует master-приложение содержать расширение перед публикацией в App Store.

Выводы

Плюсы подхода:

  • Расширение функционала приложения
  • Конфигурируемость расширения

Минусы:

  • Нерационально с точки зрения юзабилити. От пользователя требуется дополнительное действие — скачивание стороннего приложения.
  • Потенциальная дыра в безопасности и необходимость дополнительных проверок, согласованности форматов.

Данная статья предлагает решение проблемы с ограничениями функционала продукта, диктуемыми Sandbox, для разработчиков. В будущем мы расскажем вам о том, как ситуация выглядит со стороны пользователей. Если вы находили другие обходные пути, будем рады, если вы поделитесь ими в комментариях.
Tags:objective-capple scriptmac osxosxsandboxпесочницаmacleaner
Hubs: Productivity Inside corporate blog Objective C Development for MacOS
Rating +1
Views 2.6k Add to bookmarks 13
Comments
Comments 2