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
associatedtype
: Placeholder for a type to be specified later.- Flexible and Reusable: Protocols with associated types are more flexible than concrete type protocols.
- Conforming Types Specify the Type: The type is defined when a protocol is implemented.
- 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)")
}
}