Instantiate collection without knowing concrete type - swift

I have a class which is generic over a type T which conforms to the Collection protocol. Now I want to instantiate the collection without knowing the concrete type of the collection, is this possible?
class FileStore<T: Collection>{
var collection: T
init(){
collection = T() // This will never work
}
}

Collection doesn't have init amongst its protocol requirements, so you cannot directly initialize a Collection instance. You have several solutions though. You can either restrict your generic type constraint to a type that does guarantee having an init (such as Array) or you can create your own protocol requiring init, make T require conformance to that protocol and extend all Collections that you want to be able to store to conform to your protocol.
The second approach is shown below:
protocol Initializable {
init()
}
class FileStore<T: Collection> where T: Initializable {
var collection: T
init(){
collection = T.init()
}
}
// Extend the `Collection` conformant types
extension Array: Initializable {}
extension Dictionary: Initializable {}
extension Set: Initializable {}
// Create a FileStore
FileStore<Array<Int>>()
FileStore<[String:Int]>()
FileStore<Set<String>>()
Or depending on which exact types you want to be able to store, using the built-in RangeReplaceableCollection as your type constraint is even better. (Bear in mind that quite a few standard library types don't conform to RangeReplaceableCollection that do conform to Collection and have empty inits, such as Dictionary, Set, etc.).
class OtherFileStore<T: RangeReplaceableCollection> {
var collection = T.init()
}

Related

Missing argument for parameter 'from' in call when creating instance of Codable class

I am trying to create a new instance of a codable class, but I am not sure how to:
var x = Articles();
gives me the following error:
Missing argument for parameter 'from' in call
class Articles: Codable {
let value: [Article]?
}
I don't understand since this is a parameterless class. I have no idea what the from parameter is all about.
I don't understand since this is a parameterless class. I have no idea
what the from parameter is all about.
I get no error when I run the following:
class Articles: Codable {
let value: [Articles]?
init() {
value = nil
print("in init()")
}
}
var x = Articles()
output:
in init()
And without init():
class Articles: Codable {
let value: [Articles]?
// init() {
// value = nil
// print("in init()")
// }
}
var x = Articles() //Missing argument for parameter 'from' in call
First, read this:
Automatic Initializer Inheritance
As mentioned above, subclasses do not inherit their superclass
initializers by default. However, superclass initializers are
automatically inherited if certain conditions are met. In practice,
this means that you do not need to write initializer overrides in many
common scenarios, and can inherit your superclass initializers with
minimal effort whenever it is safe to do so.
Assuming that you provide default values for any new properties you
introduce in a subclass, the following two rules apply:
Rule 1 If your subclass doesn’t define any designated initializers, it
automatically inherits all of its superclass designated initializers.
If you look at the docs, Codable is a typealias for the Decodable protocol (a protocol is like an interface in java). Protocols specify functions that a class must implement if they adopt the protocol. Your Articles class adopts the Decodable protocol. However, the docs for Decodable say,
init(from: Decoder)
Creates a new instance by decoding from the given decoder. Required.
Default implementation provided.
Protocols can actually implement functions by using extensions, which are then inherited by the class adopting the protocol.
As a result, the only designated initializer for your class is the one defined in the Decodable protocol, which takes one argument--which you inherit according to Rule 1. On the other hand, when you explicitly define another designated initializer in your class that takes no arguments, then you will call that initializer when you provide no arguments.

Storing a generic conforming to an associated type inside A collection

I am trying to store a generic who uses an an associated type, however when trying to create a type which should conform to the generic type I describe in the generic list at the top of class A, but I get the error.
"Cannot invoke 'append' with an argument list of type '(B)'"
How can I properly declare the generic so that this code works?
class A<DataType: Any, AssociatedType: Runable> where
AssociatedType.DataType == DataType {
var array = Array<AssociatedType>()
func addAssociatedValue(data: DataType) {
array.append(B(data: data))
}
func runOnAll(with data: DataType) {
for item in array {
item.run(with: data)
}
}
}
class B<DataType>: Runable {
init(data: DataType) { }
func run(with: DataType) { }
}
protocol Runable {
associatedtype DataType
func run(with: DataType)
}
I am also using Swift 4.2 so if there is a solution that uses one of the newer Swift features that will also work as a solution.
B conforms to Runnable, yes, but you can't put it into an array that's supposed to store AssociatedTypes. Because the actual type of AssociatedType is decided by the caller of the class, not the class itself. The class can't say, "I want AssociatedType to always be B". If that's the case, you might as well remove the AssociatedType generic parameter and replace it with B. The caller can make AssociatedType be Foo or Bar or anything conforming to Runnable. And now you are forcing to put a B in.
I think you should rethink your model a bit. Ask yourself whether you really want AssociatedType as a generic parameter.
You could consider adding another requirement for Runnable:
init(data: DataType)
And add required to B's initializer. This way, you could write addAssociatedValue like this:
func addAssociatedValue(data: DataType) {
array.append(AssociatedType(data: data))
}

Inheritance, Generics, and Protocols in Swift

I have a generic class of the form:
class BaseClass<T> {
var prop: T
...
}
I then have multiple subclasses of the form:
class SubClassOne: BaseClass<SomeSubClass> {
...
}
class SubClassTwo: BaseClass<SomeOtherSubClass> {
...
}
Where the type parameters SomeSubClass and SomeOtherSubClass both inherit from a common base class SomeBaseClass.
I now want to define a variable to store instances of both SubClassOne and SubClassTwo. I have tried many possibilities:
var obj: BaseClass
var obj: BaseClass<SomeBaseClass>
var obj: BaseClass<Any>
But the first attempt results in the error Reference to generic type 'BaseClass' requires arguments in <...>, and the other two result in the error Cannot assign value of type 'SubClassOne' to type ... when trying to assign a value. I even tried to trick the Swift compiler into inferring the type for me by initializing an array:
var testArray = [SubClassOne(), SubClassTwo()]
But even this failed, resulting in the error Heterogeneous collection literal could only be inferred to [Any]; add explicit type annotation if this is intentional. Indeed, the only type annotation that successfully allows storage of both SubClasses is Any or AnyObject. Is it possible to store these instances with a more specific type? If not, why?
The reason it's important to do so is that I ultimately want to get the property prop from the stored variable obj. I am unable to do so if obj is stored as Any. I am also unable to simply cast it to SubClassOne or SubClassTwo because the method itself where I am trying to access the properties is a generic method, and which of SubClassOne or SubClassTwo to cast to depends on the generic type parameter of the method:
func castObj<T>(asType: T.Type) {
(self.obj as? T).prop
}
Which would be called as: castObj(asType: SubClassOne.self) or castObj(asType: SubClassTwo.self). However, we run into the same problem: the only generic type parameter constraint I can define that accepts both SubClassOne and SubClassTwo is Any, and then the Swift compiler complains: Value of type 'T' has no member 'prop'.
As a workaround I tried to define a protocol that encapsulates the desired property:
protocol HasProp {
var prop: SomeBaseClass { get }
}
Then I added this to the declaration of SubClassOne and SubClassTwo. However this resulted in still another error: Type 'SubClassOne' does not conform to protocol 'HasProp'. This confuses me as well, since SubClassOne and SubClassTwo both inherit prop from BaseClass<SomeSubClass> and so actually do conform to the protocol.
In summary:
Is it possible to store instances of SubClassOne and SubClassTwo with a more specific type that gives access to properties of BaseClass? If not, why?
Why do the SubClasses not conform to the protocol as expected?
How can I change the design to attain my desired behavior?
The problem is that at the moment the function castObj has no type constraints for its generic parameter, T. By giving a type constraint of BaseClass you should be fine, since BaseClass has both properties.
func castObj<T: BaseClass>(asType: T.Type) {
(self.obj as? T).propOne
(self.obj as? T).propTwo
}
In your example, the type of propTwo was common to both subclasses and the type of propOne was specialized. Make your design reflect that.
[was]
class BaseClass<T,U> {
var propOne: T
var propTwo: U
...
}
class SubClassOne: BaseClass<SomeSubClass, SomeClass> {}
class SubClassTwo: BaseClass<SomeOtherSubClass, SomeClass> {}
[could be]
class BaseClass<U> {
var propTwo: U
...
}
class SubClassOne<T>: BaseClass<SomeClass> {
var propOne: T
...
}
class SubClassTwo<T>: BaseClass<SomeClass> {
var propOne: T
...
}
The point is to keep common things in the base class and compose your specializations.
There's a fundamental misconception that SubclassOne and SubclassTwo are in the same inheritance hierarchy. Because of the generic type, they inherit from different base classes. You cannot mix and match them.
Think about it. With inheritance you should be able to use any subclass anywhere where you have the base class, so in your test example:
var testArray = [SubClassOne(), SubClassTwo()]
What type would the right hand side of the following expressions have to be?
testArray[0].prop = something
And this one
testArray[1].prop = something;
In SubClassOne, the type of prop is SomeSubClass and in SubClassTwo the type of prop must be SomeOtherSubClass.
The only way for you to get this to work is for prop to be declared as SomeBaseClass and that removes the necessity for BaseClass to be generic.
Edit
Why doesn't the protocol work?
The problem with the protocol is that you define the property as having the type of the base class but it is read/write. A property in an implementation of the protocol cannot fulfill the contract with a property that is specialised to one of the subclasses because other bits of code need to be able to assign any instance of the base class to the property.
protocol MyProtocol
{
var prop: BaseClass
}
struct MyImplementation: MyProtocol
{
var prop: SubClass
}
class BaseClass {}
class SubClass: BaseClass {}
class DifferentSubClass: BaseClass {}
var instance: MyProtocol = MyImplementation()
instance.prop = DifferentSubClass()
// Should be legal because the protocol says so but the type of prop in instance is SubClass.

Swift generic reference type

I'm having issues trying to constrain generic type requirements to just reference types. Here's some example code:
class WeakHolder<Element: AnyObject> {
weak var element: Element?
init(element: Element) {
self.element = element
}
}
protocol Animal: class { }
class Dog: Animal { }
let dog: Animal = Dog()
let holder = WeakHolder<Animal>(element: dog) // Error: Using "Animal" as a concrete type conforming to protocol 'AnyObject' is not supported.
If I change the generic requirement to <Element: class>, I get the error class constraint can only appear on protocol declarations.
Is this a limitation of generics? Marking a protocol as class is enough to have a weak reference to that protocol, is there no equivalent in generics?
The simple answer is that you cannot have a generic type that is a protocol.
Writing out the syntax makes it clear how this works:
class/struct GenericType<TypeName: TypeConstraints> {}
let thing = GenericType<Type>() where Type is a class or struct that adheres to any constraints
A Protocol Requiring adopting Types to be a class means any adopters are classes, but the Protocol itself is still not a Type.
It's possible generics could at some point support protocols, but it would require changing the general approach to either protocols or generics. Though your specific example may be possible with a smaller amount of work behind the scenes, so it's possible this may be implemented at some point.
You can take a look at The Generics Manifesto if you want to see the direction they're going. Skimming it I didn't find anything directly related to your use case, but it's fairly specific so it may not be included in the parameters of the document.
Another solution that worked in my particular case is the following:
class WeakHolder<Element: AnyObject> {
weak var element: Element?
init(element: Element) {
self.element = element
}
}
protocol Animal: class { }
class Dog: Animal { }
let dog: Animal = Dog()
let holder = WeakHolder<AnyObject>(element: dog as AnyObject)
When accessing element, I simply need to perform a downcast back to my protocol. Of course, I'll lose compile time safety when using this class with value types, but that's a non-issue in my situation.

Swift: extending sequences with a generic element [duplicate]

I have a class that takes a generic class Collection: <T: Model> (Model is a class) and a protocol (Resource) that some of the subclasses of Collection implement:
class Collection: <T: Model> {
typealias Callback = (result: Collection <T>) -> ()
}
protocol Resource {...}
Is it possible to write a protocol extension where Self is an instance of Collection?
Trying to extend the protocol with the class that takes a generic:
extension Resource where Self: Collection {
func fetch() {}
}
Gives:
Reference to generic type 'Collection' requires arguments in <...>
Trying to extend the class that takes a generic with the protocol:
extension Collection where Self: Resource {
func fetch(callback: Callback?) {}
}
Gives:
'Self' is only available in a protocol or as the result of method in a class
I'm not sure how to proceed. The goal is for the function to only be available on instances of Collection that conform to Resource.
The problem is Collection is a generic class so every where you declare it, you must attached the specialized type, like Collection<T>. However extension to a protocol can't be specified with a generic type so you end up not being able to supply T to Collection.
In your case though, T is constrained to be of type Model so why not use that in the default protocol implementation:
extension Resource where Self: Collection<Model> {
func fetch() {
// default implementation
}
}