how to make protocol to conform another protocol in Swift? - swift

I make a extension for Array where Elements are Object, so I can compare them by their address.
public extension Array where Element: AnyObject {
func hasExactSameItem(_ searchingItem: Element) -> Bool {
guard let _ = indexOfExactSameItem(searchingItem) else { return false }
return true
}
func indexOfExactSameItem(_ searchingItem: Element) -> Int? {
for (index, item) in self.enumerated() {
if searchingItem === item {
return index
}
}
return nil
}
}
There is another protocol conforming AnyObject protocol
public protocol IMServerListener: AnyObject {
}
I have a array containing IMServerListener
private var listeners = [IMServerListener]()
When I start adding listener to that array, the compiler complaining that '[IMServerListener]' requires that 'IMServerListener' conform to 'AnyObject'
func addListener(_ listener: IMServerListener) {
listeners.hasExactSameItem(listener)
}
I thought the IMServerListener is conforming the AnyObject protocol, so why did it happened?

I tried to construct a similar example in a playground:
import UIKit
protocol Foo: AnyObject {
func foo()
}
protocol Bar: Foo {}
class A: Bar {
func foo() {}
}
var bars = [Bar]()
bars.append(A())
bars.append(A())
bars[0] === bars[1] // equals operator used from AnyObject
Observations:
(1) Protocols can inherit from each other. If I leave out the implementation of foo() in class A it leads to a compiler error.
(2) Class A does inherit from AnyObject through Bar and Foo otherwise I could not use the equals operator.
To your question:
compiler complaining that '[compiler complaining that '[IMServerListener]' requires that 'IMServerListener' conform to 'AnyObject']' requires that 'IMServerListener' conform to 'AnyObject' sounds like there might be something wrong with the implementation of IMServerListener itself.
I am happy to extend my answer if you can show us this implementation / more code in general.
Cheers,
Dominic

Related

swift generic constraint type is also a return type

protocol Builder {
associatedtype Output where Output: MyProtocol
func build() -> Output?
}
// concrete type
struct ABuilder: Builder {
func builder() -> MyProtocol {
if someCondition {
return aSubClassOfMyProtocol
} else {
return anotherSubClassOfMyProtocol
}
}
}
MyProtocol is a protocol type. It is also the Output constraint. Because the concrete Builder ABuilder is going to return two different sub class types that conform MyProtocol. How can I make the generic constraint work?
I am trying to make the generic constraint be the same.
You can make build() function generic or use typecasting. Anyway this two ways determine concrete type outside of method not inside.
So first of all you need to remove associatedtype. If you specify associated type Output it means that the implementation of your Builder protocol should return some concrete type which conforms to MyProtocol. If you want your Builder to return any type which conforms to MyProtocol not the concrete one specified for this implementation of Builder then you shouldn't declare any associated types.
Let's see how type casting can work:
protocol Builder {
func build() -> MyProtocol?
}
struct ABuilder: Builder {
func build() -> MyProtocol? {
// some code
}
}
let builder = ABuilder()
guard let a = builder.build() as? SomeClass else { return }
Or we can use generic approach:
protocol Builder {
func build<T: MyProtocol>() -> T?
}
struct ABuilder: Builder {
func build<T: MyProtocol>() -> T? {
// some code
}
}
let builder = ABuilder()
let a: SomeClass = builder.build()
You can read more about that here, here and here. Moreover I strongly recommend watching this video which describes all the limitations of generics in swift.
I think you are correct that having the typecasting outside the Builder is a better way. I also found another possibility. The difference is that we have the concrete class types from the build function.
protocol MyProtocol { }
protocol Builder {
associatedtype Output
func build() -> Output?
}
struct ASubClassOfMyProtocol: MyProtocol {}
struct AnotherSubClassOfMyProtocol: MyProtocol {}
struct ABuilder: Builder {
func build() -> (any MyProtocol)? {
if Int.random(in: 0...10) < 5 {
return ASubClassOfMyProtocol()
} else {
return AnotherSubClassOfMyProtocol()
}
}
}

Type specific method is unavailable for a var returned with `some` directive

