Реализация Family Sharing подписки в iOS-приложении
Семейный доступ к подпискам — функция, которую App Store поддерживает, но на практике её реализация часто вызывает проблемы. Один член семьи покупает подписку, остальные до 6 человек получают доступ автоматически. Однако если приложение неправильно обрабатывает revoke при выходе пользователя из семьи, доступ остаётся — это потеря выручки (до 30% в некоторых проектах). Мы, как разработчики с опытом более 5 лет и десятками реализованных in-app покупок, решаем эту задачу под ключ. В этой статье разберём корректную настройку Family Sharing через StoreKit 2, тестирование и типичные ошибки.
Как работает проверка Family Sharing через StoreKit 2?
В StoreKit 2 каждая транзакция содержит поле ownershipType: .purchased (купил сам) или .familyShared (получил через семью). Приложение обязано проверять оба варианта при старте и постоянно слушать обновления. Вот базовая проверка с помощью Transaction.currentEntitlements:
for await verificationResult in Transaction.currentEntitlements { switch verificationResult { case .verified(let transaction): if transaction.productID == "com.app.premium" { switch transaction.ownershipType { case .purchased: grantAccess(source: .directPurchase) case .familyShared: grantAccess(source: .familySharing) @unknown default: break } } case .unverified: // не доверяем транзакции — логируем, не даём доступ break } } Чтобы подписка вообще поддерживала Family Sharing, её нужно включить в App Store Connect. В настройках In-App Purchase найдите продукт и активируйте переключатель «Family Sharing» → «On for all members». Это делается один раз и не отменяется для уже купленных подписок.
Почему возникает рассинхронизация доступа после выхода из семьи?
Основная ошибка — кэширование статуса подписки без проверки актуальности. Когда пользователь покидает Family Group, транзакция .familyShared получает revocationDate. Transaction.currentEntitlements перестаёт её возвращать, но если приложение хранит premium-статус в UserDefaults или Keychain, доступ останется. Наша практика показывает: до 30% кейсов семейного доступа содержат эту проблему. Ранее реализация с StoreKit 1 требовала серверной валидации, что увеличивало стоимость в 2 раза.
Правильное решение — слушать Transaction.updates постоянно, а не только при входе в приложение:
func listenForTransactionUpdates() { Task { for await verificationResult in Transaction.updates { if case .verified(let transaction) = verificationResult { if transaction.revocationDate != nil { revokeAccess() } else { await transaction.finish() grantAccess() } } } } } Этот код запускается при старте приложения и работает всё время жизни процесса. Без него даже проверка currentEntitlements при каждом открытии может пропустить revoke, если приложение не перезапущено.
Что важно при тестировании Family Sharing?
Тестирование в Xcode Sandbox не обойтись без нескольких тестовых Apple ID, объединённых в семью. В App Store Connect → Users and Access → Sandbox Testers создайте минимум двух пользователей, затем там же настройте Family Sharing в Sandbox. Без этого шага вы не сможете симулировать .familyShared транзакции. Мы гарантируем полное покрытие — проверяем сценарии покупки основным пользователем, получение доступа членами семьи, выход из семьи и повторный вход.
Для наглядности сведём сценарии в таблицу:
| Сценарий | Действие | Ожидаемый результат |
|---|---|---|
| Основной пользователь покупает подписку | Покупка через StoreKit 2 | Транзакция с ownershipType == .purchased |
| Член семьи получает доступ | Автоматически через Family Sharing | Транзакция с ownershipType == .familyShared |
| Член семьи выходит из группы | Выход через настройки Apple ID | revocationDate устанавливается, доступ отзывается |
| Повторный вход в семью | Возврат в группу | Новая familyShared транзакция (если подписка ещё активна) |
StoreKit 1 vs StoreKit 2: что выбрать?
Старый API (SKPaymentTransaction) не предоставляет ownershipType. Для проверки family-shared транзакции в StoreKit 1 требуется серверная валидация receipt — парсить поле in_app[].is_in_family_sharing. Это усложняет архитектуру и увеличивает время разработки. Сравнение подходов:
| Аспект | StoreKit 1 | StoreKit 2 |
|---|---|---|
| Проверка ownershipType | Receipt validation на сервере | Поле ownershipType в транзакции |
| Сложность | Высокая (серверная часть обязательна) | Низкая (на клиенте, без сервера) |
| Реакция на revoke | Требуется отдельный listener + серверная синхронизация | Встроенный Transaction.updates |
| Поддержка Swift Concurrency | Нет (только @escaping) | Да (async/await) |
Миграция на StoreKit 2 снижает время реализации в 2–3 раза и уменьшает количество багов. Для новых проектов мы однозначно рекомендуем его. Подробнее о StoreKit 2 — в официальной документации Apple.
Что входит в нашу работу
- Настройка Family Sharing для продуктов в App Store Connect
- Реализация проверки
ownershipTypeчерез StoreKit 2 - Обработка revoke при выходе из Family Group
- Постоянный listener
Transaction.updatesна протяжении жизни приложения - Тестирование с Sandbox-семьёй (до 6 членов, несколько сценариев)
- Документация для серверной части (если требуется валидация на бэкенде)
- Гарантия корректной работы в течение 30 дней после сдачи
Сроки и стоимость
Ориентировочный срок: 3–5 дней, включая настройку App Store Connect и тестирование на Sandbox аккаунтах. Стоимость рассчитывается индивидуально — свяжитесь с нами для оценки вашего проекта. Получите консультацию бесплатно: мы проанализируем текущую реализацию и предложим оптимальное решение. Закажите аудит вашего кода для проверки обработки Family Sharing — это займёт час, а сэкономит недели доработок.







