How To Adapt BLoC architecture in SwiftUI

How To Adapt BLoC architecture in SwiftUI

Written by Božidar Kokot

·

6 min read

BLoC in SwiftUI

With Apple coming out with SwiftUI and the increasing popularity of Flutter and Jetpack Compose it seems that the UI for our mobile apps in the future will become dominantly declarative. While I was Fluttering around I came across an interesting architectural pattern famous in the Flutter community called the BloC pattern and I grew to like it primarily because I liked the concept of documenting all the possible view states and events as types. So I wanted to see how it would look adapted in SwiftUI. The text below shows my adaption to that pattern in SwiftUI.

BLoC basic overview

The main goal of the BLoC architecture is to separate business logic from the user interface. It does so by acting as a middle man between our view and the data source by providing a Business Logic Component. Since it is a pattern designed to fit the need of declarative UI programming it is only natural that it communicates with the view by streams. Every time a new event occurs it sinks it to the BLoC and every time there is an update to the view state the BLoC streams it to the view.

The basic gist can be seen in the diagram below:

Dzone1.PNG

Showcase app overview

For the purpose of demonstrating this architecture adoption, I made a small showcase app that tries to adapt the BLoC architecture in SwiftUI (full source code can be found at github.com/BozidarK93/SwiftUI-BLoC-Pattern ). The gif below shows the basic usage of the app.

dzone2.PNG

So as we can see the app itself allows the user to input a city name and get the breweries in submitted city area (thanks to openbrewerydb.org for providing this cool API).

Data layer

First let’s start by getting the data needed for the view. As seen above the thing we aim to display is the list of breweries in a given area. So lets start by creating a simple model called Brewery:

dzone3.PNG

Now that we have our model we need to create a repository from which we can perform an actual network request to the API. First lets create a simple protocol so that we can later on easily mock it for our tests:

dzone4.PNG

And now let’s create the real implementation in which we will call our API. Note that app’s main focus is to demonstrate the usage of the BloC pattern so the network call approach is rather primitive. But here we could use some API client implementation. There are some good reads out there on the subject like: medium.com/better-programming/upgrade-your-..

dzone5.PNG

View states

Now that we have our data let’s move over to the view layer. First thing we need to do is to define all possible states the view can be rendered in. What I like about this pattern is that it allows us to document all possible view states as a type which makes it easier to understand the UI and all the states it can be rendered in. On the other hand this can also be ‘spaghetti’ prone and if we are not careful we may end und just dumping a lot of view states into one type and thus creating confusion.

What is cool about Swift is that it allows our enums to have associated values so we can just use enums directly when defining our view states thus passing any view data as an associated value. Another good thing is that when using this approach we are forced to define the view for each state as switching over enums is exhaustive.

So what would be the states in the context of our demo app. Well I guess we want our view to show the user a text field to input a city name which also has a small client-side validation checking if the input is numeric. It should show a loading view if we are fetching the data, show the actual breweries fetched, and in the end it should show a failure screen if an error occurs, and we can show a message if no breweries were found. So in the end the states would look like this:

dzone6neu.PNG

View events

Now that we have defined our view states let’s also define our view events to which our UI will respond. So there are two events that can occur on our UI. The user can submit the city name after which we would call our API, and another event that can occur is for checking if the text field changed. Keeping this in mind our events would be represented like this:

dzone7.PNG

BLoC

After defining the view states and events we can create our BLoC. First let us start with creating our view streams. We first need to define our bloc class as an ObservableObject, this will allow us to render the view whenever the change occurs on its publishers. Since we want to publish the changes when a view state changes we create a state @Publisher. As for the events let’s create a property observer for our event enum so that whenever the change occurs on the UI we perform the appropriate action.

dzone8.PNG

Now let us implement our handleViewEvent method which will implement the appropriate view logic. We want to handle two events previously defined, when the city is submitted we want to fetch the data and set the appropriate states. Another event we need to handle is every time the text is inputed we check if the input is numeric in order to provide a basic showcase client-side validation.

dzone9.PNG

Declaring view for different states Now that we wrote our view logic and fetched the appropriate data we can now declare the actual UI for each state.

dzone10.PNG

As we can see we define our bloc as a @StateObject so that whenever the change occurs on our state publisher we can render the view with appropriate view states. In our body we just have a switch statement going through all possible states and we can declare the view we want for each state.

Previews

By using this pattern it makes it really easy to configure our previews. All we need to do is to set the state for the view we wish to preview.

dzone11.PNG

Snapshot testing

If you use Snapshot testing technique for validating the rendering of your views using this pattern also comes in pretty handy, since similarly to the previews we just need to set the state we want to test.

dzone12.PNG

Unit testing

When it comes to logic testing the block what we can do is test that a certain event executes the appropriate action. By applying this pattern we would just need the mock the event we wish to test. In the example below we mock the event of submitting the city name and we assert that API is being called:

dzone13.PNG

So that’s pretty much it. If you want to see the full source code and try to fool around with it a bit you can check out the repo and let me know what you think in the comments =) : github.com/BozidarK93/SwiftUI-BLoC-Pattern