UIViewRepresentable
을 상속받은 클래스에서 PassthroughSubject
를 구독하는 방법을 찾아냈다.
메인 화면에는 변수를 바꾸는 것과 지도를 바꾸는 버튼과 지도를 배치한다.
struct ContentView: View { @EnvironmentObject var mapViewEnvironment: MapViewEnvironment var body: some View { VStack { Button(action: { self.mapViewEnvironment.value1 = "aaa" }) { Text("set aaa") } Button(action: { self.mapViewEnvironment.value1 = "bbb" }) { Text("set bbb") } Button(action: { self.mapViewEnvironment.currentMapCompany = "apple" }) { Text("set apple") } Button(action: { self.mapViewEnvironment.currentMapCompany = "google" }) { Text("set google") } CommonMapView() } } }
테스트를 위해 일반 타입의 변수와 PassthroughSubject 타입의 변수를 생성한다.
class MapViewEnvironment: ObservableObject { @Published var value1 = "aaa" var value2 = PassthroughSubject<String, Never>() @Published var currentMapCompany = "apple" }
지도를 표시하는 뷰에서는 지도 선택말고는 할 수 있는게 없었다. currentMapView 같은 변수를 만들어서 현재 표시 중인 지도를 넣고 싶었는데, 변수 타입을 뭘 넣어야하는지 끝내 찾이 못했다.
struct CommonMapView: View { @EnvironmentObject var mapViewEnvironment: MapViewEnvironment var body: some View { ZStack { if mapViewEnvironment.currentMapCompany == "apple" { AppleMapView() } else { GoogleMapView() } } } }
테스트를 위한 임시 함수들 추가해주고
protocol MapViewProtocol { func aaa() func bbb() }
실제 지도 화면에서는 깔끔함을 위해 willApear()
라는 함수를 만들어주고, 그 안에서 구독을 시작한다.
struct AppleMapView: UIViewRepresentable { @EnvironmentObject var mapViewEnvironment: MapViewEnvironment func makeUIView(context: Context) -> MKMapView { let view = MKMapView() view.mapType = .standard willAppear(context) return view } func makeCoordinator() -> AppleMapView.Coordinator { return Coordinator() } func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<AppleMapView>) {} static func dismantleUIView(_ uiView: MKMapView, coordinator: AppleMapView.Coordinator) { print("apple dismantleUIView") coordinator.cancellable.removeAll() } final class Coordinator { var cancellable = Set<AnyCancellable>() } } extension AppleMapView: MapViewProtocol { func willAppear(_ context: Context) { mapViewEnvironment.$value1 .filter { $0 == "aaa" } .sink { print("=============== apple 1111 \($0)") self.aaa() }.store(in: &context.coordinator.cancellable) mapViewEnvironment.$value1 .filter { $0 == "bbb" } .sink { print("=============== apple 1111 \($0)") self.bbb() }.store(in: &context.coordinator.cancellable) mapViewEnvironment.value2 .filter { $0 == "aaa" } .sink { print("--------------- apple 2222 \($0)") self.aaa() }.store(in: &context.coordinator.cancellable) mapViewEnvironment.value2 .filter { $0 == "bbb" } .sink { print("--------------- apple 2222 \($0)") self.bbb() }.store(in: &context.coordinator.cancellable) } func aaa() { print("AppleMapView - aaa") } func bbb() { print("AppleMapView - bbb") } }
일반 타입의 변수인 value1 에 @Published
를 붙이면 CurrentValueSubject 형태로 바뀌면서 구독하자마자 현재 값이 표시되는데, Rx의 BehaviorSubject와 같다고 생각하면 되고, PassthroughSubject 타입인 value2는 구독 후에 값이 새로 들어올 때부터 발행되는 것이 Rx의 PublishSubject라고 보면 될 것 같다.