I'd like to create a protocol with a method that takes a generic input and returns a generic value.
This is what I've tried so far, but it produces the syntax error.
Use of undeclared identifier T.
What am I doing wrong?
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 } }
Swift enables us to create generic types, protocols, and functions, that aren't tied to any specific concrete type — but can instead be used with any type that meets a given set of requirements.
Here, the generic function is created with type constraints. This means addition() can only work with data types that conform to Numeric protocol ( Int , Double , and so on). Note: If we try to pass other types, say String , we'll get an error: argument type 'String' does not conform to the expected type 'Numeric' .
Generics allow you to declare a variable which, on execution, may be assigned to a set of types defined by us. In Swift, an array can hold data of any type. If we need an array of integers, strings, or floats, we can create one with the Swift standard library.
You cannot declare an array [Equatable] because while two Int instances can be compared to each other and two instances of Double can be compared to each other, you cannot compare an Int to a Double although they both implement Equatable . MyProtocol is a protocol, that means it provides a generic interface.
It's a little different for protocols. Look at "Associated Types" in Apple's documentation.
This is how you use it in your example
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 } }
To expound on Lou Franco's answer a bit, If you wanted to create a method that used a particular ApiMapperProtocol
, you do so thusly:
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
is now guaranteed to only accept SourceType
s compatible with the given ApiMapperProtocol
:
let dictionary: NSDictionary = ... let uses = UsesApiMapperProtocol() let userModel: UserModel = uses.usesApiMapperProtocol(UserMapper() source: dictionary)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With