Setup app again whenever it enters background - swift

for some reason, whenever app enters background (not completely close) I want that the app initialize again and start from beginning, like it opens from a completely close app
Currently I don't use storyboard, and setup the app from scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.makeKeyAndVisible()
guard let mainWindows = window else { return }
appCoordinator = AppCoordinator.makeCoordinator(window: mainWindows)
appCoordinator?.setupRootViewController()
}
Now, I think of a way
first when app enters in background, make the window equal to nil
func sceneDidEnterBackground(_ scene: UIScene) {
window = nil
}
and then move the setup coordinator from willConnectTo to sceneWillEnterForeground
func sceneWillEnterForeground(_ scene: UIScene) {
guard let _ = (scene as? UIWindowScene) else { return }
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.makeKeyAndVisible()
guard let mainWindows = window else { return }
appCoordinator = AppCoordinator.makeCoordinator(window: mainWindows)
appCoordinator?.setupRootViewController()
}
What do you think? That is a good approach? doesn't make any issue?
And also I need to run an API call when app enters in background, is sceneDidEnterBackground a right place to detect this process and start doing that?
Thanks

Related

Could not find keyboard scene delegate for interaction view

Below iOS 13 my UITextField correctly launches a keyboard and lets the user type in their answer.
Above iOS 13, textFieldDidBeginEditing() is triggered when I tap on the text field, but the keyboard is not shown, so the user cannot give their answer .
Debug console doesn't immediately throw any errors, but eventually the following message comes up, which I think is the key:
Could not find keyboard scene delegate for interaction view
I think the error appears in the later iOSs because scenes become the main thing - and somewhere I needed to set up a delegate to allow the keyboard to appear over the fron of the first scene.
No idea how to do this though!
My UITextField is totally standard. To reproduce the error, I have the following set up code in my SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let windowScene = UIWindowScene(session: session, connectionOptions: connectionOptions)
self.window = UIWindow(windowScene: windowScene)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "VC" )
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
guard let _ = (scene as? UIWindowScene) else { return }
}
In my actual app - I use this subroutine to launch a tutorial if the user is new (i.e. I need to be able to change the starting view controller)
Something appears to be out of sorts in your SceneDelegate function scene().
Try this code which I grabbed from another project I have at hand.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
self.window?.rootViewController = ViewController()
self.window?.makeKeyAndVisible()
}

Unbalanced calls to begin/end appearance transitions; what's the proper way to set up this transition?

I have an onboarding flow that I want to present to a new user before letting them into the app. I want to specifically use present() to do this so that I can nicely dismiss() the onboarding flow. This code is working, but I am getting the unbalanced transition warning on the TabBarController. Is there a better way to do this?
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let winScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: winScene)
Core.shared.setIsNewUser()
if Core.shared.isNewUser() {
let tabBarCon = TabBarController() // this is the root of the main app
let welcomeViewCon = UINavigationController.init(rootViewController: OBPage0()) // This is my onboarding flow
window?.rootViewController = tabBarCon
window?.makeKeyAndVisible()
welcomeViewCon.modalPresentationStyle = .fullScreen
tabBarCon.present(welcomeViewCon, animated: true, completion: nil)
}
Note: I know that there are many questions with a similar title, I have reviewed them and still not found an answer to this problem.

Jump to a specific viewcontroller xcode

I have 6 view controllers embed in a navigation controller. Let says the user open the app and used the view controller 1 and 2 and then closed the app. How can I make him jump to a specific view controller once he reuses the app?
you can add some variable on the userDefault for check the status of the user, and in the scendedelagate add some validation for move the application on the view controller that you want.
for instance.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.makeKeyAndVisible()
if "your variable UserDefaults variable saved" {
// setupViewController
window?.rootViewController = SomeViewController()
}else{
window?.rootViewController = NormalViewController()
}
}
In the code of above I setup different launch screen depends of the userDefault that I saved, setup the rootViewController.

Navigation Controller Problem With SceneDelegate

