58. Получаем ответ сервера в свернутом приложении
Oleg991В этой статье покажу как на iOS < 16 можно отправить запрос к серверу, пока приложение открыто (состояние foreground
), и дождаться завершения запроса после сворачивания приложения (состояние background
). Будем использовать background task (далее - фоновый таск).
Сценарий
- Нажимаем на кнопку авторизации в приложении
- Приложение запрашивает у сервера СМС со ссылкой для авторизации
- Дожидаемся СМС
- Переходим в "Сообщения" (приложение свернули)
- Нажимаем на ссылку в СМС
- Открывается safari с предложением вернуться обратно в приложение (по диплинку)
- Жмем "открыть" и возвращаемся в приложение
В чем проблема
Система (iOS) может приостановить работу приложения в свернутом состоянии: это может произойти даже через 1-2 секунды после сворачивания приложения.
Если не использовать фоновый таск, то на шаге 7 (возврат в приложение) мы можем повторить запрос к серверу для получения СМС, и это может привести к ошибке (слишком много запросов в короткий промежуток времени).
Повторный запрос консоль Xcode не покажет - там отобразится ответ сервера на второй запрос (скорее всего с ошибкой).
Решение
Настройка проекта
У apple есть несколько гайдов на такой случай, например такой и такой, и я рекомендую с ними ознакомиться после прочтения статьи.
<key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> </array>
Код для iOS < 16
struct BackgroundTaskExample: View { var body: some View { VStack { Text("Дождитесь СМС со ссылкой для авторизации") Spacer() } .task { await requestSMS() } } /// Создает фоновый таск и возвращает его идентификатор private func startBackgroundTask() -> UIBackgroundTaskIdentifier { var identifier: UIBackgroundTaskIdentifier? identifier = UIApplication.shared.beginBackgroundTask { // Блок, выполняющийся при завершении таска, нужно вызвать `endBackgroundTask` UIApplication.shared.endBackgroundTask(identifier ?? .invalid) } return identifier ?? .invalid } private func requestSMS() async { // Создаем таск для фоновой работы, чтобы завершить его // после успешной авторизации let taskId = startBackgroundTask() // Выполняем запрос // await myService.requestSMSForAuth() // После завершения авторизации завершаем фоновый таск await UIApplication.shared.endBackgroundTask(taskId) } }
Код для iOS 16+
В SwiftUI на iOS 16 есть удобный модификатор backgroundTask
, обзор на который уже есть у других ребят, например, тут и тут.
Заключение
Нужно помнить, что если приложение сворачивается, то по умолчанию нет гарантий завершения долгих запросов к серверу. В таких ситуациях нужно использовать фоновые таски 🙂
Код для этой статьи можно посмотреть тут, а другие статьи - тут.