58. Получаем ответ сервера в свернутом приложении

58. Получаем ответ сервера в свернутом приложении

Oleg991

В этой статье покажу как на iOS < 16 можно отправить запрос к серверу, пока приложение открыто (состояние foreground), и дождаться завершения запроса после сворачивания приложения (состояние background). Будем использовать background task (далее - фоновый таск).

Сценарий

  1. Нажимаем на кнопку авторизации в приложении
  2. Приложение запрашивает у сервера СМС со ссылкой для авторизации
  3. Дожидаемся СМС
  4. Переходим в "Сообщения" (приложение свернули)
  5. Нажимаем на ссылку в СМС
  6. Открывается safari с предложением вернуться обратно в приложение (по диплинку)
  7. Жмем "открыть" и возвращаемся в приложение

В чем проблема

Система (iOS) может приостановить работу приложения в свернутом состоянии: это может произойти даже через 1-2 секунды после сворачивания приложения.

Если не использовать фоновый таск, то на шаге 7 (возврат в приложение) мы можем повторить запрос к серверу для получения СМС, и это может привести к ошибке (слишком много запросов в короткий промежуток времени).

Повторный запрос консоль Xcode не покажет - там отобразится ответ сервера на второй запрос (скорее всего с ошибкой).

Решение

Настройка проекта

У apple есть несколько гайдов на такой случай, например такой и такой, и я рекомендую с ними ознакомиться после прочтения статьи.

Добавляем в проект capability - Background Modes
Ставим галки для выполнения наших фоновых задач
Добавляем в info.plist настройку для работы нашего фонового таска (если тасков несколько, строк нужно добавить больше)
Можно открыть исходный код файла info.plist и скопировать туда готовое свойство
Добавляем сюда нужное свойство (под скриншотом есть код)
<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, обзор на который уже есть у других ребят, например, тут и тут.

Заключение

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

Код для этой статьи можно посмотреть тут, а другие статьи - тут.






Report Page