Last active
January 22, 2024 19:39
-
-
Save sabapathyk7/e884752f328142e3743a2f90ec375ea9 to your computer and use it in GitHub Desktop.
Generics and associatetype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/* | |
What is Generics in swift and how useful in the applications? | |
- Generics in swift allows flexible and reusable code that can work in different types while maintaining type safety | |
- Key Points are | |
- Type Safety, | |
- Reusability | |
- Parameterization | |
- Collections - Arrays or Dictionaries | |
- Add constraints to type parameters specifying which protocols that can conform to | |
- Performance will be good | |
- Good Example - Swap two numbers or swap two strings / doubles in a single generic method | |
- Another good example - filter an array of generic type | |
using Element | |
- Associated types are powerful feature of Generics helps in the reusable code | |
To create a generic protocol you can use Self in it, or add associated type to it. | |
Self refers to the eventual type that conforms to the protocol | |
Grammar of a type alias declaration | |
typealias-declaration → attributes? access-level-modifier? typealias typealias-name generic-parameter-clause? typealias-assignment | |
typealias-name → identifier | |
typealias-assignment → = type | |
Grammar of a protocol associated type declaration | |
protocol-associated-type-declaration → attributes? access-level-modifier? associatedtype typealias-name type-inheritance-clause? typealias-assignment? generic-where-clause? | |
*/ | |
func swapTwoInts(_ a: inout Int, _ b: inout Int) { | |
let temp = a | |
a = b | |
b = temp | |
} | |
var a = 1 | |
var b = 7 | |
swapTwoInts(&a ,&b) | |
print(a, b) | |
func swapTwoStrings(_ a: inout String, _ b: inout String) { | |
let temp = a | |
a = b | |
b = temp | |
} | |
var str1 = "Saba" | |
var str2 = "tr" | |
swapTwoStrings(&str1 ,&str2) | |
print(str1, str2) | |
// without third variable | |
func swapTwoInt(_ a: inout Int, _ b: inout Int) { | |
(a,b) = (b,a) | |
} | |
var c = 1 | |
var d = 7 | |
swapTwoInt(&c ,&d) | |
print(c, d) | |
func swapTwoString(_ a: inout String, _ b: inout String) { | |
(a,b) = (b,a) | |
} | |
var str3 = "Saba" | |
var str4 = "tr" | |
swapTwoString(&str3 ,&str4) | |
print(str3, str4) | |
// Using Generics - we can swap two values of any data type | |
func swapTwoValue<T>(_ a: inout T, _ b: inout T) { | |
(a,b) = (b,a) | |
} | |
var d1 = 2.52413132 | |
var d2 = 7.77837812 | |
swapTwoValue(&d1, &d2) | |
print(d1, d2) | |
/* | |
Generic Types | |
generic collection type called Stack | |
Stack - Ordered set of values | |
*/ | |
struct Stack<Element> { | |
var items: [Element] = [] | |
mutating func push(_ item: Element) { | |
items.append(item) | |
} | |
mutating func pop() { | |
items.removeLast() | |
} | |
} | |
var stackOfStrings = Stack<String>() | |
//Lorem ipsum dolor sit amet | |
stackOfStrings.push("Lorem") | |
stackOfStrings.push("ipsum") | |
stackOfStrings.push("dolor") | |
stackOfStrings.push("sit") | |
let fromTop: () = stackOfStrings.pop() | |
print(stackOfStrings) | |
extension Stack { | |
var topItem: Element? { | |
return items.isEmpty ? nil : items[items.count - 1] | |
} | |
} | |
if let topItem = stackOfStrings.topItem { | |
print("Top Item is \(topItem)") | |
} | |
// Type Constraints | |
func findIndex(ofString valuetoFind: String, in array: [String]) -> Int? { | |
for(index, value) in array.enumerated() { | |
if value == valuetoFind { | |
return index | |
} | |
} | |
return nil | |
} | |
let strings = ["Lorem", "ipsum", "dolor", "sit", "amet"] | |
if let foundIndex = findIndex(ofString: "amet", in: strings) { | |
print("Index is \(foundIndex)") | |
} | |
// Equatable will resolve the compile time errors | |
func findIndex<T: Equatable>(of valuetoFind: T, in array: [T]) -> Int? { | |
for(index, value) in array.enumerated() { | |
if value == valuetoFind { | |
return index | |
} | |
} | |
return nil | |
} | |
print(findIndex(of: "ipsum", in: ["Lorem", "ipsum", "dolor", "sit", "amet"])) | |
print(findIndex(of: 2, in: [1,5,2,4,7])) | |
// Associated Type | |
protocol A { | |
associatedtype Element | |
mutating func append(_ element: Element) | |
var count: Int { get } | |
} | |
// Non Generic Int Stack | |
struct IntStack: A { | |
typealias Element = Int | |
var items: [Element] = [] | |
mutating func push(_ item: Element) { | |
items.append(item) | |
} | |
mutating func pop() -> Element { | |
items.removeLast() | |
} | |
mutating func append(_ element: Element) { | |
self.push(element) | |
} | |
var count: Element { | |
return items.count | |
} | |
} | |
var intStack = IntStack(items: [1, 2, 3, 4, 5]) | |
print(intStack.count) | |
intStack.pop() | |
intStack.push(6) | |
intStack.append(7) | |
print(intStack) | |
// Generic Stack | |
struct GenericStack<Element>: A { | |
var items: [Element] = [] | |
mutating func push(_ item: Element) { | |
items.append(item) | |
} | |
mutating func pop() -> Element { | |
items.removeLast() | |
} | |
mutating func append(_ element: Element) { | |
self.push(element) | |
} | |
var count: Int { | |
return items.count | |
} | |
} | |
var stackInt = GenericStack(items: [1, 2, 3, 4, 5]) | |
print(stackInt.count) | |
stackInt.pop() | |
stackInt.push(6) | |
stackInt.append(7) | |
print(stackInt) | |
var stackStr = GenericStack(items: ["Lorem", "ipsum", "dolor", "sit", "amet"]) | |
print(stackStr.count) | |
stackStr.pop() | |
stackStr.push("Saba") | |
stackStr.append("Meat") | |
print(stackStr) | |
// Adding constraints to the associatedtype with Equatable protocol | |
func isEqual<T>(_ a: T, _ b: T) -> Bool where T: Equatable { | |
return a == b | |
} | |
let result1 = isEqual(5,5) | |
let result2 = isEqual("Hello", "Saba") | |
struct Container<T> where T: Equatable { | |
var items: [T] = [] | |
mutating func addItem(_ item: T) { | |
if !items.contains(item) { | |
items.append(item) | |
} | |
} | |
} | |
var container = Container<Int>() | |
container.addItem(1) | |
container.addItem(2) | |
container.addItem(1) | |
//Multiple Associated type Constraints | |
protocol Drawable { | |
associatedtype DrawingType | |
func draw() -> DrawingType | |
} | |
struct Circle: Drawable { | |
func draw() -> String { | |
return "Circle" | |
} | |
typealias DrawingType = String | |
} | |
struct Square: Drawable { | |
typealias DrawingType = UIColor | |
func draw() -> UIColor { | |
return UIColor.red | |
} | |
} | |
let circle = Circle() | |
let square = Square() | |
let circleDrawing: String = circle.draw() | |
let squareDrawing: UIColor = square.draw() | |
struct Employee { | |
let name: String | |
} | |
var employees = [ | |
Employee(name: "Kanagasabapathy"), | |
Employee(name: "sabapathy"), | |
Employee(name: "Saba Raj"), | |
Employee(name: "Saba"), | |
Employee(name: "Kannan") | |
] | |
extension Collection where Element == String { | |
func sortedByLength() -> [Element] { | |
sorted(by: { $0.count > $1.count }) | |
} | |
} | |
var employeeSortedByNameLength: [Employee] { | |
employees.sorted(by: { $0.name.count > $1.name.count }) | |
} | |
func longestName() -> String { | |
guard let longestName = employeeSortedByNameLength | |
.first?.name else { | |
return "No Longest Name" | |
} | |
return longestName | |
} | |
func shortestName() -> String { | |
guard let shortestName = employeeSortedByNameLength | |
.last?.name else { | |
return "No Longest Name" | |
} | |
return shortestName | |
} | |
shortestName() | |
longestName() | |
// Generic Class | |
class Info<T> { | |
var data: T | |
init(data: T){ | |
self.data = data | |
} | |
func getData() -> T { | |
return self.data | |
} | |
} | |
let ageInfo = Info(data: 7) | |
print(ageInfo.getData()) | |
let nameInfo = Info(data: "Sabapathy") | |
print(nameInfo.getData()) | |
func additionOfTwoNums<T: Numeric>(num1: T, num2: T) -> T{ | |
print(num1 + num2) | |
return (num1 + num2) | |
} | |
additionOfTwoNums(num1: 7, num2: 9) | |
protocol APIClient { | |
associatedtype ResponseModel | |
func fetchData(completion: @escaping (Result<ResponseModel, Error>) -> Void) | |
} | |
struct Person { | |
let name: String = "Saba" | |
} | |
struct Product { | |
let id: Int | |
} | |
struct PersonResponseAPIClient: APIClient { | |
typealias ResponseModel = Person | |
func fetchData(completion: @escaping (Result<ResponseModel, Error>) -> Void) { | |
DispatchQueue.global().asyncAfter(deadline: .now() + 2) { | |
completion(.success(ResponseModel.init())) | |
} | |
} | |
} | |
struct ProductResponseAPIClient: APIClient { | |
typealias ResponseModel = Product | |
func fetchData(completion: @escaping (Result<ResponseModel, Error>) -> Void) { | |
DispatchQueue.global().asyncAfter(deadline: .now() + 2) { | |
completion(.success(ResponseModel.init(id: 2))) | |
} | |
} | |
} | |
struct GetProductRequest: APIClient { | |
typealias ResponseModel = Int | |
func fetchData(completion: @escaping (Result<Int, Error>) -> Void) { | |
// Simulate network request and response | |
} | |
} | |
// Type-erasing wrapper for API requests | |
struct AnyAPIRequest<ResponseModel>: APIClient { | |
private let executeClosure: (@escaping (Result<ResponseModel, Error>) -> Void) -> Void | |
init<ConcreteResponse>(_ request: ConcreteResponse) where ConcreteResponse: APIClient, ConcreteResponse.ResponseModel == ResponseModel { | |
executeClosure = request.fetchData(completion:) | |
} | |
func fetchData(completion: @escaping (Result<ResponseModel, Error>) -> Void) { | |
executeClosure(completion) | |
} | |
} | |
let getProductRequest = GetProductRequest() | |
let anyNumberRequest = AnyAPIRequest(getProductRequest) | |
anyNumberRequest.fetchData { result in | |
switch result { | |
case .success(let number): | |
print("Number: \(number)") | |
case .failure(let error): | |
print("Error: \(error)") | |
} | |
} | |
protocol Container { | |
associatedtype Item | |
mutating func addItem(item: Item) | |
var count: Int { get } | |
subscript(index: Int) -> Item { get } | |
} | |
struct NewContainer<T>: Container { | |
typealias Item = T | |
private var items: [T] = [] | |
mutating func addItem(item: T) { | |
items.append(item) | |
} | |
var count: Int { | |
return items.count | |
} | |
subscript(index: Int) -> T { | |
return items[index] | |
} | |
} | |
var newContainer = NewContainer<Int>() | |
newContainer.addItem(item: 7) | |
newContainer.addItem(item: 18) | |
print(newContainer.count) | |
var stringContainer = NewContainer<String>() | |
stringContainer.addItem(item: "Saba") | |
stringContainer.addItem(item: "Pathy") | |
print(stringContainer[0]) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment