Generic Swift Protocol inside Generic Controller - swift

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
}

Related

Generic protocol property declaration

Suppose I have a protocol with an associated type T
protocol Helper{
associatedtype T
func help(_ item: T)
}
in a class, I want to declare a property
class Manager<T>{
let item: T?
let helper: Helper<T>
//Error: Cannot specialize non-generic type 'Helper'
let anotherHelper: Helper
//Error: Protocol 'Helper' can only be used as a generic constraint because it has Self or associated type requirements
}
How do I declare and use the helper property in such way that it enforces the type of the class conforming to the Helper protocol?
I'm sure a lot of people with a background in Java/C# or other similar languages get stuck while attempting to do similar things
In this particular case, you could make T in Manager<T> to be a Helper instead:
class Manager<T> where T : Helper {
let item: T.T?
let helper: T
init(helper: T) {
self.helper = helper
item = nil
}
}
And if you want a Helper<Int>, you do:
class IntHelper : Helper {
typealias T = Int
// ...
}
And then use Manager<IntHelper>.
You might need a type eraser:
struct AnyHelper<T>: Helper {
private let helpFunc: (T) -> Void
init<H>(_ helper: H) where Helper.T == T {
helpFunc = helper.help
}
func help(_ item: T)
helpFunc(item)
}
}
and then use the type eraser in your class:
class Manager<T>{
let item: T?
let helper: AnyHelper<T>
init<H>(item: T, helper: H) where H.T == T {
self.item = item
self.helper = AnyHelper(helper)
}
}

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

swift how to define abstract class and why apple invent associated type but not use generic protocol

I am a swift beginner. Something puzzled me when learning. Now I want to define an abstract class or define some pure virtual method, but I cannot find a way to do it. I have a protocol with associated type(this also puzzled me, why not use generic protocol), and some methods need to be implemented in a base class, and other classes inherited from the base class, they should implement other methods in the protocol, how can I do?
for instance:
Protocol P{
typealias TypeParam
func A()
func B()
}
class BaseClass<TypeParam> : P {
abstract func A()
func B(){
if someCondition {
A()
}
}
}
class ChildClass : BaseClass<Int> {
func A(){}
}
It seems very strange, and I still cannot find a method to resolve the abstract problem.
Swift has something similar: protocol extensions
They can define default implementations so you don't have to declare the method in your base class but it also doesn't force to do that in any class, struct or enum.
protocol P {
associatedtype TypeParameter
func A()
func B()
}
extension P {
func A(){}
}
class BaseClass<TypeParam> : P {
typealias TypeParameter = TypeParam
func B(){
if someCondition {
A()
}
}
}
class ChildClass : BaseClass<Int> {
// implementation of A() is not forced since it has a default implementation
func A(){}
}
Another approach would be to use a protocol instead of BaseClass which is more in line with protocol oriented programming:
protocol Base {
associatedtype TypeParameter
func A()
func B()
}
extension Base {
func B(){
if someCondition {
A()
}
}
}
class ChildClass : Base {
typealias TypeParameter = Int
// implementation of A() is forced but B() is not forced
func A(){}
}
However one of the big disadvantages would be that a variable of protocol type can only be used in generic code (as generic constraint):
var base: Base = ChildClass() // DISALLOWED in every scope
As a workaround for this limitation you can make a wrapper type:
// wrapper type
struct AnyBase<T>: Base {
typealias TypeParameter = T
let a: () -> ()
let b: () -> ()
init<B: Base>(_ base: B) where B.TypeParameter == T {
// methods are passed by reference and could lead to reference cycles
// below is a more sophisticated way to solve also this problem
a = base.A
b = base.B
}
func A() { a() }
func B() { b() }
}
// using the wrapper:
var base = AnyBase(ChildClass()) // is of type AnyBase<Int>
Regarding the use of "true" generic protocols, the Swift team has chosen to use associatedtype because you can use many generic types without having to write all out in brackets <>.
For example Collection where you have an associated Iterator and Index type. This allows you to have specific iterators (e.g. for Dictionary and Array).
In general, generic/associated types are good for code optimization during compilation but at the same time being sometimes too static where you would have to use a generic wrapper type.
A useful link to some patterns for working with associated types.
(See also above)
A more sophisticated way to solve the problem of passing the methods by reference.
// same as `Base` but without any associated types
protocol _Base {
func A()
func B()
}
// used to store the concrete type
// or if possible let `Base` inherit from `_Base`
// (Note: `extension Base: _Base {}` is currently not possible)
struct BaseBox<B: Base>: _Base {
var base: B
init(_ b: B) { base = b}
func A() { base.A() }
func B() { base.B() }
}
struct AnyBase2<T>: Base {
typealias TypeParameter = T
var base: _Base
init<B: Base>(_ base: B) where B.TypeParameter == T {
self.base = BaseBox(base)
}
func A() { base.A() }
func B() { base.B() }
}
// using the wrapper:
var base2 = AnyBase2(ChildClass()) // is of type AnyBase2<Int>

Can't create an Array of types conforming to a Protocol in Swift

I have the following protocol and a class that conforms to it:
protocol Foo{
typealias BazType
func bar(x:BazType) ->BazType
}
class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}
When I try to create an Array of foos, I get an odd error:
var foos: Array<Foo> = [Thing()]
Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.
OK, so it can only be used if it has an associated type requirement (which it does), but for some reason this is an error?? WTF?!
I'm not sure I fully understand what the compiler is trying to tell me...
Let's say, if we could put an instance of Thing into array foos, what will happen?
protocol Foo {
associatedtype BazType
func bar(x:BazType) -> BazType
}
class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}
class AnotherThing: Foo {
func bar(x: String) -> String {
return x
}
}
var foos: [Foo] = [Thing()]
Because AnotherThing conforms to Foo too, so we can put it into foos also.
foos.append(AnotherThing())
Now we grab a foo from foos randomly.
let foo = foos[Int(arc4random_uniform(UInt32(foos.count - 1)))]
and I'm going to call method bar, can you tell me that I should send a string or an integer to bar?
foo.bar("foo") or foo.bar(1)
Swift can't.
So it can only be used as a generic constraint.
What scenario requires a protocol like this?
Example:
class MyClass<T: Foo> {
let fooThing: T?
init(fooThing: T? = nil) {
self.fooThing = fooThing
}
func myMethod() {
let thing = fooThing as? Thing // ok
thing?.bar(1) // fine
let anotherThing = fooThing as? AnotherThing // no problem
anotherThing?.bar("foo") // you can do it
// but you can't downcast it to types which doesn't conform to Foo
let string = fooThing as? String // this is an error
}
}
I have been playing with your code trying to understand how to implement the protocol. I found that you can't use Typealias as a generic type because it is just an alias not a type by itself. So if you declare the Typealias outside your protocol and your class you can effectively use it in your code without any problem.
Note: the Typealias has the Int type in its declaration, that way you can always use the alias instead of the Int type and use all of its associated methods and functions.
Here's how I make it work:
typealias BazType = Int
protocol Foo{
func bar(x:BazType) -> BazType
}
class Thing: Foo {
func bar(x: BazType) -> BazType {
return x.successor()
}
}
let elements: Array<Foo> = [Thing(), Thing()]