Nik
Nik

Reputation: 9431

How to write generic function in Swift using a protocol associatedtype

I'm trying to write generic handler function which will receive ResourceModel instances and process them somehow:

func handleGeneric<R, M: ResourceModel>(resource: R, modelType: M.Type) {

I got stuck with Swift protocols usage though

This is the error I get:

Resource.playground:60:20: note: generic parameter 'R' of global function 'handleGeneric(resource:modelType:)' declared here
func handleGeneric<R, M: ResourceModel>(resource: R, modelType: M.Type) {

import UIKit

// Structs

struct ResourceA: Decodable {
    let id: Int
    let name: String
}

struct ResourceB: Decodable {
    let id: Int
    let number: Int
}

// Models

protocol ResourceModel {
    associatedtype R
    init(resource: R)
}

class ModelA: ResourceModel {
    var id: Int = 0
    var name: String = ""

    convenience required init(resource: ResourceA) {
        self.init()
        id = resource.id
        name = resource.name
    }
}

class ModelB: ResourceModel {
    var id: Int = 0
    var number: Int = 0

    convenience required init(resource: ResourceB) {
        self.init()
        id = resource.id
        number = resource.number
    }
}

// Save

func handleA(resource: ResourceA) {
    let m = ModelA(resource: resource)
    // save ... A ...
    print("handle A completed")
}

func handleB(resource: ResourceB) {
    let m = ModelB(resource: resource)
    // ... save B ...
    print("handle B completed")
}

// Generic handler

func handleGeneric<R, M: ResourceModel>(resource: R, modelType: M.Type) {
    let m = M.init(resource: resource)
    // ... save m ...
    print("handle generic completed")
}

// Download

func downloadA() {
    let r = ResourceA(id: 1, name: "A")
    handleA(resource: r)
}

func downloadB() {
    let r = ResourceB(id: 2, number: 10)
    handleB(resource: r)
}

downloadA()
downloadB()

The question is how can I implement the generic function I need? I.e.

handleGeneric<ResourceA, ModelA>(ResourceA(id: 1, name: "A"))
handleGeneric<ResourceB, ModelB>(ResourceB(id: 2, number: 10))
handleGeneric<ResourceC, ModelC>(ResourceC(id: 3, "foo": "bar"))
handleGeneric<ResourceD, ModelD>(ResourceD(id: 4, "egg": "spam"))

Upd

I tried

handleGeneric(resource: ResourceA(id: 1, name: "A"), modelType: ModelA.Type)
handleGeneric(resource: ResourceB(id: 2, number: 10), modelType: ModelB.Type)

But I get

Cannot convert value of type 'ResourceA' to expected argument type '_.R'

enter image description here

Upvotes: 1

Views: 82

Answers (1)

Lou Franco
Lou Franco

Reputation: 89172

Don't put R in the <> -- use M.R in your signature.

func handleGeneric<M: ResourceModel>(resource: M.R, modelType: M.Type) {

Upvotes: 1

Related Questions