aws-amplify / docs

AWS Amplify Framework Documentation
https://docs.amplify.aws
Apache License 2.0
487 stars 1.06k forks source link

Amplify Libraries Swift - Authentication - Sign In with Web UI - Looking for SwiftUI/UIKit example #5791

Open lawmicha opened 1 year ago

lawmicha commented 1 year ago

Describe the content issue:

image

I feel like there's some missing content here. I wasn't working from a UIViewController and was building a SwiftUI app, I wasn't sure If i wanted to go down the route of creating a UIViewRepresentable, and then get the UIWindow? I had no idea how to get this working, and went searching else where. I feel we should improve this documentation to include the entire UIViewController example and the SwiftUI example. In the end, I went with this, but not sure if this should be the recommended approach

Globally define:

// Use `@MainActor` since the window will be accessed from the UI, via a button tap for example.
@MainActor
private var window: UIWindow {
    guard
        let scene = UIApplication.shared.connectedScenes.first,
        let windowSceneDelegate = scene.delegate as? UIWindowSceneDelegate,
        let window = windowSceneDelegate.window as? UIWindow
    else { return UIWindow() }

    return window
}
func signInWithWebUI() async {
    do {
        let signInResult = try await Amplify.Auth.signInWithWebUI(presentationAnchor: window)
        if signInResult.isSignedIn {
            print("Sign in succeeded")
        }
    } catch let error as AuthError {
        print("Sign in failed \(error)")
    } catch {
        print("Unexpected error: \(error)")
    }
}

Then provide a simple SwiftUI example

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Sign In with HostedUI") {
                Task {
                    await signInWithWebUI()
                }
            }
        }
    }
}

URL page where content issue is:

https://docs.amplify.aws/lib/auth/signin_web_ui/q/platform/ios/#update-infoplist

atierian commented 1 year ago

Another option I tend to use when invoking signInWithWebUI(...) from SwiftUI.

// ContentView.swift
struct ContentView: View {
    @EnvironmentObject var sceneDelegate: SceneDelegate

    var body: some View {
        // ...
    }

    func signInWithWebUI() async {
        do {
            let signInResult = try await Amplify.Auth.signInWithWebUI(
                presentationAnchor: sceneDelegate.window!, // probably best to gracefully unwrap in the example :smile:
            )

            if signInResult.isSignedIn {
                print("Sign in succeeded")
            }
        } catch let error as AuthError {
            print("Sign in failed \(error)")
        } catch {
            print("Unexpected error: \(error)")
        }
    }
}
// MyApp.swift
@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }

    init() {
        // configure Amplify
    }

}

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        configurationForConnecting connectingSceneSession: UISceneSession,
        options: UIScene.ConnectionOptions
    ) -> UISceneConfiguration {
        let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
        if connectingSceneSession.role == .windowApplication {
            configuration.delegateClass = SceneDelegate.self
        }
        return configuration
    }
}

class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        if #available(iOS 15.0, *) {
            self.window = (scene as? UIWindowScene)?.keyWindow
        } else {
            self.window = (scene as? UIWindowScene)?.windows
                .first(where: \.isKeyWindow)
        }
    }
}