I am having difficulty using Swift Dictionaries to store generic objects.
I am trying to stores objects in the following way:
[C: Container<C>], where C: ContentType
Such that the dictionary is structured as below:
Key Value
--- -----
A Container<A>
B Container<B>
...
ContentType
I have a class called "ContentType" which stores metadata about a type of content (i.e. name, size, weight, etc).
class ContentType: NSObject { ... }
I have many different subclasses of "ContentType", each subclass describing a particular type of content.
class ContentTypeA: ContentType { ... }
class ContentTypeB: ContentType { ... }
Container
I also have a class called "Container" which is designed to store information relating to the content type. Appropriately, it is a generic class, with it's generic type as "ContentType".
class Container<C: ContentType>: NSObject { }
I have also implemented extensions on "Container" that define functions for specific "ContentType".
extension Container where C: ContentTypeA {
func fancyFunctionOnlyFoundInContentTypeA() { }
}
extension Container where C: ContentTypeB {
func fancyFunctionOnlyFoundInContentTypeB() { }
}
Controller
In my main controller class, I have implemented the following subscript overloader.
subscript<C: ContentType>(contentType: C) -> Container<C> {
// If Container for ContentType "C" does not exist
if (self.containers[contentType] == nil) {
// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
}
// And return it
return self.containers[contentType]! as! Container<C>
}
The idea is that you can obtain/reuse a "Container" of a particular "ContentType" and it will be returned ready.
// Define a ContentType singletons somewhere
static let ContentTypeASingleton = ContentTypeA.init()
static let ContentTypeBSingleton = ContentTypeA.init()
...
// Using the subscript, the Container for ContentTypeA/ContentTypeB can be obtained
let containerForTypeA = self[ContentTypeASingleton]
let containerForTypeB = self[ContentTypeBSingleton]
From here, it would be possible to call the Container extensions, specific to each ContentType.
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
The Problem
I believe all of this should work theoretically, and it certainly does compile in Swift 4, Xcode 9.0. However, upon executing the following:
// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
I get the following error:
Could not cast value of type 'Test.Container<Test.ContentTypeA>' (0x109824a00) to 'Test.Container<Test.ContentType>' (0x109824638).
As ContentTypeA inherits from ContentType, I don't understand why this is a problem. It seems Swift Dictionaries are unable to store objects of type ContentType < AnySubclassOfContentType > , and must store objects as ContentType < ContentType > .
Does anyone have any suggestions? Cheers.
Full code below:
class ViewController: UIViewController {
var containers = [ContentType : Container<ContentType>]()
override func viewDidLoad() {
super.viewDidLoad()
let contentTypeA = ContentTypeA.init()
let contentTypeB = ContentTypeB.init()
let containerForTypeA = self[contentTypeA]
let containerForTypeB = self[contentTypeB]
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
}
subscript<C: ContentType>(contentType: C) -> Container<C> {
if (self.containers[contentType] == nil) {
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
}
return self.containers[contentType]! as! Container<C>
}
}
class ContentType: NSObject { }
class Container<C: ContentType>: NSObject { }
class ContentTypeA: ContentType { }
class ContentTypeB: ContentType { }
extension Container where C: ContentTypeA {
func fancyFunctionOnlyFoundInContentTypeA() { }
}
extension Container where C: ContentTypeB {
func fancyFunctionOnlyFoundInContentTypeB() { }
}