Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass the workflow's scope into initialState. #286

Merged
merged 1 commit into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ import com.squareup.sample.authworkflow.AuthState.SecondFactorPrompt
import com.squareup.sample.authworkflow.LoginScreen.SubmitLogin
import com.squareup.sample.authworkflow.SecondFactorScreen.Event.CancelSecondFactor
import com.squareup.sample.authworkflow.SecondFactorScreen.Event.SubmitSecondFactor
import com.squareup.workflow.ui.BackStackScreen
import com.squareup.workflow.Snapshot
import com.squareup.workflow.StatefulWorkflow
import com.squareup.workflow.Workflow
import com.squareup.workflow.WorkflowAction.Companion.emitOutput
import com.squareup.workflow.WorkflowAction.Companion.enterState
import com.squareup.workflow.WorkflowContext
import com.squareup.workflow.rx2.onSuccess
import com.squareup.workflow.ui.BackStackScreen
import kotlinx.coroutines.CoroutineScope

/**
* We define this otherwise redundant typealias to keep composite workflows
Expand All @@ -55,7 +56,8 @@ class RealAuthWorkflow(private val authService: AuthService) : AuthWorkflow,

override fun initialState(
input: Unit,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): AuthState = LoginPrompt()

override fun compose(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import com.squareup.workflow.WorkflowAction.Companion.enterState
import com.squareup.workflow.WorkflowContext
import com.squareup.workflow.invoke
import com.squareup.workflow.rx2.onSuccess
import kotlinx.coroutines.CoroutineScope

enum class RunGameResult {
CanceledStart,
Expand Down Expand Up @@ -79,7 +80,8 @@ class RealRunGameWorkflow(

override fun initialState(
input: Unit,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): RunGameState = snapshot?.let { RunGameState.fromSnapshot(snapshot.bytes) }
?: NewGame()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.squareup.workflow.WorkflowAction.Companion.emitOutput
import com.squareup.workflow.WorkflowAction.Companion.enterState
import com.squareup.workflow.WorkflowAction.Companion.noop
import com.squareup.workflow.WorkflowContext
import kotlinx.coroutines.CoroutineScope

typealias TakeTurnsWorkflow = Workflow<PlayerInfo, CompletedGame, GamePlayScreen>

Expand All @@ -43,7 +44,8 @@ class RealTakeTurnsWorkflow : TakeTurnsWorkflow,

override fun initialState(
input: PlayerInfo,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): Turn = Turn()

override fun compose(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ import com.squareup.sample.gameworkflow.RunGameWorkflow
import com.squareup.sample.mainworkflow.MainState.Authenticating
import com.squareup.sample.mainworkflow.MainState.RunningGame
import com.squareup.sample.panel.asPanelOver
import com.squareup.workflow.ui.AlertContainerScreen
import com.squareup.workflow.Snapshot
import com.squareup.workflow.StatefulWorkflow
import com.squareup.workflow.Workflow
import com.squareup.workflow.WorkflowAction.Companion.enterState
import com.squareup.workflow.WorkflowContext
import com.squareup.workflow.composeChild
import com.squareup.workflow.ui.AlertContainerScreen
import kotlinx.coroutines.CoroutineScope

/**
* Application specific root [Workflow], and demonstration of workflow composition.
Expand All @@ -49,7 +50,8 @@ class MainWorkflow(

override fun initialState(
input: Unit,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): MainState = snapshot?.let { MainState.fromSnapshot(snapshot.bytes) }
?: Authenticating

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.squareup.workflow

import kotlinx.coroutines.CoroutineScope

/**
* A composable, stateful object that can [handle events][WorkflowContext.onEvent],
* [delegate to children][WorkflowContext.composeChild], [subscribe][onReceive] to arbitrary streams from
Expand Down Expand Up @@ -71,10 +73,15 @@ abstract class StatefulWorkflow<
* If the workflow is being restored from a [Snapshot], [snapshot] will be the last value
* returned from [snapshotState], and implementations that return something other than
* [Snapshot.EMPTY] should create their initial state by parsing their snapshot.
* @param scope
* The [CoroutineScope] in which this workflow lives. The scope will be cancelled when the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we provide an example of how to use it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what kind of example would be useful. It's basically only there to support Workers (which users don't need to care about unless they want to look at source directly), or cancellation of some external resource, and I don't even know yet what the best way will be to use this scope to cancel those. I'll file an issue to track improving this doc as we go forward: #290.

* workflow is being torn down, so this scope can be used to start coroutines to track the
* lifetime of the workflow "session".
*/
abstract fun initialState(
input: InputT,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidapgar @bencochran @timdonnelly Seems like we should be able to come up with a similar object to pass into the analogous method in Swift — basically something that can accept tearDown duties instead of the method on WorkflowContext.

Copy link
Collaborator Author

@zach-klippenstein zach-klippenstein Apr 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not suggesting this is a good direction, but if Swift wanted to match this as accurately as possible, it would be a teardown hook plus a scheduler. Having access to a coroutine scope is useful for us, but given the lack of similar concepts in Swift I'm not sure this would be the best API for them. But curious to hear what yall think!

): StateT

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.squareup.workflow

