使用关联类型协议作为泛型函数的 return 类型
Use associated type protocol as a return type of a generic function
具有关联类型的协议令人困惑:
// Lets say I have two possible type of responses
struct OtpResponse {}
struct SsoResponse {}
// A simple protocol to mandate the return of token from respective concrete type
protocol AuthenticationProvider {
associatedtype ResponseType
func getToken(completion: @escaping (ResponseType?, NSError?) -> Void)
}
// A type of auth provider
struct OtpBasedAuthProvider:AuthenticationProvider {
typealias ResponseType = OtpResponse
func getToken(completion: @escaping (OtpResponse?, NSError?) -> Void) {
let otpResponse = OtpResponse()
completion(otpResponse, nil)
}
}
// Another type of auth provider
struct SsoBasedAuthProvider: AuthenticationProvider {
typealias ResponseType = SsoResponse
func getToken(completion: @escaping (SsoResponse?, NSError?) -> Void) {
let ssoResponse = SsoResponse()
completion(ssoResponse, nil)
}
}
// There is some external logic to decide which type of auth provider to be used
func getProviderTypeFromSomeLogicOtherLogic() -> Int{
return 1 // simply for dummy
}
// Factory to return a concrete implementaton of auth provider
class AuthProviderFactory {
func getAuthProvider<T: AuthenticationProvider>(type:Int) -> T {
if type == 1 {
return SsoBasedAuthProvider() as! T
}
else {
return OtpBasedAuthProvider() as! T
}
}
}
现在要使用上面的代码,我想做这样的事情:
func executeNetworkCall() -> Void {
let factory = AuthProviderFactory() // 1
let authProvider = factory.getAuthProvider(type: getProviderTypeFromSomeLogicOtherLogic()) // 2
authProvider.getToken{ (resp, error) in // 3
// some code
}
}
在上面,我试图从工厂获取提供程序类型的第 2 行给我的错误是:
Generic parameter 'T' could not be inferred.
我知道我可以通过这样做来消除编译错误:
let authProvider:SsoBasedAuthProvider = factory.getAuthProvider(type: getProviderTypeFromSomeLogicOtherLogic())
但这不是重点,我不知道将返回哪个提供商,我想从该提供商调用 .getToken。
带有associatedtype
的协议不能以组合的形式使用,这是一个缺点,有时确实令人恼火。但是,您可以创建自己的 Type Erasure
class 来完成这项工作。
您可以从这个 link:https://www.donnywals.com/understanding-type-erasure-in-swift/ 中了解更多关于类型擦除的知识。您可以在 Google.
上找到更多信息
这就是 Apple 在内部实现它的方式,只需进行少量更改,我们就可以使其按照我们的方式运行。
下面是我想出的代码:
//Let's say I have two possible type of responses
struct OtpResponse{}
struct SsoResponse{}
//A simple protocol to mandate the return of token from respective concrete type
protocol AuthenticationProvider{
associatedtype ResponseType
func getToken(completion: @escaping(ResponseType?, NSError?) -> Void)
}
//A type of auth provider
struct OtpBasedAuthProvider:AuthenticationProvider{
func getToken(completion: @escaping (OtpResponse?, NSError?) -> Void) {
let otpResponse = OtpResponse()
completion(otpResponse,nil)
}
}
//Another type of auth provider
struct SsoBasedAuthProvider:AuthenticationProvider{
func getToken(completion: @escaping (SsoResponse?, NSError?) -> Void) {
let ssoResponse = SsoResponse()
completion(ssoResponse,nil)
}
}
// there is some external logic to decide which type of auth provider to be used
func getProviderTypeFromSomeLogicOtherLogic() -> Int{
return 1//simply for dummy
}
类型擦除:
class _AnyCacheBox<Storage>:AuthenticationProvider{
func getToken(completion: @escaping (Storage?, NSError?) -> Void) {
fatalError("Never to be called")
}
}
final class _CacheBox<C:AuthenticationProvider>: _AnyCacheBox<C.ResponseType>{
private var _base:C
init(base:C) {
self._base = base
}
override func getToken(completion: @escaping (C.ResponseType?, NSError?) -> Void) {
_base.getToken(completion: completion)
}
}
struct AnyCache<Storage>:AuthenticationProvider{
private let _box: _AnyCacheBox<Storage>
init<C:AuthenticationProvider>(cache:C) where C.ResponseType == Storage {
_box = _CacheBox(base: cache)
}
func getToken(completion: @escaping (Storage?, NSError?) -> Void) {
_box.getToken(completion: completion)
}
}
//Factory to return a concrete implementaton of auth provider
class AuthProviderFactory{
func getOTPAuthProvider() -> AnyCache<OtpResponse>{
let obj : AnyCache = AnyCache(cache: OtpBasedAuthProvider())
return obj
}
func getSSoAuthProvider() -> AnyCache<SsoResponse>{
let obj : AnyCache = AnyCache(cache: SsoBasedAuthProvider())
return obj
}
}
以下是客户端如何调用 Factory-:
中的方法
func executeNetworkCall() -> Void{
let factory = AuthProviderFactory()
let authProvider = factory.getOTPAuthProvider()
authProvider.getToken{(resp,error) in
//some code
print(resp)
}
}
这有点复杂,可能需要一些时间才能理解。
您可以创建类型擦除提供程序:
struct AnyAuthProvider: AuthenticationProvider {
var getToken: (@escaping (Any?, NSError?) -> Void) -> Void
init<T: AuthenticationProvider>(provider: T) {
self.getToken = provider.getToken
}
func getToken(completion: @escaping (Any?, NSError?) -> Void) {
getToken(completion)
}
}
class AuthProviderFactory{
func getAuthProvider(type:Int) -> AnyAuthProvider {
if(type == 1 ){
return AnyAuthProvider(provider: SsoBasedAuthProvider())
}
else{
return AnyAuthProvider(provider: OtpBasedAuthProvider())
}
}
}
用法:
var type = 1
// there is some external logic to decide which type of auth provider to be used
func getProviderTypeFromSomeLogicOtherLogic() -> Int{
return type
}
func executeNetworkCall() -> Void{
let factory = AuthProviderFactory()
let authProvider = factory.getAuthProvider(type:getProviderTypeFromSomeLogicOtherLogic())
authProvider.getToken{(resp,error) in // (Any?, NSError)
//some code
print(String(describing: resp))
}
}
executeNetworkCall() // Optional(__lldb_expr_3.SsoResponse())
type = 2
executeNetworkCall() // Optional(__lldb_expr_3.OtpResponse())
具有 Any?
类型的 resp
可能不切实际,但您没有指定要用它做什么。假设您想对您的回复做一些事情,您可以创建一个协议:
protocol Response {
func doStuff()
}
struct OtpResponse: Response {
func doStuff() {
print("OtpResponse")
}
}
struct SsoResponse: Response {
func doStuff() {
print("SsoResponse")
}
}
并这样定义 AnyAuthProvider
:
struct AnyAuthProvider: AuthenticationProvider {
var getToken: (@escaping (Response?, NSError?) -> Void) -> Void
init<T: AuthenticationProvider>(provider: T) where T.ResponseType: Response {
self.getToken = provider.getToken
}
func getToken(completion: @escaping (Response?, NSError?) -> Void) {
getToken(completion)
}
}
func executeNetworkCall() -> Void{
let factory = AuthProviderFactory()
let authProvider = factory.getAuthProvider(type:getProviderTypeFromSomeLogicOtherLogic())
authProvider.getToken{(resp,error) in
//some code
resp?.doStuff()
}
}
executeNetworkCall() // prints "SsoResponse"
type = 2
executeNetworkCall() // prints "OtpResponse"
具有关联类型的协议令人困惑:
// Lets say I have two possible type of responses
struct OtpResponse {}
struct SsoResponse {}
// A simple protocol to mandate the return of token from respective concrete type
protocol AuthenticationProvider {
associatedtype ResponseType
func getToken(completion: @escaping (ResponseType?, NSError?) -> Void)
}
// A type of auth provider
struct OtpBasedAuthProvider:AuthenticationProvider {
typealias ResponseType = OtpResponse
func getToken(completion: @escaping (OtpResponse?, NSError?) -> Void) {
let otpResponse = OtpResponse()
completion(otpResponse, nil)
}
}
// Another type of auth provider
struct SsoBasedAuthProvider: AuthenticationProvider {
typealias ResponseType = SsoResponse
func getToken(completion: @escaping (SsoResponse?, NSError?) -> Void) {
let ssoResponse = SsoResponse()
completion(ssoResponse, nil)
}
}
// There is some external logic to decide which type of auth provider to be used
func getProviderTypeFromSomeLogicOtherLogic() -> Int{
return 1 // simply for dummy
}
// Factory to return a concrete implementaton of auth provider
class AuthProviderFactory {
func getAuthProvider<T: AuthenticationProvider>(type:Int) -> T {
if type == 1 {
return SsoBasedAuthProvider() as! T
}
else {
return OtpBasedAuthProvider() as! T
}
}
}
现在要使用上面的代码,我想做这样的事情:
func executeNetworkCall() -> Void {
let factory = AuthProviderFactory() // 1
let authProvider = factory.getAuthProvider(type: getProviderTypeFromSomeLogicOtherLogic()) // 2
authProvider.getToken{ (resp, error) in // 3
// some code
}
}
在上面,我试图从工厂获取提供程序类型的第 2 行给我的错误是:
Generic parameter 'T' could not be inferred.
我知道我可以通过这样做来消除编译错误:
let authProvider:SsoBasedAuthProvider = factory.getAuthProvider(type: getProviderTypeFromSomeLogicOtherLogic())
但这不是重点,我不知道将返回哪个提供商,我想从该提供商调用 .getToken。
带有associatedtype
的协议不能以组合的形式使用,这是一个缺点,有时确实令人恼火。但是,您可以创建自己的 Type Erasure
class 来完成这项工作。
您可以从这个 link:https://www.donnywals.com/understanding-type-erasure-in-swift/ 中了解更多关于类型擦除的知识。您可以在 Google.
上找到更多信息这就是 Apple 在内部实现它的方式,只需进行少量更改,我们就可以使其按照我们的方式运行。
下面是我想出的代码:
//Let's say I have two possible type of responses
struct OtpResponse{}
struct SsoResponse{}
//A simple protocol to mandate the return of token from respective concrete type
protocol AuthenticationProvider{
associatedtype ResponseType
func getToken(completion: @escaping(ResponseType?, NSError?) -> Void)
}
//A type of auth provider
struct OtpBasedAuthProvider:AuthenticationProvider{
func getToken(completion: @escaping (OtpResponse?, NSError?) -> Void) {
let otpResponse = OtpResponse()
completion(otpResponse,nil)
}
}
//Another type of auth provider
struct SsoBasedAuthProvider:AuthenticationProvider{
func getToken(completion: @escaping (SsoResponse?, NSError?) -> Void) {
let ssoResponse = SsoResponse()
completion(ssoResponse,nil)
}
}
// there is some external logic to decide which type of auth provider to be used
func getProviderTypeFromSomeLogicOtherLogic() -> Int{
return 1//simply for dummy
}
类型擦除:
class _AnyCacheBox<Storage>:AuthenticationProvider{
func getToken(completion: @escaping (Storage?, NSError?) -> Void) {
fatalError("Never to be called")
}
}
final class _CacheBox<C:AuthenticationProvider>: _AnyCacheBox<C.ResponseType>{
private var _base:C
init(base:C) {
self._base = base
}
override func getToken(completion: @escaping (C.ResponseType?, NSError?) -> Void) {
_base.getToken(completion: completion)
}
}
struct AnyCache<Storage>:AuthenticationProvider{
private let _box: _AnyCacheBox<Storage>
init<C:AuthenticationProvider>(cache:C) where C.ResponseType == Storage {
_box = _CacheBox(base: cache)
}
func getToken(completion: @escaping (Storage?, NSError?) -> Void) {
_box.getToken(completion: completion)
}
}
//Factory to return a concrete implementaton of auth provider
class AuthProviderFactory{
func getOTPAuthProvider() -> AnyCache<OtpResponse>{
let obj : AnyCache = AnyCache(cache: OtpBasedAuthProvider())
return obj
}
func getSSoAuthProvider() -> AnyCache<SsoResponse>{
let obj : AnyCache = AnyCache(cache: SsoBasedAuthProvider())
return obj
}
}
以下是客户端如何调用 Factory-:
中的方法func executeNetworkCall() -> Void{
let factory = AuthProviderFactory()
let authProvider = factory.getOTPAuthProvider()
authProvider.getToken{(resp,error) in
//some code
print(resp)
}
}
这有点复杂,可能需要一些时间才能理解。
您可以创建类型擦除提供程序:
struct AnyAuthProvider: AuthenticationProvider {
var getToken: (@escaping (Any?, NSError?) -> Void) -> Void
init<T: AuthenticationProvider>(provider: T) {
self.getToken = provider.getToken
}
func getToken(completion: @escaping (Any?, NSError?) -> Void) {
getToken(completion)
}
}
class AuthProviderFactory{
func getAuthProvider(type:Int) -> AnyAuthProvider {
if(type == 1 ){
return AnyAuthProvider(provider: SsoBasedAuthProvider())
}
else{
return AnyAuthProvider(provider: OtpBasedAuthProvider())
}
}
}
用法:
var type = 1
// there is some external logic to decide which type of auth provider to be used
func getProviderTypeFromSomeLogicOtherLogic() -> Int{
return type
}
func executeNetworkCall() -> Void{
let factory = AuthProviderFactory()
let authProvider = factory.getAuthProvider(type:getProviderTypeFromSomeLogicOtherLogic())
authProvider.getToken{(resp,error) in // (Any?, NSError)
//some code
print(String(describing: resp))
}
}
executeNetworkCall() // Optional(__lldb_expr_3.SsoResponse())
type = 2
executeNetworkCall() // Optional(__lldb_expr_3.OtpResponse())
具有 Any?
类型的 resp
可能不切实际,但您没有指定要用它做什么。假设您想对您的回复做一些事情,您可以创建一个协议:
protocol Response {
func doStuff()
}
struct OtpResponse: Response {
func doStuff() {
print("OtpResponse")
}
}
struct SsoResponse: Response {
func doStuff() {
print("SsoResponse")
}
}
并这样定义 AnyAuthProvider
:
struct AnyAuthProvider: AuthenticationProvider {
var getToken: (@escaping (Response?, NSError?) -> Void) -> Void
init<T: AuthenticationProvider>(provider: T) where T.ResponseType: Response {
self.getToken = provider.getToken
}
func getToken(completion: @escaping (Response?, NSError?) -> Void) {
getToken(completion)
}
}
func executeNetworkCall() -> Void{
let factory = AuthProviderFactory()
let authProvider = factory.getAuthProvider(type:getProviderTypeFromSomeLogicOtherLogic())
authProvider.getToken{(resp,error) in
//some code
resp?.doStuff()
}
}
executeNetworkCall() // prints "SsoResponse"
type = 2
executeNetworkCall() // prints "OtpResponse"