Update Tutorial 3
MODIFY
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/RootWorkflow.swift
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/RootWorkflow.swift
@@ -51,18 +51,17 @@ extension RootWorkflow {
enum Action: WorkflowAction {
typealias WorkflowType = RootWorkflow
- case login(name: String)
- case logout
+ case logIn(name: String)
+ case logOut
func apply(toState state: inout RootWorkflow.State) -> RootWorkflow.Output? {
switch self {
- case .login(name: let name):
- // When the `login` action is received, change the state to `todo`.
+ case .logIn(name: let name):
state = .todo(name: name)
- case .logout:
- // Return to the welcome state on logout.
+ case .logOut:
state = .welcome
}
+
return nil
}
}
@@ -98,16 +97,16 @@ extension RootWorkflow {
let welcomeScreen = WelcomeWorkflow()
.mapOutput { output -> Action in
switch output {
- // When `WelcomeWorkflow` emits `didLogin`, turn it into our `login` action.
- case .didLogin(name: let name):
- return .login(name: name)
+ // When `WelcomeWorkflow` emits `didLogIn`, turn it into our `logIn` action.
+ case .didLogIn(name: let name):
+ return .logIn(name: name)
}
}
.rendered(in: context)
let welcomeBackStackItem = BackStackScreen.Item(
key: "welcome",
- screen: welcomeScreen.asAnyScreen(),
+ screen: AnyScreen(welcomeScreen),
// Hide the navigation bar.
barVisibility: .hidden
)
@@ -116,25 +115,25 @@ extension RootWorkflow {
backStackItems.append(welcomeBackStackItem)
switch state {
- // When the state is `.welcome`, defer to the WelcomeWorkflow.
case .welcome:
// We always add the welcome screen to the backstack, so this is a no op.
break
- // When the state is `.todo`, defer to the TodoListWorkflow.
case .todo(name: let name):
+ // When the state is `.todo`, defer to the TodoListWorkflow.
let todoBackStackItems = TodoListWorkflow(name: name)
.mapOutput { output -> Action in
switch output {
case .back:
- // When receiving a `.back` output, treat it as a `.logout` action.
- return .logout
+ // When receiving a `.back` output, treat it as a `.logOut` action.
+ return .logOut
}
}
.rendered(in: context)
- // Add the todoBackStackItems to our BackStackItems.
+ // Add the todoBackStackItems to our backStackItems
+
backStackItems.append(contentsOf: todoBackStackItems)
}

MODIFY
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/Edit/TodoEditScreen.swift
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/Edit/TodoEditScreen.swift
@@ -34,20 +34,19 @@ struct TodoEditScreen: Screen {
}
final class TodoEditViewController: ScreenViewController<TodoEditScreen> {
- // The `todoEditView` has all the logic for displaying the todo and editing.
- let todoEditView: TodoEditView
+ private var todoEditView: TodoEditView!
required init(screen: TodoEditScreen, environment: ViewEnvironment) {
- self.todoEditView = TodoEditView(frame: .zero)
-
super.init(screen: screen, environment: environment)
- update(with: screen)
}
override func viewDidLoad() {
super.viewDidLoad()
+ todoEditView = TodoEditView(frame: view.bounds)
view.addSubview(todoEditView)
+
+ updateView(with: screen)
}
override func viewDidLayoutSubviews() {
@@ -57,10 +56,14 @@ final class TodoEditViewController: ScreenViewController<TodoEditScreen> {
}
override func screenDidChange(from previousScreen: TodoEditScreen, previousEnvironment: ViewEnvironment) {
- update(with: screen)
+ super.screenDidChange(from: previousScreen, previousEnvironment: previousEnvironment)
+
+ guard isViewLoaded else { return }
+
+ updateView(with: screen)
}
- private func update(with screen: TodoEditScreen) {
+ private func updateView(with screen: TodoEditScreen) {
// Update the view with the data from the screen.
todoEditView.title = screen.title
todoEditView.note = screen.note

MODIFY
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/Edit/TodoEditWorkflow.swift
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/Edit/TodoEditWorkflow.swift
@@ -140,6 +140,7 @@ extension TodoEditWorkflow {
))
)
)
+
return backStackItem
}
}

MODIFY
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/List/TodoListScreen.swift
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/List/TodoListScreen.swift
* limitations under the License.
*/
-import TutorialViews
import Workflow
import WorkflowUI
+import TutorialViews
struct TodoListScreen: Screen {
// The titles of the todo items
@@ -31,18 +31,19 @@ struct TodoListScreen: Screen {
}
final class TodoListViewController: ScreenViewController<TodoListScreen> {
- let todoListView: TodoListView
+ private var todoListView: TodoListView!
required init(screen: TodoListScreen, environment: ViewEnvironment) {
- self.todoListView = TodoListView(frame: .zero)
super.init(screen: screen, environment: environment)
- update(with: screen)
}
override func viewDidLoad() {
super.viewDidLoad()
+ todoListView = TodoListView(frame: view.bounds)
view.addSubview(todoListView)
+
+ updateView(with: screen)
}
override func viewDidLayoutSubviews() {
@@ -52,10 +53,14 @@ final class TodoListViewController: ScreenViewController<TodoListScreen> {
}
override func screenDidChange(from previousScreen: TodoListScreen, previousEnvironment: ViewEnvironment) {
- update(with: screen)
+ super.screenDidChange(from: previousScreen, previousEnvironment: previousEnvironment)
+
+ guard isViewLoaded else { return }
+
+ updateView(with: screen)
}
- private func update(with screen: TodoListScreen) {
+ private func updateView(with screen: TodoListScreen) {
// Update the todoList on the view with what the screen provided:
todoListView.todoList = screen.todoTitles
todoListView.onTodoSelected = screen.onTodoSelected

MODIFY
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/List/TodoListWorkflow.swift
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Todo/List/TodoListWorkflow.swift
@@ -37,9 +37,11 @@ extension TodoListWorkflow {
struct State {
var todos: [TodoModel]
var step: Step
+
enum Step {
// Showing the list of todo items.
case list
+
// Editing a single item. The state holds the index so it can be updated when a save action is received.
case edit(index: Int)
}
@@ -51,7 +53,7 @@ extension TodoListWorkflow {
TodoModel(
title: "Take the cat for a walk",
note: "Cats really need their outside sunshine time. Don't forget to walk Charlie. Hamilton is less excited about the prospect."
- ),
+ )
],
step: .list
)
@@ -90,7 +92,6 @@ extension TodoListWorkflow {
case .saveChanges(todo: let todo, index: let index):
// When changes are saved, update the state of that `todo` item and return to the list.
state.todos[index] = todo
-
state.step = .list
return nil
}
@@ -123,9 +124,8 @@ extension TodoListWorkflow {
// Define a sink to be able to send actions.
let sink = context.makeSink(of: Action.self)
- let titles = state.todos.map { (todoModel) -> String in
- todoModel.title
- }
+ let titles = state.todos.map(\.title)
+
let todoListScreen = TodoListScreen(
todoTitles: titles,
onTodoSelected: { index in
@@ -138,7 +138,7 @@ extension TodoListWorkflow {
key: "list",
screen: todoListScreen.asAnyScreen(),
barContent: .init(
- title: "Welcome \(name)",
+ title: "Welcome, \(name)",
leftItem: .button(.back(handler: {
// When the left button is tapped, send the .onBack action.
sink.send(.onBack)
@@ -154,8 +154,7 @@ extension TodoListWorkflow {
case .edit(index: let index):
// On the "edit" step, return both the list and edit screens.
- let todoEditItem = TodoEditWorkflow(
- initialTodo: state.todos[index])
+ let todoEditItem = TodoEditWorkflow(initialTodo: state.todos[index])
.mapOutput { output -> Action in
switch output {
case .discard:

MODIFY
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Welcome/WelcomeScreen.swift
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Welcome/WelcomeScreen.swift
@@ -32,17 +32,18 @@ struct WelcomeScreen: Screen {
}
final class WelcomeViewController: ScreenViewController<WelcomeScreen> {
- var welcomeView: WelcomeView
+ private var welcomeView: WelcomeView!
required init(screen: WelcomeScreen, environment: ViewEnvironment) {
- self.welcomeView = WelcomeView(frame: .zero)
super.init(screen: screen, environment: environment)
- update(with: screen)
}
override func viewDidLoad() {
super.viewDidLoad()
+ welcomeView = WelcomeView(frame: view.bounds)
+ updateView(with: screen)
+
view.addSubview(welcomeView)
}
@@ -53,11 +54,14 @@ final class WelcomeViewController: ScreenViewController<WelcomeScreen> {
}
override func screenDidChange(from previousScreen: WelcomeScreen, previousEnvironment: ViewEnvironment) {
- update(with: screen)
+ super.screenDidChange(from: previousScreen, previousEnvironment: previousEnvironment)
+
+ guard isViewLoaded else { return }
+
+ updateView(with: screen)
}
- private func update(with screen: WelcomeScreen) {
- /// Update UI
+ private func updateView(with screen: WelcomeScreen) {
welcomeView.name = screen.name
welcomeView.onNameChanged = screen.onNameChanged
welcomeView.onLoginTapped = screen.onLoginTapped

MODIFY
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Welcome/WelcomeWorkflow.swift
Samples/Tutorial/Frameworks/Tutorial3Complete/Sources/Welcome/WelcomeWorkflow.swift
@@ -23,7 +23,7 @@ import WorkflowUI
struct WelcomeWorkflow: Workflow {
enum Output {
- case didLogin(name: String)
+ case didLogIn(name: String)
}
}
@@ -48,7 +48,7 @@ extension WelcomeWorkflow {
typealias WorkflowType = WelcomeWorkflow
case nameChanged(name: String)
- case didLogin
+ case didLogIn
func apply(toState state: inout WelcomeWorkflow.State) -> WelcomeWorkflow.Output? {
switch self {
@@ -58,9 +58,9 @@ extension WelcomeWorkflow {
// Return `nil` for the output, we want to handle this action only at the level of this workflow.
return nil
- case .didLogin:
- // Return an output of `didLogin` with the name.
- return .didLogin(name: state.name)
+ case .didLogIn:
+ // Return an output of `didLogIn` with the name.
+ return .didLogIn(name: state.name)
}
}
}
@@ -97,8 +97,8 @@ extension WelcomeWorkflow {
sink.send(.nameChanged(name: name))
},
onLoginTapped: {
- // Whenever the login button is tapped, emit the `.didLogin` action.
- sink.send(.didLogin)
+ // Whenever the login button is tapped, emit the `.didLogIn` action.
+ sink.send(.didLogIn)
}
)
}

Update Tutorial 3