There are some similar questions but they are all about how to get window when project uses scenes. But my one is slightly different.
You propose to get the active window via the different way - it is ok. But what to do with old libraries (in my case it is custom activity views) which access to the optional window property with "implicitly unwrapping":
appDelegate.window!
Is it possible to override appDelegate.window property getter to return the current window?
Here is possible approach
1) declare window property in app delegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
...
2) assign it explicitly in scene on window showing (or creation, which place is preferable)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
...
window.makeKeyAndVisible()
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.window = window
}
Related
SO Community
I have been self-teaching myself SwiftUI and I've just come across a problem while creating an application in the Scene Delegate. When I add an environment object method to the scene delegate to assign an object to the environment of a view hierarchy, Xcode sends these errors on my let contentView = ContentView().environmentObject(delegate.myData) line of code, "Expected member name following '.' ", and "Missing argument for parameter 'appData' in call ".
I will also attach a picture of my ContentView.swift File to show where I placed my #EnviromentObject reference within the body Content View. If you guys have any questions can you please try to help me out, this has been a bum time. Thanks.
Link to Scene Delegate.Swift file : enter image description here
Link to ContentView.Swift file : enter image description here
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options
connectionOptions: UIScene.ConnectionOptions) {
let app = UIApplication.shared
let delegate = app.delegate as! AppDelegate
let contentView = ContentView()
.environmentObject(delegate.myData)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
EnvironmentObjects are not passed via parameter like you're trying to do in your ContentView initializer. Instead, they are passed "magically" behind-the-scenes and exposed via the #EnvironomentObject property wrapper.
My suggestion is to get rid of your init method and use onAppear { } to set your contentData fields:
var body: some View {
VStack {
//...
}.onAppear {
// do initial setup
}
}
You should also make sure that you're using the same capitalization of appData -- you're using appdata sometimes, which is leading to many of your compilation errors.
Hi I am new to firebase and want to confirm my understanding of an auth function.(addStateDidChangeListener)
A little background:
When the app is first launched, the app delegate is called first and then this sceneDelegate function is called. Now it will present a screen based on whether the user is signed in or not. If they are not signed up it will present them with my signin/signup screen (self.createHandleSignInOrSignUp()).
Now 2 questions:
After they sign up or sign in the app jumps back to this block of code inside .addStateDidChangeListener.
This is because the auth state changes, right?
Also while the app is running you can sign out, and then this block of code is called.
This is because this function is always 'listening' for auth state changes while the app is running?
The code below is my part of my SceneDelegate.swift file and the function I have questions about.
Thank you
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
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
handle = Auth.auth().addStateDidChangeListener({ auth, user in
if (user != nil) {
self.window!.rootViewController = self.createTabBar()
}
else {
self.window!.rootViewController = self.createHandleSignInOrSignUp()
}
})
window?.makeKeyAndVisible()
}
The completion handler for your auth state change listener will be called each time the auth state changes. This typically happens asynchronously, for example when the user signs in or signs out.
The auth state listener remains active until you remove it, or until the app exits.
I am developing an iOS app with Swift.
I want to send a parameter with Firebase Dynamic Links.
I created a Dynamic Link called https://test.page.link/test1.
I want to attach parameters as below.
https://test.page.link/test1?id=000000
How can I get this parameter when I click on the link and launch the app?
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).
// Create the SwiftUI view that provides the window contents.
let rootView = ListView()
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: rootView)
self.window = window
window.makeKeyAndVisible()
}
guard let userActivity = connectionOptions.userActivities.first(where: { $0.webpageURL != nil }) else { return }
print("url: \(userActivity.webpageURL!)") // <- No parameters could be obtained from here.
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
print("url: \(userActivity.webpageURL!)") // <- No parameters could be obtained from here.
}
...
}
So you can not pass parameters by short-version dynamic link.
https://PROJECTNAME.page.link/LINKNAME is short-version dynamic link.
You can pass get-query-parameters by using long-version dynamic link.
Here is an example for passing {param1: 'one', param2: 'two'} to app.
https://PROJECTNAME.page.link/?link=https%3A%2F%2FPROJECTNAME.page.link/LINKNAME%3Fparam1%3Done¶m2%3Dtwo&apn=XXXXXX&isi=XXXXX&ibi=XXXXXX
You can find your long-version dynamic link by clicking the ⋮ menu next to the link on the console, going in Link Details and using your 'Long Dynamic Link'. Add parameters to the inner link and they will come through in the app.
I have been looking for a while and cannot find an answer to this question that does not involve storyboards.
It's quite simple. When you build a new iOS app in Xcode you are given the ViewController.swift file that contains the initial ViewController() class for the app. This view controller is the main view for the app.
If I rename the class to say ViewControllerTest(), when I build and run the app now only loads a blank screen since it cannot find the ViewController class as I renamed it.
How do I set the project in Xcode to use the new ViewControllerTest() class as the initial view controller?
Thanks
If you want to disregard using the storyboard entirely, which I prefer because then all code is in one place, you need to add the following in your AppDelegate or SceneDelegate depending on what version Xcode you are creating your project with.
Xcode 10 and earlier
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
return true
}
}
for Xcode 10 and earlier you should also change the value of the UIMainStoryboardFile property to an empty string in your Info.plist.
Xcode 11 and later
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
}
}
for Xcode 11 projects and newer, you should populate the Application Scene Manifest property as shown here:
I am using Xcode 11 (beta3) and building an app for iOS 13. In my project I created the delegate methods for UIWindowSceneDelegate declaring it in Info.plist.
Now I'm able to create multiple windows (and UIScene).
How can I access the rootViewController now I've not anymore a single window? I need it to get some reference to objects and bounds it holds.
In my AppDelegate window is nil, and in my ViewController (child view controller) instance I tried using self.view.window.rootViewController but I found out that viewDidLoad() is too soon (I think) and the window is still nil, works in viewDidAppear(), but I don't need to make this process every time the view controller appears.
What's the best practice with this new way to handle application scenes?
Here is my AppDelegate:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
return true
}
func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
My SceneDelegate:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// yes it's empty, I'm using storyboard
}
Now you have more than one rootViewController, one for each scene.
First, you have to answer which one you need at the moment of usage.
Probably you want to get one of the rootViewController of the currently active scene then you can use this:
var rootVC:UIViewController? = nil
if #available(iOS 13.0, *) {
for scene in UIApplication.shared.connectedScenes {
if scene.activationState == .foregroundActive {
rootVC = ((scene as? UIWindowScene)!.delegate as! UIWindowSceneDelegate).window!!.rootViewController
break
}
}
} else {
// Fallback on earlier versions
}
You can access connected scenes using:
UIApplication.shared.connectedScenes
As per Apple documentation:
Connected scenes are those that are in memory and potentially doing active work. A connected scene may be in the foreground or background, and it may be onscreen or offscreen.
Then you can iterate through them and try to get UIWindowScene from there.
guard let windowScene = (scene as? UIWindowScene) else { return }
print(windowScene.windows) // This will print windows associated with the scene.
On the other hand, from the view controller you can access the window through the view:
self.view.window