Hide Sensitive Information in the iOS App Switcher Snapshot Image

Geoff Hackworth
10 min readFeb 11, 2020

--

Introduction

Some apps store personal or sensitive information. For example, my Medical ID Record app stores a comprehensive record of your medical information. This is very private information which the user does not want to be accidentally seen by other people.

One way in which the data could be exposed is in the snapshot images that iOS shows in the App Switcher. Medical ID Record protects against this by showing a privacy protection view on top of its main interface when the app moves into the background. This alternate view is what iOS snapshots and shows in the App Switcher:

Medical ID Record Hides Sensitive Information in the App Switcher

In this article I’m going to explain how to hide an app’s interface in the snapshot images shown in the App Switcher.

Sample Project

The simplest way to demonstrate the technique is to apply it to a sample iOS project. You should then be able to apply the technique to your own projects.

You can download a completed sample project from GitHub. There are separate commits for each significant step in the process. You can use the git command line or a Git client (I use Atlassian’s SourceTree) to switch to specific commits or to examine the code changes for each step. I also link to the changes for individual commits so you can view the differences on GitHub.

The code in this article is in Swift but I have also created an Objective-C version of the sample project with equivalent code changes.

Create a Single View Application

To make use of iOS 13’s scene-based app lifecycle we need to be using at least Xcode 11. At the time of writing, the current released version of Xcode is 11.3.1.

Open Xcode and select File->New->Project. Choose the iOS Single View App template:

Choose “Next” and give your project a name. I used AppSwitcherPrivacyScreen. Ensure that the language is Swift. We don’t need Core Data, Unit Tests or UI Tests:

Choose “Next” and save your project somewhere. Check “Create Git repository on my Mac” if you want to enable source control.

In my sample project I removed my own Team name from “Signing & Capabilities” tab for the app target. That should allow the sample project to run in the simulator for anyone. You should not do this if you are creating your own sample project. If you are using my sample and have problems, or would like to run on a real device, then you will need to change the project settings to use your own team for the app target:

The first commit in my sample project corresponds to this initial state.

Add a Label to the App

If you run the app you with ⌘R will see an empty white screen. In order to make it clear when the app’s primary interface is visible, add a label to Main.storyboard titled “Super Secret Information”:

  • use Alt-Enter to place a linefeed between each word
  • change the Font to use the Large Title text style
  • select Centre alignment
  • set Lines to 0
  • create constraints to centre the label horizontally and vertically in its containing view

Now when you run either the app you will see the label. The second commit in my sample project has the changes from this step.

Enable Multi-Window Support on iPad

To ensure that the privacy protection sample works fully, enable support for multiple windows on iPad. Select the Info.plist file, expand the Application Scene Manifest section and change Enable Multiple Windows to YES. Of course, you wouldn’t change this in your own app unless you really do want to support multiple windows. I did it in my sample project to demonstrate that the technique is compatible with apps that do support multiple windows.

The third commit in my sample project has the changes from this step.

Add Privacy Protection View

This is where the real work happens and a number of code changes need to be made. We first need to create a PrivacyProtectionViewController which will be used to overlay the app’s main interface when the app moves to the background. This can be as simple or complex as you like but should be readable at the smaller size your app is shown in the App Switcher. For Medical ID Record and the sample project I just show a simple message.

Add PrivacyProtectionViewController

Select File->New->File… (or press ⌘N) to add a file named PrivacyProtectionViewController.swift.

Inheriting from UITableViewController is a simple way to get the standard grouped table view background colour (which matches the appearance of Medical ID Record’s other view controllers).

Lazy block-based initialisation is used to create a privacyLabel property. This is initialised and added to the view when the property is first accessed. The label contains a simple multi-line message “Content hidden to protect your privacy” in a gray text color. I want the text size to adapt with the user’s Dynamic Type settings so configure the font for the Title1 text style and ensure it will automatically adjust if the user change’s their preferred text size.

Rather than vertically centre the label I preferred to have the label centred within the top two thirds of the view. The code in viewWillLayoutSubviews calculates the desired height for the label and uses the divided(atDistance:from:) method on CGRect to split the view controller’s view’s bounds into two frames corresponding to the top two thirds and the bottom third. The top frame (the slice element in the tuple returned by the method) is assigned to the frame of the privacyLabel and the bottom frame (the remainder element in the tuple) is ignored.

Show PrivacyProtectionViewController when Backgrounded

In order to hide the app’s main interface from the iOS App Switcher we need to show our PrivacyProtectionViewController when the scene enters the background and remove it when the scene enters the foreground. Later I will show how to adapt the solution to work with the older application-based lifecycle prior to iOS 13.

Edit SceneDelegate.swift to add calls to hide/show the privacy protection window to the sceneWillEnterForeground(_:) and sceneDidEnterBackground(_:) methods. Add a section to the end of the class to define an optional privacyProtectionWindow property and the methods to show/hide a window which contains our PrivacyProtectionViewController.

A window is used to show the PrivacyProtectionViewController instead of presenting the view controller full screen on top of the app. This simplifies the code because we don’t have to find the most-presented view controller in order to present another view controller. It is also less likely to cause any problems with the code in the main app which might not like suddenly having a new view controller presented in its view hierarchy.

