Asynchronous operations are commonplace in modern software. In C# (C-Sharp), opening a file can be asynchronous with the code asking the operating system to open the file and the operating system responding with “ok, I’ll do that. You can go do something while you wait.” This type of operation can be infuriating because a user might have tapped or clicked on a button in the UI and if your function to handle that button returns before the entire button operation is completed, the user might tap that button again. Imagine what happens when they accidentally bounce their finger on the button and the second tap event happens while the system is still processing the request to open a file. It’s catastrophic. It also then requires that the programmer write code to disable the entire UI until the file is opened and the operation for the button tap or click is completed, at which time the UI is then fully enabled again. I never write that code to disable the UI and I usually write code that just waits for the file opeation to complete. Sometimes this is not possible and that is the issue I’m writing about.
I have this asynchronous code problem right now. The user can tap a button to create a route between a set of locations (see the Traveling Salesman Problem) but the app might not have permission to use user location data due to the user having set the pemissions to “denied” in the operatring system settings. Consider the case where the code should check the permissions again while the app is running (since the user is specifically asking for location information to be shown) and the user has changed the settings in the operating system to “ask each time.” In this case, the code will check the permissions and get a “denied” result. After getting denied, the system is asked to get permission and the issues of asynchronous code start to multiply.
I solved this problem in the last few hours after starting to write this post. I solved it by first creating a generic completion handler protocol. The code is designed so that all results are handled in the completion handlers for all all operations. I set up the onRequestPermissionResult function in the activity to use a completion handler so even that stupid OS way of notifying the app of the permission result is handled in a more sensible way – A callback on the activity is confusing and moves the code for the result away form the code asking for the result and the system shoud have used some sort of completion handler instead. It makes it difficult to handles this sort of operation across multiple activities and in places where the current activity is not stored other than to deal with this sort of clumsy issue. Anyhow, the code that wants to act on the button tap was requried to set up a completion handler for the location data request. If the location is available, that handler is called immediately with the result of the request. If the premission was asked of the user then the completion handler for getting permission calls the completion handler for the location data request.
A few things came up during this coding session. One issue I found was that I could not successfully ask for permission to use the location using the startup activity of the app; The current activity is needed in order to get the system to show the box asking for permission to use the location. I also had to make sure to dismiss the alert dialog that contains the button before asking for permission since two dialogs cannot be shown at the same time!
I’m almost done. I’m doing testing to make sure that if the app has no permission and I detect that, that the user is prompted, by the system though my asking for permission in the app, to allow the location to get used.
I forgot one other issue that came up that might be important to others: My app has a service that continually logs the location changes that the system reports. I initially set up my code to start that service when pemissions is obtaied to use the location data, and then the code gets the current location from that service object. But this doesn’t work right in the new async code since the service start function returns before the service is fully functional. I had to add code that asks the system for the current location directly and not wait for a notification callback that the location has changed. The location service class is handy for logging the location each time the location changes but it wasn’t designed quite right for getting the current location for immediate use. I’ll improve it to have static functions for operations that don’t require any variables that are kept in that service object.