For some time I have been trying to find a solution to authenticating Dropbox using their SwiftyDropbox SDK in a SwiftUI project, but this was to no avail.
The instructions provided in the readme use an AppDelegate and and SceneDelegate. The latter of which from what I understand is not possible with SwiftUI. I have been able to get the OAuth2 Safari window to launch, but DropboxClientsManager.authorizedClient is always nil.
Finally, I figured it.
Setup info.plist as the SwiftyDropbox readme instructs.
// <app_name>.swift
import SwiftUI
import SwiftyDropbox
@main
struct DropboxTestApp: App {
init() {
DropboxClientsManager.setupWithAppKey("<app key>")
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
// ContentView.swift
import SwiftUI
import SwiftyDropbox
struct ContentView: View {
@State var isShown = false
var body: some View {
VStack {
Button(action: {
self.isShown.toggle()
}) {
Text("Login to Dropbox")
}
DropboxView(isShown: $isShown)
Button {
if let client = DropboxClientsManager.authorizedClient {
print("successful login")
} else {
print("Error")
}
} label: {
Text("Test Login")
}
}
.onOpenURL { url in
let oauthCompletion: DropboxOAuthCompletion = {
if let authResult = $0 {
switch authResult {
case .success:
print("Success! User is logged into DropboxClientsManager.")
case .cancel:
print("Authorization flow was manually canceled by user!")
case .error(_, let description):
print("Error: \(String(describing: description))")
}
}
}
DropboxClientsManager.handleRedirectURL(url, completion: oauthCompletion)
}
}
}
struct DropboxView: UIViewControllerRepresentable {
typealias UIViewControllerType = UIViewController
@Binding var isShown : Bool
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if isShown {
let scopeRequest = ScopeRequest(scopeType: .user, scopes: ["account_info.read", "files.metadata.write", "files.metadata.read", "files.content.write", "files.content.read"], 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()
}
}
You don't need to create an AppDelegate.
I hope that someone may find this useful.