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"
}
Related
I didn't see this in the similar questions, so I think this is unique. I have a protocol
protocol Zot {}
protocol Foo {
associatedType Bar: Zot
var prop1: Bar { get set }
}
Now I could say
class Zoty: Zot {}
class Fooy: Foo {
typealias Bar = Zoty
var prop1: Zoty = Zoty()
}
But I haven't quite moved to the class layer yet. Is there a way for me to set the type alias in a protocol like so?
protocol Fooie: Foo {
typealias Bar = Zoty
var prop1: Zoty { get set }
}
The compiler says to make it like this
protocol Fooie: Foo where Bar == Zoty {
var prop1: Bar { get set }
}
But then it throws the error Cannot override mutable property 'prop1' of type 'Self.Bar' with covariant type 'Zoty'
To fix the error, just remove this line in Fooie
var prop1: Bar { get set }
You do not need to redeclare this again, and I do agree the error message is a bit confusing. Swift thinks that you are trying to override the property, which as far as it is concerned, is declared to be a different type, but in this context, the two types can only be the same.
After removing the property, you can now do:
class Fooy: Fooie {
var prop1 = Zoty()
}
and this gives errors
class SomethingElse: Zot {}
class Fooy: Fooie {
var prop1 = SomethingElse()
}
'Fooie' requires the types 'SomethingElse' and 'Zoty' be equivalent
Type 'Fooy' does not conform to protocol 'Fooie'
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()
}
}
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'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.
I have a class that needs to be set with a variable of a NSObject subclass and that implements a certain protocol.
protocol ProtoTest {
var foo: Int { get set }
}
class AClass: NSObject, ProtoTest {
var foo: Int = 3
}
class BClass: NSObject, ProtoTest {
var foo: Int = 4
}
class Consumer {
var protoInstance: ProtoTest? //Does not cary any information of the class just the protocol
var protoInstance2: protocol<NSObjectProtocol, ProtoTest>?
init(x: ProtoTest) {
self.protoInstance = x
self.protoInstance2 = nil
}
init(x: protocol<NSObjectProtocol, ProtoTest>) {
self.protoInstance2 = x
self.protoInstance = nil
}
func doSomething() {
if let x = protoInstance {
x.copy() //'ProtoTest' does not have a member named 'copy'
}
if let x = protoInstance2 {
x.copy() //protocol<NSObjectProtocol, ProtoTest> does not have a member named 'copy'
}
}
}
In the example above, neither declarations of the variable are gonna work. since neither of them have any knowledge of a base class?
How do I implement this in swift ?
The usual equivalent of NSObject<Protocol> in Swift is simply Protocol. Typically, this protocol is declared as a class protocol to guarantee that it will be adopted by a class.
If you also need the NSObject protocol methods (such a respondsToSelector:, then make Protocol adopt NSObjectProtocol.
If the problem is merely that you want to call copy() and you can't persuade the compiler to let you do it, then adopt NSCopying as well (or just use respondsToSelector: and performSelector: to bypass the compiler altogether).
You can do this a couple of ways. First, you can make Consumer generic:
class Consumer<T: NSObject where T: Prototest> {
var protoInstance: T?
var protoInstance2: T?
}
If you do that, then all references to protoInstance or protoInstance2 will inherit from NSObject, and you will be able to call methods like .copy() directly on the object.
If you don't want Consumer to be generic, you can enforce restraints on the init methods using generic parameters, like this:
class Consumer {
// ...
init<T: NSObject where T: Prototest>(x: T) {
protoInstance = x
}
}
If you do that, you will be guaranteed that protoInstance will be an NSObject, but you will have to cast to NSObject to use any of NSObject's methods:
func doSomething() {
if let x = protoInstance as? NSObject {
x.copy()
}
}
Edit:
Note that I wasn't sure if you really wanted protoInstance and protoInstance2 to be of different types I was a little unclear from your question. If you do want them to be different types, I can add additional suggestions to this answer.