Swift - UNUsernotification checking changes in BadgeNumberIcon - ios

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.

Related

ScreenShot Detection in whole App from single Page in 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

Can we customize SwiftEventBus to notify only for the current active screen and not all the places where it is registered?

Can we customize SwiftEventBus Library to only trigger in the current active ViewController.
I'm trying to trigger an action when ever a notification occurs, so i'm using swift event bus to trigger when ever a push notification comes but it is triggering in all the places it is registered. Can we make so that it will only trigger the action in the active view. If not is there any other library I can use?
Wouldn't it be enough to deregister inactive ViewControllers as mentioned in the SwiftEventBus readme?
//Perhaps on viewDidDisappear depending on your needs
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
SwiftEventBus.unregister(self)
}
Modify library(or subclass SwiftEventBus) like below:
#discardableResult
open class func on(_ target: AnyObject, name: String, sender: Any? = nil, queue: OperationQueue?, handler: #escaping ((Notification?) -> Void)) -> NSObjectProtocol {
let id = UInt(bitPattern: ObjectIdentifier(target))
//modification start
let handlerIner:((Notification?) -> Void) = { [weak target] n in
if let vc = target as? UIViewController, vc.view?.window != nil {
handler(n)
}
}
let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: name), object: sender, queue: queue, using: handlerIner)
// modification end
let namedObserver = NamedObserver(observer: observer, name: name)
Static.queue.sync {
if let namedObservers = Static.instance.cache[id] {
Static.instance.cache[id] = namedObservers + [namedObserver]
} else {
Static.instance.cache[id] = [namedObserver]
}
}
return observer
}

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.

Keep a Firebase listener in memory when the app is in background

