0

I’m currently building out a new MVVM app in SwiftUI and wanted to see whether there was a cleaner way of assigning a value from a CurrentValueSubject to my ViewModel, whilst still achieving animations? 



I’m currently leaning toward solution 2 below as that keeps the animation code neatly within the View, but it’s quite annoying having to setup another State for every animated change. Hoping there’s another way!

Solution 1 - use sink and withAnimation together in the ViewModel:

ViewModel:

authenticationService.authenticationState
    .receive(on: DispatchQueue.main)
    .sink { [unowned self] state in
        withAnimation(.easeInOut(duration: Constants.Animation.slideSpeed)) {
            self.authenticationState = state
        }
    }
    .store(in: &cancellables)

Solution 2 - Use assign in the ViewModel and onReceive (+ withAnimation) in the view

ViewModel:

authenticationService.authenticationState
    .receive(on: DispatchQueue.main)
    .assign(to: &$authenticationState)

View:

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()
    @State private var authenticationState: AuthenticationState = .loggedOut

    private var shiftAnimation: AnyTransition {
        .asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading))
    }

    var body: some View {
        ZStack {
            switch authenticationState {
            case .loggedOut:
                LoginView()
                    .transition(shiftAnimation)
            case .loggedIn:
                AdminView()
                    .transition(shiftAnimation)
            }
            if viewModel.showProgressView {
                ProgressView()
            }
        }
        .onReceive(viewModel.$authenticationState) { state in
            withAnimation(.easeInOut(duration: Constants.Animation.slideSpeed)) {
                self.authenticationState = state
            }
        }
    }
}
Liam Bates
  • 21
  • 1
  • 3

0 Answers0