Cannot assign to generic property in protocol - swift

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.

Related

Is there a way to set a typealias of a protocol's associated type in a sub protocol?

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'

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"

Why can't I declare generic protocol type in a similar way to generic class in Swift?

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)

Can I cast a metaclass object to a protocol type in Swift?

Swift inherited Objective-C's metaclass concept: classes themselves are also considered objects. A class Foo's object's class is Foo.self, and it is of type Foo.Type. If Foo inherits from Bar, then Foo.self can be assigned to a variable of type Bar.Type, too. This has at least two benefits:
it allows to override "static methods";
it's easy to create an instance of an unknown class in a type-safe way and without using reflection.
I'm particularly looking at the second one right now. Just to be sure that everybody understands what I'm after, here's an example:
class BaseFoo {
var description: String { return "BaseFoo" }
}
class DerivedFoo: BaseFoo {
override var description: String { return "DerivedFoo" }
}
let fooTypes: [BaseFoo.Type] = [BaseFoo.self, DerivedFoo.self] // metaclass magic!
for type in fooTypes {
let object: BaseFoo = type() // metaclass magic!
println(object)
}
Now, I have an array of AnyClass objects (any metaclass instance can be assigned to AnyClass, just like any object can be assigned to AnyObject), and I want to find which ones implement a given protocol. The protocol would declare an initializer, and I would instantiate the class just like I do in the example above. For instance:
protocol Foo {
init(foo: String)
}
class Bar: Foo {
required init(foo: String) { println("Bar initialized with \(foo)") }
}
class Baz {
required init() { println("I'm not a Foo!") }
}
let types: [AnyClass] = [Bar.self, Baz.self]
So far so good. Now, the problem is determining if the class implements the protocol. Since metaclass instances are polymorphic, I'd expect to be able to cast them. However, I'm apparently missing something, because Swift won't let me write this:
for type in types {
if let fooType = type as? Foo.Type {
let obj = fooType(foo: "special snowflake string")
}
}
The compiler error I get is:
error: 'Foo' is not identical to 'AnyObject'
Is there any way to determine if a metaclass instance represents a class that implements a protocol, and is there any way to cast that instance into a protocol type?
I tried to declare Foo as a class protocol, but it's apparently not enough.
EDIT: I just tried with the Any type, and while it doesn't cause a syntax error, it crashes the Swift compiler.
As of Xcode 7 beta 2 and Swift 2 it has been fixed. You can now write:
for type in types {
if let fooType = type as? Foo.Type {
// in Swift 2 you have to explicitly call the initializer of metatypes
let obj = fooType.init(foo: "special snowflake string")
}
}
Or if you only want type as type Foo.Type you can use for case
for case let type as Foo.Type in types {
let obj = type.init(foo: "special snowflake string")
}