swiftswiftuidropboxdropbox-sdk

How to authenticate using the SwiftyDropbox API in a SwiftUI project?


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.


Solution

  • 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.