Protocol composition confusing - swift

I have protocols SocketIORepresentable and Typeable. Typeable is protocol with associatedType. Why I have error when i try to create protocol composition in my func?
swift 5.0
import Foundation
protocol Typeable {
associatedtype Item: Decodable
var responseType: Item.Type { get }
}
protocol SocketIORepresentable {
func representable() throws -> [Any]
}
extension SocketIORepresentable where Self: Encodable {
func representable() throws -> [Any] {
let tempSendData = try JSONEncoder().encode(self)
return [try JSONSerialization.jsonObject(with: tempSendData, options: .allowFragments)]
}
}
public struct APIRequest: Encodable {
let type: String
let payload: Data
let ts = Date()
}
extension APIRequest: SocketIORepresentable {}
public struct Request<T: Decodable>: Typeable {
let apiRequest: APIRequest
var responseType: T.Type
public init(apiRequest: APIRequest) {
self.apiRequest = apiRequest
self.responseType = T.self
}
}
private func sendRequest<T: Typeable>(_ request: T & SocketIORepresentable, completion: #escaping (Result<T.Item, Error>) -> Void) {
}

T is not a protocol. T is a concrete type that conforms to Typeable. You cannot then say that request must be "of the concrete type T and also conforms to SocketIORepresentable." What you meant is that T is a concrete type that conforms to both:
private func sendRequest<T: Typeable & SocketIORepresentable>(_ request: T, completion: #escaping (Result<T.Item, Error>) -> Void) { ... }
Typically you'd write this with a where clause instead to avoid such a large angle-bracket:
private func sendRequest<T>(_ request: T, completion: #escaping (Result<T.Item, Error>) -> Void)
where T : Typeable & SocketIORepresentable { ... }

Related

Dependency Inversion with assosiatedType in protocol

I am having a hard time trying to implement Dependency Inversion. Looking around, find a fantastic article Swift Type Erasure. I am not sure how to can I get advantage in my situation. Here is what I am trying to achieve.
Protocol for Networking
protocol Networkable {
associatedtype Response: Decodable
func request(handler: #escaping((Result<Response, Error>) -> ()))
}
Concrete Implementation of Networkable protocol
final class Networker<Response: Decodable>: Networkable {
private let session: URLSession
private let url: URL
init(session: URLSession, url: URL) {
self.session = session
self.url = url
}
func request(handler: #escaping ((Result<Response, Error>) -> ())) {
session.dataTask(with: url) { data, response, error in
handler(.success(try! JSONDecoder().decode(Response.self, from: data!)))
}
}
}
A ViewModel that is dependent on Networkable protocol
class ViewModel {
let networker: Networkable
// Error-> Protocol 'Networkable' can only be used as a generic constraint because it has Self or associated type requirements
init(netwrorker: Networkable) {
self.networker = netwrorker
}
func execute() {
networker.request { _ in
print("Responsed")
}
}
}
Usage:
class View {
let viewModel: ViewModel
init() {
let networker = Networker<ResponseModel>(session: .shared, url: URL(string: "SOME URL")!)
self.viewModel = ViewModel(netwrorker: networker)
}
func load() {
viewModel.execute()
}
}
struct ResponseModel: Decodable {}
Questions:
Is this a correct way to implement dependency-inversion?
How can I achieve Generic behavior on Network class and still open for testing?
Your ViewModel class is ill-formed:
class ViewModel<T: Decodable> {
let networker: Networker<T>
…
}
Otherwise you might create a type-erased AnyNetworkable concrete type, which you could use as in:
class ViewModel<T: Decodable> {
let networker: AnyNetworkable<T>
…
}
Or you could go with an even more generic approach as in:
class ViewModel<R, T: Networkable> where T.Response == R {
let networker: T
}
Obviously the same reflects on your View type, which you also need to make either generic or specialised over a specific concrete type of ViewModel.

Protocol with associatedtype conformance with generic gives compiler error

I'm trying to make a DB mock to test some UI implementations, but compiler keeps giving me the following error:
Type 'DBClientMock<T>' does not conform to protocol 'DBClient'
This is my code...
protocol DBClient {
associatedtype Model
func get(id: UUID, completion: #escaping ((Model?, Error?)) -> Void)
func insert(_ model: Model, completion: #escaping (Error?) -> Void)
func delete(_ model: Model, completion: #escaping (Error?) -> Void)
}
final class DBClientMock<T>: DBClient where T: Identifiable, T: Equatable {
typealias Model = T
private let queue: DispatchQueue
private var modelDB = [T]()
enum Error: Swift.Error {
case notFound
}
init(queue: DispatchQueue = DispatchQueue.global()) {
self.queue = queue
}
private func getModel(id: UUID) -> T? {
let results = modelDB.filter({ $0.id as? UUID == id })
guard results.count > 0 else { return nil }
return results[0]
}
// Extension
func get(id: UUID, completion: #escaping ((T?, Error?)) -> Void) {
let record = getModel(id: id)
queue.asyncAfter(deadline: .now() + .milliseconds(1500), execute: {
if let model = record {
completion((model, nil))
} else {
completion((nil, Error.notFound))
}
})
}
func insert(_ model: T, completion: #escaping (Error?) -> Void) {
modelDB.append(model)
queue.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
completion(nil)
})
}
func delete(_ model: T, completion: #escaping (Error?) -> Void) {
modelDB.removeAll(where: { $0 == model })
queue.asyncAfter(deadline: .now() + .milliseconds(800), execute: {
completion(nil)
})
}
}
XCode: Version 12.4 (12D4e)
Swift: 5.0
What am I doing wrong? Do I have to be more explicit with the generic type in some way? I tried replacing T with Model but had the same result.
Thanks for your help!
It doesn't conform because you declared another Error type inside the class, so everywhere where you use Error in the required methods, it uses DBClientMock.Error instead of the protocol-required Swift.Error.
Either rename DBClientMock.Error to something else, or change the Error in methods to Swift.Error, like below:
// Extension
func get(id: UUID, completion: #escaping (T?, Swift.Error?) -> Void) {
//...
}
func insert(_ model: T, completion: #escaping (Swift.Error?) -> Void) {
//...
}
func delete(_ model: T, completion: #escaping (Swift.Error?) -> Void) {
//...
}

How to infer type in static generic method with the type of the caller in Swift

I'm declaring this method on a protocol extention
protocol Storable { ... }
extention Storable {
static func get<T: Decodable>(by identifier: String, completion: #escaping (T?) -> Void)
...
}
Now I'm using the method on a type which implements Storable.
struct User: Storable {
...
}
User.get(by: "userId", completion: { user in
print(user)
}
But the compiler says: Generic parameter 'T' could not be inferred
I want to tell to the compiler "T is the class who calls the static method"
I succeed to compile with :
static func get<T>(by identifier: String, type: T.Type, completion: #escaping (T?) -> Void) where T: Decodable
and
User.get(by: "mprot", type: User.self) { ... }
But it seems redundant :(
I want to tell to the compiler "T is the class who calls the static method"
Assuming you want to apply your get only when T is the class who calls the static method, how is this?
protocol Storable {
//...
}
extension Storable where Self: Decodable {
static func get(by identifier: String, completion: #escaping (Self?) -> Void) {
//...
}
}
struct User: Storable, Decodable {
//...
}
This will be compiled successfully:
User.get(by: "userId", completion: { user in
print(user)
})
T must not be optional. You should have:
static func get<T: Decodable>(by identifier: String, completion: #escaping (T) -> ())
been called
User.get(by: "userId", completion: { (value: User) in ... })

How to get generics with completion and resultType working?

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!
APPROACH #1
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<>'
APPROACH #2
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
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!

How to store a generic closure?

I'm trying to create a type erased type using a closure in Swift 3 but I can't figure out how to store the closure in a property. How can I store the closure for later use? See the code below:
protocol ProtocolA {
associatedtype AssociatedType
}
protocol ProtocolB {
associatedtype AssociatedType
func fn<A: ProtocolA>(a: A) where A.AssociatedType == AssociatedType
}
class AnyProtocolB<T>: ProtocolB {
typealias AssociatedType = T
init<A: ProtocolA>(fn: #escaping (A) -> Void) where A.AssociatedType == AssociatedType {
_fn = fn
}
func fn<A: ProtocolA>(a: A) where A.AssociatedType == AssociatedType {
_fn(a)
}
let _fn: (A) -> Void // how to declare this so it will compile?
}
As a side note, I was able to do this in Swift 2 by declaring it like this:
let _fn: (AnyProtocolA<AssociatedType>) -> Void
But the above doesn't work in Swift 3 (it crashes the compiler.)
It looks like this works:
protocol ProtocolA {
associatedtype AssociatedType
}
class AnyProtocolA<T>: ProtocolA {
typealias AssociatedType = T
init<A: ProtocolA>(_ a: A) where AssociatedType == A.AssociatedType {
}
}
protocol ProtocolB {
associatedtype AssociatedType
func fn<A: ProtocolA>(a: A) where A.AssociatedType == AssociatedType
}
class AnyProtocolB<T>: ProtocolB {
typealias AssociatedType = T
init<A: ProtocolA>(fn: #escaping (A) -> Void) where A.AssociatedType == AssociatedType {
_fn = { fn(wrap($0)) }
}
func fn<A: ProtocolA>(a: A) where A.AssociatedType == AssociatedType {
_fn(AnyProtocolA(a))
}
let _fn: (AnyProtocolA<T>) -> Void
}
func wrap<A: ProtocolA, T>(_ a: AnyProtocolA<T>) -> A where A.AssociatedType == T {
return a as! A
}