Swift from Scratch: A Complete Guide (Part 2)

Closure

{ (parameters) -> returnType in
    // code block
}

Types of Closures

func addNumbers(a: Int, b: Int) -> Int {
    return a + b
}
func outerFunction() {
    func innerFunction() {
        print("This is a nested function")
    }
    innerFunction()
}
let sum = { (a: Int, b: Int) -> Int in
    return a + b
}

Removing parameter types and return types

let greet = { name in
return "Hello, \(name)!"
}

Removing the RETURN keyword

let greet = { name in
    "Hello, \(name)!"
}

Using Shorthand Argument Names

let greet = { "Hello, \($0)!" }

// code 
let result = greet("John")
print(result)

// Output "Hello John"

Closures as function arguments

func performOperation(_ operation: (Int, Int) -> Int) {
    let result = operation(5, 3)
    print("Result: \(result)")
}

performOperation { $0 + $1 } // Output: Result: 8
performOperation { $0 * $1 } // Output: Result: 15

Escaping Closures

func fetchData(completion: @escaping () -> Void) {
    DispatchQueue.global().async {
        // Simulate some work
        print("Fetching data...")
        completion()
    }
}

fetchData {
    print("Data fetched!")
}
//OUPUT: Fetching data...
         Data fetched!

Non-Escaping Closure

func performAction(action: () -> Void) {
    print("Action performed")
    action()  // Immediately CAll
    print("Action performed done")
}

performAction {
    print("Action is Performing")
}    
//Output  Action performed
Action is Performing
Action performed done

When to Use a Closure vs a Function?

Protocol

// Defining a protocol
protocol Drivable {
    var speed: Int { get set } // Property
    func drive() // Method
}

// Class conforming to the protocol
class Car: Drivable {
    var speed: Int = 0
    
    func drive() {
        print("The car is driving at \(speed) km/h.")
    }
}

// Using the protocol
let myCar = Car()
myCar.speed = 60
myCar.drive()  // Output: The car is driving at 60 km/h.

Define properties and methods in a protocol:

Multiple Protocols ka Adoption:

protocol Flyable {
    func fly()
}

class Airplane: Drivable, Flyable {
    var speed: Int = 0

    func drive() {
        print("Airplane is driving.")
    }
    
    func fly() {
        print("Airplane is flying at \(speed) km/h.")
    }
}

Protocol Methods with Default Implementations:

protocol Eatable {
    func eat()
}

extension Eatable {
    func eat() {
        print("Eating food.")
    }
}

class Person: Eatable {
    // No need to implement 'eat' method, default implementation from extension will be used.
}

let person = Person()
person.eat() // Output: Eating food.

Protocol with Associated Types:

protocol Container {
    associatedtype Item
    var items: [Item] { get set }
    mutating func addItem(_ item: Item)
}

struct Box: Container {
    var items: [String] = []
    
    mutating func addItem(_ item: String) {
        items.append(item)
    }
}

var myBox = Box()
myBox.addItem("Apple")
print(myBox.items) // Output: ["Apple"]

Extension

class AddFund {
     
}

extension AddFund {
    // New methods, properties, or initializers go here
}

Extension Example in Swift

// Extension for String class
extension String {
    func reverse() -> String {
        return String(self.reversed())
    }
}

// Using the extension method
let newString = "Inspire, Dev!"
let reverse = newString.reverse()
print(reverse)  // Output: "!veD ,eripsnI"

Using Extensions with Protocols

protocol Pirates {
    var name: String { get }
}

extension String: Pirates {
    var name: String {
        return "JACK SPARROW \(self.count)"
    }
}

// Using the protocol conformance
let ship = "BLACK PEARL"
print(ship.name)  // Correct Output: JACK SPARROW 11

Limitations of Extensions

Cannot Override Existing Methods:

Cannot Add Stored Properties:

No Deinitializers:

Error Handling

Types Of Error Handling

How does error handling work?

Syntax Of Error Handling

// Define a custom error type
enum MyError: Error {
    case networkError
    case invalidData
}

// A function that throws an error
func fetchData(from url: String) throws {
    if url.isEmpty {
        throw MyError.invalidData
    }
    
    // Simulating network error
    let success = false
    if !success {
        throw MyError.networkError
    }
    
    print("Data fetched successfully!")
}

// Handling errors using do-try-catch
do {
    try fetchData(from: "https://inspire.com")
} catch MyError.networkError {
    print("Network Error: Failed to fetch data.")
} catch MyError.invalidData {
    print("Invalid Data Error: URL is empty.")
} catch {
    print("An unexpected error occurred: \(error).")
}
// Output : Network Error: Failed to fetch data.

Important Keywords in Error Handling

Access Control

open

public

internal

fileprivate

private

Conclusion

Comments

One response to “Swift from Scratch: A Complete Guide (Part 2)”

Leave a Reply

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