Consider a factory method pattern implementation:
import UIKit
protocol TransportProtocol: CustomStringConvertible {
func techReview()
}
// Make default implemetation opposed to #objc optional methods and properties
extension TransportProtocol {
func techReview() {
print("Reviewing \(type(of: self))")
}
var description: String {
"This is a \(type(of: self))"
}
}
final class Car: TransportProtocol {
func changeOil() {
print("Changed oil")
}
}
final class Ship: TransportProtocol {
}
protocol LogisticProtocol {
associatedtype Transport: TransportProtocol
func createTransport() -> Transport
func delivery(from A: Any, to B: Any)
func techRewiew(for transport: TransportProtocol)
}
extension LogisticProtocol {
func delivery(from A: Any, to B: Any) {
print("Moving \(type(of: self)) from \(A) to \(B)")
}
func techRewiew(for transport: TransportProtocol) {
transport.techReview()
}
}
final class RoadLogistics: LogisticProtocol {
func createTransport() -> some TransportProtocol {
Car()
}
}
final class SeaLogistics: LogisticProtocol {
func createTransport() -> some TransportProtocol {
Ship()
}
}
// Usage:
class Client {
// ...
static func someClientCode<L: LogisticProtocol>(creator: L) -> some TransportProtocol {
let transport = creator.createTransport()
print("I'm not aware of the creator's type, but it still works.\n"
+ transport.description + "\n")
creator.delivery(from: "Source", to: "Destination")
return transport
}
// ...
}
let someTransport = Client.someClientCode(creator: RoadLogistics())
type(of: someTransport.self) // Car
someTransport.changeOil() // Error: Value of type 'some TransportProtocol' has no member 'changeOil'
The question is why I can't call changeOil() on someTransport if the compiler knows it is a Car not just a TransportProtocol.
Are there any benefits we can get from using some directive, not just bare protocol types?
The return type of the method is some TransportProtocol, not Car. So even though the returned instance is of type Car, you cannot call any methods on it that only exist on Car, but not on TransportProtocol.
The runtime knows that the concrete type of someTransport is Car, but the compiler only knows that the return type conforms to TransportProtocol.
If you want to be able to access Car methods on someTransport, you need to downcast it to Car.
if let car = someTransport as? Car {
car.changeOil()
}
Using the some keyword has type-system benefits when used for opaque returns types (introduced in SE-0244 as part of Swift 5.1), since it actually enables returning a protocol with associated types without having to explicitly make the method generic. This is what drives SwiftUI, since it enables you to return some View.
On the other hand, using opaque return types for protocols without associated types holds no benefits, so in your particular example, there's no reason to use it.

Why in Swift generics factory method does compiler warn Generic class 'X' requires that 'Object' conforms to 'Y'

