Static member cannot be used on protocol meta type - ios

What I am trying to accomplish is to make Proxy protocol that will route my class to appropriate service.
I have 3 types of service per 1 proxy:OnlineService,OfflineService,DemoService each for one of modes (online, offline,demo).
I created protocol :
protocol Proxy {
associatedtype ServiceProtocol
associatedtype OfflineServiceType: OfflineService
associatedtype OnlineServiceType: WebService
associatedtype DemoServiceType: DemoService
}
extension Proxy {
static var service: ServiceProtocol.Type {
if isOnlineMode() {
return OfflineServiceType.self as! ServiceProtocol.Type
} else if isDemoMode(){
return DemoServiceType.self as! ServiceProtocol.Type
}else{
return OnlineServiceType.self as! ServiceProtocol.Type
}
}
}
and then on Customer proxy class
class CustomersServiceProxy: Proxy, CustomersService {
typealias ServiceProtocol = CustomersService
typealias OfflineServiceType = CustomersOfflineService
typealias OnlineServiceType = CustomerWebService
public static func customerDetails(for customer: Customer, completion: #escaping (CustomerDetails) -> Void) {
service.customerDetails(for: customer, completion: completion)
}
}
But I got error:
Static member 'customerDetails' cannot be used on protocol metataype 'CustomerServiceProxy.ServiceProtocol.Protocol' (aka 'CustomerService.Protocol').
I suggest that this happens because Proxy service variable is returning CustomerService.Type instead of Type that is conforming to CustomerService. Is there any workaround to this?

