IOS Dev
IOS Dev

Reputation: 21

How do I use swift generic Decodable protocol in generic functions

I would like to write a generic function which parses the result and return in success block of closure or sends back error in failure block.

I'm getting error like this 'Generic parameter 'T' could not be inferred'.

Here is my sample code.

let jsonString = """
{
"id": 1,
"msg": "Sample msg"
}
"""

struct Post: Codable {
    var id: String?
    var msg: String?
}

enum PostError: Error {
    case empty
}


func fetch<T:Decodable>(_ completion: @escaping (Result<T?, PostError>) -> Void)  {

        do {
            let data = Data(jsonString.utf8)
            let post = try JSONDecoder().decode(T.self, from: data)
            completion(.success(post))
        }
        catch {
            completion(.failure(.empty))
        }
    }


fetch { res in
        switch res {
        case .success( let p):
            print(p.description)

        case .failure(let error):
            print(error)
        }
    }

Here is the error I'm getting.

I'm getting error like this.

Upvotes: 1

Views: 268

Answers (2)

Chackle
Chackle

Reputation: 2269

I understand what you're trying to do, but the main problem is that you never specify the type. The compiler knows you want to call fetch but it doesn't know what you want to fetch or what to parse it to.

If you take a look at your fetch call there is nowhere there where the compiler could possibly tell what you're trying to do or what you expect to happen.

You either have to:

1. Pass the type as a parameter

func fetch<T: Decodable>(_ type: T.Type, _ completion: @escaping (Result<T, PostError>) -> Void)  {

    do {
        let data = Data(jsonString.utf8)
        let post = try JSONDecoder().decode(T.self, from: data)
        completion(.success(post))
    }
    catch {
        completion(.failure(.empty))
    }
}


fetch(Post.self) { res in
        switch res {
        case .success(let p):
            print(p)

        case .failure(let error):
            print(error)
        }
    }

or...

2. Specify the type at instantiation of the object that the function is a member of:

struct Fetcher<T: Decodable> {

    func fetch(_ completion: @escaping (Result<T, PostError>) -> Void)  {

        do {
            let data = Data(jsonString.utf8)
            let post = try JSONDecoder().decode(T.self, from: data)
            completion(.success(post))
        }
        catch {
            completion(.failure(.empty))
        }
    }
}

let fetcher = Fetcher<Post>()
fetcher.fetch { res in
    switch res {
    case .success(let p):
        print(p)

    case .failure(let error):
        print(error)
    }
}

Upvotes: 0

Jacob Relkin
Jacob Relkin

Reputation: 163318

You can accept the generic type as a parameter, like so:

func fetchObject<T:Decodable>(ofType type: T.Type, _ completion: @escaping (Result<T, PostError>) -> Void)  {
   do {
      let data = Data(jsonString.utf8)
      let post = try JSONDecoder().decode(type, from: data)
      completion(.success(post))
   }
   catch {
      completion(.failure(.empty))
   }
}

Usage:

fetchObject(ofType: Post.self) { res in
   switch res {
       case .success(let post):
          print(post.description)
       case .failure(let error):
          print(error)
   }
}

Upvotes: 1

Related Questions