How to create a protocol with a generic function extending type - swift

I'm trying to do a protocol with a generic function where T is not just equal to the type, but extends it.
class MainItem {}
class Item1: MainItem {}
class Item2: MainItem {}
protocol MyProtocol {
func myFunc<T: MainItem>() -> T // T extends MainItem
}
class ClassA: MyProtocol {
func myFunc() -> Item1 { // not MainItem
return Item1()
}
}
class ClassB: MyProtocol {
func myFunc() -> Item2 { // not MainItem
return Item2()
}
}
But I get this error
Type 'ClassA' does not conform to protocol 'MyProtocol'
because Item1 is not equal to MainItem (it expands it). How can you make it work?
For example, in Java everything can be done using abstract class:
abstract class MyProtocol {
abstract <T extends MainItem> T myFunc()
}

Generics is not the way to go for your requirements. When you declare a generic function in a protocol, the generic type parameter will mean that the same function works for all types that satisfy the generic type restriction, but the function signature still needs to be intact for all conforming types.
What you are looking for is a protocol with associated type. An associated type on a protocol means that the conforming type can decide what concrete type to use in place of the associated type, hence allowing you to use different associated types in different conforming classes.
protocol MyProtocol {
associatedtype MyType: MainItem
func myFunc() -> MyType
}
class ClassA: MyProtocol {
func myFunc() -> Item1 {
return Item1()
}
}
class ClassB: MyProtocol {
func myFunc() -> Item2 {
return Item2()
}
}

Related

Difference between protocol combining typealias and empty conforming protocol

Is there a difference between these two in Swift?
protocol ABProtocol: AProtocol, BProtocol {}
typealias ABProtocol = AProtocol&BProtocol
To make things clearer, I will rename the second one to:
typealias ABProtocolIntersection = AProtocol & BProtocol
I can think of two differences off the top of my head.
If your type conform to AProtocol and BProtocol, the type is automatically a subtype of ABProtocolIntersection, but it does not automatically conform to ABProtocol. After all, ABProtocol is a totally different protocol.
Example:
class Foo: AProtocol, BProtocol { ... }
func foo<T: ABProtocolIntersection>(type: T.Type) { }
func bar<T: ABProtocol>(type: T.Type) { }
foo(type: Foo.self) // works
bar(type: Foo.self) // error
Another difference is that you can put extensions on ABProtocol, but not ABProtocolIntersection:
extension ABProtocol { } // OK
extension ABProtocolExtension { } // error
This is because ABProtocolIntersection is a non-nominal type, similar to types like (Int, Int) or (Int) -> String. See also: What is a 'non-nominal type' in Swift?
Yes there is a difference. The former defines a new protocol to which types must conform when it is used. The latter only defines a "placeholder" for AProtocol&BProtocol
Consider the following code:
protocol AProtocol{}
protocol BProtocol{}
protocol ABProtocol1: AProtocol, BProtocol {}
typealias ABProtocol2 = AProtocol & BProtocol
func f1(value: ABProtocol1) {}
func f2(value: ABProtocol2) {}
Arguments to f1 must conform to ABProtocol1 but arguments to f2 can conform to AProtocol and BProtocol. You do not need to explicitly conform types to ABProtocol2. For example:
struct A: AProtocol, BProtocol
{
}
f1(value: A()) // Error!
f2(value: A()) // OK

Protocol extensions not using most specific implementation

I have two classes, Object and SubObject. A protocol, MyProtocol, has an associatedtype of type Object. In two extensions, I have provided implementations of the save function. When I create an instance of TestClass with either of the classes, they both result in a call to the least specific extension implementation, while it would be expected to call the most specific one.
class Object {}
class SubObject: Object {}
protocol MyProtocol {
associatedtype T: Object
func save()
}
extension MyProtocol {
func save() {
print("Object")
}
}
extension MyProtocol where T == SubObject {
func save() {
print("SubObject")
}
}
class MyClass<T: Object>: MyProtocol {
}
class TestClass<T: Object> {
typealias U = MyClass<T>
func test() {
let myClass = U()
myClass.save()
}
}
let testClass1 = TestClass<Object>()
testClass1.test() // returns "Object"
let testClass2 = TestClass<SubObject>()
testClass2.test() // returns "Object" (should be "SubObject")
How can I solve this, so that the TestClass calls the correct implementation of save? Or is this not currently possible in Swift? Any help would be appreciated!

Default implementation of protocol extension in Swift not working

