Swift Generic Protocol

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 to Decodable
  • U must conform to AProtocol
  • U.T must be the same as T

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



Leave a reply



Submit