Search code examples
iosswiftrx-swift

Passing data from parent VC to child VC after getting response using subjects


I have a VC(A) that has a container view and changes the VC(B-C) it based on the segementControl value, Im sending a request and getting a response inside the A ViewController and i want to make sure both B and C view controllers observe the response from A and set the data

Im new to rxswift so bear with me please

initiating both child VCS inside A VC

private lazy var profileVC: ProfileVC = {
    // Load Storyboard
    let storyboard = UIStoryboard(name: "Profile", bundle: Bundle.main)

    // Instantiate View Controller
    var viewController = storyboard.instantiateViewController(withIdentifier: "ProfileVC") as! ProfileVC

    // Add View Controller as Child View Controller
    self.add(asChildViewController: viewController)

    return viewController
}()

private lazy var socialMediaVC: SocialMediaVCViewController = {
    // Load Storyboard
    let storyboard = UIStoryboard(name: "Profile", bundle: Bundle.main)

    // Instantiate View Controller
    var viewController = storyboard.instantiateViewController(withIdentifier: "SocialMediaVC") as! SocialMediaVCViewController

    // Add View Controller as Child View Controller
    self.add(asChildViewController: viewController)

    return viewController
}()

adding and removing functions inside A ViewController

func add(asChildViewController viewController: UIViewController) {
    // Add Child View as Subview
    containerView.addSubview(viewController.view)

    // Configure Child View
    viewController.view.frame = view.bounds
    viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}



private func remove(asChildViewController viewController: UIViewController) {
    // Notify Child View Controller
    viewController.willMove(toParent: nil)

    // Remove Child View From Superview
    viewController.view.removeFromSuperview()

    // Notify Child View Controller
    viewController.removeFromParent()
}

segemntController change

 @IBAction func segmentedControlClicked(_ sender: UISegmentedControl) {
        segmentedControl.changeUnderlinePosition()
        if segmentedControl.selectedSegmentIndex == 0 {
            remove(asChildViewController: socialMediaVC)
            add(asChildViewController: profileVC)
        } else {
            remove(asChildViewController: profileVC)
            add(asChildViewController: socialMediaVC)
        }
    }

sending request and getting response inside A ViewModel:

    startedUp.accept(true)
    startedUp.filter({ $0})
        .flatMap{ [weak self] _ -> Observable<Event<Result<ProfileResponse>>> in
            self?.loadInProgress.accept(true)
            return network.showProfile(startupId: 1).materialize()}
        .subscribe(onNext: { [weak self] event in
            self?.loadInProgress.accept(false)
            switch event {
            case .next(let result):
                switch result{
                case .Success(let response):
                    self?.startedUp.accept(false)
                    self?.sectionSubject.onNext(0)
                    self?.output.editProfileData = (self?.setProfileData(response:response))!
                    self?.tableViewcellsSubject.onNext((self?.createArray(response: response))!)
                    self?.userscellsSubject.onNext((self?.createUserArray(response: response.users!))!)
                case .Failure(let error):
                    self?.errorsSubject.onNext(error)
                }
            case .error( _):
                print("error")
            default:
                break
            }
        })
        .disposed(by: disposeBag)

Solution

  • Let's say your AViewModel has stream of data your children interested in:

    protocol AViewModel {
        let importantDataStream: Observable<SomeData> { get }
    }
    

    And your ParentVC has reference on his view model as:

    var viewModel: AViewModel
    

    Then you could declare protocol:

    protocol ImportantDataListener {
        func subscribe(to stream: Observable<SomeData>)
    }
    

    And both B and C will conform to this protocol.

    Then we change declaration of add(asChildViewController ...) function like that:

    func add(asChildViewController viewController: UIViewController & ImportantDataListener) {
        // Add Child View as Subview
        containerView.addSubview(viewController.view)
    
        // Configure Child View
        viewController.view.frame = view.bounds
        viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    
        // Passing stream
        viewController.subscribe(to: viewModel.importantDataStream)
    }