Using a subclass of a protocol requirement - swift

I'm working on a project that's using MVVM and we'd like to introduce more safety by stopping the caller being able to mutate the objects we're exposing.
We can't make Immutable a struct and just mark mutate() as a mutating, because we add observers which mutates the object that we need in the Immutable form.
This example shows what we'd like to do, but we just get an error that C doesn't confirm to P because we're trying to satisfy the protocol with a subclass of the required Immutable, Mutable
class Immutable {}
class Mutable: Immutable {
func mutate() {}
}
​
protocol P {
var a: Immutable { get }
}
​
class C: P {
let a: Mutable
​
init() {
a = Mutable()
a.mutate()
}
}
Does anyone have a creative solution to this that doesn't require lots of boilerplate to work around, like making a Immutable to satisfy the type requirements and then casting in line like:
class C: P {
let a: Immutable
​
init() {
a = Mutable()
(a as? Mutable)?.mutate()
}
}
This isn't ideal for us because we call mutate() many, many times and don't want to have to refactor all of our code.

Change protocol P to have an associatedtype as below,
protocol P {
associatedtype T: Immutable
var a: T { get set }
}
As you were assigning a value to variable a inside the class that conform to P so variable a declaration changed as above to allow setting a value.
Now, you can define the type inside the conforming class as,
class C: P {
var a: Mutable
init() {
self.a = Mutable()
self.a.mutate()
}
}

Related

Why can't I assign this protocol conforming class to a variable of the protocol type?

I've got a toy example here that I can't find any solutions for online.
protocol Tree {
associatedtype Element
// some nice tree functions
}
class BinaryTree<T> : Tree {
typealias Element = T
}
class RedBlackTree<T> : Tree {
typealias Element = T
}
enum TreeType {
case binaryTree
case redBlackTree
}
// Use a generic `F` because Tree has an associated type and it can no
// longer be referenced directly as a type. This is probably the source
// of the confusion.
class TreeManager<E, F:Tree> where F.Element == E {
let tree: F
init(use type: TreeType){
// Error, cannot assign value of type 'BinaryTree<E>' to type 'F'
switch(type){
case .binaryTree:
tree = BinaryTree<E>()
case .redBlackTree:
tree = RedBlackTree<E>()
}
}
}
I'm not sure what the problem here is or what I should be searching for in order to figure it out. I'm still thinking of protocols as I would interfaces in another language, and I view a BinaryTree as a valid implementation of a Tree, and the only constraint on F is that it must be a Tree. To confuse things even more, I'm not sure why the following snippet compiles given that the one above does not.
func weird<F:Tree>(_ f: F){ }
func test(){
// No error, BinaryTree can be assigned to an F:Tree
weird(BinaryTree<String>())
}
Any pointers or explanations would be greatly appreciated.
I don't understand the context of the situation this would be in. However, I have provided two solutions though:
1:
class Bar<F:Foo> {
let foo: FooClass.F
init(){
foo = FooClass.F()
}
}
2:
class Bar<F:Foo> {
let foo: FooClass
init(){
foo = FooClass()
}
}
What you are currently doing doesn't make logical sense, to whatever you are trying to achieve
I don't know what are you trying for, but of course it's not possible to do that. from your example, you attempt to create Bar class with generic. and that not the appropriate way to create a generic object, because the creation of generic object is to make the object to accept with any type.
Here's some brief explanation of the generic taken from Wikipedia.
On the first paragraph that says, "Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters."
it's very clear about what are the meaning of to-be-specified-later, right :)
Back to your example :
class Bar<F: Foo> {
let foo: F
init() {
// Error, cannot assign value of type 'FooClass' to type 'F'
foo = FooClass()
}
}
From the above code, it is type parameter F which has a constraint to a type Foo. and, you try to create an instance for foo variable with a concrete implementation, that is FooClass. and that's not possible since the foo variable is a F type(which is abstract). of course we can downcast it, like this foo = FooClass() as! F, but then the foo is only limited to FooClass, so why even bother with generic then ?
Hope it help :)
Your approach to this is wrong. In your original example, you would have to specify the type of both the element (E) and the container (BinaryTree or RedBlackTree), in addition to the enum value. That make no sense.
Instead, you should construct the manager to take a tree as the constructor argument, allowing Swift to infer the generic arguments, i.e.
class TreeManager<E, F: Tree> where F.Element == E {
var tree: F
init(with tree: F) {
self.tree = tree
}
}
let manager = TreeManager(with: BinaryTree<String>())
Alternatively, you should look into using opaque return types in Swift 5.1 depending on what the final goal is (the example here is obviously not a real world scenario)
Something like this seems reasonable. The point was to try and have some piece of logic that would determine when the TreeManager uses one type of Tree vs another.
protocol TreePicker {
associatedtype TreeType : Tree
func createTree() -> TreeType
}
struct SomeUseCaseA<T>: TreePicker {
typealias TreeType = RedBlackTree<T>
func createTree() -> TreeType {
return RedBlackTree<T>()
}
}
struct SomeUseCaseB<T>: TreePicker {
typealias TreeType = BinaryTree<T>
func createTree() -> TreeType {
return BinaryTree<T>()
}
}
class TreeManager<T, Picker: TreePicker> where Picker.TreeType == T {
let tree: T
init(use picker: Picker){
tree = picker.createTree()
}
This introduces another protocol that cares about picking the tree implementation and the where clause specifies that the Picker will return a tree of type T.
I think all of this was just the result of not being able to declare the tree of type Tree<T>. Its a bit more verbose but its basically what it would have had to look like with a generic interface instead. I think I also asked the question poorly. I should have just posted the version that didn't compile and asked for a solution instead of going one step further and confusing everyone.
protocol Foo {
associatedtype F
}
class FooClass : Foo {
typealias F = String
}
class Bar<M:Foo> {
let foo: M
init(){
foo = FooClass() as! M
}
}

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"
}

