How to provide the best location experience for iOS and survive
Updated: Oct 21, 2022
A brief guide from a verified hacker.
A brief overview of how things came to be
One of our clients wants us to build a running app in Flutter (to cover two bases in one shot) with a special social twist. It is called RunGrateful and as it says on the can you run, but while being grateful. Good news is that you can already download it from the stores: IOS the Mighty and Sir Android. We even went as far as to implement our own tracking page, so that we depend on third parties as little as we possibly can. This was all well and good until, however, we realized there is a little something called background mode and that it, on its own, introduces a myriad of issues and interesting exercises in patience. That being said, the main challenge we had to face was formulated as follows:
“We have observed that the app now shows two dialogs, regarding location, and another running app shows only one. We need to match the other running app ASAP.”
Aw snap! Here we go again! Start the digging machine - now…
As developers normally do, I began by poking this with a stick to see what happens.
Nothing happened. Depression and panic kick in. Why? Why? Why?
A lifesaver on the horizon?
In order to have a good, battery conscious and reliable solution for tracking location in the background, we prefer TransistorSoft solutions. The packages are extremely well documented, the issues are minimal, and the main man behind it is really, really knowledgeable and well versed in the world of background location tracking.
In this article (my hope to get off the hook) the man himself (Chris Scott) explains in detail exactly what happens when the iOS version is above 13 and if Android is all the way up to Q (won’t they run out of letters?? I wonder, but anyway. Moving on!). Long story short, he explains that Apple wants you to provide an explicit permission for the app to track the location in the background. Hence the “Allow while in use” state is not enough and the second you track in the background you will get a system dialog to change the permission to “Allow always”. This, at the time, seemed like the end of the road. That must be it, right. We cannot force iOS to behave differently. This is how the system does it. Our hands are tied. You catch my drift. I can’t do anything about it and iOS will stay like that.
However, how does one tackle an obvious argument, that goes like this: “OK, that may be so, but this other app does it. We install it fresh off the store, it requires one permission (while in use) and does its thing in the background. It does not even have the “always allow” option in sight.” How do you, as a developer, handle this? Answer is you can’t. Client has a valid argument. Bugger!
Well if all else fails you try to summon all your best, and most powerful, developer friends, so you can form an Exodia of sorts and demolish this issue at once. Here I called upon my two best sources in the field of iOS development. One is my wife @Gabriela Yonkova, the other is my great colleague Yordan Kenarov.
Spoiler alert - it worked! Asking for help when you are stuck always works.
So with the super team in place, I went to work. Long, tedious process of trial and error. Banging my head against the wall and all of that good stuff. Here is what we tried out:
Write a native implementation in Swift - Failed;
Remove permissions from the Plist file - Failed;
Use other packages for location - Failed;
Use lower versions of some packages - Failed;
[100% credit to Gabi for this] Try lowering the deployment target - BINGO.
The nitty gritty
So what is the trick then? Follow these steps and you will have it in your app as well!
1. First go to the iOS project named Runner (or whatever it is called if you renamed Runner). Under the general tab find the section “Deployment Info”. In it there is a drop down for the deployment target. Set that to 10. You will see why and thank me later.
2. Go to the Podfile and set the deployment target to 10 there too.
Usually the line sits on top of the file.
3. With these two major changes made, you will need to go into the ios folder and make sure you reinstall the pods. So here is an excerpt from the developer-pain-file.md (ugh, I mean README.md):
If you see pod install failure (mainly due to higher target required) like so `In Podfile: firebase_app_check (from .symlinks/plugins/firebase_app_check/ios) was resolved to 0.0.6-4, which depends on Firebase/AppCheck (~> 8.10.0) Specs satisfying the Firebase/AppCheck (~> 8.10.0) dependency were found, but they required a higher minimum deployment target.` you need to perform this sequence of actions. -- Go to the ios folder (cd ios) and do `rm -rf Podfile.lock`. -- run `sudo arch -x86_64 gem install ffi` <- that might err out if it is already there, but you need it as it may not come with the initial setup. -- finally run `arch -x86_64 pod install --repo-update`. If the latest command does not work, try running the clean pod install --repo-update.
If you have a hard time fixing this on an Intel-powered Mac, drop us a line, we can look at it together.
4. The last piece to the puzzle. Rig up the Info.plist file.
Look for the keyword location in the file. These are what you need:
That is it. You made it! This is our solution for you. Can you dig it?!?!
From what we have observed, this solution probably has its time limited. If the good people at Apple decide to deprecate iOS 10 as deployment target, or if a package you use cannot stand to be involved with the plebian iOS 10, then we/you are out of luck, but then again we will see massive development teams trying to solve the issue or just move along. For now we can call it a day and enjoy this small paradise.
Hey you made it to the end. How cool is that!
If you liked this article and what I suggested worked for you, drop us a line. If it did not work, then do so too. We at @LoomingTech love sharing knowledge and helping the great programming community, as we believe knowledge makes the World a better place.
Hackerman Peter out.
Happy coding everybody!
Author: Peter Yonkov