Generic controller with storyboard

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.

Replies

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)?

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"

Thanks for your suggested solution, it did however not resolve my issues with the storyboard GUI and generic super classes. Sad to see it is 2019 and it is still an issue >.>

Would you explain what are precisely those issues with the storyboard GUI and generic super classes ?


Are they the same as in the OP ?

it did however not resolve my issues with the storyboard GUI and generic super classes

I just re-tested the example I posted and it works for me (Xcode 10.2 targeting the iOS 12.2 simulator). Perhaps you could explain more about the specific problems you’re seeing?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Well, I assume the OP refers to issues like as soon as the generics derived class is not "automatically" exposed via the Objective-C runtime, the IBOutlets aren't exposed either:

class DescriptionHeaderController:
    UIViewController
    where ModelType: NSManagedObject, ModelType: ModelAdapterFactory
{
    var model : ModelType!
    
    // No chance to set in IB:    
    @IBOutlet weak var ratingContainerView: UIView! 
}

class DerivedHeaderController : DescriptionHeaderController<Foo> {
}


(Just hitting that roadblock after sorting out protocols with associated type and quite a lot of other issues..)