I'm trying to add functionality to an NSManagedObject via a protocol. I added a default implementation which works fine, but as soon as I try to extend my subclass with the protocol it tells me that parts of it are not implemented, even though I added the default implementation.
Anyone having Ideas of what I'm doing wrong?
class Case: NSManagedObject {
}
protocol ObjectByIdFetchable {
typealias T
typealias I
static var idName: String { get }
static func entityName() -> String
static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T]
}
extension ObjectByIdFetchable where T: NSManagedObject, I: AnyObject {
static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T] {
let r = NSFetchRequest(entityName: self.entityName())
r.predicate = NSPredicate(format: "%K IN %#", idName, ids)
return context.typedFetchRequest(r)
}
}
extension Case: ObjectByIdFetchable {
typealias T = Case
typealias I = Int
class var idName: String {
return "id"
}
override class func entityName() -> String {
return "Case"
}
}
The error I get is Type Case doesn't conform to protocol ObjectByIdFetchable
Help very much appreciated.
We'll use a more scaled-down example (below) to shed light on what goes wrong here. The key "error", however, is that Case cannot make use of the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject; since type Int does not conform to the type constraint AnyObject. The latter is used to represent instances of class types, whereas Int is a value type.
AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, including function types.
From the Language Guide - Type casting.
Subsequently, Case does not have access to any implementation of the blueprinted objectWithId() method, and does hence not conform to protocol ObjectByIdFetchable.
Default extension of Foo to T:s conforming to Any works, since Int conforms to Any:
protocol Foo {
typealias T
static func bar()
static func baz()
}
extension Foo where T: Any {
static func bar() { print ("bar") }
}
class Case : Foo {
typealias T = Int
class func baz() {
print("baz")
}
}
The same is, however, not true for extending Foo to T:s conforming to AnyObject, as Int does not conform to the class-type general AnyObject:
protocol Foo {
typealias T
static func bar()
static func baz()
}
/* This will not be usable by Case below */
extension Foo where T: AnyObject {
static func bar() { print ("bar") }
}
/* Hence, Case does not conform to Foo, as it contains no
implementation for the blueprinted method bar() */
class Case : Foo {
typealias T = Int
class func baz() {
print("baz")
}
}
Edit addition: note that if you change (as you've posted in you own answer)
typealias T = Int
into
typealias T = NSNumber
then naturally Case has access to the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject, as NSNumber is class type, which conforms to AnyObject.
Finally, note from the examples above that the keyword override is not needed for implementing methods blueprinted in a protocol (e.g., entityName() method in your example above). The extension of Case is an protocol extension (conforming to ObjectByIdFetchable by implementing blueprinted types and methods), and not really comparable to subclassing Case by a superclass (in which case you might want to override superclass methods).
I found the solution to the problem. I thought it's the typealias T which is the reason for not compiling. That's actually not true, it's I which I said to AnyObject, the interesting thing is that Int is not AnyObject. I had to change Int to NSNumber

Generic Swift Protocol inside Generic Controller

Is it possible to have generic inside generic?
I have this protocol
public protocol ListViewModelProtocol {
typealias ViewModel
typealias Cell
func titleForHeaderInSection(section: Int) -> String?
func numberOfSections() -> Int
func numberOfRowsInSection(section: Int) -> Int
func viewModelAtIndexPath(indexPath: NSIndexPath) -> ViewModel
}
I also have base ListViewModel that implements this protocol
public class BaseListViewModel<T, U> : ListViewModelProtocol {
}
But already here it says that my ListViewModelProtocol is not implemented. How can I set T and U to be of specific class inside protocol? Because if I write this in protocol
typealias ViewModel: CustomClass
typealias Cell: CustomCell
Its still not working.
My goal is to subclass BaseListViewModel like
public class TestListViewModel : BaseListViewModel<TestCellViewModel, TestTableViewCell> {
}
Then I could just do this in my BaseViewController
public class BaseViewController<T: ListViewModelProtocol>: UITableViewController {
}
And in some subclass ViewController do this:
public class CustomViewController: BaseViewController<TestListViewModel> {
}
and that way CustomViewController would "get" TestCellViewModel and TestTableViewCell (actually its BaseViewController).
But of course this is not working as I expected. What am I missing? Or I have to define typealias for ListViewModelProtocol in every class that implements it or uses it as generic type? Which means I would have to define ViewModel and Cell of ListViewModelProtocol in both BaseListViewModel class and BaseViewController class, but thats not so generic since I just want to put base types of those in protocol and thats it.
Or maybe there is something wrong with my approach and I should implement this differently?
Any suggestions are useful. Thanks
EDIT
I have managed to fix this but I have another problem.
public class BaseViewController<T: ListViewModelProtocol>: UITableViewController {
var dataSource: T?
}
This datasource is used inside UITableViewDataSource methods by calling its own methods (see ListViewModelProtocol methods). Everything is working fine but when some custom controller:
Controller: BaseViewController<TestListViewModel>
is being deinitialized I get EXC_BAD_ACCESS error. If I put
deinit {
self.dataSource = nil
}
it works but I would like to know why I need to set it to nil.
Thanks.
typealias keyword has more than one meaning ...
// protocol can't be generic
protocol P {
// here typealias is just placeholder, alias
// for some unknown type
typealias A
func foo(a:A)->String
}
// C is generic
class C<T>:P {
// here typealias define the associated type
// in this example it is some generic type
typealias A = T
func foo(a: A) -> String {
return String(a)
}
}
let c1 = C<Int>()
print(c1.foo(1)) // 1
let c2 = C<Double>()
print(c2.foo(1)) // 1.0
// D is not generic!!!
class D: C<Double> {}
let d = D()
print(d.foo(1)) // 1.0
Update, to answer the question from discussion
class Dummy {}
protocol P {
// here typealias is just placeholder, alias
// for some inknown type
typealias A : Dummy
func foo(a:A)->String
}
// C is generic
class C<T where T:Dummy>:P {
// here typealias define the associated type
// in this example it is some generic type
typealias SomeType = T
func foo(a: SomeType) -> String {
return String(a)
}
}
class D:Dummy {}
let c = C<D>()
print(c.foo(D())) // D
and
// now next line doesn't compile
let c1 = C<Int>() // error: 'C' requires that 'Int' inherit from 'Dummy'
If you want to implement a protocol with associated types you have to set these associated types in the your generic implementation:
public class BaseListViewModel<T, U> : ListViewModelProtocol {
typealias ViewModel = T
typealias Cell = U
// implement the methods as well
}

protocol associated type typealias assignment compile error

Following code:
protocol SomeProtocol {
typealias SomeType = Int // used typealias-assignment
func someFunc(someVar: SomeType)
}
class SomeClass: SomeProtocol {
func someFunc(someVar: SomeType) {
print(someVar)
}
}
gives compile-time error:
Use of undeclared type 'SomeType'
Adding, say typealias SomeType = Double, to the SomeClass resolves the error.
The question is, what's the point of typealias-assignment part (which is optional btw) of protocol associated type declaration though?
In this case the assignment of Int to the typealias is equal to no assignment because it gets overridden by your conforming type:
// this declaration is equal since you HAVE TO provide the type for SomeType
protocol SomeProtocol {
typealias SomeType
func someFunc(someVar: SomeType)
}
Such an assignment provides a default type for SomeType which gets overridden by your implementation in SomeClass, but it is especially useful for protocol extensions:
protocol Returnable {
typealias T = Int // T is by default of type Int
func returnValue(value: T) -> T
}
extension Returnable {
func returnValue(value: T) -> T {
return value
}
}
struct AStruct: Returnable {}
AStruct().returnValue(3) // default signature: Int -> Int
You get the function for free only by conforming to the protocol without specifying the type of T. If you want to set your own type write typealias T = String // or any other type in the struct body.
Some additional notes about the provided code example
You solved the problem because you made it explicit which type the parameter has. Swift also infers your used type:
class SomeClass: SomeProtocol {
func someFunc(someVar: Double) {
print(someVar)
}
}
So SomeType of the protocol is inferred to be Double.
Another example where you can see that SomeType in the class declaration doesn't refer to to the protocol:
class SomeClass: SomeProtocol {
typealias Some = Int
func someFunc(someVar: Some) {
print(someVar)
}
}
// check the type of SomeType of the protocol
// dynamicType returns the current type and SomeType is a property of it
SomeClass().dynamicType.SomeType.self // Int.Type
// SomeType gets inferred form the function signature
However if you do something like that:
protocol SomeProtocol {
typealias SomeType: SomeProtocol
func someFunc(someVar: SomeType)
}
SomeType has to be of type SomeProtocol which can be used for more explicit abstraction and more static code whereas this:
protocol SomeProtocol {
func someFunc(someVar: SomeProtocol)
}
would be dynamically dispatched.
There is some great information in the documentation on "associated types" in protocols.
Their use is abundant throughout the standard library, for an example reference the SequenceType protocol, which declares a typealias for Generator (and specifies that it conforms to GeneratorType). This allows the protocol declaration to refer to that aliased type.
In your case, where you used typealias SomeType = Int, perhaps what you meant was "I want SomeType to be constrained to Integer-like behavior because my protocol methods will depend on that constraint" - in which case, you may want to use typealias SomeType: IntegerType in your protocol, and then in your class go on to assign a type to that alias which conforms to IntegerType.
UPDATE
After opening a bug w/ Apple on this and having had extensive discussion around it, I have come to an understanding of what the base issue is at the heart of this:
when conforming to a protocol, you cannot directly refer to an associated type that was declared only within that protocol
(note, however, that when extending a protocol the associated type is available, as you would expect)
So in your initial code example:
protocol SomeProtocol {
typealias SomeType = Int
func someFunc(someVar: SomeType)
}
class SomeClass: SomeProtocol {
func someFunc(someVar: SomeType) { // use of undeclared type "SomeType"
print(someVar)
}
}
...the error re: "use of undeclared type" is correct, your class SomeClass has not declared the type SomeType
However, an extension to SomeProtocol has access to the associated type, and can refer to it when providing an implementation:
(note that this requires using a where clause in order to define the requirement on the associated type)
protocol SomeProtocol {
typealias SomeType = Int
func someFunc(someVar: SomeType)
}
extension SomeProtocol where SomeType == Int {
func someFunc(someVar: SomeType) {
print("1 + \(someVar) = \(1 + someVar)")
}
}
class SomeClass: SomeProtocol {}
SomeClass().someFunc(3) // => "1 + 3 = 4"
There is great article that actually gives you answer for your question. I suggest everyone to read it to get into type-aliases and some more advanced stuff that comes up when you use it.
Citation from website:
Conceptually, there is no generic protocols in Swift. But by using
typealias we can declare a required alias for another type.