Spec-Zone .ru
спецификации, руководства, описания, API

Библиотека разработчика Mac

Разработчик

Руководство по программированию расширения приложения

PDF
На этой странице

Обработка общих сценариев

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

Используя встроенную платформу для совместного использования кода

Можно создать встроенную платформу для совместного использования кода между расширением приложения и его содержанием приложения. Например, если Вы разрабатываете фильтр изображения для использования в Вашей фотографии, Редактируя расширение, а также в его содержании приложения, помещаете код фильтра в платформу и встраиваете платформу в обе цели.

Удостоверьтесь, что Ваша встроенная платформа не содержит APIs, недоступный к расширениям приложения, как описано в Некотором APIs Недоступны к Расширениям Приложения. Если у Вас есть пользовательская платформа, действительно содержащая такой APIs, Вы можете безопасно соединиться с нею от Вашего содержания приложения, но не можете совместно использовать тот код с содержавшими расширениями приложения. App Store отклоняет любое расширение приложения, соединяющееся с такими платформами, или это иначе использует недоступный APIs.

Для конфигурирования цели расширения приложения для использования встроенной платформы поставьте цель, “Требуют Только Расширения Приложения Безопасный API” установка сборки в Да. Если Вы не делаете, XCode напоминает Вам делать так путем отображения предупреждения “соединение против dylib, не безопасного для использования в расширениях приложения”.

При конфигурировании проекта XCode необходимо выбрать «Frameworks» в качестве места назначения для встроенной платформы в фазе сборки Файлов Копии.

Вы можете сделать содержание приложения доступным для пользователей рабочий iOS 7 или ранее, но тогда должны принять меры предосторожности для безопасного соединения встроенных платформ при выполнении в iOS 8 или позже. Считайте Развертывание Содержания Приложения к Более старым Версиям iOS для подробных данных.

Для больше при создании и использовании встроенных платформ, смотрите видеофильм WWDC 2014 “Создание современных Платформ”, доступный в https://developer.apple.com/videos/wwdc/2014.

Совместное использование данных с содержанием приложения

Даже при том, что пакет расширения приложения вкладывается в его содержании пакета приложения, расширение запущенного приложения и содержащий приложение не имеют никакого прямого доступа к контейнерам друг друга.

Можно, однако, включить совместное использование данных. Например, Вы могли бы хотеть позволить Вашему расширению приложения и его содержанию приложения совместно использовать единственный большой набор данных, такой как предварительно представленный активами.

Для включения совместного использования данных используйте XCode или портал Разработчика для включения групп приложений для содержания приложения и его содержавших расширений приложения. Затем, зарегистрируйте группу приложений в портале и укажите группу приложений для использования в содержании приложения. Для приобретения знаний о работе с группами приложений посмотрите Добавление Приложения к Группе приложений.

После включения групп приложений расширение приложения и его содержание приложения могут оба использовать NSUserDefaults API для совместного использования доступа к пользовательским настройкам. Для включения этого совместного использования используйте initWithSuiteName: метод для инстанцирования нового NSUserDefaults объект, передающий в идентификаторе совместно используемой группы. Например, расширение Доли могло бы обновить последний раз используемую учетную запись совместного использования пользователя, с помощью кода как это:

  • // Create and share access to an NSUserDefaults object
  • NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName: @"com.example.domain.MyShareExtension"];
  • // Use the shared user defaults object to update the user's account
  • [mySharedDefaults setObject:theAccountName forKey:@"lastAccountName"];

Иллюстрация, 4-1shows, как расширение и его содержание приложения могут использовать совместно используемый контейнер для совместного использования данных.

Полагайте, что 4-1An контейнер расширения приложения отличен от своего содержания контейнера приложения image: ../Art/app_extensions_container_restrictions_2x.png

При установке совместно используемого контейнера содержание приложения — и каждое содержавшее расширение приложения, что Вы позволяете участвовать в совместном использовании данных — имеют доступ для чтения и доступ для записи к совместно используемому контейнеру. Для предотвращения повреждения данных необходимо синхронизировать доступы данных.

Используйте Базовые Данные, SQLite или блокировки Posix, чтобы помочь скоординировать доступ к данным в совместно используемом контейнере.

Доступ к веб-странице

В расширениях Доли (на обеих платформах) и расширениях Действия (только iOS), можно предоставить пользовательский доступ к веб-контенту, прося, чтобы Safari выполнил файл JavaScript и возвратил результаты расширению. Можно также использовать файл JavaScript, чтобы получить доступ к веб-странице перед дополнительными выполнениями (на обеих платформах), или получить доступ или изменить веб-страницу после того, как расширение выполнит свою задачу (только iOS). Например, расширение Доли может помочь пользователям совместно использовать содержание от веб-страницы, или расширение Действия в iOS могло бы вывести на экран перевод текущей веб-страницы пользователя.

