wayfair / vsm-ios

An iOS framework for the VSM Architecture
MIT License
9 stars 2 forks source link

Support SwiftUI Pull-to-Refresh #50

Open albertbori opened 1 month ago

albertbori commented 1 month ago

Problem Statement

SwiftUI's pull-to-refresh functionality requires an operation that uses structured concurrency (async/await) to indicate to the UI when the refreshing is complete. This controls the animation of the refresh indicator. Currently, VSM does not directly support a mechanism for awaiting a specific state in a structurally concurrent context.

Example

List(mailbox.conversations) { conversation in
    ConversationCell(conversation)
}
.refreshable {
    await mailbox.fetch()
}

Proposed Solution

Add an async observe overload that allows the engineer to wait for a specific state using Swift Concurrency. When the view state is detected, the function should return, which will complete the refresh animation. If the view state is not hashable, a closure can be provided to test the state's equality.

Hypothetical Example

List(model.conversations) { conversation in
    ConversationCell(conversation)
}
.refreshable {
    await $state.observe(
        model.refresh(), 
        until: { state in
            if case .loaded(_) = state {
                return true
            }
            return false
        }
    )
}

Alternatives Considered

Creating a refreshable overload in an extension on relevant SwiftUI types that relies on a Binding instead of swift concurrency. (Tangent: This is the API that the SwiftUI team should have used.)

Example

List(mailbox.conversations) { conversation in
    ConversationCell(conversation)
}
.refreshable($isRefreshing)

This is a less-favored solution because it deviates the engineer from Apple's frameworks and creates a standard that can be confusing to many.

Additional Context

refreshable's tech design is an interesting move from the SwiftUI team in choosing a swift concurrency approach in a library that is otherwise almost exclusively driven by observability (KVO, Publishers, and Bindings). It's hard to tell if this is a new pattern that we will see more of, or a small digression in an otherwise largely declarative/observable framework.