[英]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 行給了我錯誤:
無法推斷通用參數“T” 。
我知道我可以通過執行以下操作來擺脫編譯錯誤:
let authProvider:SsoBasedAuthProvider = factory.getAuthProvider(type: getProviderTypeFromSomeLogicOtherLogic())
但這不是重點,我不知道將返回哪個提供程序,我想從該提供程序調用.getToken。
具有associatedtype
類型的協議不能以組合的形式使用,這是一個缺點,有時肯定很煩人。 但是,您可以創建自己的Type Erasure
class 來完成這項工作。
您可以通過以下鏈接了解有關類型擦除的更多信息: https://www.donnywals.com/understanding-type-erasure-in-swift/ 。 你可以在谷歌上找到更多。
這就是 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())
有resp
類型的Any?
可能不實用,但您沒有指定您打算用它做什么。 假設您想對您的響應做一些事情,您可以創建一個協議:
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"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.