Для добавления доступа веб-страницы и манипулирования к расширению приложения выполните следующие шаги:

  • Создайте файл JavaScript, включающий названный глобальный объект ExtensionPreprocessingJS. Присвойте новый экземпляр своего пользовательского класса JavaScript этого объекта.

  • В NSExtensionActivationRule словарь в Вашем расширении приложения Info.plist файл, дайте NSExtensionActivationSupportsWebPageWithMaxCount включите ненулевое значение. (Для узнавания больше о словаре правила активации посмотрите Объявление Поддерживаемых Типов данных для Расширения Доли или Действия.)

  • Когда Ваше расширение приложения запустится, используйте NSItemProvider класс для возвращения результатов выполнением файла JavaScript.

  • В расширении приложения для iOS передача оценивает файлу JavaScript, если Вы хотите, чтобы Safari изменил веб-страницу, когда Ваше расширение выполняет свою задачу. (Вы используете NSItemProvider класс на этом шаге, также.)

Чтобы сказать Safari, что Ваше расширение приложения включает файл JavaScript, добавьте NSExtensionJavaScriptPreprocessingFile ключ к NSExtensionAttributes словарь. Значение ключа должно быть файлом, который Вы хотите, чтобы Safari загрузил, прежде чем запустится Ваше расширение. Например:

  • <key>NSExtensionAttributes</key>
  • <dict>
  • <key>NSExtensionJavaScriptPreprocessingFile</key>
  • <string>MyJavaScriptFile</string> <!-- Do not include the ".js" filename extension -->
  • </dict>

На обеих платформах Ваш пользовательский класс JavaScript может определить a run() функция, которую вызывает Safari, как только это загружает файл JavaScript. В run() функция, Safari обеспечивает названный параметр completionFunction, с которым можно передать результаты расширению приложения в форме объекта значения ключа.

В iOS можно также определить a finalize() функция, которую вызывает Safari, когда Ваше расширение приложения вызывает completeRequestReturningItems:completion: в конце его задачи. A finalize() функция может использовать элементы Ваши дополнительные передачи в completeRequestReturningItems:completion: изменить веб-страницу, как желаемый.

Например, если для Вашего расширения приложения для iOS нужен основной URI веб-страницы, когда оно запускается, и оно изменяет цвет фона веб-страницы, когда оно останавливается, Вы могли бы записать код JavaScript как показанный в Перечислении 4-1.

4-1Example перечисление run() и finalize() функции
  • var MyExtensionJavaScriptClass = function() {};
  • MyExtensionJavaScriptClass.prototype = {
  • run: function(arguments) {
  • // Pass the baseURI of the webpage to the extension.
  • arguments.completionFunction({"baseURI": document.baseURI});
  • },
  • // Note that the finalize function is only available in iOS.
  • finalize: function(arguments) {
  • // arguments contains the value the extension provides in [NSExtensionContext completeRequestReturningItems:completion:].
  • // In this example, the extension provides a color as a returning item.
  • document.body.style.backgroundColor = arguments["bgColor"];
  • }
  • };
  • // The JavaScript file must contain a global object named "ExtensionPreprocessingJS".
  • var ExtensionPreprocessingJS = new MyExtensionJavaScriptClass;

На обеих платформах необходимо записать код для обработки значений, пасующихся назад от Вашего run() функция. Для получения словаря результатов укажите kUTTypePropertyList идентификатор типа в NSItemProvider метод loadItemForTypeIdentifier:options:completionHandler:. В словаре используйте NSExtensionJavaScriptPreprocessingResultsKey ключ для получения элемента результата. Например, для получения основного URI в run() функция в Перечислении 4-1, Вы могли бы использовать код как это:

  • [imageProvider loadItemForTypeIdentifier:kUTTypePropertyList options:nil completionHandler:^(NSDictionary *item, NSError *error) {
  • NSDictionary *results = (NSDictionary *)item;
  • NSString *baseURI = [[results objectForKey:NSExtensionJavaScriptPreprocessingResultsKey] objectForKey:@"baseURI"];
  • }];