well, u r missing a step, for example:
protocol Proxcy {}
extention Proxcy {
static func a() { print("A")
}
struct A: Proxcy {}
struct B: Proxcy {
A.a()
}

Related

iOS - Error with Protocols and generics when used as a dependency

I have a protocol ContainerService whose sessionService object is used in our network layer. I'm trying to inject the sessionService into an object that encapsulates the network layer. I used a base protocol to avoid the error
Protocol `SessionService` can only be used as a generic constraint because it has Self or associated type requirements
This issue was fixed by adding
var sessionService: SessionServiceBase { get set }
as a requirement for ContainerService
However when I try to use sessionService as a property of TestClass, I come across the same error. Also I ran into another error
Member connectSession cannot be used on value of Protocol typeSessionService; use a generic constraint instead
Here is a sample code
protocol NetworkService: AnyObject {
var sessionService: SessionServiceBase { get set }
}
protocol SessionServiceBase: AnyObject { }
protocol SessionService: SessionServiceBase {
associatedtype T: Tokenable
func connection(credential: T)
}
protocol Tokenable {
var token: String { get set }
var key: String { get set }
}
struct CustomToken: Tokenable {
var token: String
var secretKey: String
}
class CustomSessionService: SessionService {
func connection(credential: CustomToken) {
print(credential.token)
}
}
class CustomNetworkService: NetworkService {
var sessionService: SessionServiceBase = CustomSessionService()
}
class ConsumerClass {
var networkService: NetworkService?
init(networkService: NetworkService) {
self.networkService = networkService
}
func test(){
let tokens = CustomToken(token: "", key: "")
guard let sessionService: SessionService = networkService?.sessionService as? SessionService else {
return
}
sessionService.connection(credential: tokens)
}
}
Is there a work around this generic error issue? If this code is run on a playground there will be two error inside test func.
1.
Protocol SessionService can only be used as a generic constraint because it has Self or associated type requirements
2.
Member `connection` cannot be used on value of protocol type `SessionService`; use a generic constraint instead.
The SessionService protocol can only be used a generic type constraint because the associated type implies that there is not one specific type of SessionService but any number, analogous to how there is not one Array type in Swift. See my answer here for some more details about this.
For instance, we could not guarantee that the sessionService instance actually connected with a CustomToken because its associated type is not specified. Hence, Swift does not allow it.
There are some alternatives though. At some point you have to make something concrete. To implement what you may be looking for with generics, observe the following:
// Protocols
protocol SessionService: AnyObject {
associatedtype T: Tokenable
func connection(credential: T)
}
protocol NetworkService: AnyObject {
associatedtype Service: SessionService
var sessionService: Service { get set }
}
protocol Tokenable {
var token: String { get set }
var key: String { get set }
// Provide an initializer to require that a token can be made in this way
init(token: String, key: String)
}
// Custom types
class CustomSessionService: SessionService {
func connection(credential: CustomToken) {
print(credential.token)
print(credential.doSomething())
}
}
class CustomNetworkService: NetworkService {
var sessionService: CustomSessionService = CustomSessionService()
}
struct CustomToken: Tokenable {
var token: String
var key: String
var secretKey: String = ""
init(token: String, key: String) {
self.token = token
self.key = key
}
var myStr: String = "aaa"
func doSomething() -> String { myStr }
}
// Type erasure
class AnyNetworkService<ServiceType>: NetworkService where ServiceType: SessionService {
typealias Service = ServiceType
var sessionService: ServiceType
init(service: ServiceType) { self.sessionService = service }
convenience init<T: NetworkService>(networkService: T) where T.Service == ServiceType { self.init(service: networkService.sessionService) }
}
// Combines everything
class ConsumerClass<Service: SessionService> {
var networkService: AnyNetworkService<Service>?
init(networkService: AnyNetworkService<Service>) {
self.networkService = networkService
}
func test() {
let tokens = Service.T(token: "asd", key: "fff") // create a token for the specific Service passed in
guard let sessionService = networkService?.sessionService else { return }
sessionService.connection(credential: tokens)
}
}
// Example
let networkService = CustomNetworkService()
let consumer = ConsumerClass<CustomSessionService>(networkService: .init(networkService: networkService))
consumer.test()
The last line will call connection(credential:) in CustomSessionService.
The only potential drawback is that there is no longer a single ConsumerClass but instead many. Also, a Token created in test() cannot have its other properties assigned for the its type is not known to you. However, given its flexibility for using different Network and SessionService concrete types, it may be of use.

How can I use Type Erasure with a protocol using associated type

I am working on a project that has a network client that basically follows the below pattern.
protocol EndpointType {
var baseURL: String { get }
}
enum ProfilesAPI {
case fetchProfileForUser(id: String)
}
extension ProfilesAPI: EndpointType {
var baseURL: String {
return "https://foo.bar"
}
}
protocol ClientType: class {
associatedtype T: EndpointType
func request(_ request: T) -> Void
}
class Client<T: EndpointType>: ClientType {
func request(_ request: T) -> Void {
print(request.baseURL)
}
}
let client = Client<ProfilesAPI>()
client.request(.fetchProfileForUser(id: "123"))
As part of tidying up this project and writing tests I have found the it is not possible to inject a client when conforming to the ClientType protocol.
let client: ClientType = Client<ProfilesAPI>() produces an error:
error: member 'request' cannot be used on value of protocol type
'ClientType'; use a generic constraint instead
I would like to maintain the current pattern ... = Client<ProfilesAPI>()
Is it possible to achieve this using type erasure? I have been reading but am not sure how to make this work.
To your actual question, the type eraser is straight-forward:
final class AnyClient<T: EndpointType>: ClientType {
let _request: (T) -> Void
func request(_ request: T) { _request(request) }
init<Client: ClientType>(_ client: Client) where Client.T == T {
_request = client.request
}
}
You'll need one of these _func/func pairs for each requirement in the protocol. You can use it this way:
let client = AnyClient(Client<ProfilesAPI>())
And then you can create a testing harness like:
class RecordingClient<T: EndpointType>: ClientType {
var requests: [T] = []
func request(_ request: T) -> Void {
requests.append(request)
print("recording: \(request.baseURL)")
}
}
And use that one instead:
let client = AnyClient(RecordingClient<ProfilesAPI>())
But I don't really recommend this approach if you can avoid it. Type erasers are a headache. Instead, I would look inside of Client, and extract the non-generic part into a ClientEngine protocol that doesn't require T. Then make that swappable when you construct the Client. Then you don't need type erasers, and you don't have to expose an extra protocol to the callers (just EndpointType).
For example, the engine part:
protocol ClientEngine: class {
func request(_ request: String) -> Void
}
class StandardClientEngine: ClientEngine {
func request(_ request: String) -> Void {
print(request)
}
}
The client that holds an engine. Notice how it uses a default parameter so that callers don't have to change anything.
class Client<T: EndpointType> {
let engine: ClientEngine
init(engine: ClientEngine = StandardClientEngine()) { self.engine = engine }
func request(_ request: T) -> Void {
engine.request(request.baseURL)
}
}
let client = Client<ProfilesAPI>()
And again, a recording version:
class RecordingClientEngine: ClientEngine {
var requests: [String] = []
func request(_ request: String) -> Void {
requests.append(request)
print("recording: \(request)")
}
}
let client = Client<ProfilesAPI>(engine: RecordingClientEngine())

How to determine the generic type from protocol implementation

I have a protocol that has a function that can return a String or a [String: String]. This is my declaration:
protocol Test {
associatedtype T: Hashable
func returnSomething() -> T
}
Then I want a default implementation for returnSomething, so I made a protocol extension:
extension Test {
func returnSomething() -> T {
let valueToReturn = readValueFromPLISTthatCanReturnAStringOrDictionary() as T
return valueToReturn
}
}
So finally I have 2 clases, TestString and TestDictionary that both implements Test protocol and I want to indicate the T parameter and I want to use the default implementation. How I do this?
class TestString: Test {}
class TestDictionary: Test { }
class TestString: Test where Test.T = String or similar?
I have a protocol that has a function that can return a String or a [String: String]. This is my declaration:
No problem. Let's write that down.
enum StringOrDictionary {
case string(String)
case dictionary([String: String])
}
protocol Test {
func returnSomething() -> StringOrDictionary
}
Then I want a default implementation for returnSomething, so I made a protocol extension:
Sounds good. I'll assume that readValueFromPLISTthatCanReturnAStringOrDictionary() actually returns Any, since that's what is returned by propertyList(from:).
extension Test {
func returnSomething() -> StringOrDictionary {
let value = readValueFromPLISTthatCanReturnAStringOrDictionary()
switch value {
case let string as String: return .string(string)
case let dictionary as [String: String]: return .dictionary(dictionary)
default: fatalError() // Or perhaps you'd like to do something else
}
}
}
It'd probably be nice to name your type something more meaningful than StringOrDictionary, but other than that, it should be pretty straightforward. Just make a type that means what you say. You want a type that means "OR" and that is an enum. (If you want a type that means "AND" that's a struct BTW.)
Regarding your answer, this isn't legal:
class RandomClass: Test where Test.T == String {
func getValue() {
let bah = doSomething() // I don't need here to specify bah's type.
}
}
The way to define your T is to implement the required method.
class RandomClass: Test {
func returnSomething() -> String {
return ""
}
}
If you wanted to share some common code, then you can attach that as an extension rather than a default implementation. You could write a returnString() method and call it from the RandomClass.returnSomething(). This is all very useful in some cases, but I definitely wouldn't use it in this case. You don't mean "returns any possible type (T)." You mean "returns one of two possible types" and that's an enum, not a generic.
Update: Apparently they've added a new feature that they've talked about but I thought wasn't in yet. You could now implement RandomClass this way:
class RandomClass: Test {
typealias T = String
}
(Which is a very nice new feature, even if it's not a good answer for this problem.)
Here's a solution to your immediate problem:
Create 2 subtypes of your protocol, each with a different definition of the associated type, and a different default implementation. You select which default implementation you'd like your classes to use by picking between the 2 sub types.
The next issue here is that [String: String] isn't Hashable. This is due to a lack of support for conditional conformances (e.g. the ability to express that a Dictionary is Hashable iff the keys and values are both Hashable), one of Swift's largest downfalls, IMO. You'll probably want to use the type erasing wrapper AnyHashable.
protocol ResultProvider {
associatedtype Result: Hashable
func getResult() -> Result
}
protocol StringResultProvider: ResultProvider {
typealias Result = String
}
extension StringResultProvider {
func getResult() -> String {
return "A string result"
}
}
protocol IntResultProvider: ResultProvider {
typealias Result = Int
}
extension IntResultProvider {
func getResult() -> Int {
return 123
}
}
class TestIntResult: IntResultProvider {}
class TestString: StringResultProvider {}
print(TestString().getResult())
print(TestIntResult().getResult())
// protocol DictionaryResultProvider: ResultProvider {
// typealias Result = [String: String]
// }
// extension DictionaryResultProvider {
// func getResult() -> [String: String] {
// return ["A dictionary": "result"]
// }
// }
// class TestDictionaryProvider: DictionaryResultProvider {}
You need to specify the typealias when you extend the class, like so:
protocol Test {
associatedtype T: Hashable
func returnSomething() -> T
}
extension String: Test {
typealias T = Int
}
func def() -> Int {
return 6
}
extension Test {
func returnSomething() -> T {
return def() as! Self.T
}
}
"".returnSomething()
6
However, I couldn't find a way to do it without force casting.
The only working solution is made the generic in the function and specify the variable type when calling the function. I was wondering if i could specify the T type when i implement the protocol in the class, similar like this:
class RandomClass: Test where Test.T == String {
func getValue() {
let bah = doSomething() // I don't need here to specify bah's type.
}
}
But previous example just don't work, so an alternative could be this:
protocol Test {
func doSomething<T>() -> T
}
extension Test {
func doSomething<T>(key: String) -> T {
return returnDictOrStringFromPLIST(key: key) as! T
}
}
class TestString: Test {
func getValue() {
let bah: String = doSomething()
}
}
class TestDict: Test {
func getValue() {
let bah: [String: String] = doSomething()
}
}

Swift 2 - Protocol conforming to Equatable issue

I have an issue with a protocol I've defined below. I've got two requirements:
I'd like to be able to use the protocol Peer as a type in other classes while keeping the concrete class private.
I'd like to store the protocol in an array and be able to determine the index of an instance.
In order to satisfy the second point, I need to make the protocol conform to the Equatable protocol. But when I do that, I can no longer use Peer as a type, since it needs to be treated as a generic. This means I cannot have the concrete implementation private anymore, and requirement 1 is broken.
Wondering if anyone else has encountered this problem and gotten around it somehow. Maybe I'm misinterpreting the error I'm getting at indexOf...
Group.swift
import Foundation
class Group {
var peers = [Peer]()
init() {
peers.append(PeerFactory.buildPeer("Buddy"))
}
func findPeer(peer: Peer) -> Bool {
if let index = peers.indexOf(peer) {
return true
}
return false
}
}
Peer.swift
import Foundation
protocol Peer {
var name: String { get }
}
class PeerFactory {
static func buildPeer(name: String) -> Peer {
return SimplePeer(name: name)
}
}
private class SimplePeer: Peer {
let name: String
init(name: String) {
self.name = name
}
}
Error at indexOf if Peer is not Equatable:
cannot convert value of type 'Peer' to expected argument type '#noescape (Peer) throws -> Bool'
So I found a solution to get around the Equatable requirement by extending CollectionType to define a new indexOf for elements are of Peer type, which takes advantage of the other closure-based indexOf. This is essentially a convenience function which saves me from using the closure indexOf directly. Code below:
extension CollectionType where Generator.Element == Peer {
func indexOf(element: Generator.Element) -> Index? {
return indexOf({ $0.name == element.name })
}
}
This of course assumes everything I need to test equality can be obtained from the Peer protocol (which is true for my specific use case).
EDIT: Update for Swift 3:
extension Collection where Iterator.Element == Peer {
func indexOf(element: Iterator.Element) -> Index? {
return index(where: { $0.name == element.name })
}
}
I would suggest you use public super class, so the class can conform to Equatable
class Peer: Equatable {
// Read-only computed property so you can override.
// If no need to override, you can simply declare a stored property
var name: String {
get {
fatalError("Should not call Base")
}
}
// should only be called from subclass
private init() {}
}
private class SimplePeer: Peer {
override var name: String {
get {
return _name
}
}
let _name: String
init(name: String) {
_name = name
super.init()
}
}
func == (lhs: Peer, rhs: Peer) -> Bool {
return lhs.name == rhs.name
}
class PeerFactory {
static func buildPeer(name: String) -> Peer {
return SimplePeer(name: name)
}
}

Factory of generic types in Swift (Protocols and generics)

I'm trying to create a factory of generic types that implement a protocol. The problem is that in the make method of the adapter factory I get the following error: Protocol 'Adapter' can only be used as a generic constraint because it has Self or associated type requirements.
Here is an example of what I'm doing now:
protocol Adapter {
typealias T
static func method1(parameter: T)
}
final class AdapterFactory<T>: NSObject {
static func make(name: String = "") -> Adapter.Type {
switch name {
case "Adapter1":
return ConcreteAdapter1<T>.self
default:
return ConcreteAdapter2<T>.self
}
}
}
final class ConcreteAdapter1<T>: NSObject, Adapter {
static func method1(parameter: T) {
// bla, bla, bla
}
}
You can use a pattern used in swift standard library(see Sequence and AnySequence) i.e using something like AnyAdapter that implements the Adapter protocol by delegating every method call to the underlying implementation(a concrete implementation of Adapter protocol like ConcreteAdapter1) or by using closures.
Your factory would then return AnyAdapter instead of Adapter. It may seems unnatural at first but using AnyAdapter as the type gives the same advantage as using the protocol (obviously it is a workaround) since AnyAdapter is not a concrete implementation by itself instead it delegates the implementation to a concrete implementation.
Here's the code
protocol Adapter {
typealias Element
func method1(parameter: Element)
func method2(parameter : Element)
}
struct AnyAdapter<Element> : Adapter {
private let _method1 : (Element) -> ()
private let _method2 : (Element) -> ()
init<A:Adapter where A.Element == Element>(_ base:A) {
_method1 = { base.method1($0) }
_method2 = { base.method2($0) }
}
func method1(parameter: Element) {
_method1(parameter)
}
func method2(parameter: Element) {
_method2(parameter)
}
}
final class ConcreteAdapter1<T>: NSObject, Adapter {
func method1(parameter: T) {
print("Concrete Adapter 1 method 1")
}
func method2(parameter: T) {
print("Concrete Adapter 1 method 2")
}
}
final class ConcreteAdapter2<T> : Adapter {
func method1(parameter: T) {
print("Concrete adapter 2 method 1")
}
func method2(parameter: T) {
print("Concrete Adapter 2 method 2")
}
}
final class AdapterFactory<T>: NSObject {
static func make(name: String = "") -> AnyAdapter<String> {
switch name {
case "Adapter1":
let concreteAdapter1 = ConcreteAdapter1<String>()
return AnyAdapter(concreteAdapter1)
default:
let concreteAdapter2 = ConcreteAdapter2<String>()
return AnyAdapter(concreteAdapter2)
}
}
}
I am not using a static method in protocol to make things simpler since statics don't operate well with generic types.
To be honest this is a shortcoming in the language and i would like it work like this thing to be simplified like in java or C#.
Hope this helps.

Resources