iOS Developer in search for meaning 🧘‍♂️

Custom @Environment keys in SwiftUI

July 08, 2019

environment

@Environment - is a property wrapper that allows any view access to global dependencies e.g Calendar, Locale, ColorScheme etc.

struct CalendarView: View {
    @Environment(\.calendar) var calendar: Calendar
    
    var body: some View {}
}

But what if we want to create our own global dependencies that are tight to our app domain. For example, we may want ImageFetcher to be accessible for any view that can display remote images, so that images that were displayed on previous screens of our app would not need to be fetched again.

Turns out SwiftUI allows us to do this:

struct MovieCell: View {
    @Environment(\.imageFetcher) var fetcher: ImageFetcher
    var movie: Movie

    private var poster: AnyPublisher<UIImage, Never> {
        return fetcher.image(for: movie.posterURL)
    }

    var body: some View {}
}

In order to achieve that we need to implement a couple of requirements.

EnvironmentKey - requires us to provide a default value for our custom dependency.

public protocol EnvironmentKey {

    associatedtype Value

    static var defaultValue: Self.Value { get }
}
struct ImageFetcherKey: EnvironmentKey {
    static let defaultValue: ImageFetcher = ImageFetcher()
}

The defaultValue will be created when we first time access it via @Environment.

defaultValue

By extending EnvironmentValues we provide a property that we are going to use to access it via @Environment property wrapper.

extension EnvironmentValues {
    var imageFetcher: ImageFetcher {
        get {
            return self[ImageFetcherKey.self]
        }
        set {
            self[ImageFetcherKey.self] = newValue
        }
    }
}

Complete implemetation can be found here


Serg Dort

Written by Serg Dort, who works and lives in London builds useful things. You can follow him on Twitter