I have been trying to use a Firebase listener to trigger local notifications. I have found a post that addresses exactly what I am trying to do with much of it explained, however I do not have the reputation to comment on the post and there seems to be no indication of how to accomplish what I want anywhere else.
The original poster says this.
I figured it out! I had to use a different approach but i was able to
get my Firebase Database observer to trigger notifications in the
background.
As long as the object containting the database observer is not
deallocated from memory it will continue to observe and trigger. So I
created a global class which contains a static database object
property like this:
class GlobalDatabaseDelegate {
static let dataBase = DataBase()
}
This is where I am confused as to what to do for my own project. It is my understanding that I have to create a class similar to DataBase() which contains my database reference. The problem is I do not understand how to create class object that will contain the database listener.
say for example my reference is :
let userRef = FIRDatabase.database.reference().child("users")
And I want to observe any users added to the database and then trigger a local notification. I am able to write the code to do so, just not sure how to contain it in an object class of its own and then make it static.
Forgive me for being a little slow. Any help would be very much appreciated.
The rest of the post follows :
I also extended the DataBase class to be the
UNUserNotificationCenterDelegate so it can send the push notitications
like this:
extension DataBase: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Tapped in notification")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Notification being triggered")
completionHandler( [.alert,.sound,.badge])
}
func observeNotificationsChildAddedBackground() {
self.notificationsBackgroundHandler = FIREBASE_REF!.child("notifications/\(Defaults.userUID!)")
self.notificationsBackgroundHandler!.queryOrdered(byChild: "date").queryLimited(toLast: 99).observe(.childAdded, with: { snapshot in
let newNotificationJSON = snapshot.value as? [String : Any]
if let newNotificationJSON = newNotificationJSON {
let status = newNotificationJSON["status"]
if let status = status as? Int {
if status == 1 {
self.sendPushNotification()
}
}
}
})
}
func sendPushNotification() {
let content = UNMutableNotificationContent()
content.title = "Here is a new notification"
content.subtitle = "new notification push"
content.body = "Someone did something which triggered a notification"
content.sound = UNNotificationSound.default()
let request = UNNotificationRequest(identifier: "\(self.notificationBackgroundProcessName)", content: content, trigger: nil)
NotificationCenter.default.post(name: notificationBackgroundProcessName, object: nil)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().add(request){ error in
if error != nil {
print("error sending a push notification :\(error?.localizedDescription)")
}
}
}
}
In essence I am trying to keep a firebase listener in memory when the app is in background.
So the original post that I have linked in has the answer but it is a matter of understanding it. I have also implemented my code in a slightly different approach.
I found another post detailing the technique needed to run a custom data service class. Custom Firebase Data Service Class : Swift 3
To set keep the firebase listener in memory there are few steps.
1.Create a firebase data service class. In that class I have a static variable that is of the same class
class FirebaseAPI {
var isOpen = false
static let sharedInstance = FirebaseAPI()
// I added functions for firebase reference in this class
func observeNotifications(){
//firebase call here
}
}
2.Set up notification settings in app delegate. This is where my set up differs from the original post.
let notificationSettings = UIUserNotificationSettings(types: [.badge, .alert, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(notificationSettings)
3.Create a reference to the firebase class in a viewcontroller of your choice, it works in app delegate but not advisable.
let sharedInstance = FirebaseAPI.sharedInstance
4.Call functions to setup observer
self.sharedInstance.observeNotifications()
You can then trigger fire a local notification using a completion handler with the function or fire off notifications within the firebase function.
Update: Apple have implemented updates in regards to background modes which have stopped this method from working . Currently the only method is to use APNS

NotificationCenter issue on Swift 3 [duplicate]

This question already has answers here:
NSNotificationCenter addObserver in Swift
(16 answers)
Closed 6 years ago.
I'm learning Swift 3 and I'm trying to using NSNotificationCenter. Here is my code:
func savePost(){
let postData = NSKeyedArchiver.archivedData(withRootObject: _loadedpost)
UserDefaults.standard().object(forKey: KEY_POST)
}
func loadPost(){
if let postData = UserDefaults.standard().object(forKey: KEY_POST) as? NSData{
if let postArray = NSKeyedUnarchiver.unarchiveObject(with: postData as Data) as? [Post]{
_loadedpost = postArray
}
}
//codeerror
NotificationCenter.default().post(NSNotification(name: "loadedPost" as NSNotification.Name, object: nil) as Notification)
}
and this is the observer:
override func viewDidLoad() {
super.viewDidLoad()
//codeerorr
NotificationCenter.default().addObserver(self, selector: Selector(("onPostLoaded")), name: "loadedPost", object: nil)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
It always gives me the error "signal SIGBRT". When I try to change the name in the observer, it's not an error, but obviously it didn't show anything. How do I fix this?
Swift 3 & 4
Swift 3, and now Swift 4, have replaced many "stringly-typed" APIs with struct "wrapper types", as is the case with NotificationCenter. Notifications are now identified by a struct Notfication.Name rather than by String. For more details see the now legacy Migrating to Swift 3 guide
Swift 2.2 usage:
// Define identifier
let notificationIdentifier: String = "NotificationIdentifier"
// Register to receive notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name: notificationIdentifier, object: nil)
// Post a notification
NSNotificationCenter.defaultCenter().postNotificationName(notificationIdentifier, object: nil)
Swift 3 & 4 usage:
// Define identifier
let notificationName = Notification.Name("NotificationIdentifier")
// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification), name: notificationName, object: nil)
// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)
// Stop listening notification
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil)
All of the system notification types are now defined as static constants on Notification.Name; i.e. .UIApplicationDidFinishLaunching, .UITextFieldTextDidChange, etc.
You can extend Notification.Name with your own custom notifications in order to stay consistent with the system notifications:
// Definition:
extension Notification.Name {
static let yourCustomNotificationName = Notification.Name("yourCustomNotificationName")
}
// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)
Swift 4.2 usage:
Same as Swift 4, except now system notifications names are part of UIApplication. So in order to stay consistent with the system notifications you can extend UIApplication with your own custom notifications instead of Notification.Name :
// Definition:
UIApplication {
public static let yourCustomNotificationName = Notification.Name("yourCustomNotificationName")
}
// Usage:
NotificationCenter.default.post(name: UIApplication.yourCustomNotificationName, object: nil)
Notifications appear to have changed again (October 2016).
// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(yourClass.yourMethod), name: NSNotification.Name(rawValue: "yourNotificatioName"), object: nil)
// Post notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "yourNotificationName"), object: nil)
For all struggling around with the #selector in Swift 3 or Swift 4, here a full code example:
// WE NEED A CLASS THAT SHOULD RECEIVE NOTIFICATIONS
class MyReceivingClass {
// ---------------------------------------------
// INIT -> GOOD PLACE FOR REGISTERING
// ---------------------------------------------
init() {
// WE REGISTER FOR SYSTEM NOTIFICATION (APP WILL RESIGN ACTIVE)
// Register without parameter
NotificationCenter.default.addObserver(self, selector: #selector(MyReceivingClass.handleNotification), name: .UIApplicationWillResignActive, object: nil)
// Register WITH parameter
NotificationCenter.default.addObserver(self, selector: #selector(MyReceivingClass.handle(withNotification:)), name: .UIApplicationWillResignActive, object: nil)
}
// ---------------------------------------------
// DE-INIT -> LAST OPTION FOR RE-REGISTERING
// ---------------------------------------------
deinit {
NotificationCenter.default.removeObserver(self)
}
// either "MyReceivingClass" must be a subclass of NSObject OR selector-methods MUST BE signed with '#objc'
// ---------------------------------------------
// HANDLE NOTIFICATION WITHOUT PARAMETER
// ---------------------------------------------
#objc func handleNotification() {
print("RECEIVED ANY NOTIFICATION")
}
// ---------------------------------------------
// HANDLE NOTIFICATION WITH PARAMETER
// ---------------------------------------------
#objc func handle(withNotification notification : NSNotification) {
print("RECEIVED SPECIFIC NOTIFICATION: \(notification)")
}
}
In this example we try to get POSTs from AppDelegate (so in AppDelegate implement this):
// ---------------------------------------------
// WHEN APP IS GOING TO BE INACTIVE
// ---------------------------------------------
func applicationWillResignActive(_ application: UIApplication) {
print("POSTING")
// Define identifiyer
let notificationName = Notification.Name.UIApplicationWillResignActive
// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)
}
I think it has changed again.
For posting this works in Xcode 8.2.
NotificationCenter.default.post(Notification(name:.UIApplicationWillResignActive)

Resources