Передать значение finalize() функционируйте, когда Ваше расширение приложения для iOS заканчивает свою задачу, используйте NSItemProvider initWithItem:typeIdentifier: метод для упаковки значения в словаре для NSExtensionJavaScriptFinalizeArgumentKey ключ. Например, для указания красный для цвета фона, используемого в finalize() функция в Перечислении 4-1, Ваше расширение могло бы использовать код как это:

  • NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
  • extensionItem.attachments = @[[[NSItemProvider alloc] initWithItem: @{NSExtensionJavaScriptFinalizeArgumentKey: @{@"bgColor":@"red"}} typeIdentifier:(NSString *)kUTTypePropertyList]];
  • [[self extensionContext] completeRequestReturningItems:@[extensionItem] completion:nil];

Выполнение загрузок и загрузок

Пользователи склонны возвращаться к приложению узла сразу после того, как они закончат свою задачу в Вашем расширении приложения. Если задача включает потенциально длинную загрузку или загрузку, необходимо гарантировать, что это может закончиться после того, как расширение завершается. Для выполнения загрузки или загрузки используйте NSURLSession класс, чтобы создать сеанс URL и инициировать фоновую загрузку или задачу загрузки.

После того, как Ваше расширение приложения инициирует загрузку или задачу загрузки, расширение может завершить запрос приложения узла и быть завершено, не влияя на результат задачи. Чтобы узнать больше, как расширение обрабатывает запрос из приложения узла, посмотрите, Реагируют на Запрос Приложения Узла. В iOS, если Ваше расширение не работает, когда фоновая задача завершается, система запускает Ваше содержание приложения в фоновом режиме и вызывает application:handleEventsForBackgroundURLSession:completionHandler: метод делегата приложения.

Перечисление 4-2 показывает один способ сконфигурировать сеанс URL и использовать его для инициирования загрузки.

Перечисление 4-2An примера конфигурирования NSURLSession возразите и запуск загрузки
  • NSURLSession *mySession = [self configureMySession];
  • NSURL *url = [NSURL URLWithString:@"http://www.example.com/LargeFile.zip"];
  • NSURLSessionTask *myTask = [mySession downloadTaskWithURL:url];
  • [myTask resume];
  • - (NSURLSession *) configureMySession {
  • if (!mySession) {
  • NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@“com.mycompany.myapp.backgroundsession];
  • // To access the shared container you set up, use the sharedContainerIdentifier property on your configuration object.
  • config.sharedContainerIdentifier = @“com.mycompany.myappgroupidentifier;
  • mySession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
  • }
  • return mySession;
  • }

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

Если необходимо завершить запрос приложения узла перед инициированием фонового сеанса URL удостоверьтесь, что код, создающий и использующий сеанс, эффективен. После Ваших вызовов расширения приложения completeRequestReturningItems:completionHandler: чтобы сказать приложение узла, что его запрос завершен, система может завершить Ваше расширение в любое время.

Объявление поддерживаемых типов данных для расширения доли или действия

В Вашем расширении Доли или Действия вероятно, что можно работать с некоторыми типами данных, но не других. Чтобы гарантировать, что приложение узла предлагает Ваше расширение только, когда пользователь выбрал данные типа, который Вы поддерживаете, добавьте NSExtensionActivationRule ключ к Вашему расширению Info.plist файл списка свойств. Можно также использовать этот ключ для указания максимального количества элементов каждого типа, который может обработать расширение.

Когда Ваше расширение работает, система выдерживает сравнение NSExtensionActivationRule значения ключа с информацией в дополнительном элементе attachments свойство. Для полного списка ключей можно использовать с этим ключом, видеть Ключи Расширения Действия.

Например, чтобы объявить, что Ваше расширение Доли может поддерживать до десяти изображений, один фильм и одну веб-страницу URL, Вы могли бы использовать следующий словарь для значения NSExtensionAttributes ключ:

  • <key>NSExtensionAttributes</key>
  • <dict>
  • <key>NSExtensionActivationRule</key>
  • <dict>
  • <key>NSExtensionActivationSupportsImageWithMaxCount</key>
  • <integer>10</integer>
  • <key>NSExtensionActivationSupportsMovieWithMaxCount</key>
  • <integer>1</integer>
  • <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
  • <integer>1</integer>
  • </dict>
  • </dict>

Если Вы не поддерживаете определенный тип данных, использовать 0 для значения соответствующего ключа или удаляют ключ из Вашего NSExtensionActivationRule словарь.

Ключи NSExtensionActivationRule словарь достаточен для удовлетворения потребностей фильтрации расширений типового приложения. Если необходимо сделать более сложную или более определенную фильтрацию, такую как различение public.url и public.image, можно создать оператор предиката. Затем используйте пустую строку, представляющую предикат как значение NSExtensionActivationRule ключ. (Во время выполнения система компилирует эту строку в NSPredicate объект.)

