'How to authenticate with the Box SDK using SwiftUI?

I have been having a hard time trying to figure out how to authenticate with the Box API using SwiftUI.

As far as I understand, SwiftUI does not currently have the ability to satisfy the ASWebAuthenticationPresentationContextProviding protocol required to show the Safari OAuth2 login sheet. I know that I can make a UIViewControllerRepresentable to use UIKit within SwiftUI, but I can't get this to work.



Solution 1:[1]

I have figured out how to get the OAuth2 login sheet for Dropbox to appear and authenticate the client using SwiftUI.

The trick is to use a Coordinator to make the UIViewControllerRepresentable satisfy a protocol.

import SwiftUI
import BoxSDK
import AuthenticationServices

var boxSDK = BoxSDK(clientId: "<Client ID>", clientSecret: "<Client Secret>")
var boxClient: BoxClient

struct BoxLoginView: View {
    
    @State var showLogin = false
    
    var body: some View {
        VStack {
            Button {
                showLogin = true
            } label: {
                Text("Login")
            }

            BoxView(isShown: $showLogin)
                // Arbitrary frame size so that this view does not take up the whole screen
                .frame(width: 40, height: 40)
        }
    }
}

/// A UIViewController that will present the OAuth2 Safari login screen when the isShown is true.
struct BoxView: UIViewControllerRepresentable {

    typealias UIViewControllerType = UIViewController
    
    let letsView = UIViewController()
    
    @Binding var isShown : Bool
    
    // Show the login Safari window when isShown
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        if(isShown) {
            getOAuthClient()
        }
    }
    
    func makeUIViewController(context _: Self.Context) -> UIViewController {
        return self.letsView
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }
    
    func getOAuthClient() {
        boxSDK.getOAuth2Client(tokenStore: KeychainTokenStore(), context:self.makeCoordinator()) { result in
            switch result {
            case let .success(client):
                boxClient = client
            case let .failure(error):
                print("error in getOAuth2Client: \(error)")
            }
        }
    }
    
    class Coordinator: NSObject, ASWebAuthenticationPresentationContextProviding {
        var parent: BoxView
        
        init(parent: BoxView) {
            self.parent = parent
        }
        
        func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
            return parent.letsView.view.window ?? ASPresentationAnchor()
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michael C