How to prevent screen Recording in iOS app - ios

Is there any way to disable the screen recording like ZEE5 or Netfix? or is it possible through a configuration profile? or any third-party library is available? My application not using DRM protected video.
I am aware of UIScreen.capturedDidChange observer but it will give info for recording start/stop.
NotificationCenter.default.addObserver(self, selector: #selector(preventScreenRecording), name: NSNotification.Name.UIScreenCapturedDidChange, object: nil)
func preventScreenRecording() -> void {
if (#available(iOS 11.0, *)) {
var isCaptured = UIScreen.main.isCaptured
if (isCaptured) {
self.blockView.hidden = false
}
else {
self.blockView.hidden = true
}
}
}

Related

iOS Face ID/Touch ID Lock Screen Conflict in willEnterForegroundNotification

I have code that puts a blurred UIVisualEffectView over the current window when the didEnterBackgroundNotification notification is fired. It then tries to reverse this when willEnterForegroundNotification is fired (simplified for easy reading):
class AutoBlur {
init() {
NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appDidEnterBackground() {
blur()
}
#objc func appWillEnterForeground() {
guard UserState.isScreenLockEnabled else {
unblur()
return
}
let authContext = LAContext()
authContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "test") { success, error in
DispatchQueue.main.async {
guard success else {
return
}
unblur()
}
}
}
//...
}
While this approach generally works, when testing the Face ID functionality of app I found the following anomaly:
Open the app
Turn the screen off on the device
Wake the device
Swipe up on the device's lock-screen
Expected results: The device should unlock, and then my app should run its Face ID evaluate code to un-blur the view.
Actual results: The swipe-up action is undercut by my app's Face ID evaluation code. It prevents the user from being able to swipe up on their lock-screen, effectively locking them out of their device.
Is there some state I should be monitoring or some other event that's safer to respond to for triggering my Face ID evaluation?

How to understand if device screen is being recorded in IOS 11