As an overview, I'm trying to implement a data abstraction layer in Swift. I'm using two database SDKs but I'm trying to be able to isolate their specific APIs from the rest of the system.
I'm trying to implement a factory pattern that will return the correct object based on a protocol conformance of the supplied concrete type. But the compiler is giving me red flags that I can't wrap my head around.
The Thing class is a first class entity that moves freely through the logic and UI layers of the app, and certain objects persist on a Realm store (which shouldn't matter) and have a specific type that is a subclass of the Realm object. That type is returned by a static function that has to be there to conform with the protocol FromDataSourceA.
protocol FromDataSourceA {
static func A_objectType () -> A_Object.Type
}
class MyObject {
...
}
class Thing: MyObject, FromDataSourceA {
static func A_objectType () -> A_Thing.Type
...
}
// Example usage
let target = DataTarget<Thing>(url: "url")
let dataSource = target.dataSourceBuilder()
As you can see, the concrete type Thing conforms to FromDataSourceA and is passed into the DataTarget initializer. DataSourceBuilder needs to be able to check that Thing conforms to FromDataSourceA to be able to return the correct DataSource.
The DataSourceTypeA class looks like
class DataSourceTypeA<T: MyObject & FromDataSourceA>: DataSource<T> {
let db: DatabaseTypeA // All db-specific APIs contained here
override init (target: DataTarget<T>) {
self.db = try! DatabaseTypeA()
super.init(target: target)
}
override func create<T: MyObject & FromDataSourceA> (object: T) {
db.beginWrite()
db.create(T.A_objectType(), value: object.dict()) // <-- T.A_objectType() is what the conformance to FromDataSourceA is needed for
try! db.commitWrite()
}
override func get<T: MyObject & FromDataSourceA>(id: Int) -> T {
...
}
}
class DataSource<T>: AnyDataSource {
let target: DataTarget<T>
init (target: DataTarget<T>) {
self.target = target
}
func create<T> (object: T) { }
func get<T>(id: Int) -> T { fatalError("get(id:) has not been implemented") }
}
protocol AnyDataSource {
func create<T> (object: T)
func get<T> (id: Int) -> T
}
The problem I'm facing right now is when I check that the metatype conforms to FromDataSourceA, the compiler gives me this warning
class DataTarget<T: MyObject> {
...
func dataSourceBuilder () -> DataSource<T> {
if T.self is FromDataSourceA { // <-- Thing.self conforms to FromDataSourceA
// The Problem:
return DataSourceTypeA(target: self) // Generic class 'DataSourceTypeA' requires that 'MyObject' conform to 'FromDataSourceA'
} else {
...
}
}
}
Why won't the compiler let me return the DataSourceTypeA instance with argument self if the generic T passes the conditional statement and is proven as being in conformance with FromDataSourceA?
The problem is that the call
return DataSourceTypeA(target: self)
is resolved at compile time, therefore it does not help to check
if T.self is FromDataSourceA { }
at runtime. A possible solution is to make the conformance check a compile-time check by defining a restricted extension:
extension DataTarget where T: FromDataSourceA {
func dataSourceBuilder() -> DataSource<T> {
return DataSourceTypeA(target: self)
}
}
If necessary, you can add more implementations for other restrictions:
extension DataTarget where T: FromDataSourceB {
func dataSourceBuilder() -> DataSource<T> {
// ...
}
}
or add a default implementation:
extension DataTarget {
func dataSourceBuilder() -> DataSource<T> {
// ...
}
}
For a call
let dataSource = target.dataSourceBuilder()
the compiler will pick the most specific implementation, depending on the static (compile-time) type information of target.

Returning a nil from an optional generic extension

Here's something I'm playing with. The problem is that I have a container class that has a generic argument which defines the type returned from a closure. I want to add a function that is only available if they generic type is optional and have that function return a instance containing a nil.
Here's the code I'm currently playing with (which won't compile):
open class Result<T>: Resolvable {
private let valueFactory: () -> T
fileprivate init(valueFactory: #escaping () -> T) {
self.valueFactory = valueFactory
}
func resolve() -> T {
return valueFactory()
}
}
public protocol OptionalType {}
extension Optional: OptionalType {}
public extension Result where T: OptionalType {
public static var `nil`: Result<T> {
return Result<T> { nil } // error: expression type 'Result<T>' is ambiguous without more context
}
}
Which I'd like to use like this:
let x: Result<Int?> = .nil
XCTAssertNil(x.resolve())
Any idea how to make this work?
I don't think you can achieve this with a static property, however you can achieve it with a static function:
extension Result {
static func `nil`<U>() -> Result where T == U? {
return .init { nil }
}
}
let x: Result<Int?> = .nil()
Functions are way more powerful than properties when it comes to generics.
Update After some consideration, you can have the static property, you only need to add an associated type to OptionalType, so that you'd know what kind of optional to have for the generic argument:
protocol OptionalType {
associatedtype Wrapped
}
extension Optional: OptionalType { }
extension Result where T: OptionalType {
static var `nil`: Result<T.Wrapped?> {
return Result<T.Wrapped?> { nil }
}
}
let x: Result<Int?> = .nil
One small downside is that theoretically it enables any kind of type to add conformance to OptionalType.

Can you explain/solve these 'generic constraint' compiler errors?

I get the following two compiler errors when trying to declare a protocol with an associatedType - not sure what a generic constraint is.
protocol Listener {
associatedType ValueType
func call(_ binding:Binding<ValueType>, value:ValueType)
}
class Binding<T> {
var value:T?
var listeners:[Listener] = [] // error 1: Protocol 'Listener' can only be used as a generic constraint because it has Self or associated type requirements
func fire() {
listeners.forEach { $0.call(self,value:self.value) } // error 2: Member 'call' cannot be used on value of protocol type 'Listener'; use a generic constraint instead
}
}
This is an incorrect use of a protocol. Protocols associated types are determined by the implementer of the protocol, not the user of the protocol. Generics are determined by the user of the protocol. There is no automatic wrapping of protocols into generics (that feature will be called "existential containers" and we don't know when it will come if ever).
[Listener] is not a complete type. what is the ValueType?
For this particular case, there is no reason for a protocol. It captures exactly one function. You should just pass the function:
class Binding<T> {
var value: T?
var listeners:[(Binding<T>, T) -> ()] = []
func fire() {
if let value = value {
listeners.forEach { $0(self, value) }
}
}
init() {}
}
If you really need the protocol, you can lift it into a type eraser (AnyListener):
protocol Listener {
associatedtype ValueType
func call(_ binding:Binding<ValueType>, value:ValueType)
}
struct AnyListener<ValueType>: Listener {
let _call: (Binding<ValueType>, ValueType) -> ()
init<L: Listener>(_ listener: L) where L.ValueType == ValueType {
_call = listener.call
}
func call(_ binding: Binding<ValueType>, value: ValueType) {
_call(binding, value)
}
}
class Binding<T> {
var value:T?
var listeners: [AnyListener<T>] = []
func fire() {
if let value = value {
listeners.forEach { $0.call(self, value: value) }
}
}
}
let listener = ... some listener ....
binding.listeners.append(AnyListener(listener))
Or you could just make AnyListener into a more concrete struct and get rid of the protocol. But for such a simple case I'd pass the function usually.