I implemented branch.io links and it is working fine for WhatsApp and from Notes, but not Facebook Messenger or any other place with in-app browser.
As per documentation:
To work around this limitation, your links must have deepviews or something similar enabled, with a call-to-action link/button that has a Universal Link behind it. This way, clicking a link from the app feed will open a webview containing your deepview page, and the user can then click the link/button to launch your app.
For example Facebook Messenger should open in-app browser with deepview of my link, and if user tap button - my app should be opened, it is fine for me, but doesn't work like this.
In my case facebook is opening in-app browser with my deepview and automatically (I didn't tap button) redirects me to... AppStore :(. But if I open my app manually, I'm getting callback and app is redirecting me to proper place.
It is recommended to use forced redirections for links shared on Facebook since Universal Links work conditionally. You can force Facebook to open your app by appending $uri_redirect_mode=2 as a query parameter to your Branch link.
eg: https://example.app.link/83jlowd0?$uri_redirect_mode=2
You can learn more about forced redirections here- https://docs.branch.io/pages/links/integrate/#forced-redirections
If you still face issues, please write to support#branch.io and someone from the Branch team will be able to assist you with this.
I had the same problem. the branch object returned from Branch.io SDK was returning an object without all parameters.
All I did was follow the great document provided by Branch.io
document provided by Branch.io
So as I was using the Scene in my app. Calling these methods in my sceneDelegate solved the issue.
import UIKit
import Branch
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 }
// workaround for SceneDelegate continueUserActivity not getting called on cold start
if let userActivity = connectionOptions.userActivities.first {
BranchScene.shared().scene(scene, continue: userActivity)
}
}
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 scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
BranchScene.shared().scene(scene, continue: userActivity)
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
BranchScene.shared().scene(scene, openURLContexts: URLContexts)
}
}
Related
I'm implementing deep links in ios using universal links.
From what I can see, when the app is in the background, it's working,using the sceneDelegate method
func scene(_ scene: UIScene, continue userActivity: NSUserActivity)
however, if the app is not in the background, the deep link, just opens the app without sending the information of the link.
Is it possible to redirect the user to a specific place in the app using universal links or some other way if the app is not in the background, for example after a reboot or if the user killed the app?
Thanks
That delegate method will only be triggered when the user is the app is in the background, in order to make universal links work when the app is killed you need to trigger the willConnectTo delegate method:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let userActivity = connectionOptions.userActivities.first,
userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL?.absoluteString else {
return
}
handleUniversalLink(url: url, scene: scene)
}
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 have an old Xcode project without UISceneDelegate methods. Is it possible to migrate an old Xcode project to a new one with UISceneDelegate methods BUT still maintaining compatibility with iOS 12?
If so, how? Because I see a lot of bugs in iOS 14 for which the only workaround is using UISceneDelegate methods.
EDIT 1:
Make sure you query for windowOrientation, after View Controller's view is rendered. Typically in viewDidLoad() and viewWillAppear(_:), view.window is nil, check this answer. Just check value for windowOrientation in viewDidAppear(_:).
If you have some issues to access this value even before try the following definition
private var windowOrientation: UIInterfaceOrientation {
if #available(iOS 13.0, *) {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? .unknown
} else {
// Fallback on earlier versions
return UIApplication.shared.statusBarOrientation
}
}
I am not sure if your app uses multiple windows or not but if there is only one(since you are not creating any window programatically), the following definition should work fine.
var hasTopNotch: Bool {
return UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0 > 20
}
Original Answer:
UISceneDelegate has been introduced in iOS 13.0 so no way to be compatible with iOS 12, you need to depend on UIApplicationDelegate totally. To support UISceneDelegate in for iOS 13.x, you need to add explicit availability checking to avoid compilation error.
Steps 1: Add Scene Manifest in Info.plist
Open Info.plist as Source Code and add the following
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
Step 2: Create SceneDelegate.swift file with the following content
import UIKit
#available(iOS 13.0, *)
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.
}
}
Step 3: Update AppDelegate
Add UISceneSession Lifecycle methods.
// 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.
}
ii) Finally refer to Apple Documentation. You may refer to https://dev.to/kevinmaarek/add-a-scene-delegate-to-your-current-project-5on for additional clean up and setup tasks.
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.
}
}
My issue
I'm implementing URL Schemes in my application and they're overall working fine when the app is in the foreground or the background. However, I've noticed that when it is completely closed and another app tries to access content using my URL (eg. app:page?image=1 ) which would normally work, it just opens the app but the content is never caught.
My Approach
I've set up code in both my AppDelegate and SceneDelegate methods
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:])
And
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
Desired behavior
It opens when the app is in the background, foreground or closed
Actual behavior
It only opens when in foreground or background
To handle incoming URLs we simply call this function in both the scene(_:willConnectTo:options:) and the scene(_:openURLContexts:) delegate methods:
If the App is closed:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
// Since this function isn't exclusively called to handle URLs we're not going to prematurely return if no URL is present.
if let url = connectionOptions.urlContexts.first?.url {
handleURL(url: url)
}
}
If the app is in background or foreground
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
// Get the first URL out of the URLContexts set. If it does not exist, abort handling the passed URLs and exit this method.
guard let url = URLContexts.first?.url else {
return NSLog("No URL passed to open the app")
}
handleURL(url: url)
}
You can revert to the following article for more about scene delegate and URL Schemes: Custom URL Schemes in iOS
Since your app is currently not running, it will be launched with those launch options. i.e. those options will be passed to willFinishLaunchingWithOptions: / didFinishLaunchingWithOptions: instead. Add you code to one of these methods.
For more information, read documentation about how to Respond to the Launch of Your App, or, more specifically Determine Why Your App Was Launched.
EDIT:
As commented by #paulw11 below, scene delegate works differently, and must be handled separately.
However, in Respond to Scene-Based Life-Cycle Events section, the last point is:
In addition to scene-related events, you must also respond to the
launch of your app using your UIApplicationDelegate object. For
information about what to do at app launch, see
Responding to the Launch of Your App
So I assume, we still need to handle launch in willdidFinishLaunchingWithOptions / didFinishLaunchingWithOptions.