Swift optional protocol requirements - swift

I have the following protocols declared.
protocol TypeAProtocol {
...
}
protocol TypeBProtocol {
...
}
protocol SomeProtocol {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
var objA: TypeA? { get set }
var objB: TypeB? { get set }
}
Now if I want to create a class that implements SomeProtocol, I would have done this
class SomeClass: SomeProtocol {
var objA: ClassOfTypeAProtocol?
var objB: ClassOfTypeBProtocol?
}
The problem I am facing now is, I want to be able to create classes that implement SomeProtocol without var objB: TypeB? { get set }. I want objB to be optional. How can I achieve that?

In Objective-C you can declare an optional member (method/computed property) in a protocol.
In pure Swift this is not possible unless you use the #objc annotation (but in this case structs and enums won't be able to conform to your protocol so it's not the best way to go).
So, how can you solve your problem?
Let's split SomeProtocol into 3 protocols
These are your protocols
protocol TypeAProtocol { }
protocol TypeBProtocol { }
protocol SomeProtocol {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
var objA: TypeA? { get set }
var objB: TypeB? { get set }
}
Now we'll split SomeProtocol into 3 protocols
protocol SomeProtocolBase {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
}
protocol SomeProtocolPartA: SomeProtocolBase {
var objA: TypeA? { get set }
}
protocol SomeProtocolPartB: SomeProtocolBase {
var objB: TypeB? { get set }
}
That's it
Now you can write
class ClassOfTypeAProtocol: TypeAProtocol { }
class ClassOfTypeBProtocol: TypeBProtocol { }
class SomeClass: SomeProtocolPartA {
typealias TypeA = ClassOfTypeAProtocol
typealias TypeB = ClassOfTypeBProtocol
var objA: ClassOfTypeAProtocol?
}

Related

Swift Associated type constraints

I have two protocols with each defining an associated type. One of the protocols needs to define a variable of typed the other protocol where they both have the same type for associated type. Is it possible to somehow infer the type of associated type?
protocol A {
associatedtype AModel
var b: B { get }
}
protocol B {
associatedtype BModel
func doAnother(anotherModel: BModel)
}
Here is what I tried with no success
protocol A {
associatedtype AModel
associatedtype TypedB = B where B.BModel == AModel
var another: TypedB { get }
}
protocol B {
associatedtype BModel
func doAnother(anotherModel: BModel)
}
Please find the following working playground example. You need to use the associated type's name, not the constraining protocol's name. The reason for this is described here.
import Foundation
protocol A {
associatedtype AModel
associatedtype TypedB: B where TypedB.BModel == AModel
var another: TypedB { get }
}
protocol B {
associatedtype BModel
func doAnother(anotherModel: BModel)
}
// compiles
struct One: B {
typealias BModel = String
func doAnother(anotherModel: String) {}
}
struct Second: A {
typealias AModel = String
typealias TypedB = One
var another: One
}
// does not compile
struct Third: B {
typealias BModel = Int
func doAnother(anotherModel: Int) {}
}
struct Fourth: A { // A' requires the types 'Fourth.AModel' (aka 'String') and 'Third.BModel' (aka 'Int') be equivalent
typealias AModel = String
typealias TypedB = Third
var another: Third
}

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"

Class does not conform to protocol. Why?

class Controller<U: NSObject> {}
protocol Robert {
associatedtype T
associatedtype U: Controller<T>
var fetcher: U { get }
}
class Telephone: NSObject {}
class Object: Telephone {}
class Turtle: Controller<Object> {}
class Fish: Robert {
typealias T = Object
typealias U = Turtle
let x = Turtle()
var fetcher: Turtle {
return x
}
}
I don't understand why. Any help appreciated.
When selecting the XCode 'fix it' option, a stub for 'Fetcher is inserted. But there is already a typealias for Fetcher.
This has now been recognized as a bug in Swift 4. For now we must avoid associated types constrained by types that have generic constraints.
So this is not cool
associatedtype U: Controller<T>
Removing it results in the following, which works.
protocol Robert {
associatedtype T: NSObject
var fetcher: Controller<T> { get }
}

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

How do you structure generic type protocol conformance in Swift?

The following contrived Swift 2 example from real-world code won't compile:
protocol SomeModelType { }
protocol SomeProtocol {
var someVar: SomeModelType? { get }
}
class ConcreteClass<T: SomeModelType>: SomeProtocol {
var someVar: T?
}
This doesn't make sense to me fully. I would assume in ConcreteClass that because I have T being constrained to SomeModelType and have T as the backing type for the someVar property, the compiler would be able to figure out that the SomeProtocol was being conformed to by ConcreteClass.
How should an example like this be structured? Is it possible to the Swift compiler to determine protocol conformance through generic type constraints?
protocol SomeModelType { }
protocol SomeProtocol {
associatedtype T: Any
var someVar: T? { get }
}
class ConcreteClass<T> :SomeProtocol where T: SomeModelType {
var someVar: T?
}