Using SwiftCurrent with MVVM Part 2

Pt 2. The second. Say it’s not so, Expenso!

Richard Gist
SwiftCurrent
4 min readNov 1, 2021

--

In part 1, I forked Wiggles to update it to use SwiftCurrent. Ultimately, that proved trivial to do and didn’t really impact much, so this time I’m going to fork Expenso and see if that proves any harder to do.

Moderately Expenso

Getting Started

Unlike last time, Expenso already has some dependencies, and they are being brought in with CocoaPods. Like last time, this is a SwiftUI app, so I’ll limit my pod install to just bring in SwiftUI needed things.

CocoaPods adding SwiftCurrent with a pod named SwiftCurrent slash SwiftUI.

Finding the First Workflow

Like last time, I’ll track down the screens I want to use to create a Workflow. Starting with ExpensoApp, I see that there is a conditional view.

A scene body with an if else statement returning an Authenticate View or Expense View.

Digging through AuthenticateView, we see that it also leads to ExpenseView. LIGHTBULB! These 2 screens definitely need to be in the same workflow. Let’s go ahead and convert these to use SwiftCurrent. First, we conform them to the protocol.

Two files being conformed to Flow Representable. Both files have an added weak variable called “underscore workflow pointer.” Authenticate View now has an initializer to set the view model.

Let’s make sure everything is still working.

Demonstration of the Expenso App functioning as expected.

Good, everything works.

Making the First Workflow

Let’s switch to SwiftCurrent for the starting view. Something we’re going to want to change is that the view showing AuthenticateView shouldn’t be the one to have to check for if we use authentication. AuthenticateView should be responsible for that. So let’s implement shouldLoad, but because this is MVVM, the view is going to request the model to do that logic:

Authenticate View and Authenticate View Model updated to each have a function called should load. The model returns the value from User Defaults used earlier.

Now, I just need to remove the NavigationLink from AuthenticateView and have it proceedInWorkflow when it successfully authenticates.

A new on receive closure tied to did authenticate on the model. The closure calls proceed in workflow.

Not shown in this screenshot is the change to have didAuthenticate to be Published. Something else I’m noticing is that we have a NavigationView defined in each view, and these are causing an issue with the action sheets. So I will remove the navigation views and leave only 1 call to hide the navigation bar per view. Once I’ve done that, then I’ll make the Workflow in ExpensoApp.

The Scene body has been updated to use Workflow Launcher and calls then proceed with Authenticate view dot self and then proceed with expense view dot self.

And let’s see if it works.

Demonstration of Expenso’s authentication feature functioning as expected.
I assure you that this is not the same GIF!

Woohoo! We got AuthenticateView into the workflow, and it optionally shows up based on the logic found in its view model.

Finish the Workflow

I think our workflow should take us all the way to the details for an expense, so let’s update ExpenseDetailedView. Like last time, I edited the init and conformed to FlowRepresentable, and I removed the NavigationView (though this was not strictly necessary).

Expense Detailed View updated with a weak variable called underscore workflow pointer, and a modified initializer to now call the parameter “with.”

Now over in ExpenseView, there’s a little wrinkle. The navigation link to the details is inside ExpenseMainView, but that view is not what we want to be FlowRepresentable. So we’re going to update ExpenseMainView to take a binding called selectedExpense.

Expense Main View now has a binding called selected expense of type optional expense CD. The initializer has also been updated to take in the binding.

And ExpenseView will now have a State for the selectedExpense that will be monitored, and when it changes, we’ll move forward.

Creation of the expense main view now has a binding to a variable called selected expense. The view also has a new on change closure tied to the selected expense variable. The closure unwraps the value passed to the closure and calls proceed in workflow with the unwrapped variable.

Now update the Workflow in ExpensoApp:

Scene body has an added call to then proceed with Expense detailed view dot self.

WHEW! We’re finally done!

The main screen of Expenso App is navigating to the expense detailed screen and then back to the main screen.

What’s next?

This time around, we did have to adjust a little bit about our views and our models, but overall this was still pretty straightforward. We could keep going and see what would happen if every screen was FlowRepresentable, but I think that would pan out the same as we’ve been seeing. Did we gain anything integrating SwiftCurrent here? Well, ExpenseView no longer references ExpenseDetailedView, nor does AuthenticateView have a reference to ExpenseView. AuthenticateView is now responsible for when it should appear, which means with a little more modification, we could update it to be a PassthroughFlowRepresentable and drop it at various points in the application (maybe requiring another authentication when updating settings or editing an expense).

Again, shout out to Sameer Nawaz for open sourcing these great-looking apps! And drop a comment if there’s anything more you’d like to dig into, or try out SwiftCurrent in your own app, and let me know how it goes!

--

--

Richard Gist
SwiftCurrent

I’m the technical lead on SwiftCurrent. I like Swift and solving problems. Check me out on LinkedIn(/in/richardgist) and GitHub(/Richard-Gist).