Добавить в корзинуПозвонить
Найти в Дзене

SwiftUI & In-App Purchases (Swift)

Добавляем встроенные покупки в приложение
Здраствуйте дорогие читатели. В этой статье я расскажу вам, как я добавлял встроенные покупки в свое приложение на iOS, разработанное с использованием фреймворка SwiftUI.
Я не профессиональный разработчик приложений, никогда этому не учился. Разработка - это скорее мое хобби. Решения, использованные мной в коде, возможно, кому то покажутся далекими от

Добавляем встроенные покупки в приложение

Здраствуйте дорогие читатели. В этой статье я расскажу вам, как я добавлял встроенные покупки в свое приложение на iOS, разработанное с использованием фреймворка SwiftUI.

Я не профессиональный разработчик приложений, никогда этому не учился. Разработка - это скорее мое хобби. Решения, использованные мной в коде, возможно, кому то покажутся далекими от идеала. Но только таким образом я смог реализовать задуманное.

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

Здесь я не буду рассказывать как создавать покупки в AppStore Connect. Будем использовать готовые, которые я создал ранее. Выглядят они так:

-2

Итак - поехали!

Первым делом создаем проект:

  • в Xcode 11 выбираем: Создать новый проект-> Single View App и в свойствах проекта выбираем фреймворк SwiftUI;
  • в XCode 12 выбираем: Создать новый проект -> App и в свойствах проекта выбираем: Interface: SwiftUI; Life Cycle: UIKit App Delegate.
-3

Создается проект, в котором у нас должно быть несколько файлов, с двумя из которых мы будем работать:

  • AppDelegate.swift;
  • ContentView.swift;

и создадим несколько новых.

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

  • в файле ContentView создаем TabView с двумя экранами, один их которых мы оставим под главный экран приложения, а второй отдадим под краткое описание нашего приложения, где и будет расположена кнопка перехода на экран с покупками (в нашем случае "Donation").
  • на экране "Donation" мы расположим наши встроенные покупки с их названиями и ценами.

Что бы TabView у нас нормально работал, создадим два View:

  • MainView, в котором оставим простое текстовое View;
  • AboutView, в котором пока напишем только текст.

MainView:

AboutView:

А вот и наш ContentView с двумя экранами в TabView():

Далее нам надо включить встроенные покупки с свойствах проекта. Делается это следующим образом:

1. Переходим в свойства проекта: BeerCrafterDemo -> TARGETS BeerCrafterDemo -> Signing & Capabilities и нажимаем кнопку + Capability:

-4

2. Во всплывшем окне ищем и выбираем In-App Purchase.

-5

3. В итоге в нашем списке Signing & Capabilities должны появиться встроенные покупки:

-6

Теперь приступаем к созданию менеджера покупок.

Первым делом наше приложение должно каким-нибудь образом знать список идентификаторов покупок. Здесь пространства для манёвра мало. Можно список продуктов получать с удаленного сервера или вшить в приложение. Я воспользуюсь вторым вариантом.

Создадим новый Swift-файл и назовем его IAPManager.swift. Сразу после импортированного фреймворка Foundation импортируем StoreKit и создадим enum типа String, назвав его IAPProducts. Здесь мы запишем все наши идентификаторы продуктов, точно так же, как они записаны в AppStore Connect:

Далее мы создаем класс IAPManager и несколько функций:

  • функцию, добавляющую наблюдателя за транзакциями после проверки возможности совершать платежи,
  • и функцию, удаляющую наблюдателя за транзакциями.

Следующим шагом нам надо сделать так, что бы наша функция setupPurchases исполнялась при запуске приложения, а функция removePurchases исполнялась при завершении приложения. Делается это следующим образом:

  • переходим в файл AppDelegate.swift;
  • в функции application запускаем нашего наблюдателя;
  • добавляем функцию applicationWillTerminate для удаления нашего наблюдателя при завершении приложения.

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

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

Далее мы должны получить локализованные цены на наши продукты. Для этого мы напишем короткое расширение для SKProduct:

Следующим шагом мы доработаем наш наблюдатель за транзакциями, что бы мы могли отслеживать их состояния. Для этого мы должны в расширение IAPManager: SKPaymentTransactionObserver дописать несколько функций отслеживания состояния транзакций:

На финальном этапе создания нашего менеджера мы научим его совершать сделки и восстанавливать покупки. Последней функцией мы пользоваться в нашем приложении не будем. Она понадобиться, если вы примените покупки других типов, например авто-возобновляемые подписки. В основной класс IAPManager запишем несколько строк:

На этом этапе я столкнулся с проблемой, решить которую удалось только способом, о котором напишу далее. Мой IAPManager никак не хотел подружится и передать список покупок и цены во View, созданную при помощи фреймворка SwiftUI.

В основном классе IAPManager я создал два пустых массива типа String:

и переписал функцию productRequest:

Теперь создадим экран покупок и сделаем переход в него из экрана AboutView:

Первым делом нам нужно уведомить пользователя, если по какой то причине список покупок оказался недоступен. Для этого создадим переменную productCount, присвоим ей значение количества продуктов:

var productCount = IAPManager.shared.products.count;

и создадим условие, если их количество равно нулю, вывести сообщение о недоступности продуктов:

Последним этапом допишем две кнопки, которые будут содержать информацию о названии продукта и его цене. Кнопка будет показана только если продукт доступен:

Обратите внимание, что для получения названий продуктов и цен из IAPManager мы ввели две переменные:

var productsLocalizedTitle = IAPManager.shared.purchases

var productsLocalizedPrice = IAPManager.shared.purchasesPrise

Вот собственно и всё. Теперь можно собрать проект и проверить наши покупки на реальном устройстве при помощи тестировщика.

Экран покупок:

-7

Нажимаем кнопку покупки:

-8

Совершаем покупку:

-9

У этого способа реализации есть два недостатка, которые я увидел:

1. Если пользователь очень быстро перейдет в экран покупок, то приложение может не успеть загрузить часть или все продукты, тогда будет показано сообщение о отсутствии продуктов;

2. Список покупок загружается только при старте приложения, и не обновляется в случае изменения.

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

Благодарю за прочтение. Ставьте лайки и оставляйте комментарии.

Ссылка на проект в GitHub.

Ссылка на реальное приложение в AppStore: BeerCrafter.