UWP apps have been designed to be more secure and explicit about what they are allowed to do on the user’s device so that she is always in control. Hand in hand with that comes the fact that user can manually grant or deny individual permissions in the system Settings. However, this presents a challenge for developers – how can we make sure we have access to a capability when the user can deny it any time? That’s what I would like to show you in this article.
What can go wrong?
Let’s illustrate the issue on a straightforward example. Your app declares the Pictures library capability in Package.appxmanifest
.

In the code you usually access the library and expect to work with it without issues.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var library = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures); |
Everything is bright and shiny until the user explicitly denies the permission in Settings -> Apps -> [App name] -> Advanced settings -> App permissions:

When we relaunch the app and try to access Pictures library we end up with an ugly UnauthorizedAccessException
:

The solution
We will wrap access to the capability in a helper method, which will have appropriate logic to make sure our app handles the situation gracefully.
The first key element of our fix is not letting the app just crash when we lack the capability permission. A simple try...catch
will do the trick:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private async Task<StorageLibrary> TryAccessLibraryAsync(KnownLibraryId library) | |
{ | |
try | |
{ | |
return await StorageLibrary.GetLibraryAsync(library); | |
} | |
catch (UnauthorizedAccessException) | |
{ | |
… | |
} | |
} |
Once we are in the catch
statement, we know we have been denied access, and we need the user to grant us the required permission again. We can inform her via a MessageDialog
:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//explain the issue | |
MessageDialog requestPermissionDialog = | |
new MessageDialog($"The app needs to access the {library}. " + | |
"Press OK to open system settings and give this app permission. " + | |
"If the app closes, reopen it afterwards. " + | |
"If you Cancel, the app will have limited functionality only."); | |
//setup dialog commands | |
var okCommand = new UICommand("OK"); | |
requestPermissionDialog.Commands.Add(okCommand); | |
var cancelCommand = new UICommand("Cancel"); | |
requestPermissionDialog.Commands.Add(cancelCommand); | |
requestPermissionDialog.DefaultCommandIndex = 0; | |
requestPermissionDialog.CancelCommandIndex = 1; | |
//display to user | |
var requestPermissionResult = await requestPermissionDialog.ShowAsync(); |
When the user chooses Cancel
, we are merely returning null
. Depending on how much our app relies on the permission we can decide to either keep the app running with limited functionality or just terminate it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if (requestPermissionResult == cancelCommand) | |
{ | |
//user chose to Cancel, app will not have permission | |
return null; | |
} |
In case the user presses OK
, we will navigate him to the advanced settings page for our app in the system Settings and give us the required permission using the special ms-settings:appsfeatures-app
(see Docs).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//open app settings to allow users to give us permission | |
await Launcher.LaunchUriAsync(new Uri("ms-settings:appsfeatures-app")); | |
//confirmation dialog to retry | |
var confirmationDialog = new MessageDialog($"Please give this app the {library} permission " + | |
"in the Settings app which has now opened."); | |
confirmationDialog.Commands.Add(okCommand); | |
await confirmationDialog.ShowAsync(); |
This specific example handles check for library capability, but you can analogously implement it for other permissions as well.
Warning – don’t forget about user’s data
There is one catch with the presented solution – changing application permissions cannot be done at runtime. When the user modifies them in any way, the app will get terminated. That is the reason why the MessageDialog
informs the user to reopen the app after granting the required permission. The key takeaway for your app is then the fact that you should always make sure all user data are safely saved before accessing a permission-bound API. This way you will be able to restore the user’s current action and will not break her flow when the app is reopened.
Alternatively, you can check for all permissions which are really required for your app to run properly at startup. The advantage of this is the fact that the user doesn’t have any work in progress at that point, so it is perfectly safe to close.
Source code
Full code of our helper method looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private async Task<StorageLibrary> TryAccessLibraryAsync(KnownLibraryId library) | |
{ | |
try | |
{ | |
return await StorageLibrary.GetLibraryAsync(library); | |
} | |
catch (UnauthorizedAccessException) | |
{ | |
//explain the issue | |
MessageDialog requestPermissionDialog = | |
new MessageDialog( | |
$"The app needs to access the {library}. " + | |
"Press OK to open system settings and give this app permission. " + | |
"If the app closes, reopen it afterwards. " + | |
"If you Cancel, the app will have limited functionality only."); | |
var okCommand = new UICommand("OK"); | |
requestPermissionDialog.Commands.Add(okCommand); | |
var cancelCommand = new UICommand("Cancel"); | |
requestPermissionDialog.Commands.Add(cancelCommand); | |
requestPermissionDialog.DefaultCommandIndex = 0; | |
requestPermissionDialog.CancelCommandIndex = 1; | |
var requestPermissionResult = await requestPermissionDialog.ShowAsync(); | |
if (requestPermissionResult == cancelCommand) | |
{ | |
//user chose to Cancel, app will not have permission | |
return null; | |
} | |
//open app settings to allow users to give us permission | |
await Launcher.LaunchUriAsync(new Uri("ms-settings:appsfeatures-app")); | |
//confirmation dialog to retry | |
var confirmationDialog = new MessageDialog( | |
$"Please give this app the {library} permission " + | |
"in the Settings app which has now opened."); | |
confirmationDialog.Commands.Add(okCommand); | |
await confirmationDialog.ShowAsync(); | |
} | |
return null; | |
} |
You can find the example solution for this article on my GitHub.
Summary
We have seen how we can gracefully handle the situation when user has denied us permission we require for our app to run properly. This way we offer excellent user experience and can even specifically explain why the given API is needed.