Specialize generic function requirement on protocol inheritance

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"

How should I write this in Swift?

Let's say that I create a protocol like this:
protocol A {
associatedtype T
func computeSomething(with:T) -> Double
}
In my generic typed class, I would like to do something like this:
class B<U> {
var doSomething:A<U>
}
This thing is that this generates an error, but I would like to accept any type that would support computeSomething on my type U but I don't know at all how to do that?
Edit for clarity
Basically if A was a generic struct or class, that would be possible, but what if no default implementation (provided by class or struct) makes sense here and the only thing I want is to ensure that the type does what I want?
Edit #2 (with concrete example)
I wanted to simplify my question which makes it pretty hard to understand so here is a still simplified and fictional problem that probably matches better the issue I am encountering:
I am writing a generic class that processes its generic type T:
class Process<T> { ... }
The class Process itself includes code that processes T, but in order for this code to work, it needs T to conform to some protocols, for instance:
protocol A {
func mixWith(other:A) -> A
}
protocol B {
var isFoo:Bool { get set }
}
So my first approach was to simply require T to conform to those protocols:
class Process<T:<A,B>> { ... }
This looks like the simplest approach and probably is in many cases, but in my case I think that this actually is problematic, for this reason:
First, I may need to process the same type in many different ways, and changing a way a type is being processed often requires changing the actual implementation of protocols A and B for instance in this case, fooProcess and barProcess are both of type Process with generic type MyType:
let fooProcess = Process<MyType>()
let barProcess = Process<MyType>()
But I want fooProcess and barProcess to do different operations which in many cases would require to change the implementation of the A and B protocols of my MyType type and that's simply not possible.
So my first idea was to simply require some closures and variables to be defined so that I wouldn't need protocols anymore and would define the way data is being processed only in my Process class, a little bit like this:
class Process<T> {
//
var mix:(_ lhs:T, _ rhs:T)->(T)
var isFoo:(_ a:T)->(Bool)
...
}
There all of the processing would be directly implemented in my Processing class, again this would have looked like the right solution but now comes another issue, which led me to my associated type approach: it turns out that in many cases, the user of my Process class would want to get some default behaviour implemented by my framework, for instance, I could automatically implement protocol A and B for them as long as their class conformed to protocol X, here is how it did it:
protocol X:A,B {
...
}
extension protocol X {
// Here was my default implementation of A and B, which enabled my user to directly get A and B implemented as long as their type conformed to X
}
By using this method, I would let my user directly choose what they wanted to implement themselves, by conforming to protocol X they would only need to write a little bit of code and let my framework to all of the rest by itself, and if they wanted to implement themselves A or B they still could.
So if I am right, there is no way to do such a thing with my closures implementation.
So for this reason, I thought that an associated type protocol would be a good solution because here I could let my users easily get some default behaviour or write their own, so now we are getting back to my original question:
protocol AProcessing {
associatedtype U
func mix(_ lhs:U, _ rhs:U) -> U
}
protocol BProcessing {
associatedtype U
func isFoo(_ a:U) -> Bool
}
And then do something like that:
class Process<T> {
var aProcessor:AProcessing<T>
var bProcessor:BProcessing<T>
}
Here the advantage compared to closures is that I could write a special class conforming to AProcessing that could provide default implementation, this way:
class AutomaticAProcessing<T:X>:AProcessing { ... }
That would have enabled my users to so something like that:
var processData = Process<SomeType>()
processData.aProcessor = AutomaticAProcessing<SomeType>()
processData.bProcessor = TheirOwnImplemtation
Not only is this not possible in Swift, but it also feels like I am using too many "hacks" to get things done and there should be an easier language feature to do that, unfortunately I don't know what I should use.
I don't think it is possible, because the generic type of the protocol is specified in the class that implements it.
You could write something like this:
class B<U, P: A> where P.T == U {
var someVar: P?
}
But then you would need to specify a second parameter with the specific class. For example:
class C: A {
typealias T = String
func computeSomething(with: String) -> Double {
return 0.0
}
}
let b = B<String, C>()
let c = b.someVar
But it can't return a protocol with specific type in its associatedtype
One way would be to start with an empty generic struct, and then extend it on types where it makes sense:
struct A<T> {}
extension A where T: Base {
func computeSomething(with: T) -> Double {
return 1
}
}
Usage:
protocol Base {}
class B<U: Base> {
let doSomething = A<U>()
func foo(x: U) -> Double {
return doSomething.computeSomething(with: x)
}
}
class Z : Base {}
let x = B<Z>()
let y = x.foo(x: Z())
print(y)
To EDIT #2:
Remove associated types, and it should be workable:
protocol A {}
protocol B {}
protocol AProcessing {
func mix(_ lhs: A, _ rhs: A) -> A
}
protocol BProcessing {
func isFoo(_ a: B) -> Bool
}
Then, your processor:
class Process<T: A & B> {
var aProcessor: AProcessing!
var bProcessor: BProcessing!
func foo(_ a: T) -> Bool {
let x = aProcessor.mix(a, a)
guard let b = x as? B else { return false }
return bProcessor.isFoo(b)
}
}
And usage:
struct AutomaticAProcessing : AProcessing {
func mix(_ lhs: A, _ rhs: A) -> A { return lhs }
}
struct TheirOwnImplemtation : BProcessing {
func isFoo(_ a: B) -> Bool { return false }
}
struct SomeType : A, B {}
var processData = Process<SomeType>()
processData.aProcessor = AutomaticAProcessing()
processData.bProcessor = TheirOwnImplemtation()
let x = SomeType()
let y = processData.foo(x)
print(y)

Protocol downcasting in Swift

I have the following situation where I have a generic protocol A that can have variants that inherit from it (e.g. B) and a class that implements a variant (C).
protocol A {}
protocol B: A {
var foo: String { get }
}
class C: B {
let foo: String = "foo"
}
Now, if I have an object of type A, but is actually a C, how can I get to stuff declared in B?
func foo() {
let c: A = C()
}
If I try to cast as in let b = c as B I get Cannot downcast from 'A' to non-#objc protocol type 'B'.
Your code looks good - the only problem is that the as operator requires objc compatibility. You can fix it by prefixing your protocols with the #objc attribute:
#objc protocol A {}
#objc protocol B: A {
var foo: String { get }
}
Unfortunately there's a downside on doing that: you lose the ability to use swift-specific features that are not available in objective C, such as enums, tuples, generics, etc.
More info here: Checking for Protocol Conformance