Hello I'm try the hide my navigation controller's back button for all my views. Because that I'm creating my navigation controller in sceneDelegate it work normal but just back button I can't hide it. I have been try all possibilities I can't fix it. (Also I try to change back button title I didn't change as well)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let newScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: newScene)
let navigationController = UINavigationController()
navigationController.navigationBar.backItem?.backBarButtonItem?.title = "Test"
navigationController.navigationBar.backItem?.hidesBackButton = true
navigationController.navigationItem.hidesBackButton = true
navigationController.navigationItem.backBarButtonItem?.accessibilityElementsHidden = true
let appStartPoint = Bundle.main.loadNibNamed("CityController", owner: self, options: nil)?.first as! CityController
navigationController.setViewControllers([appStartPoint], animated: true)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
}

Method 'scene(_:openURLContexts:)' is not called

In the info.plist file I configured URL Identifier and URL Scheme successfully. Also I am able to open the app using the custom URL. The problem is when the app launches for the first time, the method
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)
does not get called.
I have some dependent functionality based on the above method. So when the app launches for the first time, I am not able to see anything in my app.
Also I added code in the method
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
let url = connectionOptions.urlContexts.first?.url
}
but I get url as nil here.
However if my app is in background mode and I hit URL then above method calls successfully and dependent functionality working fine. Following is my code on scene(_:openURLContexts:) method in sceneDelegate.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>){
let url = URLContexts.first?.url
let urlString: String = url!.absoluteString
if let urlComponents = URLComponents(string: urlString),let queryItems = urlComponents.queryItems {
queryParams = queryItems
} else {
print("invalid url")
}
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
//self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let rootVC = storyboard.instantiateViewController(identifier: "LocationViewIdentifier") as? UIViewController else {
print("ViewController not found")
return
}
let rootNC = UINavigationController(rootViewController: rootVC)
self.window?.rootViewController = rootNC
self.window?.makeKeyAndVisible()
}
Can anyone tell me why first time above method does not call?
Maybe this will help your situation.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let urlinfo = connectionOptions.urlContexts
let url = urlinfo.first?.url as! NSURL
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext*h>) {
let url = URLContexts.first!.url as NSURL
}
Update at 17.6.2021
If you wish to use Deeplink and Universal Link both this is my method.
//Deeplink or Universial Link Open when app is start.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Universial link Open
if let userActivity = connectionOptions.userActivities.first,
userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let urlinfo = userActivity.webpageURL{
print ("Universial Link Open at SceneDelegate on App Start ::::::: \(urlinfo)")
}
//deeplink Open
if connectionOptions.urlContexts.first?.url != nil {
let urlinfo = connectionOptions.urlContexts.first?.url
print ("Deeplink Open at SceneDelegate on App Start ::::::: \(String(describing: urlinfo))")
}
guard let _ = (scene as? UIWindowScene) else { return }
}
// Universial link Open when app is onPause
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let urlinfo = userActivity.webpageURL{
print ("Universial Link Open at SceneDelegate on App Pause ::::::: \(urlinfo)")
}
}
// Deeplink Open when app in onPause
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
let urlinfo = URLContexts.first?.url
print ("Deeplink Open on SceneDelegate at App Pause :::::::: \(String(describing: urlinfo))")
}
Thanks
I had the same problem, and the method scene(_ scene:, continue userActivity:) was only called when the app is open.
When I checked the connectionOptions passed to the method scene(_ scene:, willConnectTo session, options connectionOptions:) it had a user activity with the expected URL. So, I ended up calling the first method manually:
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
if let userActivity = connectionOptions.userActivities.first {
self.scene(scene, continue: userActivity)
}
}
I think the Apple docs explain it most clearly, i.e.,
If your app has opted into Scenes, and your app is not running, the system delivers the URL to the scene(_:willConnectTo:options:) delegate method after launch, and to scene(_:openURLContexts:) when your app opens a URL while running or suspended in memory.
Hence, it would only call your scene(_:openURLContexts:) (i.e., the one with this signature: func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) in the question) when your app opens a URL while running or suspended in memory only.
Instead, when the app is launched the first time, it calls scene(_:willConnectTo:options:).
My situation was that the openURL extra function provided in the SceneDelegate was not being called after returning from Facebook Authentication.
The solution was using
.onOpenURL { (url) in
ApplicationDelegate.shared.application(
UIApplication.shared,
open: url,
sourceApplication: nil,
annotation: [UIApplication.OpenURLOptionsKey.annotation]
)
}
To call the function in my extra application delegate, which then worked properly.