ScreenShot Detection in whole App from single Page in ios - ios

Screenshot prevention is not possible that i Understand but we can do the same as snapchatdoes,We can Detect it.
My application consist of more than 10+ controller so on every page addobserver is bit tedious so want the solution if i can place it on appdelegate/Scenedelegate or any other so that on whichever controller screenshot Captured i l be notified.Placing is the main required thing here
Something like reachability which works in similar way for network detection
Here is the Code :
func detectScreenShot(action: #escaping () -> ()) {
UIScreen.main.addObserver(self, forKeyPath: "captured", options: .new, context: nil)
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
print(notification)
action()
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if (keyPath == "captured") {
let isCaptured = UIScreen.main.isCaptured
print(isCaptured)
}
}

I think you can implement this by making a BaseViewController,And all the other View Controllers should be inherited by the BaseViewController,So u just have to observe the screenshot detection in BaseViewController and you don't have to write the code on every ViewController

Related

Why is it ok to remove the observer from a different thread than its original thread in Swift?

Why is it ok to remove the observer from a different thread than its original thread in Swift?
I think we may need to dig inside the Swift source code, but I am really curious about this.
Here is a sample code to demonstrate:
class ViewController: UIViewController {
var counter = 0
#objc dynamic var testValue: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true, block: { [weak self] (timer) in
guard let self = self else {
print("return")
return
}
self.testValue = !self.testValue
})
addObserver(self, forKeyPath: "testValue", options: .new, context: nil)
Timer.scheduledTimer(withTimeInterval: 7.0, repeats: false, block: { (timer) in
DispatchQueue.global(qos: .background).async {
self.removeObserver(self, forKeyPath: "testValue")
}
})
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "testValue" {
print("counter : \(counter)")
counter += 1
}
}
}
There aren’t any technical issues with removing observer from another thread, but I would advise against it. The biggest concern would be race conditions between the deallocation of the observer and its unregistration from KVO. As the documentation for removeObserver(_:forKeyPath:) says:
Be sure to invoke this method (or removeObserver(_:forKeyPath:context:)) before any object specified in addObserver(_:forKeyPath:options:context:) is deallocated.
Now in your case, both of your timers happens to be keeping a strong reference to the observer (which itself is a problem; you should be using [weak self] pattern in your timers and invalidating them when necessary), but if you fixed that, you’d now introduce a race.
If you add your observers in init and remove them in deinit, that eliminates any race conditions. (Also if you use modern KVO syntax, it eliminates this deallocation/unregistration race, too.)
Also, remember that observeValue(forKeyPath:of:change:context:) is called on the thread that updated the testValue property. So if you’re concerned about threading issues, the thread-safety of both testValue and counter must be considered, too.
In the absence of any synchronization (e.g., perhaps you’re going to assume that testValue will never be updated from a background thread, even though you’re contemplating removing the observer from a background thread), I would suggest making that assumptions inherit to observeValue explicit. So, assuming you added your observer like so:
addObserver(self, forKeyPath: #keyPath(testValue), options: .new, context: &observerContext)
Then you might add that dispatchPrecondition to make the assumption explicit:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if keyPath == #keyPath(testValue) {
dispatchPrecondition(condition: .onQueue(.main)) // given that `counter` is not synchronized, let’s warn developer if ever updated `testValue` on background thread
print("counter: \(counter)")
counter += 1
}
}
Obviously, you don’t need this precondition if you made counter thread-safe through some synchronization.
Also, we’d obviously not use KVO to observe a property of the current class (we’d generally just use a Swift observer), but I’m assuming you simplified this for illustrative purposes.

UserDefaults.didChangeNotification not firing

The project I am working on has an extension that writes data to UserDefaults. Then in the containing app should the UI should get updated according to the changes. The problem is that UserDefaults.didChangeNotification does not get fired unless the screen comes from background. What could be the reason and is there a way to be fixed or another way to get the needed update?
Writing the data in the extension:
let sharedUserDefaults = UserDefaults(suiteName: Common.UserDefaultsSuite)
var receivedNotifications = sharedUserDefaults?.array(forKey: Common.ReceivedNotifications)
if receivedNotifications != nil {
receivedNotifications?.append(aData)
} else {
receivedNotifications = [aData]
}
sharedUserDefaults?.set(receivedNotifications, forKey: Common.ReceivedNotifications)
Registering for the notification in the view controller:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
}
And working with changed user defaults (that actually does not get called):
#objc func userDefaultsDidChange(_ notification: Notification) {
print("User defaults did change")
gatherReceivedNotifications()
}
Still no idea why the other way doesn't work but the following works so it's a solution. As per suggested here I did the following:
override func viewDidLoad() {
super.viewDidLoad()
UserDefaults(suiteName: Common.UserDefaultsSuite)?.addObserver(self, forKeyPath: Common.ReceivedNotifications, options: .new, context: nil)
}
Then implemented observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?):
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == Common.ReceivedNotifications {
gatherReceivedNotifications()
}
}
It is fired immediately and only when a change to UserDefaults for the key Common.ReceivedNotifications is made.
The code #selector(userDefaultsDidChange) is means func userDefaultsDidChange() without parameter.
But you defined func userDefaultsDidChange(_ notification: Notification), it's have one parameter.
Next step:
Change #selector(userDefaultsDidChange) to #selector(userDefaultsDidChange(_:)) can fixed it.

Swift - UNUsernotification checking changes in BadgeNumberIcon

