Associated Type(Generics in Protocol)

Associated types are placeholders that a protocol defines for the type it expects. The actual type is specified when a protocol is adopted. This allows the protocol to work generically with any type.

protocol Container {
    associatedtype Item

    func add(item: Item)
    func getAllItems() -> [Item]
}


//Example with a Generic Class
class Box<T>: Container {
    typealias Item = T
    private var items: [T] = []

    func add(item: T) {
        items.append(item)
    }

    func getAllItems() -> [T] {
        return items
    }
}

let intBox = Box<Int>()
intBox.add(item: 42)
intBox.add(item: 7)
print(intBox.getAllItems()) // Output: [42, 7]

let stringBox = Box<String>()
stringBox.add(item: "Swift")
stringBox.add(item: "Generics")
print(stringBox.getAllItems()) // Output: ["Swift", "Generics"]



//Example with a Struct
struct Bag: Container {
    typealias Item = String
    private var items: [String] = []

    func add(item: String) {
        items.append(item)
    }

    func getAllItems() -> [String] {
        return items
    }
}

var bag = Bag()
bag.add(item: "Laptop")
bag.add(item: "Notebook")
print(bag.getAllItems()) // Output: ["Laptop", "Notebook"]

Advantages of Associatedtype

  1. associatedtype: Placeholder for a type to be specified later.
  2. Flexible and Reusable: Protocols with associated types are more flexible than concrete type protocols.
  3. Conforming Types Specify the Type: The type is defined when a protocol is implemented.
  4. Constraints: You can constrain associated types to conform to certain protocols.

Realtime Example Using in API call

// Define the protocol
protocol APIDataFetchable {
    associatedtype Model: Decodable
    
    func fetch(from url: URL, completion: @escaping (Result<Model, Error>) -> Void)
}



// Extend the protocol with a default implementation for fetching and decoding JSON

extension APIDataFetchable {
    func fetch(from url: URL, completion: @escaping (Result<Model, Error>) -> Void) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                completion(.failure(error))
                return
            }
            
            guard let data = data else {
                completion(.failure(error))
                return
            }
            
            do {
                let decodedData = try JSONDecoder().decode(Model.self, from: data) // Model is generic type here 
                completion(.success(decodedData))
            } catch {
                 completion(.failure(error))
            }
        }.resume()
    }
}

// Example usage:
struct Post: Codable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

struct User: Codable {
    let id: Int
    let name: String
    let username: String
}

// Conform specific types to APIDataFetchable
extension Post: APIDataFetchable {}
extension User: APIDataFetchable {}

// Example usage of fetching posts
let postURL = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!

Post().fetch(from: postURL) { (result: Result<Post, Error>) in
    switch result {
    case .success(let post):
        print("Post title: \(post.title)")
    case .failure(let error):
        print("Error: \(error)")
    }
}

// Example usage of fetching users
let userURL = URL(string: "https://jsonplaceholder.typicode.com/users/1")!
User().fetch(from: userURL) { (result: Result<User, Error>) in
    switch result {
    case .success(let user):
        print("User name: \(user.name)")
    case .failure(let error):
        print("Error: \(error)")
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *