Skip to content

Instantly share code, notes, and snippets.

@sabapathyk7
Last active January 22, 2024 19:39
Show Gist options
  • Save sabapathyk7/e884752f328142e3743a2f90ec375ea9 to your computer and use it in GitHub Desktop.
Save sabapathyk7/e884752f328142e3743a2f90ec375ea9 to your computer and use it in GitHub Desktop.
Generics and associatetype
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