I wondered if anyone could help me with this problem. I built a protocol to allow conformance for multiple different db connections, I then used type erasure to unwrap the concrete type. But my final step was to allow a factory to return the given connection, but I'm having some trouble. So any help would be appreciated. This is the code:
public protocol ViiConnection: class {
associatedtype ViiConnectionType
var connection: ViiConnectionType { get set }
}
public class AnyViiConnection<ErasedConnectionType>: ViiConnection {
public var connection: ErasedConnectionType
public init<Injected: ViiConnection>(_ connect: Injected) where Injected.ViiConnectionType == ErasedConnectionType{
connection = connect.connection
}
}
public class ConnectionFactory {
@available(OSX 10.15.0, *)
public static func getViiConnection(selectedDb: ViiDatabaseType, eventLoop: EventLoop, credentials: Credential) throws -> some ViiConnection {
switch selectedDb {
case .mysql:
return AnyViiConnection(ViiMySQLConnection(eventLoop: eventLoop, credentials: credentials))
case .postgres:
return AnyViiConnection(ViiPostgresConnection(eventLoop: eventLoop, credentials: credentials))
...
}
}
}
So, I get Function declares an opaque return type, but the return statements in its body do not have matching underlying types, which I understand, I just don't understand if there is an approach to solve my problem? I tried a few different approaches with the factory, this was just the latest
No they don't one returns EventLoopFuture<MySQLConnection> the other EventLoopFuture<PostgresConnection>, I figured I might have to do another Type erasure, but was trying to avoid for my own sanity when I go to edit this a few months later
Is it really necessary to know the type of ViiConnectionType at compile time? It's be easier, and far more maintainable, to make ViiConnectionType a protocol and return that.
If there are some type-specific information, that may be in the protocol too.
Especially since ConnectionFactory seems to be public-facing interface with opaque return type, it already means the user of such function doesn't really care at all about the internal working of it, only the methods they have.
Originally I was just returning the protocol type of ViiConnectionType. But my problem was I couldn't satisfy the compiler when I tried to use the underlying type. Here's an example of the connection type.
public class ViiPostgresConnection: ViiConnection {
public typealias ViiConnectionType = EventLoopFuture<PostgresConnection>
public var connection: EventLoopFuture<PostgresConnection>
public init(eventLoop: EventLoop, credentials: Credential){
self.connection = PostgresConnection.create(on: eventLoop, credentials: credentials)
}
}
It's public because It's a console application and I split the targets, so that the library of code could be tested as the console application it can't be. I haven't done much with more advanced aspects of Swift's type system other than some not overly complex generics. So it's a bit of a new area for me
Rather, I'm questioning if you need to explore that far to get to the solution that will satisfy you. Likely the user can be agnostic to the underlying distinction between SQLConnection and PostgresConnection–they are just Connection.
So that it may be possible to do
protocol Connection { ... }
class PostgresConnection: Connection { ... }
class MySQLConnection: Connection { ... }
protocol ViiConnection: AnyObject {
var connection: Connection { get set }
}
class ViiPostgresConnection: ViiConnection {
public var connection: Connection { PostgresConnection(...) }
...
}
Which would be much easier to reason. (And you don't need to do any type-erase to begin with). Though it you insist that ViiConnection must be Protocol with Associated Type (PAT), you'll need to type-erase both ViiConnection and ViiConnectionType. IMO it's a code smell that you may be trying to preserve too much type information (unless of course, you utilise that information).