Specialize generic function requirement on protocol inheritance - swift

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"

Related

Protocol only implemented by struct or immutable

I have a class A that holds an object B implementing the protocol P.
I would like to forbid any modification to object B without class A acknowledging it. Is it possible without any delegate or mutual reference?
Considering that it is possible to specify a protocol that can only be implemented by class type objects protocol P: class {} , if there is something similar for structs, I could bind the protocol to a struct, makeing it explicit (being known that structs are passed by value) that the object B has to be set but not edited.
Long story short:
Is there a way to force a protocol implementation to be a struct?
The only I would suggest is to wrap class A by a protocol Q and define a variable setter of an instance of protocol P inside.
protocol Q {
var p: P? { get set }
}
class A : Q {
var p: P? {
get {
// logic
}
set {
//logic
}
}
}
Protocols shouldn't be used this way. Protocols is to define behaviour, not the exact shape of object.
I assume by restricting protocol to structs you want to achieve immutability of it's implementers. If so we can design protocol with getters only
protocol Foo {
var foo: string { get }
}
This way Foo is immutable and it's can't be changed from anywhere no matter if it's struct or class.
Then, we can inherit FooMutable from Foo and add mutators there
protocol FooMutable: Foo {
var foo: string { get set }
}
Finally class A is the only place where we can mutate Foo:
class A {
private var fooValue: FooMutable = FooImpl()
var foo: Foo { get { return fooValue } }
func mutateFoo() {
fooValue.foo = "bar"
}
}
class FooImpl: FooMutable {
var foo = "foo"
}

Cannot assign to generic property in protocol

I'd like to know if there's something simple that I'm missing here in this code or if it's just a mix of Swift trickeries that are preventing me from doing what I want.
I'm allowing types implementing the Foo protocol to contain an entity property of any type, as long as it conforms to StringIdentifiable:
protocol StringIdentifiable {
var id: String? { get }
}
protocol Foo: class {
associatedtype AnyStringIdentifiable: StringIdentifiable
var entity: AnyStringIdentifiable? { get set }
}
As of Swift 3.1, this "any type" part wouldn't be possible if not using the associatedtype. Going ahead, let's say I have another protocol that requires a Foo property. However, Foo is generic, so as you may know we can't do that because "generic protocols can only be used as a generic constraint". Trying to avoid the type erasure mess, I decided to use another associatedtype in my second protocol and the compiler doesn't complain:
protocol Bar {
//var foo: Foo { get set } // can't do because Foo is generic
associatedtype AnyFoo: Foo
var foo: AnyFoo { get set }
}
But now, if I try to set something in foo, the compiler will complain:
extension Bar {
func setEntity(_ entity: StringIdentifiable) {
foo.entity = entity
}
}
The error is Cannot assign value of type 'StringIdentifiable' to type '_?'
Note: this question's code is testable in a playground.
You can do it like this
//: Playground - noun: a place where people can play
import Cocoa
protocol StringIdentifiable {
var id: String? { get }
}
protocol Foo: class {
associatedtype AnyStringIdentifiable: StringIdentifiable
var entity: AnyStringIdentifiable? { get set }
}
protocol Bar {
//var foo: Foo { get set } // can't do because Foo is generic
associatedtype AnyFoo: Foo
var foo: AnyFoo { get set }
}
extension Bar {
func setEntity(_ entity: AnyFoo.AnyStringIdentifiable) {
foo.entity = entity
}
}
Within Bar you can use AnyFoo.AnyStringIdentifiable to make sure the types are correct when setting foo.entity, because foo.entity is of the type AnyFoo.AnyStringIdentifiable.

var in protocol as specific protocol

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])

Swift protocol extension implementing another protocol with shared associated type

Consider the following:
protocol Foo {
typealias A
func hello() -> A
}
protocol FooBar: Foo {
func hi() -> A
}
extension FooBar {
func hello() -> A {
return hi()
}
}
class FooBarClass: FooBar {
typealias A = String
func hi() -> String {
return "hello world"
}
}
This code compiles. But if I comment out explicit definition of associated type typealias A = String, then for some reason, swiftc fails to infer the type.
I'm sensing this has to do with two protocols sharing the same associated type but without a direct assertion through, for example, type parameterization (maybe associated type is not powerful/mature enough?), which makes it ambiguous for type inference.
I'm not sure if this is a bug / immaturity of the language, or maybe, I'm missing some nuances in protocol extension which rightfully lead to this behaviour.
Can someone shed some light on this?
look at this example
protocol Foo {
typealias A
func hello() -> A
}
protocol FooBar: Foo {
typealias B
func hi() -> B
}
extension FooBar {
func hello() -> B {
return hi()
}
}
class FooBarClass: FooBar {
//typealias A = String
func hi() -> String {
return "hello world"
}
}
with generics
class FooBarClass<T>: FooBar {
var t: T?
func hi() -> T? {
return t
}
}
let fbc: FooBarClass<Int> = FooBarClass()
fbc.t = 10
fbc.hello() // 10
fbc.hi() // 10
Providing explicit values for associated types in a protocol is required for conformance to said protocol. This can be accomplished by hard coding a type, as you've done with typealias A = String, or using a parameterized type as you mentioned, such as below:
class FooBarClass<T>: FooBar {
typealias A = T
...
}
Swift will not infer your associated type from an implemented method of the protocol, as there could be ambiguity with multiple methods with mismatching types. This is why the typealias must be explicitly resolved in your implementing class.

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>