Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create generic protocols in Swift?

Tags:

swift

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     }  } 
like image 851
Farhad-Taran Avatar asked Jun 28 '14 18:06

Farhad-Taran


People also ask

What is generic protocol in Swift?

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.

How do I create a generic in Swift?

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' .

How do you declare a generic variable in Swift?

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.

How do you declare a generic array in Swift?

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.


2 Answers

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     } } 
like image 112
Lou Franco Avatar answered Sep 30 '22 16:09

Lou Franco


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 SourceTypes compatible with the given ApiMapperProtocol:

let dictionary: NSDictionary = ... let uses = UsesApiMapperProtocol() let userModel: UserModel = uses.usesApiMapperProtocol(UserMapper()     source: dictionary) 
like image 38
Heath Borders Avatar answered Sep 30 '22 17:09

Heath Borders