How to Create Generic Protocols in Swift

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
}
}

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)

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
}
}

Swift Generic Protocol

There is no such thing as generics for protocols. But there is something else, which ressembles a lot to the generics when you look at it.

Here is an example taken from the Swift standard library:

protocol Generator {
typealias Element
func next() -> Element?
}

The Swift book scratches the surface in the Generics chapter, Associated Types.

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?

How to create a protocol with a generic function extending type

Generics is not the way to go for your requirements. When you declare a generic function in a protocol, the generic type parameter will mean that the same function works for all types that satisfy the generic type restriction, but the function signature still needs to be intact for all conforming types.

What you are looking for is a protocol with associated type. An associated type on a protocol means that the conforming type can decide what concrete type to use in place of the associated type, hence allowing you to use different associated types in different conforming classes.

protocol MyProtocol {
associatedtype MyType: MainItem
func myFunc() -> MyType
}

class ClassA: MyProtocol {
func myFunc() -> Item1 {
return Item1()
}
}

class ClassB: MyProtocol {
func myFunc() -> Item2 {
return Item2()
}
}

Use generic struct with protocol

With current implementation you cannot inherit the protocol from AnyObject. What you can do is to create a Type Eraser for your protocol and use that instead. Now your type eraser can be inherited from AnyObject.

Something like this:

protocol Observer {
func stateChanged(sender: SomeClass, newState: Int)
}

class AnyObserver: NSObject, Observer {
private let observer: Observer

init(observer: Observer) {
self.observer = observer
}

func stateChanged(sender: SomeClass, newState: Int) {
observer.stateChanged(sender: sender, newState: newState)
}
}

struct WeakList<T> where T: AnyObject {
struct Ptr {
weak var p: T?
}
private var storage: [Ptr] = []
var aliveObjects: [T] {
var result: [T] = []
for ptr in storage {
if let p = ptr.p {
result.append(p)
}
}
return result
}
mutating func add(_ obj: T) {
storage.append(Ptr(p: obj))
}
// Let's ignore for a moment that this implementation leaks memory badly.
}

class SomeClass {
var someVar: WeakList<AnyObserver> = WeakList<AnyObserver>()

var thisIsOk: WeakList<NSObject> = WeakList<NSObject>()
}

Swift 5: how to specify a generic type conforming to protocol when declaring a variable

An associated type is used when you want your protocol to work with a variety of types, think a Container protocol that might have several methods all dealing with one contained type.

But your protocol is not that, it doesn't need to know any other types to specify the necessary behavior, so get rid of the associated type.

protocol Pipe {
func await() -> Void
func yield( to: Any, with listener: Selector ) -> Void
}

class Foo {
var imageSource: Pipe & Renderable
}

Generics Protocol as Type in Swift

I've solved this from few months ago with descriptions below. Please check it and give me another solution if you have.

Firstly, make some change for Protocol. At associatedtype T should be change to associatedtype Component and Component is a class which will be inherited from another class (important step).

public protocol ProComponentFactory {
associatedtype Component
func create() -> Component?
}

Second, I will make a Generic Struct with inheritance from ProComponentFactory:

public struct ComponentFactory<T>: ProComponentFactory {
public typealias Component = T
public func create() -> T? { return T.self as? T }
}

Well done, for now you can define a variable as I example in my question above:

fileprivate var mComponentFactoryMap = Dictionary<String, ComponentFactory<Component>>()

As well for any class was inherit from Component and variable mComponentFactoryMap can using extension inside.



Related Topics



Leave a reply



Submit