aws-amplify / amplify-ui-swift-authenticator

The Amplify UI Authenticator is a component that supports several authentiation flows using Amplify Authentication.
https://ui.docs.amplify.aws/swift/connected-components/authenticator
Apache License 2.0
15 stars 9 forks source link

Inputs get reset when @Published value is updated if AuthenticatorTheme is used #52

Closed steefeen closed 10 months ago

steefeen commented 10 months ago

Every time the value of a @​Published property is updated, all the inputs get reset. (see video) When removing the AuthenticatorTheme there is no problem. Example code is attached.

https://github.com/aws-amplify/amplify-ui-swift-authenticator/assets/30449693/cf8bcbf9-bbb3-43d1-96ec-962209da155e

import SwiftUI
import Authenticator
import Amplify
import AWSCognitoAuthPlugin
import CoreLocation

class LocationDelegate: ObservableObject{
    @Published var location: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 10.0, longitude: 10.0)

    init() {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
            self.location = CLLocationCoordinate2D(latitude: self.location.latitude + 0.01,
                                                   longitude: self.location.longitude)
        }
    }
}

@main
struct AuthTestApp: App {
    @StateObject private var locationManager = LocationDelegate()

    init() {
         do {
             try Amplify.add(plugin: AWSCognitoAuthPlugin())
             try Amplify.configure()
         } catch {
             print("Unable to configure Amplify \(error)")
         }
     }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(locationManager)
        }
    }
}

struct ContentView: View {
    @EnvironmentObject private var locationManager: LocationDelegate

    var body: some View {
        TabView {
            AuthViewTheme()
                    .tabItem{
                        Image(systemName: "person.3")
                        Text("not ok (with theme)")
                    }.clipped()
            AuthViewNoTheme()
                    .tabItem{
                        Image(systemName: "person.3")
                        Text("ok (no theme)")
                    }.clipped()
        }
    }
}

struct AuthViewNoTheme: View {
    var body: some View {
        Authenticator { state in
        }
    }
}

struct AuthViewTheme: View {
    private let theme = AuthenticatorTheme()

    var body: some View {
        Authenticator { state in
        }
        .authenticatorTheme(theme)
    }
}
ruisebas commented 10 months ago

Hi @steefeen, thanks for opening this issue.

As your LocationDelegate is being set as an environment object on the root ContentView, having its published property updated will trigger an update in all your views. This causes AuthViewTheme to be redrawn each time as its identity changes due to the theme property being re-created.

To address this, you can annotate the theme with a @StateObject:

struct AuthViewTheme: View {
    @StateObject private let theme = AuthenticatorTheme()

    var body: some View {
        Authenticator { state in
        }
        .authenticatorTheme(theme)
    }
}

Or you can move its initialization to your AuthTestApp file and set it as another environment object.

@main
struct AuthTestApp: App {
    @StateObject private var locationManager = LocationDelegate()
    private var theme = AuthenticatorTheme()

    init() {
         do {
             try Amplify.add(plugin: AWSCognitoAuthPlugin())
             try Amplify.configure()
         } catch {
             print("Unable to configure Amplify \(error)")
         }
     }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(locationManager)
                .environmentObject(theme)
        }
    }
}

struct AuthViewTheme: View {
    @EnvironmentObject private var theme: AuthenticatorTheme

    var body: some View {
        Authenticator { state in
        }
        .authenticatorTheme(theme)
    }
}

Please let me know if any of these address your issues. Thanks!

steefeen commented 10 months ago

Hi @ruisebas

replacing:

private let theme = AuthenticatorTheme()

with:

 @StateObject private var theme = AuthenticatorTheme()

in my original example, resolves my issue.

Thanks

ruisebas commented 10 months ago

Great to hear! I'm closing this issue, feel free to open a new one if something comes up.

Thanks!