Reputation: 73
I'm currently writing and database access class for two database APIs (realm and Firestore). With the intention to slim down the code i try to solve the whole thing a little sleaker with generics (#1). Unfortunately, it's not working. Where do I miss the point?
I tried with defining associatedtypes (#2) and setting them within the RealmAccessStragy class. But on this point the compiler returns error if try to access the protocol via the PersistenceController.
I am grateful for any help!
enum DataResult<T> {
case success(T)
case failure(Error)
}
protocol DataApiAccess: AnyObject {
func read<U, T>(primaryKey: U, completion: @escaping ((DataResult<T>) -> Void))
}
class RealmAccessStrategy {
...
func read<U, T>(primaryKey: U, completion: @escaping ((DataResult<T>) -> Void)) {
guard let realmObject = realmInstance.object(ofType: realmObjectType, forPrimaryKey: primaryKey) else {
completion(.failure(RealmAccessError.noObject))
return
}
completion(.success(realmObject)) // ERROR: Member 'success' in 'DataResult<_>' produces result of type 'DataResult<T>', but context expects 'DataResult<_>'
}
}
// Later implementation
class PersistenceController {
private let strategy: DataApiAccess
init(use: DataApiAccess) {
self.strategy = use
}
func load<U, T>(primaryKey: U, completion: @escaping ( (DataResult<T>) -> Void ) ) {
strategy.read(primaryKey: primaryKey, completion: completion)
}
}
🆘 ERROR: Member 'success' in 'DataResult<>' produces result of type 'DataResult', but context expects 'DataResult<>'
enum DataResult<T> {
case success(T)
case failure(Error)
}
protocol DataApiAccess {
associatedtype ReturnType
func read(primaryKey: PrimaryKeyType, completion: @escaping DataApiHandler<ReturnType>)
}
class RealmAccessStrategy: DataApiAccess {
...
// Typealias
internal typealias ReturnType = Object
func read(primaryKey: Any, completion: @escaping ((DataResult<Object>) -> Void)) {
guard let realmObject = realmInstance.object(ofType: realmObjectType, forPrimaryKey: primaryKey) else {
completion(.failure(RealmAccessError.noObject))
return
}
completion(.success(realmObject))
}
}
class PersistenceController {
private let strategy: DataApiAccess // ERROR: Protocol 'DataApiAccess' can only be used as a generic constraint because it has Self or associated type requirements
init(use: DataApiAccess) {
self.strategy = use
}
...
}
}
🆘 ERROR: Protocol 'DataApiAccess' can only be used as a generic constraint because it has Self or associated type requirements
Upvotes: 0
Views: 186
Reputation: 660
You cannot set variable generic protocols but you can set methods
Example code below
Create enum for base result:
enum DataResult<T> {
case success(T)
case failure(Error)
}
///Set a protocol generic methods:
protocol DataApiAccess {
func read<T: Codable>(primaryKey: PrimaryKeyType, completion: @escaping (DataResult<T>) -> Void)
}
class RealmAccessStrategy: DataApiAccess {
func read<T: Codable>(primaryKey: PrimaryKeyType, completion: @escaping (DataResult<T>) -> Void) {
// Read data from database
}
}
class NetworkAccessStrategy: DataApiAccess {
func read<T: Codable>(primaryKey: PrimaryKeyType, completion: @escaping (DataResult<T>) -> Void) {
// Get data from request
}
}
class PersistenceController {
private let strategy: DataApiAccess
init(use: DataApiAccess) {
// Set dependency inversion for offline or online state
self.strategy = use
}
func foo() {
// TODO
//strategy.read(primaryKey: <#T##PrimaryKeyType#>, completion: <#T##(DataResult<Decodable & Encodable>) -> Void#>)
}
}
Enjoy!
Upvotes: 1