I'm currently developing a framework through which i want to check if the app has been launched and if it is in foreground or not.
The thing is that UIApplication cannot be imported in a framework. Is there a way to catch that event without having to do it in the app ?
Instead of accessing the AppDelegate, which is a bad coding style in my opinion, you may listen for the events instead:
func startListen() {
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground(_:)), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
}
func stopListen() {
NotificationCenter.default.removeObserver(self)
}
func applicationWillEnterForeground(_ notification: NSNotification) {
print("App moved to foreground!")
}
There are more notification types like UIApplicationDidEnterBackground that may notify you about the whole app lifecycle.
Related
My application has a menu with option "Contacts". When user selects Contacts for the first time, application asks user permission to use device Contacts. If user allows access, opens a view controller with contacts of his/her device, if he doesn't, nothing happens. After that every time they select "Contacts" option, it heads to Device Settings where they should allow access to contacts or dismiss it. When they allow access and go back to the app, I want it to open the view controller with contacts, but it doesn't.
I've tried to use NotificationCenter with didBecomeActiveNotification option, but it does not seem to help.
func checkPermissionToDeviceContacts() {
switch CNContactStore.authorizationStatus(for: .contacts) {
case .notDetermined:
self.requestContactsAccess()
case .authorized:
self.openContactsViewController()
case .denied:
self.headToSettingsOfPermissions()
default: return
}
}
func headToSettingsOfPermissions() {
if let bundleId = Bundle.main.bundleIdentifier,
let url = URL(string: "\(UIApplication.openSettingsURLString)&path=APPNAME/\(bundleId)")
{
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
self.setupNotifications()
}
func setupNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(self.applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc func applicationDidBecomeActive() {
if CNContactStore.authorizationStatus(for: .contacts) == .authorized {
self.openContactsViewController()
}
NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
}
I also cannot debug it, because when Device Settings are opened and I change the access status of Contacts, my console prints a message "Message from debugger: Terminated due to signal 9".
The problem is that the selector function works when I do not change the authorization status, but when I do, it does not work anyway.
P.S. This is my first time to use NotificationCenter, maybe I do not use it properly.
NotificationCenter sends always the affected notification as parameter, you have to declare
#objc func applicationDidBecomeActive(_ notification : Notification) {
and remove self in the selector parameter
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
I'm working on SDK and try to catch app termination Notification. It's easy to get this as closure for (ex) NSNotification.Name.UIApplicationWillResignActive
self.resignActiveNotification = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationWillResignActive,
object: nil,
queue: nil) { _ in
//something goes here and it works like a charm.
}
So I want to have similar behavior for termination:
self.terminateNotification = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationWillTerminate,
object: nil,
queue: nil) { _ in
NSLog("%#",#function)
}
And this one never gets called!
Of course if I put in AppDelegate:
func applicationWillTerminate(_ application: UIApplication) {
//termination
}
It will work, but since I'm building an SDK I cannot have AppDelegate methods. There is a way to get termination closure call? Or any other way to know that application is about to terminate?
In Apple documentation you can find:
After calling this method, the app also posts a
UIApplicationWillTerminate notification to give interested objects a
chance to respond to the transition.
However this seems to be broken
This seems working for me:
init() {
NotificationCenter.default.addObserver(self,
selector: #selector(applicationWillTerminate(notification:)),
name: UIApplication.willTerminateNotification,
object: nil)
}
#objc func applicationWillTerminate(notification: Notification) {
// Notification received.
}
deinit {
NotificationCenter.default.removeObserver(self)
}
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'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