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.
Related
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
I am working on adding deep linking to my app so that the app will go to a separate page when launched through a deep link. This code that I have so far works perfectly the only issue is that it dosen't work when the app is closed. Is there any way to make this code work when the app is not already running? Thanks for any help in advance!
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).
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
if urlStr.contains("deeplinktopage") {
variable = "NotNone"
if variable != "None" {
DispatchQueue.main.async {
let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homePage = mainStoryboard.instantiateViewController(withIdentifier: "VIEWCONTROLLER") as! VIEWCONTROLLER
self.window?.rootViewController = homePage
}
}
else {
DispatchQueue.main.async {
print("HERE")
let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homePage = mainStoryboard.instantiateViewController(withIdentifier: "VIEWCONTROLLER") as! VIEWCONTROLLER
self.window?.rootViewController = homePage
}
}
}
}
}
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: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
}
I can't get firebase dynamic links to work on new app installs for a SwiftUI app. Their documentation on step 6 and 7 shows stuff for AppDelegate https://firebase.google.com/docs/dynamic-links/ios/receive?authuser=0
Try 1
I moved it to scene delegate but then it asked me to a add application open url so I did and ended up with this code:
extension AppDelegate {
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let sceneConfig = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
sceneConfig.delegateClass = SceneDelegate.self // 👈🏻
return sceneConfig
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
print("deeep shit")
return application(app, open: url,sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,annotation: "")
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
print("deeep shit 2")
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
// Handle the deep link. For example, show the deep-linked content or
// apply a promotional offer to the user's account.
// ...
return true
}
return false
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// 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(userActivity)
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
print(URLContexts, "URLContexts")
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
print(userActivity.webpageURL, userActivity.referrerURL, "sdhagvdgjks")
}
}
RootView()
.onOpenURL(perform: { url in
DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicL, error in
if let url = dynamicL?.url {
/// do stuff
}
}
})
Try 2
I skipped Scene Delegate and used the SwiftUI Modfier onContinuesUserActivity https://developer.apple.com/documentation/swiftui/view/oncontinueuseractivity(_:perform:)
extension AppDelegate {
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
return application(app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: "")
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
// Handle the deep link. For example, show the deep-linked content or
return true
}
return false
}
}
RootView()
.onOpenURL(perform: { url in
DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicL, error in
if let url = dynamicL?.url {
/// do stuff
}
}
})
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb, perform: { activity in
print(activity.webpageURL, activity.referrerURL, "please work")
})
But then I get this error:
[Firebase/Analytics][I-ACS023001] Deep Link does not contain valid required params. URL params: {
"match_message" = "No pre-install link matched for this device.";
"request_ip_version" = "IP_V6";
}
Could someone please help me out?
Had the same problem as I was trying to implement referrals in my app Whisper Memos. Here's what worked for me:
Go to settings for your target, tab Info.
Find section URL Types
Add URL type with your bundle identifier. Should look something like this:
Your onOpenURL handler will now be called, but with a long URL, such as: tech.median.Whisper://google/link/?deep_link_id=https...
Convert your long URL to DynamicLink instance:
let dynamicLinks = DynamicLinks.dynamicLinks()
if let fromCustom = dynamicLinks.dynamicLink(fromCustomSchemeURL: url) {
deepLinkState.handleLink(link: fromCustom)
return
}
Now you can use this dynamic link to navigate inside your app.
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()
}
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.