[SOLVED] Solution to authenticating SwiftyDropbox in a SwiftUI project

Issue

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.

Answered By – Michael C

Answer Checked By – Gilberto Lyons (BugsFixing Admin)

Leave a Reply

Your email address will not be published.