Skip to main content

Swift API Manager -Alamofire-Refresh Token-With TestCases

  import Foundation import KeychainAccess enum APIError : Error { case accessTokenExpired case networkError // Add more error cases as needed } class APIManager { private let keychain = Keychain (service: "com.example.app.refreshToken" ) private let refreshTokenKey = "refreshToken" private var accessToken: String ? func callAPI < T : Codable >( urlString : String , method : String , parameters : [ String : Any ] ? , completion : @escaping ( Result < T , APIError >) -> Void ) { guard let url = URL (string: urlString) else { completion(.failure(.networkError)) return } var request = URLRequest (url: url) request.httpMethod = method // Add access token to the request headers if available if let token = accessToken { request.setValue( "Bearer \(token) " , forHTTPHeaderField: "Aut...

SOLID Principles in Swift - ( Single Responsibility Principle(SRP), Open/Closed Principle, Liskov Substitution Principle, Interface Segregation, Dependency Inversion)

 

SOLID Principles in Swift

  1. Single Responsibility Principle 
  2. Open/Closed Principle 
  3. Liskov Substitution Principle 
  4. Interface Segregation 
  5. Dependency Inversion

S - Single Responsibility Principle(SRP) 

Single Responsibility Principle (SRP) is that every class, module, or function in a program should have one responsibility(Task) in a program.


Example:-  A Simple Class For multiple Responsibility 


class SimpleHandler { 

    

    func simpleHandle() {        

        let data = requestToDataAPI()        

        let array = parseToResponse(data: data)        

        saveToDatabase(array: array)    

    }   

  

    private func requestToDataAPI() -> Data {        

        // Network request and wait the response    

        return Data()

    } 

    

    private func parseToResponse(data: Data) -> [String] {        

        // Parse the network response into array  

        return []

    }  

   

    private func saveToDatabase(array: [String]) {        

        // Save parsed response into database   

        print(array)

    }

}


This Simple class perform multiple Responsibility like first network, second parsing data and last loadData Binding data 


Example:-   It states that every module or Class should have only one responsibility.



class SrpHandler {

    let api: APIHandler

    let parse: ParsingData

    let datahander: DataHandler


    init(api: APIHandler, 

    parse: ParsingData, 

    datahander: DataHandler) {

        self.api = api

        self.parse = parse

        self.datahander = datahander

    }


    func handler(){

        let data = api.reqDataToApi()

        let array = parse.parseResponse(data: data)

        datahander.loadDataUI(array: array)

    }

}



// Network request and wait the response 

class APIHandler {

    func reqDataToApi() -> Data{

        return Data()

    }

}


// Parse the network response into array 

class ParsingData {

    func parseResponse(data: Data) -> [String]{

        return []

    }

}


// Save parsed response into database

class DataHandler {

    func loadDataUI(array: [String]){

        print(array)

    }

}


OOpen/Closed Principle

Open meansOpen for extension 


closed meansclosed for modification.


(Software entities (Classes, modules, functions)  should be open for extension but closed for modification.)



Open for Extension You should be able to extend or change the behaviour's of a class without effort. mean If you have to change in an existing class or change the behaviour.


Closed For Modification You must extend a class without changing the implementation


In simple words:


You should be able to add new functionality without modifying existing code.
Prevents breaking changes and makes the system more scalable and maintainable.


// Abstract Protocol representing discount behavior



protocol Discount {

    func applyDiscount(price: Double) -> Double

}


//Percentage Discount 10% off


class PercentageDiscount: Discount {

    func applyDiscount(price: Double) -> Double {

        price * 0.90

    }

}

    //Flat Discount 5 off

    

    class FlatDiscount: Discount {

        func applyDiscount(price: Double) -> Double {

            price - 5.0

        }

    }

    

    // Other Discount Lyoalty

    

    class LyoaltyDiscount: Discount {

        func applyDiscount(price: Double) -> Double {

            price * 0.85 //15% off Lyoal

        }

    }

    


//Check out Class


class checkout {

    func calculateTotal(price: Double, discount: Discount) -> Double {

        return discount.applyDiscount(price: price)

    }

}


let check = checkout()

let price = 100.0


let prs = PercentageDiscount()

let flat = FlatDiscount()

let other = LyoaltyDiscount()



print(" Total After percentage discount \(check.calculateTotal(price: price, discount: prs))")

print(" Total After flat discount \(check.calculateTotal(price: price, discount: flat))")

print(" Total After lyoal discount \(check.calculateTotal(price: price, discount: other))")



LLiskov Substitution Principle (LSP)

The Liskov Substitution principle :-  A Derived class should be substitutable for its base class without affecting the correctness of the program.
Or
If you have a reference to a base class,  you should be able to substitute a derived class without breaking the program.

Example: - 

import Foundation
 
class Rectangle {
    
    var width: Double
    var height: Double
    
    init(width: Double, height: Double){
        self.width = width
        self.height = height
    }
    
    func calculateArea()-> Double{
        return width * height
    }
}

class Square: Rectangle {
    
    override var width: Double {
        didSet{
            height = width
        }
    }
    
    override var height: Double{
        didSet {
            width = height
        }
    }
    
    init(side: Double){
        super.init(width: side, height: side)
    }
    
    func printArea(rectangle: Rectangle){
        let area = rectangle.calculateArea()
        print("Area : \(area)")
    }
}

let square = Square(side: 10.0)
printArea(rectangle: square)




I - Interface Segregation Principle (ISP)

Clients should not be forced to depend on interfaces they do not use.


Other Means:


1. Break large, fat interfaces into smaller, 

2. classes only need to implement the methods they actually need.


Keep your interfaces focused and avoid forcing classes to implement methods they don't need.


This leads to cleaner, scalable, and robust codebases!


Concrete implementations.


-Both should rely on protocols (interfaces), allowing flexibility and testability.


Example - Definitions Definitions


// Interface segregation Protocol Principle


// Example:- Protocol Definitions(Smaller, Specific Protocols)



protocol Workable {

func work()

}


protocol Etable {

    func eat()

}


class Robot: Workable {

    func work() {

        print("Robots only working task")

    }

}


class human: Workable, Etable {

    func work() {

        print("man working")

    }

    

    func eat() {

        print("man eting")

    }

}




 





















Comments