Разработка
Оптимизируем обновление местоположения с помощью CLLocationUpdate в Swift
Новый, современный и упрощенный API для получения местоположений от Apple.
На WWDC 23 Apple значительно улучшила процесс обновления местоположения, представив класс CLLocationUpdate
. Этот новый класс использует возможности современного Swift Concurrency, упрощая получение и управление данными о местоположении в ваших приложениях.
Что нового?
- Раньше для получения данных о местоположении требовались сложные методы-делегаты и обратные вызовы, что делало наш код менее читабельным и неорганизованным.
- Класс CLLocationUpdate упрощает этот процесс, предоставляя асинхронный подход, использующий функции параллелизма Swift.
- Это означает, что теперь мы можем использовать синтаксис async/await для управления обновлениями местоположения, что делает наш код чище и читабельнее.
- Кроме того, он автоматически приостанавливает/возобновляет обновление местоположения в зависимости от того, движется пользователь или неподвижен.
Почему стоит использовать CLLocationUpdate
- Упрощенный код: синтаксис
async/await
делает код чище и проще для понимания и тестирования, чем традиционные методы с делегатами и колбеками. - Улучшенная читаемость: код становится организованным и читабельным и помещает все компоненты в одно место, что позволяет больше сосредоточиться на фактической логике обработки местоположения.
- Обработка ошибок: поскольку мы можем использовать
async/await
в этом методе, мы можем использовать блокtry/catch
внутри цикла для простой обработки ошибок. - Поддержка параллелизма: поскольку местоположение предоставляется в виде
AsyncSequence
, мы можем интегрировать его с другими асинхронными операциями в нашем приложении. - Эффективность использования аккумулятора: поскольку функция обеспечивает автоматическую паузу/возобновление обновления местоположения в зависимости от того, движется пользователь или неподвижен, она также способствует увеличению времени автономной работы.
Как использовать CLLocationUpdate
Использование класса CLLocationUpdate для обновления местоположения требует выполнения следующих шагов:
- Импортируйте
CoreLocation
. - Затем вызовите статическую фабричную функцию
.liveUpdates()
, предоставляемую классом CLLocationUpdate, чтобы получить обновления в виде последовательностиAsyncSequence
. Эта последовательность может быть итерирована напрямую с помощьюfor/try/await
для получения CLLocationUpdate в замыкании. - Теперь используйте CLLocationUpdate, полученный в замыкании, чтобы получить местоположение, обратившись к его свойству location.
import CoreLocation func startLocationUpdates() { // Take location permissions before Task { do { let updates = CLLocationUpdate.liveUpdates() for try await update in updates { print ("Current Location is \(update.location)") // To stop updates break out of the for loop if update.isStationary { break } } } catch { debugPrint("Some Error Occured") } } }
Как это работает за кулисами?
- В новом классе CLLocationUpdate у нас есть статическая функция
.liveUpdates()
. Она возвращает последовательность AsyncSequence под названием Updates, которую можно напрямую итерировать с помощью циклаfor/try/await
. - Updates при итерации с помощью цикла
for/try/await
даетCLLocationUpdate
, содержащий местоположение типаCLLocation
и булевский флагisStationary
для управления автоматическими паузами и возобновлениями.
public struct CLLocationUpdate : Sendable { public let location : CLLocation? // nil if no location is available public let isStationary : Bool // true if device becomes stationary }
- Мы также можем передать функции
.liveUpdates()
аргумент типаLiveConfiguration
, который является перечислением. Он используется в качестве маркера типа активности, на которую вы ориентируетесь при запросе местоположения.
public enum LiveConfiguration { case default case automotiveNavigation case otherNavigation case fitness case airborne }
Добавление фильтра расстояний при обновлении
Мы также можем отфильтровать только те места, которые удовлетворяют нашему минимальному расстоянию. Чтобы добавить эту логику, нам нужно добавить фильтр в нашу функцию .liveUpdates()
.
private var previousLocation: CLLocation? = nil private var distanceThreshold: CLLocationDistance = 0.0 ... let locationUpdates = CLLocationUpdate.liveUpdates(.fitness).filter { [weak self] update in guard let self else { return false } guard let previousLocation = self.previousLocation else { self.previousLocation = update.location return true } let distanceMoved = update.location?.distance(from: previousLocation) if distanceMoved ?? 0.0 >= distanceThreshold || update.isStationary { self.previousLocation = update.location return true } return false } ....
Как получать обновления местоположения в фоновом режиме
Мы можем получать обновления местоположения в фоновом режиме двумя способами.
1. Live Activity
- Live Activity отображает актуальную информацию из вашего приложения, позволяя людям сразу же увидеть ход выполнения действия, события или задачи.
- Пока Live Activity остается активной, приложение может получать обновления без дополнительных настроек.
2. CLBackgroundActivitySession
- Многие приложения не имеют Live Activity. В этом случае мы можем использовать
CLBackgroundActivitySession
. - Она не только обеспечивает обновление местоположения в фоновом режиме, но и помогает отслеживать такие события, как геозонирование, с помощью
CLMonitor
. - Для использования
CLBackgroundActivitySession
нам нужно инстанцировать его объект, чтобы начать новую сессию. - Также необходимо убедиться в том, что мы удерживаем объект, поскольку его удаление автоматически аннулирует сессию, что может привести к прекращению доступа приложения к фоновому получению данных о местоположении.
- После инстанцирования объекта мы присваиваем его любой глобальной переменной, чтобы он не мог быть деаллоцирован даже в случае завершения скоупа.
- Когда мы захотим прекратить обновление местоположения, мы можем просто вызвать
.invalidate()
для свойства, в котором мы разместилиCLBackgroundActivitySession
.
// CLBackgroundActivitySession in action import CoreLocation private var backgroundActivity: CLBackgroundActivitySession? func startLocationUpdates() { // Take background location permissions before Task { do { // Assign the CLBackgroundActivitySession to global var self.backgroundActivity = CLBackgroundActivitySession() let updates = CLLocationUpdate.liveUpdates() for try await update in updates { ... } } catch { debugPrint("Some Error Occured") } } } func stopBackgroundLocation() { // to stop the background updates self.backgroundActivity?.invalidate() }
Жизненный цикл приложения
1. Переход из фонового состояния в приостановленное
- Как уже говорилось, этот новый класс может предоставлять обновления местоположения в фоновом режиме и приостанавливать обновление, когда пользователь неподвижен.
- Когда приложение находится в фоновом режиме, а обновления местоположения не запускаются, оно может перейти из фонового состояния в приостановленное (это также может быть связано с нехваткой ресурсов).
- Интересно то, что класс
CLLLocationUpdate
не может оставлять приложение в приостановленном состоянии. - Как только обновления местоположения станут доступны, он отменит приостановку приложения и перейдет обратно в фоновое состояние. А нам не нужно писать никакой код для продолжения обновления местоположения.
2. Что делать, если приложение завершено
CLLocationUpdate
может восстановить приложение, даже если оно завершено и не запущено. Он начнет предоставлять обновления местоположения, как только они станут доступны, и приложение будет перезапущено в фоновом режиме.- В этом случае приложение перейдет из завершенного в работающее в фоновом режиме.
- Но когда приложение будет завершено, нам нужно выполнить несколько шагов, чтобы запустить обновления местоположения.
- Во-первых, нужно вызвать
CLLocationUpdate.liveUpdates()
. Также нам нужно воссоздатьCLBackgroundActivitySession
. - Когда приложение находится в фоновом режиме и ему нужно продолжать получать обновления местоположения, оно может присоединиться к существующей
CLBackgroundActivitySession
. ПересоздавCLBackgroundActivitySession
, приложение может подключиться к уже запущенному сеансу, гарантируя, что фоновые обновления местоположения будут поступать без перерыва. - Чтобы приложение могло повторно подключиться к существующей
CLBackgroundActivitySession
при запуске в фоновом режиме, воссоздание необходимых объектов должно быть помещено в метод, который выполняется во время фонового запуска приложения. Одним из подходящих мест является методdidFinishLaunchingWithOptions
делегата приложения, который вызывается, когда приложение завершает запуск, независимо от того, было ли оно запущеноявно или в фоновом режиме.
Вывод
Заключение
Класс CLLocationUpdate
, представленный на WWDC 23, это значительный шаг вперед для разработчиков, работающих с данными о местоположении в Swift. Используя современные функции параллелизма, он упрощает процесс получения и управления обновлениями местоположения, что приводит к созданию более чистого, читабельного и эффективного кода. Примите этот новый подход и оптимизируйте свои приложения на основе данных о местоположении в Swift!