3 Replies
      Latest reply: Sep 15, 2016 3:54 AM by eskimo RSS
      sagits Level 1 Level 1 (0 points)

        Hi, im trying to load a class that extends a generic uiViewController in a storyboard. But if the super usesgenerics (T), the storyboard cant recognize the class (wont even autocomplete)  and give me "... class not found" error. More info at http://stackoverflow.com/questions/32836343/generic-controller-in-swift-2-0-using-storyboards

         

        Anyone can help? Thanks in advance.

        • Re: Generic controller with storyboard
          ahurlburt Level 1 Level 1 (0 points)

          I was wondering if you ever found an answer to this?

           

          I see many stack overflows describing the same problem and provide the reason that this is not possible being that interface builder uses the obj-c runtime.

           

          I have been burned by this several times in my app and need to produce (in my mind) unnecessary code because of it. Until generic classes are supported in storyboards it really limits the use of generics which is frustrating because generics are so useful for producing cleaner code.

           

          I am posting here to hopefully gain some visibility for apple engineers (who I realize are probably already well aware of this), maybe this is something coming in the future? Like I said there are alot of stack overflows about this. If swift is the future seems like interface builder should maybe make use of it (and support all it's functions)?

          • Re: Generic controller with storyboard
            rickbdotcom Level 1 Level 1 (0 points)
              • Re: Generic controller with storyboard
                eskimo Apple Staff Apple Staff (6,270 points)

                I got this work actually

                That’s a neat trick.  Some comments:

                • You don’t need to instantiate the derived class; any reference to it will do.  Removing that instantiation makes the view controller code much simpler.

                • If the derived class is referenced by the main storyboard, you need to make sure you ‘touch’ it before UIApplicationMain runs.

                To test this I first started with an obvious generic view controller.

                class BaseViewController<Element>: UITableViewController {
                
                    var content: [Element] = []
                
                    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                        return self.content.count
                    }
                
                    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
                        cell.textLabel!.text = "\(self.content[indexPath.row])"
                        return cell
                    }
                }
                

                I then specialised it for the sake of IB.

                class DerivedViewController : BaseViewController<String> {
                }
                

                I then set up my main storyboard to reference it and modified application(_:didFinishLaunchingWithOptions:) to set it up.

                func application(… didFinishLaunchingWithOptions …) -> Bool {
                    let nav = self.window?.rootViewController! as! UINavigationController
                    let derived = nav.topViewController! as! DerivedViewController
                    derived.content = ["Hello", "Cruel", "World"]
                    return true
                }
                

                Finally, I removed @UIApplicationMain from my AppDelegate and replaced it with a main.swift as shown below.

                import UIKit
                
                print(DerivedViewController.self)
                
                // The following is required because there's an impedence mismatch between
                // `CommandLine` and `UIApplicationMain` <rdar://problem/25693546>.
                let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory( 
                    to: UnsafeMutablePointer<Int8>.self, 
                    capacity: Int(CommandLine.argc)
                )
                UIApplicationMain(CommandLine.argc, argv, nil, NSStringFromClass(AppDelegate.self))
                

                The magic is in line 3, which touches the DerivedViewController class and thereby makes it available to the Objective-C runtime.  The rest of the stuff is boilerplate, made more difficult by the above-mentioned bug.


                Clearly this sort of workaround shouldn’t be necessary.  I presumed that folks have filed bugs about this.  If you have the bug number handy, please post it, just for my records.

                Share and Enjoy

                Quinn “The Eskimo!”
                Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                let myEmail = "eskimo" + "1" + "@apple.com"