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
?