I Would like to display the value of the badge number in a label.
So far i've put everything in my viewWillAppear. So every time the controller is loaded the variable is assigned. Here's the code:
var deliveredNotif = Int()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
deliveredNotif = UIApplication.shared.applicationIconBadgeNumber
myBadgeLabel.text = "\(deliveredNotif)"
}
My question is:
How can i update deliveredNotif if the controller is active and so viewWillAppear is already been called? Meaning if I am in the controller is there a way to trigger a func which will update the value of deliveredNotif every time the value of applicationIconBadgeNumber is changed?
Thank you!
-------UPDATE----MY SOLUTION
I created a constant variable:
var iconBadgeNumber = NSNumber()
in my Appdelegate i have:
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .badge])
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "applicationIconBadgeNumber"), object: nil)
iconBadgeNumber = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber
}
and then in my controller:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(updateIconBadgeNumber), name: NSNotification.Name(rawValue: "applicationIconBadgeNumber"), object: nil)
}
#objc func updateIconBadgeNumber() {
if iconBadgeNumber == 0 {
print("zero")
} else {
print("there unread notification")
}
}
You can set your UIViewController as an observer for the key path "applicationIconBadgeNumber":
First register for remote notifications. In your didFinishLaunchingWithOptions function, add:
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
don't forget to add also: import UserNotifications on the top of your file
Then add in your viewDidLoad:
UIApplication.shared.addObserver(self, forKeyPath: "applicationIconBadgeNumber", options: .new, context: nil)
Then, override the observeValue function:
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == "applicationIconBadgeNumber" {
deliveredNotif = UIApplication.shared.applicationIconBadgeNumber
myBadgeLabel.text = "\(deliveredNotif)"
}
}
You have 3 different ways in order to notify an object about a changed value :
First of all you have the Notification center in order to notify an observer when your value changed.
Check out this post, and more informations on the Internet, you will find what you want.
Moreover, you can also use protocol and delegate but it seems kind of heavy for just a notification update on a value.
Finally You should definitely look around this post, it explains the principle of the Key-Value Observing (KVO), I am sure you already heard about it.
And to conclude, the ultimate article, will explain you when you should use each of this principle.
KVO vs NSNotification vs protocol/delegates?
You should try to investigate more seriously before posting a question that is already answered multiple times.
Hope this articles will help you.

How to check status of AVPlayer?

I thought that I could check the status of an AVPlayer simply by the property "rate".
This is how I create a player instance:
player = AVPlayer(URL: stream) // streaming from the internet
player!.play()
At some later point I would do something like this
println(player!.rate)
This is what I discovered:
In Simulator I get "0.0" in case the player is not running or "1.0" if it is running.
If I start the player but interrupt the internet connection it changes values from 1 to 0.
However, on my iPhone the property keeps value 1 even if I enter Airplane Mode?!
Do you have any idea why that happens and how I could check the stream condition otherwise?
I have tried an observer so far:
player!.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.New, context: nil)
But even the method "observeValueForKeyPath" does not get fired in my iPhone test case.
Check out the Apple docs here
and scroll to the "Key-Value Observing" section. Especially #3 in that section.
It helped me get my implementation to work. My resulting code looks like this:
//Global
var player = AVPlayer()
func setUpPlayer() {
//...
// Setting up your player code goes here
self.player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions(), context: nil)
//...
}
// catch changes to status
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
print("obsrved")
}
I could not make it work with adding an observer on the currentItem as user #gabbler suggested.
However it helped using the notification center like this:
NSNotificationCenter.defaultCenter().addObserverForName(
AVPlayerItemFailedToPlayToEndTimeNotification,
object: nil,
queue: nil,
usingBlock: { notification in
self.stop()
})
Note that stop() is a method in the same class which stops the stream as if a stop button were clicked.

Detect when camera is auto-focusing

I am working on video application. I want to discard the video frames when camera is autofocusing. During autofocus image captured become blurred and image processing for that frame become bad but once autofocus is done, image processing become excellent. Any body give me solution?
adjustingFocus property.
Indicates whether the device is currently adjusting its focus setting. (read-only)
*Notes: You can observe changes to the value of this property using Key-value observing.
iOS 4.0 and later
https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVCaptureDevice_Class/Reference/Reference.html#//apple_ref/occ/instp/AVCaptureDevice/adjustingFocus
Following is a sample code in Swift 3.x.
First a observer should be added to the selected capture device at camera initialization.
captureDevice.addObserver(self, forKeyPath: "adjustingFocus", options: [.new], context: nil)
Then observeValue method is overridden. By accessing the optional value returned by the method, autoFocussing frames can be identified.
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let key = keyPath, let changes = change else {
return
}
if key == "adjustingFocus" {
let changedValue = changes[.newKey]
if (changedValue! as! Bool){
// camera is auto-focussing
}else{
// camera is not auto-focussing
}
}
}
Example on Swift 4+
class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {
//#objc var captureDevice: AVCaptureDevice?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.addObservers()
}
func addObservers() {
self.addObserver(self, forKeyPath: "captureDevice.adjustingFocus", options: .new, context: nil)
}
func removeObservers() {
self.removeObserver(self, forKeyPath: "captureDevice.adjustingFocus")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "captureDevice.adjustingFocus" {
print("============== adjustingFocus: \(self.captureDevice?.lensPosition)")
}
} //End of class
Observing adjustingFocus is not working for me. It's always no. And I find this.
Note that when traditional contrast detect auto-focus is in use, the AVCaptureDevice adjustingFocus property flips to YES when a focus is underway, and flips back to NO when it is done. When phase detect autofocus is in use, the adjustingFocus property does not flip to YES, as the phase detect method tends to focus more frequently, but in small, sometimes imperceptible amounts. You can observe the AVCaptureDevice lensPosition property to see lens movements that are driven by phase detect AF.
from Apple
I have not try it yet, I will try and update later.
Edit. I have try it, and confirm this's right.

Resources