How to create generic protocols in Swift?
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
}
}
Swift - implement protocol with generic method for different T types
The way you have it written, by conforming to the protocol Client, you have to implement a function get
, with a generic, unconstrained argument T
. In the example implementations provided, you added a type constraint to the generic parameter T
, which does not match the function in the protocol.
There's more than one way you can approach a solution to this problem. Keeping in mind that you said all entities will conform to Mockable
, the solution that requires the least change to the code you provided is to use protocol composition to enforce that all parameters T
conform to both Decodable
and Mockable
.
protocol Client {
func get<T: Decodable & Mockable>(_ url: String) -> Promise<T>
}
In your clients, you would implement that function exactly as written.
Now, in your MockClient
, you can call T.mock()
, and in your real implementations, you can treat T
as Decodable
as required. Of course, this now requires that even your mock arguments conform to Decodable
, but I would assume your mock arguments will be fairly lightweight and thus this wouldn't be a problem.
Swift protocol with generic types
Maybe it's much better to use this protocol AWDeserializer
:
protocol AWDeserializer {
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? where T.T == T
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? where T.T == T
}
Instead of:
protocol AWDeserializer {
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T?
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]?
}
And here is the rest of code:
protocol AWSerializable {
associatedtype T
static func deserialize(dictionary: [String : Any]) -> T?
func serialize() -> [String : Any]
}
protocol AWDeserializer {
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? where T.T == T
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? where T.T == T
}
class FooBar: AWSerializable {
typealias T = FooBar
var foo = ""
var bar = ""
static func deserialize(dictionary: [String : Any]) -> FooBar? {
let fooBar = FooBar()
fooBar.foo = (dictionary["foo"] as? String) ?? ""
fooBar.bar = (dictionary["bar"] as? String) ?? ""
return fooBar
}
func serialize() -> [String : Any] {
var serialized = [String : Any]()
serialized["foo"] = foo
serialized["bar"] = bar
return serialized
}
}
extension UserDefaults: AWDeserializer {
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? where T.T == T {
if let serialized = UserDefaults.standard.object(forKey: key) as? [String : Any] {
return T.deserialize(dictionary: serialized)
}
return nil
}
func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? where T.T == T {
if let data = UserDefaults.standard.array(forKey: key) as? [[String : Any]] {
var values = [T]()
for entry in data {
if let value = T.deserialize(dictionary: entry) {
values.append(value)
}
}
return values
}
return nil
}
}
How to use generic protocol in the method signature?
You can't use a protocol with associated types like that. Protocols with associated types are not the same as generic protocols. The latter does not exist in Swift.
What you usually do in this situation is to introduce a new generic parameter, U
to the method and constrain that to the protocol:
func show<T, U>(a: U) where T: Decodable, U : AProtocol, U.T == T {
}
The 3 constraints are:
T
must conform toDecodable
U
must conform toAProtocol
U.T
must be the same asT
Makes sense, right?
SwifT: Generic function that gets generic protocol as parameter not working
I'm not sure why you say "the protocol only demands that Element implements CustomStringConvertible." The protocol demands that echo
accept and return its Element, which may or may not be String. For example, I can implement this conforming type:
struct AnotherFoo: FooProtocol {
func echo(element: Int) -> Int { fatalError() }
}
So what should happen if I then call:
callEcho(container: AnotherFoo())
This would try to pass a string literal to a function that requires an Int.
Swift Generics: Conforming to protocol by constraining generic parameters to variable types defined by said protocol
The protocol says a different thing from what your implementation says.
Fact 1
According to the protocol this getter
var assignment: Assignment { get }
can return any value conforming to Assignment
.
Fact 2
On the other hand your implementation here
var assignment: A
says that assignment
will contain a value of a specific type A
(which happens to conform to Assignment
).
These are 2 very different statements.
The fix
Here's an easy fix
protocol AssignmentTimeLog {
associatedtype A: Assignment
associatedtype B: TimeLog
var assignment: A { get }
var timeLog: B { get }
}
struct MyAssignmentTimeLog<A, T>: AssignmentTimeLog where A: Assignment, T: TimeLog {
var assignment: A
var timeLog: T
}
Using swift Generic Types with protocols
Swift protocols are incomplete types, which means that you can't use them in places like generic arguments, as the compiler needs to know the whole type details so it can allocate the proper memory layout.
Your createWeak
function can still be used if you make it generic:
func createWeak<T: SomeProtocol>(object: T) -> Weak<T> {
return Weak<T>(value: object)
}
The above code works because the compiler will generate at compile time a function mapped to the concrete type you pass.
Even better, you can make the initializer generic, and convert Weak
to a struct (value types are preferred Swift over reference ones):
struct Weak<T: AnyObject> {
weak var value: T?
init(_ value: T) {
self.value = value
}
}
which you can use it instead of the free function:
let weakRef = Weak(temp)
Related Topics
Arkit - How to Put 3D Object on Qrcode
How to Alloc/Dealloc Unsafe Pointers in Swift
Scenekit -- How to Get Animations for a .Dae Model
How to Quit Swift Repl Without Using Ctrl-D
Storyboard Entry Point Missing
Present Actionsheet in Swiftui on iPad
How to Simulate Traits/Mixins in Swift
How to Avoid Using Anypublisher/Erasetoanypublisher All Over the Place
How to Recognize Continuous Touch in Swift
Nsuserdefaults in Swift - Implementing Type Safety
Get the Current Position of Scrollview in Swiftui
Skaudionode() Crashes When Plugging In/Out Headphones
Rxswift: Use Zip with Different Type Observables
Swift 2.0 Constraintswithvisualformat
Using Uiactivityindicatorview with Uiwebview in Swift