Например, элемент расширения приложения attachments свойство может указать файл PDF как это:

  • {extensionItems = ({
  • attachments = ({
  • registeredTypeIdentifiers = (
  • "com.adobe.pdf",
  • "public.file-url"
  • );
  • });
  • })}

Чтобы указать, что Ваше расширение приложения может обработать точно один файл PDF, Вы могли бы создать строку предиката как это:

  • SUBQUERY (
  • extensionItems,
  • $extensionItem,
  • SUBQUERY (
  • $extensionItem.attachments,
  • $attachment,
  • ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "com.adobe.pdf"
  • ).@count == $extensionItem.attachments.@count
  • ).@count == 1

Вот пример более сложного оператора предиката:

  • SUBQUERY (
  • extensionItems,
  • $extensionItem,
  • SUBQUERY (
  • $extensionItem.attachments,
  • $attachment,
  • ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "org.appextension.action-one" ||
  • ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "org.appextension.action-two"
  • ).@count == $extensionItem.attachments.@count
  • ).@count == 1

Этот оператор выполняет итерации по массиву NSExtensionItem объекты, и во вторую очередь по attachments массив в каждом дополнительном элементе. Для каждого присоединения предикат оценивает универсальный идентификатор типа (UTI) для каждого представления в присоединении. Когда присоединяемое представление, из которого UTI соответствует любому из двух различных, указало UTIs (который Вы видите на правой стороне каждого UTI-CONFORMS-TO оператор), соберите это UTI для заключительного сравнительного испытания. Заключительные возвраты строки TRUE если расширение приложения было дано точно одно дополнительное присоединение элемента с поддерживаемым UTI.

Во время разработки только, можно использовать TRUEPREDICATE постоянный (который всегда оценивает к true) как тупиковый оператор предиката, для тестирования пути выполнения кода перед реализацией оператора предиката.

Для узнавания больше о синтаксисе операторов предиката посмотрите Синтаксис Строки формата Предиката в Руководстве по программированию Предиката.

Развертывание Содержания Приложения к Более старым Версиям iOS

Если Вы соединяетесь со встроенной платформой от Вашего содержания приложения, можно все еще развернуть его на версиях iOS, более старого, чем 8,0, даже при том, что встроенные платформы не доступны в тех версиях.

Механизм, позволяющий Вам сделать, это - dlopen команда, которую Вы используете для условно ссылки и загружаете пакет платформы. Вы используете эту команду, поскольку альтернатива времени изготовления, соединяя Вас может указать в Общем XCode, или Фазы Сборки предназначаются для редактора. Основная идея состоит в том, чтобы соединить встроенные платформы в Ваше содержание приложения только при выполнении в iOS 8.0 или более новый.

Необходимо использовать Objective C, не Swift, в операторах кода, условно загружающих пакет платформы. Остальная часть Вашего приложения может быть записана на любом языке, и сама встроенная платформа может аналогично быть записана на любом языке.

После вызова dlopen, получите доступ к встроенным классам платформы с помощью следующего типа оператора:

  • MyLoadedClass *loadedClass = [[NSClassFromString (@"MyClass") alloc] init];

Устанавливать расширение приложения проект XCode использовать в своих интересах условное соединение

  1. Для каждого из Ваших содержавших расширений приложения, поставленных цель развертывания, чтобы быть iOS 8.0 или позже, как обычно.

    Сделайте это в разделе «Deployment info» вкладки «Общие» в XCode предназначается для редактора.

  2. Для Вашего содержания приложения, поставленного цель развертывания, чтобы быть самой старой версией iOS, который Вы хотите поддерживать.

  3. В Вашем содержании приложения conditionalize вызывает к dlopen команда в проверке на этапе выполнения на версию iOS при помощи systemVersion метод.

    Вызовите dlopen команда, только если Ваше содержание приложения работает в iOS 8.0 или позже. Обязательно используйте Objective C, не Swift, при совершении этого звонка.

Определенный iOS APIs использует встроенные платформы через dlopen команда. Вы должны conditionalize Ваше использование этого APIs, как Вы делаете при вызове dlopen непосредственно. Этот APIs от CFBundleRef непрозрачный тип:

И от NSBundle класс:

В содержании приложения Вы развертываетесь к версиям iOS, более старого, чем 8,0, вызываете этот APIs только в проверке на этапе выполнения, гарантирующей, что Вы работаете в iOS 8.0 или более новые, и вызываете этот APIs с помощью Objective C.