Eu gostaria de criar um protocolo com um método que leva uma entrada genérica e retorna um valor genérico.
Isso é o que tentei até agora, mas produz o erro de sintaxe.
Uso de identificador não declarado T.
O que estou fazendo errado?
protocol ApiMapperProtocol {
func MapFromSource(T) -> U
}
class UserMapper: NSObject, ApiMapperProtocol {
func MapFromSource(data: NSDictionary) -> UserModel {
var user = UserModel() as UserModel
var accountsData:NSArray = data["Accounts"] as NSArray
return user
}
}
Respostas:
É um pouco diferente para protocolos. Veja "Tipos associados" na documentação da Apple .
É assim que você usa em seu exemplo
protocol ApiMapperProtocol { associatedtype T associatedtype U func MapFromSource(_:T) -> U } class UserMapper: NSObject, ApiMapperProtocol { typealias T = NSDictionary typealias U = UserModel func MapFromSource(_ data:NSDictionary) -> UserModel { var user = UserModel() var accountsData:NSArray = data["Accounts"] as NSArray // For Swift 1.2, you need this line instead // var accountsData:NSArray = data["Accounts"] as! NSArray return user } }
fonte
x
for local, então você não precisa dizer explicitamente seu tipolet x = UserMapper()
.Para expor um pouco a resposta de Lou Franco , se você quisesse criar um método que usasse um determinado
ApiMapperProtocol
, faça-o da seguinte maneira:protocol ApiMapperProtocol { associatedtype T associatedtype U func mapFromSource(T) -> U } class UserMapper: NSObject, ApiMapperProtocol { // these typealiases aren't required, but I'm including them for clarity // Normally, you just allow swift to infer them typealias T = NSDictionary typealias U = UserModel func mapFromSource(data: NSDictionary) -> UserModel { var user = UserModel() var accountsData: NSArray = data["Accounts"] as NSArray // For Swift 1.2, you need this line instead // var accountsData: NSArray = data["Accounts"] as! NSArray return user } } class UsesApiMapperProtocol { func usesApiMapperProtocol< SourceType, MappedType, ApiMapperProtocolType: ApiMapperProtocol where ApiMapperProtocolType.T == SourceType, ApiMapperProtocolType.U == MappedType>( apiMapperProtocol: ApiMapperProtocolType, source: SourceType) -> MappedType { return apiMapperProtocol.mapFromSource(source) } }
UsesApiMapperProtocol
agora tem a garantia de aceitar apenasSourceType
s compatíveis com o fornecidoApiMapperProtocol
:let dictionary: NSDictionary = ... let uses = UsesApiMapperProtocol() let userModel: UserModel = uses.usesApiMapperProtocol(UserMapper() source: dictionary)
fonte
as!
vez de apenasas
no Swift 1.2? Segundo: você poderia me dizer por que precisamos definirtype alias
novamente (ou seja,typealias T = NSDictionary typealias U = UserModel
) na classe que está em conformidade com o protocolo? Desde já, obrigado.as
paraas!
. Verifique os devforums.typealias T=NSDictionary
etypealias U=UserModel
não são necessários. Eu atualizei o exemplo para refletir isso.Para conseguir ter genéricos e assim declarar
let userMapper: ApiMapperProtocol = UserMapper()
você tem que ter uma Classe Genérica em conformidade com o protocolo que retorna um elemento genérico.protocol ApiMapperProtocol { associatedtype I associatedType O func MapFromSource(data: I) -> O } class ApiMapper<I, O>: ApiMapperProtocol { func MapFromSource(data: I) -> O { fatalError() // Should be always overridden by the class } } class UserMapper: NSObject, ApiMapper<NSDictionary, UserModel> { override func MapFromSource(data: NSDictionary) -> UserModel { var user = UserModel() as UserModel var accountsData:NSArray = data["Accounts"] as NSArray return user } }
Agora você também pode se referir a
userMapper
como umApiMapper
que tem uma implementação específica paraUserMapper
:let userMapper: ApiMapper = UserMapper() let userModel: UserModel = userMapper.MapFromSource(data: ...)
fonte
userMapper
.COMO CRERAR E USAR PROTOCOLO GENÉRICO
protocolo Genérico {
associatedtype T associatedtype U func operation(_ t:T)->U
}
// use protocolo genérico
Teste de estrutura: Genérico {
typealias T = UserModel typealias U = Any func operation(_ t: UserModel)->Any { let dict = ["name":"saurabh"] return dict }
}
fonte
Você pode usar métodos de modelos com apagamento de tipo ...
protocol HeavyDelegate : class { func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R } class Heavy<P, R> { typealias Param = P typealias Return = R weak var delegate : HeavyDelegate? func inject(p : P) -> R? { if delegate != nil { return delegate?.heavy(self, shouldReturn: p) } return nil } func callMe(r : Return) { } } class Delegate : HeavyDelegate { typealias H = Heavy<(Int, String), String> func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R { let h = heavy as! H h.callMe("Hello") print("Invoked") return "Hello" as! R } } let heavy = Heavy<(Int, String), String>() let delegate = Delegate() heavy.delegate = delegate heavy.inject((5, "alive"))
fonte