I have an application that it has copyrighted content. I don't want users to record it. If they start recording screen I want my app to catch this. What is the function to catch if the screen is being recorded?
I don't want to prevent, I want to understand and catch it.
Note: Some answers indicate solutions including AirPlay and Mirroring. What I want is to only catch screen recording either started before or during app. I want to allow users to use AirPlay and Mirroring.
SWIFT 5.0
To prevent screen recording I created a separate class ScreenRecordingProtoector
witch looks like that:
final class ScreenRecordingProtoector {
private var window: UIWindow? {
if #available(iOS 13.0, *) {
return (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window
}
return (UIApplication.shared.delegate as? AppDelegate)?.window
}
func startPreventing() {
NotificationCenter.default.addObserver(self, selector: #selector(preventScreenShoot), name: UIScreen.capturedDidChangeNotification, object: nil)
}
#objc private func preventScreenShoot() {
if #available(iOS 13.0, *) {
if UIScreen.main.isCaptured {
window?.isHidden = true
} else {
window?.isHidden = false
}
}
}
// MARK: - Deinit
deinit {
NotificationCenter.default.removeObserver(self)
}
}
then simply I create a variable in AppDelegate above didFinishLaunchingWithOptions let screenRecordingProtoector = ScreenRecordingProtoector() and then I call inside didFinishLaunchingWithOptions screenRecordingProtoector.startPreventing()
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private let preventService = PreventCapturingService()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
preventService.startPreventScreenRecording()
return true
}
}
It works fine for me in Swift 5.0
You can check if the screen is being recorded with this
UIScreen.main.isCaptured
isCaptured will be true if the screen is being recorded, mirrored or sent over AirPlay.
I added a check to see if mirroring is the case; if not, I show an alert. Solved my issue for the time being. This is not a perfect solution and doesn't solve all the problems, but just one extra step towards it
if (#available(iOS 11.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleScreenCaptureChange)
name:UIScreenCapturedDidChangeNotification object:nil];
}
-(void)handleScreenCaptureChange
{
UIScreen *aScreen;
BOOL isMainScreenMirrored = NO;
NSArray *screens = [UIScreen screens];
for (aScreen in screens)
{
if ([aScreen respondsToSelector:#selector(mirroredScreen)]
&& [aScreen mirroredScreen] == [UIScreen mainScreen])
{
// The main screen is being mirrored.
isMainScreenMirrored = YES;
}
}
if (#available(iOS 11.0, *)) {
BOOL isCaptured = [[UIScreen mainScreen] isCaptured];
if (isCaptured && !isMainScreenMirrored) {
//Display Alert : "Please turn off screen recording and play again."
}
}
}
You can also detect if user has taken screenshot by listening to UIApplicationUserDidTakeScreenshotNotification.
You can use that in below way-
let main = OperationQueue.main
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot,
object: nil,
queue: mainQueue,
using: { notification in
// Warn user
})
And
UIScreen.main.isCaptured
This can also be used to detect recording. I haven't used this but mostly you will have to do KVO for this.
Check the screen is being recorded (for Objective C)
Put the below code on your app delegate and did finish launching and did become active.
BOOL isCaptured = [[UIScreen mainScreen]isCaptured];
Use UIScreen.main.isCaptured to get current status. Than, you need to listen to new notification UIScreenCapturedDidChangeNotification. When recording status will change, you need to do some action (for example stop playback ..etc.). Remember this is iOS11+ only.
Code fragments:
if (#available(iOS 11.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(capturedChange)
name:UIScreenCapturedDidChangeNotification object:nil];
}
- (void)capturedChange {
if (#available(iOS 11.0, *)) {
NSLog(#"Recording Status: %s", [UIScreen mainScreen].isCaptured ? "true" : "false");
//do something
}
}
In order to prevent the AirPlay and Mirroring you can take a look at the length of the UIScreen.Screens and check whether there is more than one screen, in case of airplay there will be 2 screens.
So for example when the airplay is switched on, the capturedDidChangeNotification will be raised and there you can observe, if UIScreen.MainScreen.Captured == true and also if UIScreen.Screens.Lenght > 1 then it's definitely airplay or mirror.

Getting MPRemoteCommandCenter.shared() to work in tvOS

Ok, maybe I missed something here. I want to use the black remote with my app and got this code essentially from the WWDC 2017 talk on the issue. It says ...
Consistent and intuitive control of media playback is key to many apps on tvOS, and proper use and configuration of MPNowPlayingInfoCenter and MPRemoteCommandCenter are critical to delivering a great user experience. Dive deeper into these frameworks and learn how to ensure a seamless experience whether your app is being controlled using Siri, the Siri Remote, or the iOS Remote app.
So I added these lines to viewDidLoad of my tvOS app and well they do nothing basically?
var commandCenter = MPRemoteCommandCenter.shared()
override func viewDidLoad() {
super.viewDidLoad()
commandCenter.playCommand.isEnabled = true
commandCenter.pauseCommand.isEnabled = true
commandCenter.playCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
print("You Pressed play")
return .success
}
commandCenter.pauseCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
print("You Pressed pause")
return .success
}
}
I run the app, and try the play/pause button on the black remote and nothing is printed to the debugging console? Also added some code the plist related to background mode...Should this work or did I miss the point here somewhere?
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>external-accessory</string>
</array>
The commands in MPRemoteCommandCenter aren't triggered by the Siri Remote when your app is in the foreground. To get events from the remote when you're in the foreground, use UIGestureRecognizer like you're probably already used to.
These commands in MPRemoteCommandCenter are for other ways the system may want to interact with your playback, such as:
Your app is playing audio in the background, and the user presses the pause button on the remote: your app I'll be asked to pause playback.
The user is using the TV Remote app for iOS and is using that app's playback control screen.
Posted the question to Apple support; who pointed me in the right direction, need to use the GCMicroGamepad controller or its related GameKit frameworks. Than found a 2015 example posted by blauzahn who most certainly deserves the credit really for this post. Here is his code slightly modified for Swift 3.0, ios 10.x
import GameController
..
var gamePad: GCMicroGamepad? = nil
NotificationCenter.default.addObserver(self,
selector: #selector(gameControllerDidConnect),
name: NSNotification.Name.GCControllerDidConnect,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(gameControllerDidDisconnect),
name: NSNotification.Name.GCControllerDidDisconnect,
object: nil)
func gameControllerDidConnect(notification : NSNotification) {
if let controller = notification.object as? GCController {
if let mGPad = controller.microGamepad {
// Some setup
gamePad = mGPad
gamePad!.allowsRotation = true
gamePad!.reportsAbsoluteDpadValues = true
print("MicroGamePad connected...")
// Add valueChangedHandler for each control element
if gamePad?.buttonA.isPressed == true {
print("button A pressed")
}
if gamePad?.buttonX.isPressed == true {
print("button X pressed")
}
gamePad!.dpad.valueChangedHandler = { (dpad: GCControllerDirectionPad, xValue: Float, yValue: Float) -> Void in
print("dpad xValue = \(xValue), yValue = \(yValue)")
}
gamePad!.buttonA.valueChangedHandler = { (buttonA: GCControllerButtonInput, value:Float, pressed:Bool) -> Void in
print("\(buttonA)")
}
gamePad!.buttonX.valueChangedHandler = { (buttonX: GCControllerButtonInput, value:Float, pressed:Bool) -> Void in
print("\(buttonX)")
}
}
}
}
// Game controller disconnected
func gameControllerDidDisconnect(notification : NSNotification) {
if let controller = notification.object as? GCController {
if controller.microGamepad != nil {
self.gamePad = nil
print("MicroGamePad disconnected...")
}
}
}

