Hello community,
A while ago, I was giving a workshop and a question came up regarding sensitive data being displayed when an app enters the background and becomes visible in the App Switcher. I thought this could be interesting for some of you out there so I am writing this small blog post which will show different ways of hiding sensitive data in the App Switcher.
First of all, if we talk about the privacy and security of enterprise apps or any apps which contain sensitive data you have to find a way to make sure the user's data is always secured. One way would be to let the user do an authentication every time the app enters the foreground, which is not really what we want right. Forcing the user to authenticate constantly is really cumbersome and not great when it comes to User Experience.
Using SAP Mobile Services you can change the client policies of your app to have a kind of "session timeout" after some time. This could be an interesting way to make sure that if the user hasn't used the app in a while that a re-authentication is necessary. The authentication, in that case, would be re-entering the app's passcode set by the user during the onboarding process.
But now what happens with the screenshots that have been taken by the OS when switching apps?
So there are several ways of preventing to have sensitive user data visible in the app's screenshot in the App Switcher. Let's see what we can do:
Hide sensitive data before going into the background
Before your app enters the background you can go and adjust the UI to delete or simply not show the user's sensitive. Depending on your UI that can be a bit cumbersome with the state restoration because you have to fill back in the stripped out UI when the app gets active again.
There are two ways to know when the app is about to switch from active to inactive: you can implement the
applicationWillResignActive(_:) method in the AppDelegate of your app, another way would be registering to the
UIApplication.willResignActiveNotification. The latter approach allows it to do that at any point in your app code.
override func viewDidLoad() {
super.viewDidLoad()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(doSomething), name: UIApplication.willResignActiveNotification, object: nil)
}
@objc func doSomething() {
print("Remove user sensitive data!")
}
The downside of manipulating the data or the UI when going into the background is that in the state restoration of the app you have to make sure that everything is back to the original state. That can become fairly cumbersome and complicated if you have screens with a lot of user input fields.
Blur the App's Screenshot for the App Switcher
Another approach that is less work for the developer but equally good in my personal opinion is to just blur out the app's screenshot which gets displayed in the App Switcher.
To do so you can implement the blurring and take/replace the screenshot in the App Delegate in just a few lines of code.
Let's see how that works:
In the App Delegate, you can utilize the
applicationWillResignActive(_:) method to take a screenshot, apply a blur effect and set it as current view in the app's window instance.
First, let's see how to take a screenshot of the currently displayed view:
/// This method takes a screenshot of the currently shown view.
/// Method returns nil if screenshot can't be taken.
func createScreenshotOfCurrentContext() -> UIImage? {
UIGraphicsBeginImageContext(self.window?.screen.bounds.size ?? CGSize())
guard let currentContext = UIGraphicsGetCurrentContext() else {
return nil
}
self.window?.layer.render(in: currentContext)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Now applying a Gaussian blur on an image:
/// This method applies a Gaussian blur on a given UIImage.
/// - Parameters:
/// - image: The image where the Gaussian blur will be applied on
/// - blurFactor: How high should the blur effect be
func applyGaussianBlur(on image: UIImage, withBlurFactor blurFactor : CGFloat) -> UIImage? {
guard let inputImage = CIImage(image: image) else {
return nil
}
// Add a comment where to find documentation for that
let gaussianFilter = CIFilter(name: "CIGaussianBlur")
gaussianFilter?.setValue(inputImage, forKey: kCIInputImageKey)
gaussianFilter?.setValue(blurFactor, forKey: kCIInputRadiusKey)
guard let outputImage = gaussianFilter?.outputImage else {
return nil
}
return UIImage(ciImage: outputImage)
}
Put it all together:
Now that you have a method for taking a screenshot and one for applying the Gaussian blur on an image we can now put it all together.
Go ahead and implement the
applicationWillResignActive(_:) method in the App Delegate class.
func applicationWillResignActive(_ application: UIApplication) { }
This method will help us to do all the necessary work for achieving our goal, but what happens when the user goes back into the app? - Currently, the screen will still be blurred, that's because we're not setting the blurred screenshot directly in the App Switcher, we're setting it as our currently displayed view and the App Switcher takes a screenshot of that.
To undo our small hack we'll implement the
applicationDidBecomeActive(_:) method in the App Delegate class.
func applicationDidBecomeActive(_ application: UIApplication) { }
Implement logic for applicationWillResignActive(_:) method:
Alright we have the
applicationWillResignActive(_:) defined and now we have to implement some logic to make the magic happen:
func applicationWillResignActive(_ application: UIApplication) {
// First apply the Gaussian blur on the screenshot of the current view.
let blurredImage = applyGaussianBlur(on: createScreenshotOfCurrentContext() ?? UIImage(), withBlurFactor: 4.5)
// Create the UIImageView for the blurred screenshot.
appSwitcherView = UIImageView(image: blurredImage)
// Set it as the current screen
self.window?.addSubview(appSwitcherView!)
}
Okay let's see what happens here; First, we apply the Gaussian blur to a screenshot of the current context by calling the
createScreenshotOfCurrentContext().
With the blurred image in hand, we create an UIImageView next containing the blurred image. That UIImageView is defined as a class property because we will need it later for removal when the app did become active.
var appSwitcherView: UIView?
Lastly, add the just created view as the window's current subview. Now if the App Switcher will take a screenshot of the blurred screen instead of the normal one.
Implement logic for applicationDidBecomeActive(_:) method:
As described before the current app's screen view is the blurred screenshot. To remove that view and show the original view we can simply remove that view from its super view.
func applicationDidBecomeActive(_ application: UIApplication) {
appSwitcherView?.removeFromSuperview()
}
With that, all is good and should work now.
Closing Words
However you decide if hiding data here is necessary or not, trying out both ways is a fun exercise to do.
Always keep in mind: The user's privacy and user experience should always be the first priority for us!
With that, Happy Coding!