This is what I posted as a possible solution to the view manager hierarchy in Swift (slightly modified):
extension UIViewController { func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? { var currentVC = self while let parentVC = currentVC.parentViewController { println("comparing \(parentVC) to \(T.description())") if let result = parentVC as? T {
The method must go through the hierarchy of the parent view controller and return the first instance of this class, or nil if none are found.
But this does not work, and I cannot understand why. An extra binding marked with a sign (XXX) always succeeds, so the first parent view controller is returned even if it is not an instance of T
This can be easily reproduced: Create a project from the "Applications with main iOS applications" template in Xcode 6 GM and add the following code to the viewDidLoad() MasterViewController class:
if let vc = self.traverseAndFindClass(UICollectionViewController.self) { println("found: \(vc)") } else { println("not found") }
self is a MasterViewController (a subclass of UITableViewController ), and its parent view controller is UINavigationController . The parent view does not have a UICollectionViewController hierarchy of controllers, so I expect the method to return nil and the output "not found".
But here is what happens:
comparing <UINavigationController: 0x7fbc00c4de10> to UICollectionViewController found: <UINavigationController: 0x7fbc00c4de10>
This is obviously wrong because the UINavigationController not a subclass of the UICollectionViewController . Perhaps I made some stupid mistake, but I could not find it.
To isolate the problem, I also tried to reproduce it with my own hierarchy class, regardless of UIKit:
class BaseClass : NSObject { var parentViewController : BaseClass? } class FirstSubClass : BaseClass { } class SecondSubClass : BaseClass { } extension BaseClass { func traverseAndFindClass<T where T : BaseClass>(T.Type) -> T? { var currentVC = self while let parentVC = currentVC.parentViewController { println("comparing \(parentVC) to \(T.description())") if let result = parentVC as? T {
And guess what? Now it works as expected! Output signal
comparing <MyApp.FirstSubClass: 0x7fff38f78c40> to MyApp.SecondSubClass not found
UPDATE:
Removing a type constraint in a generic method
func traverseAndFindClass<T>(T.Type) -> T?
as suggested by @POB in the comment, makes it work properly.
Replace an optional snap using a two-step snap
if let result = parentVC as Any as? T {
as suggested by @vacawama in his answer, also makes it work properly.
- Changing the assembly configuration from "Debug" to "Vacation" also makes the method work properly. (I only tested this on iOS Simulator so far.)
The last point may indicate that it is a Swift compiler or a runtime error. And I still can not understand why the problem occurs with subclasses of UIViewController , but not with subclasses of my BaseClass . Therefore, I will keep the question open for before accepting the answer.
UPDATE 2: This is fixed as Xcode 7 .
With the final Xcode 7 release the problem no longer occurs. Additional binding if let result = parentVC as? T if let result = parentVC as? T in the traverseAndFindClass() method now works (and does not work), as expected, both in the Release and Debug configurations.