WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

Multiple environmentObject of same type

Forums > SwiftUI

Hey,

If I have two object of same type: Exposure, ExposureA and ExposureB I want to have two environmentObjects but as I know @EnvironmentObject property wrapper searches for Exposure type in environment object and finds the one with the same type as the defined variable. How can I have two objects of the same type as environmentObjects?

Thanks,

Hadi

2      

Maybe this approach will work out in your case

class StateA: ObservableObject {
  @Published var exposure = Exposure()
}

class StateB: ObservableObject {
  @Published var exposure = Exposure()
}

class ExposureState: ObservableObject {
  var stateA = StateA()
  var stateB = StateB()
}

// To inject into ContentView

var exposureState = ExposureState()

ContentView()
          .environmentObject(exposureState.stateA)
          .environmentObject(exposureState.stateB)

2      

hi,

i think @ygeras's approach works in the sense that you can introduce two observable objects of different types, the sole purpose of each being to provide a "wrapper" of an object of type Exposure, and thus you can inject them into the environment as different types.

but i'd recommend some changes. for example, although each may have a @Published reference to an object of type Exposure, that reference will never change ... thus the @Published property wrapper has no effect.

rather, you want to use a little Combine technology so that each of the StateA and StateB objects listens to its Exposure instance for changes and then rebroadcasts those changes as its own. you can do this on StateA with this (repeat for StateB)

import Combine

class StateA: ObservableObject {
  var exposure: Exposure
  private var cancellables = Set<AnyCancellable>()

  init() {
  exposure = Exposure()
  exposure.objectWillChange
            .sink { [self] _ in
                objectWillChange.send() // this makes StateA publish its own change whenever exposure changes
            }
            .store(in: &cancellables)
  }

}

i'd also recommend that you make exposureState a @StateObject:

@StateObject private var exposureState = ExposureState()

hope that helps,

DMG

3      

//https://jaredsinclair.com/2020/05/07/swiftui-cheat-sheet.html?utm_campaign=%20SwiftUI%20Weekly&utm_medium=email&utm_source=Revue%20newsletter
//https://github.com/jaredsinclair/swiftui-property-wrappers/blob/master/Testo/DistinctObjectsExample.swift

import SwiftUI

struct DistinctObjectsExample: View {

    @DistinctEnvironmentObject(\.posts) var postsService: Microservice
    @DistinctEnvironmentObject(\.users) var usersService: Microservice
    @DistinctEnvironmentObject(\.channels) var channelsService: Microservice

    var body: some View {
        Form {
            Section(header: Text("Posts")) {
                List(postsService.content, id: \.self) {
                    Text($0)
                }
            }

            Section(header: Text("Users")) {
                List(usersService.content, id: \.self) {
                    Text($0)
                }
            }

            Section(header: Text("Channels")) {
                List(channelsService.content, id: \.self) {
                    Text($0)
                }
            }
        }.onAppear(perform: fetchContent)
    }

    func fetchContent() {
        postsService.fetchContent()
        usersService.fetchContent()
        channelsService.fetchContent()
    }
}

// MARK: - Property Wrapper To Make This All Work
@propertyWrapper
struct DistinctEnvironmentObject<Wrapped>: DynamicProperty where Wrapped : ObservableObject {
    var wrappedValue: Wrapped {
        _wrapped
    }

    @ObservedObject private var _wrapped: Wrapped

    init(_ keypath: KeyPath<EnvironmentValues, Wrapped>) {
        _wrapped = Environment<Wrapped>(keypath).wrappedValue
    }
}

// MARK: - Dependencies
class Microservice: ObservableObject {
    @Published private(set) var content: [String] = []

    func fetchContent() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.content = self.fakeContentForDemo
        }
    }

    private let fakeContentForDemo: [String]

    init(fakeContentForDemo: [String]) {
        self.fakeContentForDemo = fakeContentForDemo
    }
}

extension Microservice {
    static let posts = Microservice(
        fakeContentForDemo:  ["What about Mesa Verde?", "S'all good, man."]
    )
}

extension Microservice {
    static let users = Microservice(
        fakeContentForDemo: ["Jimmy", "Kim", "Mike"]
    )
}

extension Microservice {
    static let channels = Microservice(
        fakeContentForDemo: ["#watercooler", "#gifs", "#drugtrafficking"]
    )
}

// MARK: - Environment Boilerplate
struct PostsServiceKey: EnvironmentKey {
    static var defaultValue: Microservice {
        return Microservice.posts
    }
}

struct UsersServiceKey: EnvironmentKey {
    static var defaultValue: Microservice {
        return Microservice.users
    }
}

struct ChannelsServiceKey: EnvironmentKey {
    static var defaultValue: Microservice {
        return Microservice.channels
    }
}

extension EnvironmentValues {
    var posts: Microservice {
        get { return self[PostsServiceKey.self]  }
        set { self[PostsServiceKey.self] = newValue }
    }

    var users: Microservice {
        get { return self[UsersServiceKey.self]  }
        set { self[UsersServiceKey.self] = newValue }
    }

    var channels: Microservice {
        get { return self[ChannelsServiceKey.self]  }
        set { self[ChannelsServiceKey.self] = newValue }
    }
}

2      

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.