How to send parameters in Firebase Dynamic Links - swift

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&param2%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.

Related

Environment Object Method error in Scene Delegate

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.

Error when requesting review using SKStoreReviewController

When I use SKStoreReviewController.requestReview() to request a review from the Scene Delegate in my SwiftUI application, I get the following message:
Error in UIKit client: -[UIWindow setScreen:] should not be called if the client adopts UIScene lifecycle. Call -[UIWindow setWindowScene:] instead..
I am using the following code in the scene delegate to request the review:
if let windowScene = scene as? UIWindowScene {
if UserDefaults.standard.integer(forKey: "launchCount") == 10 {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: homeView)
self.window = window
window.makeKeyAndVisible()
if #available(iOS 14, *) {
SKStoreReviewController.requestReview(in: windowScene)
}
else {
SKStoreReviewController.requestReview()
}
}
}
This code is inside the scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) method of the Scene Delegate. The method for iOS 14 is working where the review is being requested from the UIWindowScene, but the method for earlier versions is not working and returns the message specified previously. Is there any way to fix this? Thanks in advance for all the help.
You can use my small wrapper around SKStoreReviewController which solves this problem.
import SwiftUI
import AppReview
struct ContentView: View {
var body: some View {
VStack {
Text("SwiftUI")
}.onAppear {
AppReview.requestIf(launches: 3)
}
}
}
https://github.com/mezhevikin/AppReview

Understanding addStateDidChangeListener firebase authentication

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.

How to fix AppDelegate's window == nil in project with scenes?

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
}

How get rootViewController with iPadOS multi window (SceneDelegate)?

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