I have a problem with one of my apps. I noticed, that my Controller is still updating while the app is in the background. The reason for this is that the methods of the AppDelegate are not being called and I can not find out why.
Here is my code:
import UIKit
import Harmony
#UIApplicationMain
class AppDelegate : UIResponder
{
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
return true
}
}
extension AppDelegate : UIApplicationDelegate
{
func applicationDidBecomeActive(_ application: UIApplication)
{
Swift.print("applicationDidBecomeActive")
Notifications.post(UIApplication.didBecomeActiveNotification.rawValue)
}
func applicationWillResignActive(_ application: UIApplication)
{
Swift.print("applicationWillResignActive")
Notifications.post(UIApplication.willResignActiveNotification.rawValue)
}
func applicationWillEnterForeground(_ application: UIApplication)
{
Swift.print("applicationWillEnterForeground")
Notifications.post(UIApplication.willEnterForegroundNotification.rawValue)
}
}
I tried the following:
Use real device and simulator.
Restart Xcode, clean build folder.
Clean Developer folder
Do not use the extension.
Debug or Release scheme
Nothing had an effect, the methods are still not being called and I am wondering why?
If you are using ios 13 with scene delegate .. all functions are shifted in scene delegate instead of AppDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
Related
If I never actually leave the current app for another app - and the screen turns off or the device gets locked.
Will lifecycle methods like applicationDidEnterBackground, applicationWillEnterForeground, etc still get called?
Basically my question is: does screen lock/device sleep behave the same way as switching to another app in terms of lifecycle methods.
Yes, except that in a modern app all of that stuff has been moved to the scene delegate. If the screen is locked and you are frontmost, you will get:
sceneWillResignActive(_:)
sceneDidEnterBackground(_:)
And then when the screen is unlocked you will get:
sceneWillEnterForeground(_:)
sceneDidBecomeActive(_:)
FWIW, if you want to diagnose these sorts of app lifecycle events yourself in the future, you can use Logger or os_log and watch these iOS events on your macOS Console app.
Note, I am not relying on the Xcode debugger, because having an app attached to the debugger alters the app lifecycle. I am explicitly not running the app through the Xcode debugger, but rather running it directly on the device having previously quit Xcode on my Mac.
In short, yes, simply turning off a phone (but not powering it down completely) puts your app into background mode. Here is the log where I launched the app, and then tapped on the power button on the side of my iPhone to turn it off:
This behavior is very similar to the following log where I just swiped up to return to the home screen:
But if you completely power down the device while the app is running, you will see it sceneWillResignActive, but unsurprisingly, it never gets to sceneDidEnterBackground:
Nor if you force-quit while the app is active:
For more information about logging an app and observing these from the macOS Console, see WWDC 2020’s Explore logging in Swift or WWDC 2016 Unified Logging and Activity Tracing for these logging options.
FWIW, here is my logging:
// AppDelegate.swift
import UIKit
import os
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "AppDelegate")
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
logger.debug(#function)
...
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
logger.debug(#function)
...
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
logger.debug(#function)
...
}
}
And:
// SceneDelegate.swift
import UIKit
import os
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "SceneDelegate")
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
logger.debug(#function)
...
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
logger.debug(#function)
...
}
func sceneDidBecomeActive(_ scene: UIScene) {
logger.debug(#function)
...
}
func sceneWillResignActive(_ scene: UIScene) {
logger.debug(#function)
...
}
func sceneWillEnterForeground(_ scene: UIScene) {
logger.debug(#function)
...
}
func sceneDidEnterBackground(_ scene: UIScene) {
logger.debug(#function)
...
}
}
If you are using os_log instead of Logger (the latter introduced in macOS 11 and iOS 14), the process is very similar. You will see os_log messages in the console in the same manner.
This question already has answers here:
The app delegate must implement the window property if it wants to use a main storyboard file swift
(12 answers)
Closed 2 years ago.
I am creating a new project using latest Xcode (12.1). the default deployment target when I create a new project was 14.1. then I change it to be 11.0. because I want to target users from iOS 11.0 and above
then it will create a lot of warnings like this in my AppDelegate and SceneDelegate
I fix all of them so my AppDelegate and SceneDelegate will be like the code below.
the problem is.....
when I run it on my real device iOS 14.1, it will have no issue. but when I run it on my iOS 12.4.8 , my app will completely dark
I have also already turn off the dark mode in info.plist using this key
<key>Appearance</key>
<string>Light</string>
how to solve this ?
completely dark like this iOS 12.4.8 .the background colour should be custom orange
SceneDelagate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
#available(iOS 13.0, *)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
}
#available(iOS 13.0, *)
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}
#available(iOS 13.0, *)
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
#available(iOS 13.0, *)
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
#available(iOS 13.0, *)
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
#available(iOS 13.0, *)
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
AppDelegate.swift
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
#available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
I have an answer from here
you have to add the following property declaration in your AppDelegate class
var window: UIWindow?
INTRODUCTION
I'm still new use Swift, for now, I have a project using Swift, not develop form start just customize the App.
PROBLEM
Now I want to create Deep Link with Parameter. So, I try to follow a few tutorials from the internet e.g: [https://medium.com/wolox/ios-deep-linking-url-scheme-vs-universal-links-50abd3802f97] and [https://www.swiftdevcenter.com/custom-url-scheme-deep-link-ios-13-and-later-swift-5/] but not work.
TRY TO DO
I want to if App detects Deep Link have a parameter, App will go to another page and show the parameter.
Add this code in scene delegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url{
print(url)
let urlStr = url.absoluteString //1
// Parse the custom URL as per your uses, this will change as per requirement
let component = urlStr.components(separatedBy: "=") // 2
if component.count > 1, let productId = component.last { // 3
self.pushToProductDetailSceen(productId: productId) // 4
}
}
}
func pushToProductDetailSceen(productId: String)
{
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene[![enter image description here][1]][1]: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
just put this line in safari and it will works.
testapp://productId=97
I am creating an App which features an Onboarding. This Onboarding takes place on a ViewController with only this specific purpose, that's why I only want the ViewController to show up ones after downloading the app.(the main app consists out of only one ViewController)
Here is what I've done so far:
1) I delete the storyboard entry point in the storyboard file
2) entered the Storyboard ID for each of the two ViewController
3) coded in the AppDelegate file that the onboarding ViewContoller only should show up the first time after the download
My problem: When running the app on the simulator it only shows a black screen
I already made sure that the Indentifiers and the name of the storyboard are correct.
(below my AppDelegate.swift file)
import UIKit
import Firebase
import FirebaseDatabase
import paper_onboarding
#UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
window = UIWindow(frame: UIScreen.main.bounds)
let sb = UIStoryboard(name: "Main", bundle: nil)
var initialViewController = sb.instantiateViewController(withIdentifier: "Onboarding")
let userDefaults = UserDefaults.standard
if userDefaults.bool(forKey: "onboardingComplete") {
initialViewController = sb.instantiateViewController(withIdentifier: "MainApp")
}
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
``
For black screen:
Have you checked MAIN INERFACE in General tab of project's target? It should be set to one of the storyboard choosing from the dropdown. Also check if Launch Screen File have the LaunchScreen or any other required storyboard selected.
For Onboarding as the first view:
Main interface should be given to the storyboard where the onboarding view controller is and then assigning that view as the initial view controller.
Let me know if you don't understand the logic.
The solution for this problem is not to use the AppDelegate.swift file but the SceneDelegate.swift file.
below my SceneDelegate.swift file
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see`application:configurationForConnectingSceneSession` instead).
let sb = UIStoryboard(name: "Main", bundle: nil)
var initialViewController = sb.instantiateViewController(withIdentifier: "Onboarding")
let userDefaults = UserDefaults.standard
if userDefaults.bool(forKey: "onboardingComplete") {
initialViewController = sb.instantiateViewController(withIdentifier: "MainApp")
}
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = initialViewController
self.window = window
window.makeKeyAndVisible()
}
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let navController = window.rootViewController as? UINavigationController,
let personController = navController.controllers[0] as? PeopleTableViewController{
print("something")
}
return true
}
But it is giving me an error, it is not recognizing the window property of the AppDelegate, did they change with IOS 13 because it works in my IOS 12 project
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "DataModel")
container.loadPersistentStores { description, error in
if let error = error {
fatalError("Unable to load persistent stores: \(error)")
}
}
return container
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let navController = window?.rootViewController as? UINavigationController,
let personController = navController.viewControllers[0] as? PeopleTableViewController{
personController.persistentContainer = persistentContainer
print("Nailed it ")
} else {
print("Sorry")
}
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
func applicationWillTerminate(_ application: UIApplication) {
persistentContainer.saveContextIfNeeded()
}
}
This is how my AppDelegate class looks
Here is how you set App's initialView Controller/rootViewController inside Scene Delegate class (this class handles Application Lifecycle Methods which were earlier handled by AppDelegate).
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let initialViewController = UIStoryboard(name: "Storyboard", bundle: nil).instantiateViewController(identifier: "DatePicker") as! DatePicker
let navigation = UINavigationController(rootViewController: initialViewController)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = tabBarCnt
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
print("App entered Background")
}
}
I found out a way around. instead of putting your code in didFinsishLoadingWithOptions method, getting the UIview comptroller's persistentContainer variable and then passing the persistentContainer in AppDelegate. you can just add this to the top of the view Controller.
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.context
if you want the appDelegate you can access like this
let AppDelegate = UIApplication.shared.delegate as! AppDelegate
This works both IOS12 and IOS13 so, you don't have to change your code