It doesn’t seem to be necessary anymore, but I configure the windowLevel to be one higher than the alert level. I think this may be related to the old UIAlertView class that was deprecated in iOS 9. The more modern UIAlertController seems to be presented as a normal view controller rather than a special window on top of the app.

The fourth commit in my sample project has the changes from this step.

Test the App

If you run the app again, return to the home screen and then open the App Switcher you should see that the privacy message is shown in the app’s snapshot image. However, if you open the App Switcher whilst the app is in the foreground you will still see the main interface. The app doesn’t actually move to the background in this scenario; it only becomes inactive. You have to return the home screen or switch to a different app to cause the app to move to the background. Only then will the task switcher show the privacy message for the app.

This can be seen by running the sample app on iPad and opening multiple windows. In the screensot below I have opened the App Switcher whist the sample app was running in the foreground. The foreground instance of the app still reveals its user interface (top right), but the background instances are showing their privacy protection views.

Moving the calls to show/hide the privacy protection window from sceneDidEnterBackground(_:) and sceneWillEnterForeground(_:) to sceneWillResignActive(_:) and sceneDidBecomeActive(_:) will cause the privacy view to appear in the App Switcher even when opened while the app is in the foreground. However, this also means that the privacy view appears immediately and can be seen as the app animates away, which doesn’t look great. It also appears as soon as you begin to open Notification Center or Control Center. I find that too jarring and so prefer to use the foreground/background state change rather than the active/inactive state change to control when the privacy view is shown.

Support for pre-iOS 13

An app created with Xcode-11 uses the scene-based lifecycle by default. It won’t build if you change the project settings to support earlier iOS versions. There’s a great summary of the changes that are required to make an app created with Xcode 11 work with both the app- and scene-based lifecycles on Geek And Dad’s Blog: Making iOS 12 projects in Xcode 11.

For the sake of completeness I want to quickly discuss how to make the sample project also work on earlier iOS versions where the app delegate needs to be responsible for managing the window and the scene delegate is not used.

I created a pre-ios-13-support branch in my sample project that contains these changes. I didn’t want to over-complicate the master branch and the main part of this article with the code for supporting earlier iOS versions.

Project Settings

When Xcode 11.3.1 created the project, it assigned iOS 13.2 as the iOS Deployment Target. We need to change this to an earlier iOS version. I arbitrarily chose iOS 11 in my sample project. The app target inherits the setting from the project and so we can change its value in the Info tab for the project:

AppDelegate

To work prior to iOS 13, AppDelegate needs to have an optional windowproperty and the UISceneSession lifecycle methods need to be marked as only being available on iOS 13. We also need to add the application lifecycle methods applicationWillEnterForeground(_:) and applicationDidEnterBackground(_:) to call methods to hide/show the privacy protection window.

The window property will be initialised automatically because the sample app uses Main.storyboard for its user interface. Note that the implementation of showPrivacyProtectionWindow in the app delegate is slightly different to the version in the scene delegate: a different initialiser is used to create the UIWindow as we don’t have a window scene prior to iOS 13.

SceneDelegate

Since it is only used on iOS 13 we need to mark SceneDelegate as only being available on iOS 13:

After making these changes, the app should now work on iOS 11 through 13.

Conclusion

This article showed how to protect sensitive information from appearing in the App Switcher’s snapshot image for an app. I use this technique in my Medical ID Record app to prevent medical information being revealed.

Instead of a simple message you might show a blur effect over the app’s content. I tried that when developing Medical ID Record and I thought it looked a bit strange in the App Switcher. Alternatively some sort of splash screen image could be shown instead of or in addition to a label.

A more complex enhancement to this technique could be to show a PIN code or password unlock screen when the app moves to the background and only remove it if the user enters the correct credentials to unlock the app.

Other Articles That You Might Like

I have written comprehensive articles on How to Switch Your iOS App and Scene Delegates for Improved Testing and the View Controller Presentation Changes in iOS 13.

As an iOS developer you might be interested in my long-running series of articles which show how apps adapt to newer device sizes depending on the Xcode version they are built with:

The screenshots in those articles were taken from the iOS simulator running my Adaptivity iOS app. The app helps developers and designers visualise how iOS’s Size Classes and margins for layout, readable content and the safe area look on real devices and how they change with respect to orientation, iPad Slide Over/Split View and Dynamic Type size changes. There are also screens to explore System Colors, System Images and System Materials. There is more information and screenshots on my website about Adaptivity’s features.

You may not have realised that there were iPad Navigation Bar and Toolbar Height Changes in iOS 12.

I have also written about External Display Support on iOS and Working with Multiple Versions of Xcode.

If you found any of these articles helpful then please take a look at my apps in the iOS App Store to see if there’s anything that you’d like to download (especially the paid ones 😀).

If you work with a lot of Xcode projects you might like my Mac Menu Bar utility XcLauncher. It’s like having browser bookmarks for your favorite Xcode projects, workspaces and playgrounds. There is more information on my website about XcLauncher’s features.

--

--

Geoff Hackworth
Geoff Hackworth

Written by Geoff Hackworth

Independent and freelance software developer for iPhone, iPad and Mac