iOS - how to detect network change when app is in background

I am developing an iOS app and I want to detect when the user connects/disconnects to Wifi even when the app is closed. I did a lot of research, but still didn't find any solutions to this problem.
Can someone point me in the general direction of how to do this?
It is not possible to detect network connection after the application is closed. The process is shut down and your code cannot be executed.
Check iOS application lifecycle for more details
Maybe you should considered option of application, that can run in background. This is of course possible in iOS, you need Capability type:Background Modes. Then you can check if wifi is availible.
For some unspecified time you can attain this by using Background Fetch.
override init() {
super.init()
initializeBackgroundTask()
NotificationCenter.default.addObserver(self, selector: #selector(networkHasChanged(notification:)), name: NSNotification.Name.reachabilityChanged, object: nil)
}
func networkHasChanged(notification : NSNotification) {
if let reachability = notification.object as? Reachability {
// Do whatever you want to do!!!
}
}
func initializeBackgroundTask() {
if bgTask == UIBackgroundTaskInvalid {
bgTask = UIApplication.shared.beginBackgroundTask(withName: "CheckNetworkStatus", expirationHandler: {
self.endBackgroundTask()
})
}
}
func endBackgroundTask() {
if deepLinkString == nil {
if (self.bgTask != UIBackgroundTaskInvalid) {
UIApplication.shared.endBackgroundTask(self.bgTask)
self.bgTask = UIBackgroundTaskInvalid
}
}
}
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.reachabilityChanged, object: nil)
}
Also try not to initialise background task if not in use.

how to Detect change in Photo asset using ALAssetsLibraryChangedNotification when app is running in background

i used ALAssetsLibraryChangedNotification to detect change in photo asset.
it will work fine but the problem is i need to open the app every time to detect the change. How i can do this in background?
here is my code
override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "assetChange:", name: ALAssetsLibraryChangedNotification, object: assetsLibrary)
}
func assetChange(notification: NSNotification) {
if var info:NSDictionary = notification.userInfo {
if((info.objectForKey(ALAssetLibraryUpdatedAssetsKey)) != nil){
var set:NSSet = info.objectForKey(ALAssetLibraryUpdatedAssetsKey) as NSSet
}
}
}

Resources