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)
}
}
Related
I have some protocol hierarchies on my code where I have protocols defining the objects I use and protocols defining functions to use with this objects.
The object protocols are inherited by other object protocols that add more functionality to the original protocols and so are the functions that use them. The problem is that I can't find a way to specialize the function to take only the inherited parameter.
Here's some code to clarify what I'm trying to do:
protocol A {
var foo: String { get set }
}
protocol B: A {
var bar: String { get set }
}
struct Test: B {
var foo: String = "foo"
var bar: String = "bar"
}
protocol UseAProtocol {
static func use<T: A>(_ obj: T)
}
protocol UseBProtocol: UseAProtocol {
}
extension UseBProtocol {
//If I change the requirement to <T: B> this won't conform to `UseAProtocol`.
static func use<T: A>(_ obj: T) {
print(obj.foo)
// print(obj.bar) - Since obj does not conform to `B` I can't access ".bar" here without a forced casting.
}
}
struct Manager: UseBProtocol {
}
Manager.use(Test())
What I want to do is make the use function on the UseBProtocol only accept objects that conform to B. B inherits from A, but when I change from <T:A> to <T:B> I got an error saying that Manager does not conform to UseAProtocol and I have to change it back to <T:A>.
I know I can do this using associatedtype and where clauses on the inherit protocols - that's what I use today - but I wanted to move the generic requirement to the method so I could group all of them together under the same struct (I have a lot of this hierarchies and by using associatedtype I must use one struct by hierarchy). When the Conditional Conformances came to Swift this would be possible with associatedtype, but until them...
I could also use as! to force the casting from A to B on the UseBProtocol implementation, but that's a really bad solution and the error would be throw only at runtime.
Is there any way to achieve what I'm looking for?
It seems like what you are actually looking for is an associatedType in UseAProtocol rather than making the use function generic.
By declaring an associated type in UseAProtocol and changing the function signature of use to static func use(_ obj: ProtocolType) your code compiles fine and you can access both foo and bar from Manager.
protocol AProtocol {
var foo: String { get set }
}
protocol BProtocol: AProtocol {
var bar: String { get set }
}
struct Test: BProtocol {
var foo: String = "foo"
var bar: String = "bar"
}
protocol UseAProtocol {
associatedtype ProtocolType
static func use(_ obj: ProtocolType)
}
protocol UseBProtocol: UseAProtocol {
}
extension UseBProtocol {
static func use(_ obj: BProtocol) {
print(obj.foo)
print(obj.bar)
}
}
struct Manager: UseBProtocol {
}
Manager.use(Test()) //prints both "foo" and "bar"
I'm trying to do something along the lines of this:
protocol Protocol {
associatedtype T
associatedtype ArrayT = Array<T>
}
struct Struct<ProtocolType: Protocol> {
func doSomething(with: ProtocolType.ArrayT) {
let _ = with.map { $0 }
// ^ compiler complains on this line
// "value of type ProtocolType.ArrayT has no member map"
}
}
where I define a convenience typealias ArrayT that uses the associatedtype T. It seems that when I try to use ArrayT like in doSomething(_:), I lose the Array type information of ArrayT.
Shouldn't ArrayT definitely be an Array and therefore a member of the Sequence protocol, exposing the map function? 🤔
the working solution I'm employing now is to just define a generic typealias outside of the protocol:
typealias ProtocolArray<ProtocolType: Protocol> = Array<ProtocolType.T>
struct Struct<ProtocolType: Protocol> {
func doSomething(with: ProtocolArray<ProtocolType>) {
let _ = with.map { $0 } // no complaints
}
}
what am I missing here?
The line associatedtype ArrayT = Array<T> only tells the compiler that the default value of ArrayT is Array<T>. An adaption of the protocol can still change ArrayT like:
struct U: Protocol {
typealias T = UInt32
typealias ArrayT = UInt64 // <-- valid!
}
If you want a fixed type, you should use a typealias...
// does not work yet.
protocol Protocol {
associatedtype T
typealias ArrayT = Array<T>
}
But the compiler complains that the type is too complex 🤷. So the best you could do is constrain the ArrayT to be a Sequence / Collection / etc, and hope that the adaptors won't change the type themselves.
// still somewhat off
protocol Protocol {
associatedtype T
associatedtype ArrayT: Sequence = [T]
}
Note that, however, the Sequence can have any element type, but we want ArrayT's Element must be T. We cannot attach a where clause to the associatedtype:
// fail to compile: 'where' clause cannot be attached to an associated type declaration
associatedtype ArrayT: Sequence where Iterator.Element == T = [T]
Instead, you need to put this constraint every time you use the protocol:
struct Struct<ProtocolType: Protocol>
where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T {
Complete, working code:
protocol Protocol {
associatedtype T
associatedtype ArrayT: Sequence = [T]
// ^~~~~~~~~~
}
struct Struct<ProtocolType: Protocol>
where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
func doSomething(w: ProtocolType.ArrayT) {
let _: [ProtocolType.T] = w.map { $0 }
}
}
When you "initialize" an associatedtype, you're not defining it. That's not the point of associated types in the first place. Associated types are deliberately unbound ("placeholders") until the adopting class resolves them all. All you're doing there is giving it a default value, which a conforming class is allowed to override. To extend your example:
protocol Protocol {
associatedtype T
associatedtype ArrayT = Array<Self.T>
func useSomeT(myFavoriteT: T)
func useSomeArrayT(myLeastFavoriteArrayT: ArrayT)
}
class Whatever: Protocol {
func useSomeT(myFavoriteT: Int) {
print("My Favorite Int: \(myFavoriteT)")
}
func useSomeArrayT(myLeastFavoriteArrayT: [Int: String]) {
print(myLeastFavoriteArrayT.map { $0.1 })
}
}
struct Struct<ProtocolType: Protocol> {
func doSomething(stuff: ProtocolType.ArrayT) {
print(type(of: stuff))
}
}
let x = Struct<Whatever>()
x.doSomething(stuff: [3: "Doggies"])
For your example, it seems all you really want is to declare with: Array<ProtocolType.T> (or simply with: [ProtocolType.T]) as the parameter type.
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 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>
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()]