I seem to be my own worst enemy when it comes to truly understanding Swift’s various mechanisms for reducing boilerplate code. I feel like there's some elegant generic associated type-erased opaque protocol trickery to do this, but I couldn't figure it out.
Given
protocol Database {}
protocol Repository {}
protocol DatabaseRepository: Repository
{
var database: Database { get }
init(database: Database)
}
protocol TokenRepository : Repository
{
func find(token: String, on: Database?) async throws -> Token?
}
struct DatabaseTokenRepository : TokenRepository, DatabaseRepository
{
let database: Database
func find(token inToken: String) async throws -> Token?
{
return try await Token
.query(on: self.database)
.filter(\.$token == inToken)
.first()
}
}
protocol Request
{
var tokens : TokenRepository { DatabaseTokenRepository(database: …) }
var db : Database
var params : [String:String]
}
I can write code like this:
private func tokenLogin(_ inReq: Request) async throws -> Response
{
let code = try await inReq.tokens.find(code: inReq.param["token"])
return Response(…)
}
But sometimes I need to do multiple things inside a transaction, and that's written like this (note that a transaction conforms to Database):
extension TokenRepository
{
func on(db: Database) -> TokenRepository
{
return DatabaseTokenRepository(database: db)
}
}
private func tokenLogin(_ inReq: Request) async throws -> Response
{
try await inReq.db.transaction // transaction conforms to Database
{ txn: Database in
let code = try await inReq.tokens.on(db: txn).find(code: inReq.param["token"])
}
return Response(…)
}
I’d like to write TokenRepository.on(db:)
once, generically, and have it work for all Repository
implementations. It would be acceptable to create an associated type Transaction
which is a Database
for Database
implementations, and something else for other implementations (including a no-op). It would be okay to “pollute” those other implementations with the notion of a Transaction, even if they don’t support it.
Not sure if this is even possible.