Разработка расширения для Share Sheet (iOS)
Share Sheet Extension (тип NSExtensionPointIdentifier: com.apple.share-services) появляется в системном шер-меню iOS, когда пользователь жмёт «Поделиться» в Safari, Фото, Файлах или любом другом приложении. Это позволяет принять контент напрямую — URL, изображение, файл — без переключения между приложениями.
NSItemProvider — сердце Share Extension
Входящий контент приходит через extensionContext?.inputItems как массив NSExtensionItem. Каждый содержит attachments: [NSItemProvider]. Нужно проверить hasItemConformingToTypeIdentifier для нужного UTI и загрузить данные через loadItem(forTypeIdentifier:options:completionHandler:).
Это асинхронно. И вот где начинаются проблемы: разработчики часто загружают данные в viewDidLoad(), не дожидаясь completion, и пытаются использовать результат синхронно. Extension крашится или показывает пустые данные.
Правильный подход для загрузки URL из Safari:
guard let item = extensionContext?.inputItems.first as? NSExtensionItem,
let provider = item.attachments?.first,
provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) else { return }
provider.loadItem(forTypeIdentifier: UTType.url.identifier) { [weak self] url, error in
DispatchQueue.main.async {
self?.receivedURL = url as? URL
self?.updateUI()
}
}
Поддержка нескольких UTI. Пользователь может выбрать несколько файлов. NSExtensionItem может содержать несколько attachments. Перебираем все, проверяем каждый через hasItemConformingToTypeIdentifier. Не забываем про kUTTypeFileURL vs kUTTypeURL — это разные типы.
Info.plist: NSExtensionActivationRule
Это правило определяет, когда ваше расширение появляется в шер-меню. Плохая настройка — расширение появляется везде, где не нужно, и раздражает пользователей.
Базовое правило через NSExtensionActivationRule словарь:
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key><integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key><integer>5</integer>
Для сложной логики — NSPredicate строка в NSExtensionActivationRule. Например, показывать только для PDF файлов: SUBQUERY(extensionItems, $item, SUBQUERY($item.attachments, $att, $att.registeredTypeIdentifiers UTI-CONFORMS-TO "com.adobe.pdf").@count >= 1).@count >= 1.
App Group и передача данных в основное приложение
Share Extension живёт в отдельном процессе. Данные из расширения не попадают в основное приложение автоматически.
Схема: расширение сохраняет данные в App Group container (UserDefaults(suiteName:) или файл в containerURL), основное приложение при следующем запуске читает из того же места. Для немедленной обработки — открываем основное приложение через extensionContext?.open(URL(string: "yourapp://share-received")!). Но это закрывает Share Sheet и переключает пользователя — не всегда желательно.
С iOS 16 можно использовать SwiftUI в Share Extension через ShareViewController на основе UIHostingController. Но UIHostingController в Extension ведёт себя иначе: не поддерживает @Environment(\.dismiss) для закрытия через extensionContext?.completeRequest(returningItems:). Закрытие делаем только через extensionContext.
Типичная структура работы
Аудит входящих типов данных, которые нужно принять. Настройка NSExtensionActivationRule. Разработка UI расширения (компактный — максимум треть экрана). Интеграция через App Group. Обработка отмены (didSelectCancel) и завершения (didSelectPost).
Сроки
Простое Share Extension (принять URL или изображение, показать форму): 2–4 недели. Extension с сложной логикой обработки, несколькими типами файлов, синхронизацией с сервером: 4–7 недель. Стоимость рассчитывается после анализа типов контента и требований к UI.







