Many times, we are required to open a view that is required to be presented from a UIViewController
in a SwiftUI view. Prominent examples of such being UIDocumentInteractionController
and less prominent ones being SwiftyDropbox to open a login screen of Dropbox in Safari for authentication. The common technique is to create a UIViewController
on demand in UIViewControllerRepresentable
subclass and present view controller from it. But it has never worked for me.
struct DropboxView: UIViewControllerRepresentable {
typealias UIViewControllerType = UIViewController
@Binding var isShown : Bool
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if isShown {
let scopeRequest = ScopeRequest(scopeType: .user, scopes: ["files.content.write"], includeGrantedScopes: false)
DropboxClientsManager.authorizeFromControllerV2(
UIApplication.shared,
controller: uiViewController,
loadingStatusDelegate: nil,
openURL: { (url: URL) -> Void in UIApplication.shared.open(url, options: [:], completionHandler: nil) },
scopeRequest: scopeRequest)
}
}
func makeUIViewController(context _: Self.Context) -> UIViewController {
return UIViewController()
}
}
When I invoke such a view from a SwiftUI view using modifier such as:
.fullScreenCover(isPresented: $showDropboxLoginView) {
DropboxView(isShown: $showDropboxLoginView)
}
I get two errors:
Cannot use Scene methods for URL, NSUserActivity, and other External Events without using SwiftUI Lifecycle. Without SwiftUI Lifecycle, advertising and handling External Events wastes resources, and will have unpredictable results.
and
Attempt to present <SwiftyDropbox.MobileSafariViewController: 0x107919600> on <UIViewController: 0x105a2f130> (from <UIViewController: 0x105a2f130>) whose view is not in the window hierarchy.
I wonder how to present such view controllers from within SwiftUI view?
With this setup you don’t need fullScreenCover
just use the UIViewControllerRepresentable
.
DropboxView(isShown: $showDropboxLoginView)
.frame(width:0, height: 0)
Put it anywhere in the body
.
Both the fullScreenCover
and DropboxClientsManager
seems to be using the root.