I recently refactored my class BookTableViewController
from a simple inheritance from UITableViewController
, so that it now inherits from a generic class FetchedResultsTableViewController<TResultType, TCellType>
which itself inherits from UITableViewController
.
The class declarations look like this:
class BookTableViewController: FetchedResultsTableViewController<Book, BookTableViewCell> {
override func viewDidLoad() {
// breakpoints in here do not catch!
}
}
class FetchedResultsTableViewController<TResultType, TCellType: UITableViewCell>:
UITableViewController, NSFetchedResultsControllerDelegate {
// implementation here
}
In the Storyboard, the Custom class and Module are both set, and I can click the arrow to jump to the code for the BookTableViewController
class, suggesting that the storyboard is linked correctly to the class. However, when I try to run the application, the class is not recognised - code in viewDidLoad()
does not run, and I receive the following logged message when running my app:
Unknown class _TtC12Reading_List23BookTableViewController in Interface Builder file.
I am running XCode 7.3 (Swift 2.2). Is this a limitation with Storyboards, a bug, or have I missed something?
Thanks!
UPDATE:
After some experimentation, it does seem to be related to the generic inheritance, rather than the accessibility of the class. With the following classes defined:
import Foundation
import UIKit
// Both of these classes are accessible by the Storyboard
class FirstInheritance : UITableViewController{}
class SecondInheritance : FirstInheritance{}
// The generic class is also accessible
class GenericViewController<T> : UITableViewController{}
// But this class is not accessible...
class ConcreteViewController : GenericViewController<String>{}
all classes except the ConcreteViewController
are suggested in the Storyboard's class autocomplete (although the generic class also does not work, as there is no way to specify the type arguments in the Storyboard).
Little late to the game, but this info might come in handy for anyone bumping into the thread.
Actually, as far as I can tell, there is now, as of Swift 3, some kind of awkward support for generics in storyboards.
I've managed to write a generic base class extending UIViewController, then constraining several subclasses of that generic class and using them in the storyboard.
The actual code has a similar layout to what is specified bellow:
class GenericParent: UIViewController {
// Has a few, non-generic members ...
}
class ActualGenericClass<T>: GenericParent {
// There are more generic members in the actual class
var adapter: Adapter<T> = Adapter()
}
// This class is usable in Interface Builder;
// although auto-complete won't show it, fully writing down
// the class name works
class SpecificInstanceOfActualGenericClass: ActualGenericClass<Int> {
@IBOutlet weak var tableView: UITableView!
}
That works perfectly, to my surprise!
On the other hand, the next example doesn't seem to work:
class GenericCell<T>: UITableViewCell {
var node: T? = nil
}
class SpecificCell: GenericCell<Int> {
@IBOutlet weak var coolTitleLabel: UILabel!
}
As before, explicitly writing the cell's class name on Interface Builder (on the cell's dynamic prototype) sets the class on the table view cell, and outlets are visible.
At runtime, though, when instancing the UIViewController that contains the cell's dynamic prototype, the "unknown class in interface builder file" warning is displayed and the App crashes when dequeueing the cell.
So @Andew Bennet, the statement that:
Storyboards do not support classes which inherit from generic classes
Doesn't seem to be 100% true any more, although you at least WERE right; it's the first time I've managed to pull this one off, albeit only partially!
It bugs me that some things work, others don't, but it's better than nothing.
This is so damn straightforward in Java...
Storyboards do not support classes which inherit from generic classes, either directly or indirectly. If you use a class which inherits from a generic class in a storyboard, you will get an error at runtime stating that the class is unknown.
An alternative is to use a protocol with typealiases and extensions:
protocol MyProtocol {
associatedtype genericType1
associatedtype genericType2
func myFunc(argument1: genericType1, argument2: genericType2)
}
extension MyProtocol {
func defaultFunc(argument1: genericType1){
// default implementation here
}
}
The protocol is used as follows:
class NonGenericClass: MyProtocol {
typealias genericType1 = Int
typealias genericType2 = String
func myFunc(argument1: genericType1, argument2: genericType2){
// specific implementation here
}
}
The class NonGenericClass
will have all functions in the MyProtocol
extension (in this case, defaultFunc(argument1: Int)
).
The protocol MyProtocol
can also inherit from other protocols, and the implementation of those functions can be defined in the extension to MyProtocol
. Thus the protocol can make any class which conforms to it also conform to another protocol, with a standard implementation.
However, this has the limitation that you cannot specify standard overrides of class functions from within the protocol.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With