Note of Caution
Before proceeding to use an answer to this question, you may wish to reflect whether the embedded things really need to be view controllers.
Eg, if you're embedding a UICollectionViewController
subclass, could you instead embed a UICollectionView
subclass? Or, even better, could you embed a UIView
subclass that hides away the UICollectionView
behind a simple ViewModel?
In the code base I'm currently working on, I'm embedding two view controllers in to another view controller. Both could fairly easily be plain views instead, and could then be more easily bound to in the storyboard, without this messy code.
Unfortunately, they are currently view controllers and I'm not in a position to simplify them in to plain views right now, so this will have to do.
Background
I'm using the approach of picking up the embed segue in prepare(for segue:, sender:)
as suggested by Playful Geek here.
I wanted to show the swift I'm using for this, as it seems to be fairly tidy…
class EditionLandingViewController: UIViewController {
fileprivate var titlesView: SectionTitlesViewController!
fileprivate var sectionsView: SectionsViewController!
}
//MARK:-
extension EditionLandingViewController {
private enum SegueId: String {
case embedTitles
case embedSections
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard
let segueRawId = segue.identifier,
let segueId = SegueId(rawValue: segueRawId)
else { return }
switch segueId {
case .embedTitles:
self.titlesView = segue.destination as! SectionTitlesViewController
case .embedSections:
self.sectionsView = segue.destination as! SectionsViewController
}
}
}
Discussion
I've chosen to name segues as action methods.
Using an enum
cases for segue identifiers means you've got the compiler and tooling on your side, so its much harder to get a segue name wrong.
Keeping the segue ids in a private enum
within the extension
scope seems appropriate in this case as these segues are not needed anywhere else (they can't be perform
ed, for example).
I'm using implicitly unwrapped types for the embedded view controllers because (in my case anyway) it's a logic error if they are missing.
Similarly, I'm also happy to force cast the destination view controller types. Again, it would be a logic error if these types are not the same.