I wouldn't care much about performance here, since a view controller
hierarchy does not have hundreds of elements. But some things can
be simplified. First,
if (currentParent is RootViewController) {
return (currentParent as? RootViewController)!
}
can be simplified to
if let rootVC = currentParent as? RootViewController {
return rootVC
}
which also has the advantage that no forced unwrapping operator is
needed. Even if we know that it cannot fail here (because the type has
been checked in the if-statement) it is better to write the code
in a way that it becomes obvious that it cannot crash.
Next, the loop itself can be simplified:
var rootViewController: RootViewController? {
var currentViewController = self
while case let parentController? = currentViewController.parent,
parentController != currentViewController {
if let rootVC = parentController as? RootViewController {
return rootVC
}
currentViewController = parentController
}
return nil
}
Here the "optional pattern"
case let parentController? = currentViewController.parent
is used to check that there is a parent, and the constraint
parentController != currentViewController
ensures that the parent is different from the current view controller.
But I don't think that a view controller can be its own parent.
If you don't have a special reason to consider this case, the code
can further be simplified using optional binding:
var rootViewController: RootViewController? {
var currentViewController = self
while let parentController = currentViewController.parent {
if let rootVC = parentController as? RootViewController {
return rootVC
}
currentViewController = parentController
}
return nil
}
Another suggestion is to make the method generic, so that it can
be used to find a parent view controller of any given class:
func parent<T : UIViewController>(ofType: T.Type) -> T? {
var currentViewController = self
while let parentController = currentViewController.parent {
if let parent = parentController as? T {
return parent
}
currentViewController = parentController
}
return nil
}
Finally: You can replace the loop by recursion, using the
flatMap method of Optional and the nil-coalescing operator ??:
func parent<T : UIViewController>(ofClass: T.Type) -> T? {
return parent.flatMap { ($0 as? T) ?? $0.parent(ofClass: T.self) }
}
Here parent.flatMap { ... } returns nil if parent == nil,
this terminates the recursion eventually. Otherwise $0 is the
(unwrapped) parent. If the conditional cast $0 as? T succeeds
then this will be the return value. Otherwise the method is called
recursively on the parent view controller.
I doubt that this will be faster, and it is more difficult to read
and to debug. So it's up to you which approach you prefer.