Swift: Setting a delegate on the destinationViewController in prepareForSegue
A common situation in Cocoa when dealing with view controllers is setting a property (frequently a custom delegate) on a destination view controller in prepareForSegue:sender:
. In Objective-C, the destinationViewController
is of type id
, so we need to first introspect the destinationViewController
's class. If it's the class we want, we then create a local variable and then cast it with our desired class. Once we have that casted variable, we can set our delegate:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"segueToSecondViewController"]) {
if ([segue.destinationViewController isKindOfClass:[SecondViewController class]]) {
SecondViewController *secondVC = (SecondViewController *)segue.destinationViewController;
secondVC.delegate = self;
}
}
}
Let's explore this in Swift:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
}
One issue is that segue.identifier
is an optional, so here we could use optional binding:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let segueIdentifier = segue.identifier {
}
}
Next, we can switch on the value of the identifier:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let segueIdentifier = segue.identifier {
switch segueIdentifier {
case "segueToSecondViewController": break // TODO: Set delegate here
default: break
}
}
}
It's a bit cleaner though to use pattern matching and remove the if let
. A Swift Optional
is actually an enum
that has a None
and a Some
case with a generic associated value. Here we match on our desired associated identifier
:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier {
case (.Some("segueToSecondViewController")): break // TODO: Set delegate here
default: break
}
}
Now we need to grab the destinationViewController
with an optional downcast and set the delegate:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier {
case (.Some("segueToSecondViewController")):
let secondVC = segue.destinationViewController as? SecondViewController
secondVC?.delegate = self
// Handle any other necessary configuration
default: break
}
}
Another approach would be to focus on the segue.destinationViewController
. Depending on the situation, we may not care about the segue.identifier
; each of our segues may be to view controllers of unique classes. In Swift, the destinationViewController
is of type AnyObject
. So, we can perform an optional downcast into a constant, and then we can set the delegate and handle any other necessary configuration that we may need using optional chaining. We can also add similar code for configuring other destinationViewControllers
:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let secondVC = segue.destinationViewController as? SecondViewController
secondVC?.delegate = self
let thirdVC = segue.destinationViewController as? ThirdViewController
thirdVC?.title = "Hi Mom!"
}
Or to simplify, if we only have one possible segue for this scenario in our app, we can combine the optional downcast and delegate setting:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
(segue.destinationViewController as? SecondViewController)?.delegate = self
}