Reputation: 271
I've got a TabBarViewController controlling 5 UIViewControllers. I'm trying to pass data (a user object in particular) from one UIViewController. What I'm currently doing is to set the user property in the TabBar Controller from within one of the UIViewControllers (HomeScreen) like so:
class HomeViewController: UIViewController {
let apiService = APIService()
var user: User?
var parentController: TabBarController?
override func viewDidLoad() {
super.viewDidLoad()
let token = UserDefaults.standard.string(forKey: "authtoken")!
self.apiService.getUserFromAuthtoken(token: token, completion: {result in
switch result {
case .success(let user):
self.user = user
self.parentController = self.tabBarController as? TabBarController
self.parentController?.user = user
print("EMAIL-HOME-SCREEN: ", user.email)
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
}
}
Then, after navigation, I'm trying to access the TabBarView's user property from another UIViewController (CoopOverview) like so:
class CoopOverviewViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var CampaignBrandSliderBackground: UIView!
@IBOutlet weak var CampaignCollectionView: UICollectionView!
@IBOutlet weak var CampaignBrandSlider: UISegmentedControl!
var user: User?
var parentController: TabBarController?
@IBOutlet weak var foo: UIView!
let campaignImages: [UIImage] = [UIImage(named: "icon-black")!,UIImage(named: "icon-white")!,UIImage(named: "icon-black")!,UIImage(named: "icon-white")!]
override func viewDidLoad() {
super.viewDidLoad()
self.parentController = self.tabBarController as? TabBarController
self.user = self.parentController?.user
print("EMAIL-COOP-SCREEN: ", user!.email)
...
}
Is this a good approach or is there a more elegant way to solve this?
Upvotes: 2
Views: 195
Reputation: 437522
I would discourage the tight coupling between the TabBarController
and each of its children. The children should not be relying on their parent’s internal implementation details.
Why’s that a problem? What if you later change the parent to a page view controller later? Or some other custom container view controller? You don’t want to have to go into all these children to fix that. Children should rely upon abstractions and not rely upon implementation details of their parent.
There are two solutions:
Avoid tight coupling of classes via protocol. For example,
UserProvider
or UserDataSource
protocol that exposes a property or method to retrieve the current user from the source of truth;UITabBarController
type, to retrieve the User
; andNow, you have the basic structure that your original pattern had, but without the tight coupling between our classes.
Going a step further, use dependency injection, where the parent injects whatever the child needs.
So, rather than having the children reach back into the parent (via a protocol) to retrieve the User
, the other approach is to have a User
property in all the children and have the parent inject it into the relevant children.
So you might have a protocol for children that have a settable User
property. Then the tab bar controller would, for any children that conform to that protocol, supply a copy of the User
. Again, using protocols keeps these loosely coupled.
Both of these patterns avoid tight coupling between our classes.
Upvotes: 2