So I have about four notification observers added in a particular view controller, but I want to remove one in particular if a condition is met and then add it back for a different condition. I was able to remove the particular observer but I have had trouble adding it back. the other ones work except the one a removed and added back and I know it's working cos when the VC loads and all the notification observers are added but then it stops working after adding it back. here's my code:
// registered the observer in ViewDIdAppear and this registers my bluetooth device and whenever i tap on the bluetooth device button this function gets called
NotificationCenter.default.addObserver(
self, selector: #selector(Myclass.deviceTapped), name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
// i removed the observer here
if condition == false {
NotificationCenter.default.removeObserver(self, name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
}
// and then use this to add it back again
NotificationCenter.default.addObserver(
self, selector: #selector(Myclass.deviceTapped), name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
// and after adding this ^ back the deviceTapped function doesn't get called anymore
The thing to do in these situations is prove to yourself that this does normally work. That way, you can track down what you are doing that prevents it from working.
For example, perhaps self is going out of existence before any notifications have a chance to arrive...? Perhaps some other code, that you have not shown, is unregistering you again...? I've certainly experienced confusions like that! And they can be very hard to debug.
But whatever the explanation, you need first to assure yourself that unregistering for a notification and then re-registering for it is not a problem.
Here's a test app. It has three buttons: Register, Unregister, and Notify. Here's the code:
let notif : Notification.Name = .init("howdy")
#objc func gotNotified() {
print("got notified")
}
#IBAction func doRegister(_ sender: Any) {
NotificationCenter.default.addObserver(self, selector: #selector(gotNotified), name: notif, object: nil)
}
#IBAction func doUnregister(_ sender: Any) {
NotificationCenter.default.removeObserver(self, name: notif, object: nil)
}
#IBAction func doNotify(_ sender: Any) {
NotificationCenter.default.post(name: notif, object: nil)
}
So configure that with the buttons hooked up, and then:
Tap Register.
Tap Notify.
Tap Unregister.
Tap Notify.
Tap Register.
Tap Notify.
You will see that on 2 and 6, but not 4, we print to the console to prove we were notified — just as you would expect.
Related
I have Login screen and I would like to present it or just add it to navigation, it doesn't matter right now, still not sure which way I would like it to be, everytime my app comes back to foreground. Code below partially works, it does start face id, but actually whole login screen isn't shown, I can still see my last screen that was opened before entering background. I would like to hide everything with login screen. When I print childrens in appMovedToBackground function, correct view controller is printed, which means its added, but as I said, its not showing, only icon from face id can be seen. Code below is on one view controller only, currently, I would like this to be applied to all my view controllers, should I move functionality to appdelegate? Thanks in advance.
let notificationCenter = NotificationCenter.default
override func viewDidLoad() {
super.viewDidLoad()
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appMovedToBackground() {
print("its in background")
let authVC = AuthViewController()
navigationController?.topViewController?.addChild(authVC)
}
#objc func appCameToForeground() {
print("app enters foreground")
if let authVC = navigationController?.topViewController?.children.first as? AuthViewController {
print("willEnterForegroundNotification")
authVC.checkBiometricsAndStart()
}
}
Here you need to correctly add the vc
#objc func appMovedToBackground() {
print("its in background")
guard let added = navigationController?.topViewController else { return }
let authVC = AuthViewController()
authVC.view.frame = UIScreen.main.bounds
added.view.addSubview(authVC.view)
added.addChild(authVC)
}
Regarding that you need it for all vcs , yes implementing it inside AppDelegate is the easiest way
I need to check if the app is moved to the background. Why?
Well because my app works with bluetooth and only one person can be connected to this device at a time. Therefore if they are not using it and the app is in the background, disconnect them and send them to the connect main page.
Now I have accomplished this. I have a selector in the main first class and a function to disconnect and send to first page. But what I didn't realise is that if the control panel is dragged up, the app is in the 'background'.
From looking around there doesn't seem to be a way to detect if the control panel is brought up. So does anyone have any ideas on how I can do this differently?
Realistically I just want it so if the app is moved to the background for any other reason than the control panel being brought up, disconnect from the device.
Selector:
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
Function:
#objc func appMovedToBackground() {
if (ViewController.connectedPeripheral != nil) {
print("App moved to background!")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "connectView") as! ViewController
self.navigationController?.pushViewController(nextViewController, animated: true)
ViewController.centralManager.cancelPeripheralConnection(ViewController.connectedPeripheral!)
}
else {
print("App moved to background but no device is connected so no further action taken")
}
}
This is not a duplicate of other questions. I know how to check if app is in background state. I just don't want to disconnect when the control panel is brought up...
In Swift:
if UIApplication.shared.applicationState == .background {
// Add code here...
}
In Objective-C:
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
// Add code here...
}
Hope it works!
Have you tried with adding observer to willResignActive in your view controller?
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: .UIApplicationWillResignActive, object: nil)
func willResignActive(_ notification: Notification) {
// code to execute
}
Then you can use this also. After entering in background state , app will be moved to inactive state.
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.shared
//Register for the applicationWillResignActive anywhere in your app.
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.applicationWillResignActive(notification:)), name: NSNotification.Name.UIApplicationWillResignActive, object: app)
}
func applicationWillResignActive(notification: NSNotification) {
}
Hi i am new in iOS application development. I am using Swift 3.0 code.
In my application I'm fetching the device token and I'm passing to my server. to pass token value from Appdelegate to UIViewController i am using UserDefaults.standard. and it's working fine. During first application load i am showing system's push notification prompt. If user click OK (allow) or Don't allow. application check it using following code.
//Checking Notification State
let isRegisteredForLocalNotifications = UIApplication.shared.currentUserNotificationSettings?.types.contains(UIUserNotificationType.alert) ?? false
if isRegisteredForLocalNotifications{
print("Notification allow")
}else{
print("Notification Don't allow")
}
Note: The above code is working fine in didRegisterForRemoteNotificationsWithDeviceToken.
I am having following issue with above code.
1) if i am using same code in ViewController its working but my application not waiting for system's push notification prompt result. i am getting results in AppDelegate.
So how to reload ViewController from didRegisterForRemoteNotificationsWithDeviceToken? is there any way to do that.
2) can i pause viewController until user response to system's push notification prompt. But i am not sure above code will work in ViewController or not.
Please advise.
Thanks for help.
For first part of your problem, you can create an Observer:
NotificationCenter.default.addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
the function that handle it will be like this:
func methodOfReceivedNotification(notification: Notification){
//Take Action on Notification
}
and then you can call it as follows:
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil)
you can call the above code whenever you want to update your viewController
You can update view controller from didRegisterForRemoteNotificationsWithDeviceToken? by post notification.
let notificationIdentifier: String = "NotificationIdentifier"
//write this to didRegisterForRemoteNotificationsWithDeviceToken
// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)
//write this to your View Controller
let notificationIdentifier: String = "NotificationIdentifier"
// Register to receive notification
NotificationCenter.default.addObserver(self, selector:#selector(YourClassName.methodOfReceivedNotification), name: notificationName, object: nil)
// Stop listening notification
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil);
func methodOfReceivedNotification(notification: Notification) {
}
I am writing a UI text in swift under the new Xcode 7 UI test framework.
the requirement is to test whether the system keyboard is shown in an app.
can someone give me a clue on how to do that? thanks
Try this check:
let app = XCUIApplication()
XCTAssert(app.keyboards.count > 0, "The keyboard is not shown")
Or check for specific keyboard keys like:
let app = XCUIApplication()
XCTAssert(app.keyboards.buttons["Next:"].exists, "The keyboard has no Next button")
You can also control interactions on the keyboard:
let app = XCUIApplication()
app.keyboards.buttons["Next:"].tap()
Add two observers
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardVisible:", name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardHidden:", name: UIKeyboardDidHideNotification, object: nil)
func keyboardVisible(notif: NSNotification) {
print("keyboardVisible")
}
func keyboardHidden(notif: NSNotification) {
print("keyboardHidden")
}
Whenever the keyboard is visible keyboardVisible will be called and whenever the keyboard is hidden keyboardHidden will be called.
I found the keyboard count check didnt work on one of my apps (it returned a count of 1 even when the keyboard was hidden), so amended it slightly:
private func isKeyboardShown() -> Bool {
return XCUIApplication().keyboards.keys.count > 0
}
I'm working on a game for iOS coded in Swift. I've tried to find a way to detect when the app enters background mode or is interrupted for other reasons, for example a phone call but can't find anything. How do I do it?
You can add an observer to your view controller:
edit/update: Xcode 11 • Swift 5
iOS13 or later
UIScene.willDeactivateNotification
iOS12 or earlier
UIApplication.willResignActiveNotification
if #available(iOS 13.0, *) {
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIScene.willDeactivateNotification, object: nil)
} else {
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willResignActiveNotification, object: nil)
}
and add a selector method to your view controller that will be executed when your app receives that notification:
#objc func willResignActive(_ notification: Notification) {
// code to execute
}
In swift 5.x: To observe app enters background event, add this code to your viewDidLoad() method.
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
#objc func appMovedToBackground() {
// do whatever event you want
}
you have to use UIApplication.didEnterBackgroundNotification.
If you want to observe if app came to foreground event, use UIApplication.willEnterForegroundNotification
So, the full code will be:
override func viewDidLoad() {
super.viewDidLoad()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
// Do any additional setup after loading the view.
}
#objc func appMovedToBackground() {
print("app enters background")
}
#objc func appCameToForeground() {
print("app enters foreground")
}
Swift3
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
func appMovedToBackground() {
print("App moved to background!")
}
To detect the app enters background, you can check in the appDelegate.m
find the application delegate method
applicationDidEnterBackground
This method will get called, once the app enters background.
SwiftUI
From background
Text("Hello, World!")
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
print("To the foreground!")
}
To the background
Text("Hello, World!")
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
print("To the background!")
}
For SwiftUI you can use:
YourView()
.onReceive(NotificationCenter.default.publisher(for: UIScene.willDeactivateNotification)) { _ in
//...
}
Take a look at the delegate methods defined in your instance of UIApplicationDeletegate (called AppDelegate.m by default). Specifically the following would be useful:
- (void)applicationWillResignActive:(UIApplication *)application
This method is called to let your app know that it is about to move from the active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the app and it begins the transition to the background state. An app in the inactive state continues to run but does not dispatch incoming events to responders.
Taken from the Apple Documentation - here