import com.squareup.workflow.WorkflowAction.Companion.emitOutput
import kotlinx.coroutines.CoroutineScope

/**
* Minimal implementation of [Workflow] that maintains no state of its own.
Expand All @@ -40,7 +41,8 @@ abstract class StatelessWorkflow<InputT : Any, OutputT : Any, RenderingT : Any>
private val statefulWorkflow = object : StatefulWorkflow<InputT, Unit, OutputT, RenderingT>() {
override fun initialState(
input: InputT,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
) = Unit

@Suppress("UNCHECKED_CAST")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ interface WorkflowContext<StateT : Any, in OutputT : Any> {
* Teardown handlers should be non-blocking and execute quickly, since they are invoked
* synchronously during the compose pass.
*/
@Deprecated("Use the CoroutineScope parameter to initialState.")
fun onTeardown(handler: () -> Unit)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ internal class WorkflowNode<InputT : Any, StateT : Any, OutputT : Any, Rendering

private var state: StateT = initialState
?: snapshot?.restoreState(initialInput, workflow)
?: workflow.initialState(initialInput, snapshot = null)
?: workflow.initialState(initialInput, snapshot = null, scope = this)

private var lastInput: InputT = initialInput

Expand Down Expand Up @@ -237,7 +237,7 @@ internal class WorkflowNode<InputT : Any, StateT : Any, OutputT : Any, Rendering
bytes.parse { source ->
val stateSnapshot = source.readByteStringWithLength()
val childrenSnapshot = source.readByteString()
val state = workflow.initialState(input, Snapshot.of(stateSnapshot))
val state = workflow.initialState(input, Snapshot.of(stateSnapshot), this@WorkflowNode)
return Pair(state, Snapshot.of(childrenSnapshot))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION")

package com.squareup.workflow.internal

Expand All @@ -28,6 +28,7 @@ import com.squareup.workflow.internal.Behavior.WorkflowOutputCase
import com.squareup.workflow.internal.RealWorkflowContext.Composer
import com.squareup.workflow.internal.RealWorkflowContextTest.TestComposer.Rendering
import com.squareup.workflow.stateless
import kotlinx.coroutines.CoroutineScope
import kotlin.reflect.full.starProjectedType
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -62,7 +63,8 @@ class RealWorkflowContextTest {
private class TestWorkflow : StatefulWorkflow<String, String, String, Rendering>() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String = fail()

override fun compose(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.squareup.workflow.WorkflowAction.Companion.emitOutput
import com.squareup.workflow.WorkflowContext
import com.squareup.workflow.internal.Behavior.WorkflowOutputCase
import com.squareup.workflow.internal.SubtreeManagerTest.TestWorkflow.Rendering
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
Expand All @@ -47,7 +48,8 @@ class SubtreeManagerTest {

override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String {
started++
return "initialState:$input"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION")

package com.squareup.workflow.internal

Expand All @@ -34,6 +34,7 @@ import com.squareup.workflow.util.ChannelUpdate
import com.squareup.workflow.util.ChannelUpdate.Closed
import com.squareup.workflow.util.ChannelUpdate.Value
import com.squareup.workflow.writeUtf8WithLength
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.TimeoutCancellationException
Expand Down Expand Up @@ -63,7 +64,8 @@ class WorkflowNodeTest {

override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String {
assertNull(snapshot)
return "starting:$input"
Expand Down Expand Up @@ -132,7 +134,8 @@ class WorkflowNodeTest {
val workflow = object : StringWorkflow() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String {
assertNull(snapshot)
return input
Expand Down Expand Up @@ -166,7 +169,8 @@ class WorkflowNodeTest {
val workflow = object : StringWorkflow() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String {
assertNull(snapshot)
return input
Expand Down Expand Up @@ -205,7 +209,8 @@ class WorkflowNodeTest {
val workflow = object : StringWorkflow() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String {
assertNull(snapshot)
return input
Expand Down Expand Up @@ -262,7 +267,8 @@ class WorkflowNodeTest {
val workflow = object : StringWorkflow() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String {
assertNull(snapshot)
return input
Expand Down Expand Up @@ -307,7 +313,8 @@ class WorkflowNodeTest {
val workflow = object : StringWorkflow() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String {
assertNull(snapshot)
return input
Expand Down Expand Up @@ -358,7 +365,8 @@ class WorkflowNodeTest {
val workflow = object : StatefulWorkflow<String, String, Nothing, String>() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String = snapshot?.bytes?.parse {
it.readUtf8WithLength()
.removePrefix("state:")
Expand Down Expand Up @@ -401,7 +409,8 @@ class WorkflowNodeTest {
val workflow = object : StatefulWorkflow<String, String, Nothing, String>() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String = if (snapshot != null) "restored" else input

override fun compose(
Expand Down Expand Up @@ -443,7 +452,8 @@ class WorkflowNodeTest {
val childWorkflow = object : StatefulWorkflow<String, String, Nothing, String>() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String = snapshot?.bytes?.parse {
it.readUtf8WithLength()
.removePrefix("child state:")
Expand All @@ -465,7 +475,8 @@ class WorkflowNodeTest {
val parentWorkflow = object : StatefulWorkflow<String, String, Nothing, String>() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String = snapshot?.bytes?.parse {
it.readUtf8WithLength()
.removePrefix("parent state:")
Expand Down Expand Up @@ -519,7 +530,8 @@ class WorkflowNodeTest {
val workflow = object : StatefulWorkflow<Unit, Unit, Nothing, Unit>() {
override fun initialState(
input: Unit,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
) {
if (snapshot != null) {
restoreCalls++
Expand Down Expand Up @@ -568,7 +580,8 @@ class WorkflowNodeTest {
val workflow = object : StatefulWorkflow<String, String, Nothing, String>() {
override fun initialState(
input: String,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): String = snapshot?.bytes?.parse {
// Tags the restored state with the input so we can check it.
val deserialized = it.readUtf8WithLength()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.squareup.workflow.util.ChannelUpdate.Closed
import com.squareup.workflow.util.ChannelUpdate.Value
import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException
import java.io.IOException
import kotlin.test.Test
Expand Down Expand Up @@ -59,7 +60,8 @@ class SubscriptionsTest {

override fun initialState(
input: Boolean,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): Boolean = input

override fun compose(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.squareup.workflow.invoke
import com.squareup.workflow.stateless
import com.squareup.workflow.testing.testFromStart
import io.reactivex.subjects.SingleSubject
import kotlinx.coroutines.CoroutineScope
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
Expand Down Expand Up @@ -73,7 +74,8 @@ class WorkflowContextsTest {
val workflow = object : StatefulWorkflow<Unit, Boolean, Nothing, Unit>() {
override fun initialState(
input: Unit,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): Boolean = true

override fun compose(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.squareup.workflow.util.ChannelUpdate
import com.squareup.workflow.util.ChannelUpdate.Closed
import com.squareup.workflow.util.ChannelUpdate.Value
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
Expand Down Expand Up @@ -60,7 +61,8 @@ class ChannelSubscriptionsIntegrationTest {

override fun initialState(
input: Boolean,
snapshot: Snapshot?
snapshot: Snapshot?,
scope: CoroutineScope
): Boolean = input

override fun compose(
Expand Down
Loading