-1

I am building a Country Tracker app. User installs the app, through several APIs app get user country and saves it in the Core Data. The problem is that BGTask is unreliable. For the last 3 days I didn't see it execute once. Is it possible to do that differently?

I want to add a third solution to my

BGTaskScheduler.shared.register(forTaskWithIdentifier: "///", using: nil) { task in
 self.handleLocationFetch(task: task as! BGAppRefreshTask) }
private func scheduleAppRefresh() {
 let request = BGAppRefreshTaskRequest(identifier: "///")
 request.earliestBeginDate = Date(timeIntervalSinceNow: 1800) // 30 minutes
 do {
 try BGTaskScheduler.shared.submit(request)
 } catch {
 print("Could not schedule app refresh: \(error)")
 }
 }

and

init() {
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.pinborg.countryTracker.locationfetch", using: nil) { task in
 self.handleLocationFetch(task: task as! BGAppRefreshTask) }
}

, the one that will always work, no matter what. Thank you in advance.

EDIT:

 private func handleLocationFetch(task: BGAppRefreshTask) {
 scheduleAppRefresh()
 let locationManager = LocationManager()
 let entryStore = EntryStore()
 let openCageVM = OpenCageVM()
 
 task.expirationHandler = {
 locationManager.locationManager.stopMonitoringSignificantLocationChanges()
 }
 
 locationManager.locationManager.startMonitoringSignificantLocationChanges()
 
 // Fetch the "app storage" toggle setting from UserDefaults (or another storage)
 let allowTrackingDuringFlight = UserDefaults.standard.bool(forKey: "allowTrackingDuringFlight")
 
 DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
 guard let location = locationManager.locationManager.location else {
 print("Failed to fetch location.")
 task.setTaskCompleted(success: false)
 return
 }
 
 let altitude = location.altitude
 
 // If app storage is enabled, check the altitude; otherwise, ignore altitude
 if !allowTrackingDuringFlight && altitude > 5000 {
 print("Location altitude is too high. Task aborted.")
 task.setTaskCompleted(success: false)
 return
 }
 
 let lat = location.coordinate.latitude
 let lng = location.coordinate.longitude
 
 // Call the API to fetch location information
 openCageVM.fetchLocationInfo(lat: lat, lng: lng) {
 print("Location info fetched")
 
 self.sendLocationNotification(location: location)
 
 // Use fetched data to add an entry to the store
 entryStore.addNewEntryHistory(
 date: Date(),
 iso2: openCageVM.info?.results?.first?.components?.iso31661Alpha2 ?? "",
 iso3: openCageVM.info?.results?.first?.components?.iso31661Alpha3 ?? "",
 name: openCageVM.info?.results?.first?.components?.country ?? ""
 )
 
 task.setTaskCompleted(success: true)
 }
 })
 
 task.setTaskCompleted(success: true)
 }
asked Sep 16, 2024 at 14:30
4
  • No. This is by design not possible. For very specific use cases there are solutions (usually involving things like Bluetooth devices or VoIP clients). What is your use case? We may be able to help you find a solution to that, but this intentionally cannot be done in the way you're trying to do it. Commented Sep 16, 2024 at 14:46
  • I'll break it down for you: when user opens the app, func starts: get user location with GPS -> send it to api -> receive info from it -> add entry with the name of country in Core Data. Added the func to edit. Currently I have a working solution, user opens app - func fires. I just wanted to know if it's possible to do without even opening the app. Commented Sep 16, 2024 at 15:19
  • startMonitoringSignificantLocationChanges is the right tool. You don't need a BGAppRefreshTask. You can get significant location changes automatically in the background; it'll even launch your app as long as the user hasn't force-quit it (force-quit is a signal from the user that they do not want your app to keep operating). You'll need to add the location updates capability: developer.apple.com/documentation/xcode/… Just don't disable signifiant location updates. Turn it on and leave it on. Commented Sep 16, 2024 at 16:09
  • See the docs for details on how this service works: developer.apple.com/documentation/corelocation/… This service is optimized for battery life, and can run indefinitely in the background. Commented Sep 16, 2024 at 16:11

1 Answer 1

1

The short answer to your question is NO. Apple specifically forbids apps from running indefinitely in the background, with a small number of exceptions, as mentioned by Rob Napier.

Given that your use-case is to track a user's travels between countries, you should use Core Location's startMonitoringSignificantLocationChanges, again as per Rob Napier's suggestion in his comment.

The GPS is extremely power-hungry, and keeping it "lit" would chew through the user's battery in short order. The significant location change API lets the system limit power use by only turning on the GPS occasionally, and only invoking your app's code when it detects that the user has moved by a meaningful distance. (The docs say you'll get notified when the user moves 500 meters from their previous location, and that you should not expect to be called more frequently than every 5 minutes. I'm not sure how to square those 2 things, since somebody in a car/bus/train would certainly travel more than 500 meters in 5 minutes.)

answered Sep 16, 2024 at 17:10
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.