Environment Values as an Alternative to Dependency Injection in SwiftUI | by Emilio Peláez | May, 2022

Utilizing Atmosphere Values to keep away from pointless physique re-evaluations and make our views extra self-contained.

Picture by Pieter on Unsplash

Dependency injection is a 25-dollar time period for a 5-cent idea. –James Shore

Dependency Injection is a flowery method of claiming that we are going to present an object with the thing(s) it requires or relies on, to carry out its job.

In SwiftUI, a quite common method of implementing Dependency Injection is by utilizing surroundings objects. By injecting an object into our view hierarchy we will summary the logic and information out of our views and into lessons, permitting us to comply with the single-responsibility precept, and making our code extra testable.

For an instance, let’s think about that we’ve a UserSettings class with a number of properties.

To be able to ensure that our View can entry the values in our UserSettings object, we will inject it utilizing the .environmentObject() modifier, and studying it utilizing the @EnvironmentObject property wrapper.

This in all probability seems fairly acquainted.

On this instance, ContentView wants two values present in UserSettings, however we’re making a a lot deeper dependency, our view merely gained’t work with out a full UserSettings object.

This has two distinctive disadvantages:

  1. First, each time any of the values within the object adjustments, the view’s physique might be re-evaluated, even when the worth modified is just not a worth that’s utilized by this particular view. In our instance, our view’s physique might be re-evaluated when soundsEnabled adjustments, though it’s not getting used within the physique. If you happen to occur to be utilizing this methodology to inject your app’s state, like I’ve prior to now, this object might maintain many values that may set off pointless view evaluations all through your view hierarchy.
  2. And second, when previewing a view that has a dependency to an surroundings object, we might want to inject that surroundings object to the preview, in any other case it’s going to fail to load. Even worse, any views that show this view as a toddler might want to obtain the surroundings object of their previews with the intention to load, though this requirement is just not apparent by taking a look at their physique.
May as properly begin injecting it in your whole previews.

EnvironmentValues, like EnvironmentObjects, are injected into the view hierarchy and propagated to all descendants till they’re changed by a brand new worth. They’re the mechanism utilized by modifiers like .foregroundColor() or .font().

The large distinction is that to outline a customized surroundings worth you could present a default worth. It requires a bit extra boilerplate, but it surely has it’s benefits.

To create your personal surroundings worth you could create an surroundings key and prolong EnvironmentValues; you possibly can then insert the worth into the hierarchy utilizing the .surroundings() modifier.

It’s worthwhile to create an EnvironmentKey struct for each worth, and add it to the EnvironmentValues kind.

By studying from a selected surroundings worth, our view’s physique will solely be re-evaluated when that worth adjustments. And since EnvironmentValues at all times have a defaultValue, these views might be utilized with out having to inject something, which suggests you don’t want to switch your previews.

An necessary factor to notice is that @Atmosphere properties are read-only. You’ll have seen that I injected the UserSettings in my SettingsProvider, that’s as a result of any views that want to switch the values, like a SettingsView would on this instance, will nonetheless must learn the entire object.

To this point I’ve used easy values for instance, however this method might be helpful when populating our app’s views with all types of knowledge.

Let’s say we’re constructing an app that shows the person’s transactions in numerous methods and subsets all through the app.

If we have been utilizing CoreData, we might use a @FetchRequest with the related question in every view; if we have been fetching information from the server we might fetch the required transactions from the community on every view.

My most popular various, nonetheless, is to inject the transactions at a really excessive degree within the hierarchy, and browse that worth in any view that may show them.

Fetching the info is an train left to the reader, however as soon as the transactions are fetched, we will inject them into the surroundings utilizing the .surroundings modifier.

Any views that need to show the values within the userTransactions array can learn that worth from the surroundings as an alternative of getting to fetch any values by itself.

This makes our views quite a bit easier, and the logic for fetching the transactions is just not solely faraway from the View struct, it’s in a special node of the view hierarchy altogether.

Through the use of worth injection, views that require read-only entry to those values will solely be refreshed when these particular values change.

As a result of surroundings values present a default worth, we not must inject dependencies to the previews of views that use them.

Dependency Injection utilizing surroundings objects nonetheless has its place in views that require write entry to the properties of the thing.

More Posts