ra1028 / swiftui-atom-properties

⚛️ Atomic approach state management and dependency injection for SwiftUI

Home Page:https://ra1028.github.io/swiftui-atom-properties/documentation/atoms

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Doc Request]: animation

smiLLe opened this issue · comments

Checklist

  • Reviewed the README and documentation.
  • Confirmed that this is uncovered by existing docs or examples.
  • Checked existing issues & PRs to ensure not duplicated.

Description

This is not exactly a documentation request or a feature request. More a question.
Previously when I had a .task {} I could do something like this:

@State private var asyncData: Data = .loading

var body: some View {
  Group {
    switch asyncData {
      case .success:
      case .loading:
      case .failure:
    }
  }
  .task {
    withAnimation {
      asyncData = .loading
    }
    let data = await fetch()
    withAnimation {
      asyncData = .data
    }
  }
}

And it had great animations by changing the content from loading > success and so on.
It no longer animates when I use Suspense because there is no withAnimation involved when I use
@Watch(DataAtom()) private var dataTask.
Do you have a suggestion or an idea on how I can achieve the "old" behaviour?

Motivation & Context

No response

@smiLLe

View.animation modifier would work for you!

@Watch(DataAtom()) 
private var dataTask

var body: some View {
  Group {
    switch asyncData {
      case .success:
      case .loading:
      case .failure:
    }
  }
  .animation(.default, value: dataTask.value)
}

@ra1028

The Atom is a ThrowingTaskAtom and my code looks like this

@Watch(DataAtom()) 
private var dataTask

var body: some View {
  List {
    Section {
      Suspense(dataTask) {
      }
      suspending: {}
      failure: {}
    }
    .animation( ... )
  }
}

It is not possible to add the .animation() mod and use the dataTask or its phase because the Phase Failure is an any Error and does not conform to Equatable. I guess I have no option but to rewrite the ThrowingTaskAtom to be a TaskAtom

I will close as this is just not possible to implement using ThrowingTaskAtom. It is working for TaskAtom and the animation() has to be used on the List List {}.animation()

@smiLLe

My bad, my example code lacked some important points I wanted to suggest.
You might want to try using phase modifier to handle the async value as an enum representation so you can set phase.value as animation's value argument.

// DataAtom is a ThrowingTaskAtom
@Watch(DataAtom().phase) 
private var dataPhase: AsyncPhase<Data, Error>

var body: some View {
  Group {
    switch dataPhase {
      case .suspending: ...
      case .success(let value): ...
      case .failure(let error): ...
    }
  }
  .animation(.default, value: dataPhase.value)
}

@ra1028 Ohhh, I was trying to use phase but not its .value which is working :) Thanks for the help

Which made me think of, why do we need the Suspense View when we could just use phase with switch cases?

Suspense is for those who want to handle Task instance directly, but yes, phase is more convenient in most cases :)