Получение текущего местоположения, когда приложение работает в фоновом режиме

#ios #swift #cllocationmanager #appdelegate

#iOS #swift #cllocationmanager #appdelegate

Вопрос:

Я создал приложение, в котором вы можете нажать кнопку «Пуск». После нажатия кнопки приложение будет получать местоположение пользователя каждые 10 секунд вплоть до нажатия кнопки остановки. Когда я выхожу из приложения или если экран становится черным, оно больше не получит никаких местоположений, пока я не войду в приложение повторно.

Итак, в настоящее время я пытаюсь обновить местоположения, когда приложение свернуто. (Я предполагаю, что это вызывается в фоновом режиме?), а также когда экран становится черным. Но мои вопросы:

  1. Должен ли я написать этот код в AppDelegate?, если да. Как я могу узнать, была нажата кнопка или нет?
  2. Куда именно в AppDelegate я должен добавить код? И как я могу передать местоположения обратно правильному ViewController? (Поскольку я не могу выполнить какую-либо подготовку к переходу из AppDelegate)

Если вы знаете ответы на эти вопросы, пожалуйста, не стесняйтесь отвечать на них. 🙂 Я был бы действительно признателен за это!

Комментарии:

1. Получили ли вы разрешение от пользователя на использование служб определения местоположения, пока пользователь использует другие приложения?

2. @RamyMohamed Я не уверен насчет этого разрешения. Но я активировал «Обновления местоположения» в «Фоновых режимах», я также установил 2 разрешения в info-plist: * Местоположение всегда и * Местоположение при использовании

3. ознакомьтесь с этим блогом: medium.com/curbside /…

4. Итак, вы хотите обновлять местоположения через регулярные промежутки времени, скажем, каждые 10 секунд, когда приложение работает в фоновом режиме. Для достижения этого вам необходимо запустить таймер для извлечения местоположения через регулярные промежутки времени. Но невозможно запустить таймер, когда приложение работает в фоновом режиме. Как только приложения войдут в BG, оно будет приостановлено раньше. Для обновления местоположения, когда приложение находится в BG, можно использовать либо службу определения местоположения, либо службу гео-ограждения. Эти обе службы будут работать, даже если приложение завершено.

5. SLC и Geo Fencing имеют свои собственные оговорки, такие как точность не будет правильной, невозможно настроить интервалы и так далее. Как только вы ознакомитесь с этим документом, вы поймете больше: developer.apple.com/documentation/corelocation /… , developer.apple.com/documentation/corelocation /…

Ответ №1:

Лучший способ получить местоположение пользователя в фоновом режиме — использовать службу определения местоположения с существенными изменениями в соответствии с документацией Apple поместите эту функцию в свой класс:

 func startReceivingSignificantLocationChanges() {
    let authorizationStatus = CLLocationManager.authorizationStatus()
    if authorizationStatus != .authorizedAlways {
    // User has not authorized access to location information.
        return
    } 

    if !CLLocationManager.significantLocationChangeMonitoringAvailable() {
    // The service is not available.
        return
    }
    locationManager.delegate = self
    locationManager.startMonitoringSignificantLocationChanges()
}
  

а также эта функция:

 func locationManager(_ manager: CLLocationManager,  didUpdateLocations 
    locations: [CLLocation]) {
       let lastLocation = locations.last!
           
       // Do something with the location. 
}
  

итак, вам просто нужно вызвать startReceivingSignificantLocationChanges () внутри вашей кнопки, и это вызовет LocationManager (_ manager: CLLocationManager,didUpdateLocations locations: [CLLocation]), так что делайте с местоположением там, что хотите.

Не забудьте запросить разрешение на использование местоположения и прекратить отслеживание с помощью locationManager.stopMonitoringSignificantLocationChanges()

Комментарии:

1. Очень интересно! Итак. Мне не нужно ничего вызывать в делегате приложения? И в соответствии с вашим первым фрагментом. Я не должен ничего делать внутри скобок по адресу: // Пользователь не авторизовал доступ к информации о местоположении и // Служба недоступна.?

2. Хорошо, код добавлен в мое приложение. Только один вопрос: как я могу автоматически обновить его примерно через 10 секунд? или через несколько метров? Я имею в виду, мне нужно, чтобы оно обновлялось очень часто. Я надеюсь, вы меня поняли, спасибо!

3. ‘Мне не нужно ничего вызывать в делегате приложения?’ Нет, в любом случае не для этого ‘Я не должен ничего делать внутри скобок’ вы должны справиться с этим, может быть, вы могли бы показать всплывающее предупреждение пользователю или что-то в этом роде

4. Что вы имеете в виду?

5. В любом случае, я добавил этот код в свое приложение. Но как часто оно обновляется? Каждые 500 метров или около того? Мне нужно обновлять очень часто, например, каждые 20 метров или каждые 10 секунд. Возможно ли это? Спасибо.

