class Controller<U: NSObject> {}
protocol Robert {
associatedtype T
associatedtype U: Controller<T>
var fetcher: U { get }
}
class Telephone: NSObject {}
class Object: Telephone {}
class Turtle: Controller<Object> {}
class Fish: Robert {
typealias T = Object
typealias U = Turtle
let x = Turtle()
var fetcher: Turtle {
return x
}
}
I don't understand why. Any help appreciated.
When selecting the XCode 'fix it' option, a stub for 'Fetcher is inserted. But there is already a typealias for Fetcher.
This has now been recognized as a bug in Swift 4. For now we must avoid associated types constrained by types that have generic constraints.
So this is not cool
associatedtype U: Controller<T>
Removing it results in the following, which works.
protocol Robert {
associatedtype T: NSObject
var fetcher: Controller<T> { get }
}
Related
How to describe a variable in a protocol on swift
any type, but supports a specific protocol
Something like this
protocol MyProtocol: class {
var itemsList: AnyObject where Collection { get } // AnyObject supports a Collection protocol
}
Maybe you want:
protocol MyProtocol: class {
associatedtype T: Collection
var itemsList: T { get }
}
If you want T to definitely be an object as well (not a struct) then you must wait for this proposal to make it into the language.
If you want a class to satisfy this protocol with T unspecified in the class's definition, make the class generic.
class C<T: Collection>: MyProtocol {
let itemsList: T
init(_ itemsList: T) {
self.itemsList = itemsList
}
}
let x = C([1,2,3])
In Swift, this will give me compile error
class TestType {
}
protocol TestProtocol {
associatedtypes T: TestType
}
class TestClass<T: TestType> {
var x: TestProtocol<T>
}
It will give me a compiler error because TestProtocol "can only be used as a generic constraint". The correct way to do this which is much less clean (because it requires adding a generic parameter of TestProtocol everywhere when it is used)
class TestClass<T: TestProtocol> {
var x: T
}
So my question is, why doesn't Swift allow referring to a generic protocol simply as TestProtocol<T> when T is already a typed parameter as in the above example ?
There is a little "hack" to almost get what you want: You'll have to specify the alias in the where-clause of the template parameter list:
class TestType {
var value:Int = 0
func doTest() { print ("doing the test \(value)") }
}
protocol TestProtocol {
associatedtype T: TestType
func doProtocolForTest (t:T)
}
class TestClass<TT: TestType, TP:TestProtocol> where TP.T == TT {
var x:TP!
}
// the following demonstrats a sample usage, without much sense
// ------------------------------------------------------------
class TestProtocolImp : TestProtocol {
func doProtocolForTest(t: TestType) {
print ("running test...")
t.doTest()
}
}
let test = TestType()
test.value = 42
let tc = TestClass<TestType, TestProtocolImp>()
tc.x = TestProtocolImp()
tc.x.doProtocolForTest(t: test)
The following contrived Swift 2 example from real-world code won't compile:
protocol SomeModelType { }
protocol SomeProtocol {
var someVar: SomeModelType? { get }
}
class ConcreteClass<T: SomeModelType>: SomeProtocol {
var someVar: T?
}
This doesn't make sense to me fully. I would assume in ConcreteClass that because I have T being constrained to SomeModelType and have T as the backing type for the someVar property, the compiler would be able to figure out that the SomeProtocol was being conformed to by ConcreteClass.
How should an example like this be structured? Is it possible to the Swift compiler to determine protocol conformance through generic type constraints?
protocol SomeModelType { }
protocol SomeProtocol {
associatedtype T: Any
var someVar: T? { get }
}
class ConcreteClass<T> :SomeProtocol where T: SomeModelType {
var someVar: T?
}
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
}
I have this Protocol, Class, and a Class that takes a generic which has to conform to both.
I want to build a registry which holds an array of such classes, but how do I define a variable in the registry which will satisfy the compiler?
Consider this example:
protocol SomeProtocol {
}
class SomeClass {
}
class AnotherClass<R, P where P:SomeProtocol, P:SomeClass> {
}
class Registry {
private init() {}
// This is not allowed since the second generic doesn't conform to neither required class
var registry:[AnotherClass<AnyObject, AnyObject>] = []
}
This is how
import UIKit
protocol SomeProtocol {
}
class SomeClass {
}
class AnotherClass<R, P where P:SomeProtocol, P:SomeClass> {
}
class TheClass : SomeClass, SomeProtocol {}
class Registry {
private init() {}
var registry:[AnotherClass<AnyObject, TheClass>] = []
}
You need to define a class that inherits SomeClass and SomeProtocol. AnyObject doesn't inherit from SomeClass and SomeProtocol and that's why you get an error.
Update
To make it less restritive
This is how
import UIKit
protocol SomeProtocol {
}
class SomeClass {
}
class AnotherClass<R, P where P:SomeProtocol, P:SomeClass> {
}
class TheClass : SomeClass, SomeProtocol {}
class Registry<T, where T:SomeProtocol, T:SomeClass> {
private init() {}
var registry:[AnotherClass<AnyObject, T>] = []
}
Update 2
Eventually, however, you will end up with something like
Registry<TheClass>
because generics needs a concrete class in the end.
What you could do is enforce the type at runtime instead - you will need to add #objc to your protocols though - via is. An alternative would be to scrap generics and use a interface or a base class.