Ответ №2:

  1. Получить разрешение на местоположение для Always Allow
  2. Установите для диспетчера местоположений значение allowsBackgroundLocationUpdates true указанным выше способом вы можете получать местоположение при каждом изменении местоположения, сохранять эту информацию и отправлять ее на сервер. Ниже приведен пример кода

    введите LocateMeCallback = (_ location: CLLocation?) -> Void

     /*
     LocationTracker to track the user in while navigating from one place to other and store new locations in locations array.
     **/
    class LocationTracker: NSObject {
    
    static let shared = LocationTracker()
    
    var lastLocation: CLLocation?
    var locations: [CLLocation] = []
    
    var previousLocation: CLLocation?
    var isPreviousIsSameAsCurrent: Bool {
        if let previous = previousLocation, let last = lastLocation {
           return previous == last
        }
        return false
    }
    var isAggressiveModeOn = false
    var locationManager: CLLocationManager = {
       let locationManager = CLLocationManager()
       locationManager.allowsBackgroundLocationUpdates = true
       locationManager.pausesLocationUpdatesAutomatically = true
       locationManager.activityType = .automotiveNavigation
       return locationManager
    }()
    
    var locateMeCallback: LocateMeCallback?
    
    var isCurrentLocationAvailable: Bool {
        if lastLocation != nil {
          return true
        }
        return false
    }
    
    func enableLocationServices() {
        locationManager.delegate = self
        switch CLLocationManager.authorizationStatus() {
        case .notDetermined:
            // Request when-in-use authorization initially
            locationManager.requestWhenInUseAuthorization()
        case .restricted, .denied:
            // Disable location features
            print("Fail permission to get current location of user")
        case .authorizedWhenInUse:
            // Enable basic location features
            enableMyWhenInUseFeatures()
       case .authorizedAlways:
            // Enable any of your app's location features
            enableMyAlwaysFeatures()
       }
    }
    
    func enableMyWhenInUseFeatures() {
       locationManager.startUpdatingLocation()
       locationManager.delegate = self
       escalateLocationServiceAuthorization()
    }
    
    func escalateLocationServiceAuthorization() {
        // Escalate only when the authorization is set to when-in-use
        if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
            locationManager.requestAlwaysAuthorization()
        }
    }
    
    func enableMyAlwaysFeatures() {
       enableCoarseLocationFetch()
       locationManager.startUpdatingLocation()
       locationManager.delegate = self
    }
    
    // Enable Rough Location Fetch
    func enableCoarseLocationFetch() {
       isAggressiveModeOn = false
       locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
       locationManager.distanceFilter = 100
    
    }
    
    // Enable Aggressive Location Fetch
    func enableAggressiveLocationFetch() {
        isAggressiveModeOn = true
        locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        locationManager.distanceFilter = 10
    
    }
    
    func locateMe(callback: @escaping LocateMeCallback) {
        self.locateMeCallback = callback
        if lastLocation == nil {
            enableLocationServices()
        } else {
           callback(lastLocation)
        }
    }
    
    func startTracking() {
         enableLocationServices()
    }
    
    func stopTracking() {
        locationManager.stopUpdatingLocation()
    }
    
    func resetPreviousLocation() {
        previousLocation = nil
    }
    
    private override init() {}
      

    }

     extension LocationTracker: CLLocationManagerDelegate {
    
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    
    
            print(locations)
            guard let location = locations.first else { return }
            guard -location.timestamp.timeIntervalSinceNow < 120, // Validate only location fetched recently
                location.horizontalAccuracy > 0, // Validate Horizontal Accuracy - Ve means Invalid
                location.horizontalAccuracy < 200 // Validate Horizontal Accuracy > 100 M
                else {
                print("invalid location received OR ignore old (cached) updates")
                return
            }
    
            self.locations.append(location)
            lastLocation = location
    
    
            if let activeRide = RideManager.shared.activeRide,
                let _ = AccessTokenHelper.shared.accessToken,
                let activeRideId = activeRide.ride_id,
                let type = activeRide.rideStatusTypeOptional,
                type == .started  {
                //Store Location For A particular Ride after Start
                LocationUpdater.shared.saveInDataBase(rideId: activeRideId, locations: [location])
            }
    
        }
    
        func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
            print(error.localizedDescription)
        }
    
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            enableLocationServices()
        }
    
    }
    
    
    /*
     This class having responsibility of Updating the location on server after n second and update path after n second.
     **/
    class LocationTimer {
        static let time: Double = 30
    }
    
    /*
      class to update locations to server after nth second
     **/
    class LocationUpdater: NSObject {
    
        static let shared = LocationUpdater(n: Double(LocationTimer.time), tracker: LocationTracker.shared)
    
        let n: Double
        private let tracker: LocationTracker
        var timer: Timer! = nil
    
        init(n: Double, tracker: LocationTracker) {
            self.n = n
            self.tracker = tracker
            super.init()
        }
    
        func startUpdater() {
            self.timer?.invalidate()
            self.timer = nil
            self.timer = Timer.scheduledTimer(timeInterval: n, target: self, selector: #selector(updateLocationsToServer), userInfo: nil, repeats: true)
            self.timer.fire()
        }
    
        func stopUpdater() {
            self.timer?.invalidate()
            self.timer = nil
        }
    
        @objc func updateLocationsToServer() {
    // update to server
     }
    }
    
    
    // usage 
    LocationTracker.shared.startTracking()
    LocationUpdater.shared.startUpdater()