Author: saqibkhan

  • Swift Polymorphism

    Swift Polymorphism

    Treat related types uniformly via inheritance or protocol conformance.

    Override methods and rely on dynamic dispatch.


    Basic Polymorphism

    Use inheritance to treat related types uniformly.

    Example

    class Animal { func speak() { print("...") } }
    class Dog: Animal { override func speak() { print("Woof") } }
    class Cat: Animal { override func speak() { print("Meow") } }
    
    let animals: [Animal] = [Dog(), Cat()]
    animals.forEach { $0.speak() }


    Protocol Polymorphism

    Use a protocol to treat unrelated types uniformly.

    Example

    protocol Speaker { func speak() }
    struct Dog: Speaker { func speak() { print("Woof") } }
    struct Cat: Speaker { func speak() { print("Meow") } }
    
    let speakers: [Speaker] = [Dog(), Cat()]
    speakers.forEach { $0.speak() }
  • Swift Inheritance

    Subclass and Override

    Use override to override a superclass method.

    Example

    class Animal { func speak() { print("...") } }
    class Dog: Animal { override func speak() { print("Woof") } }
    let a = Animal(); a.speak()
    let d = Dog(); d.speak()


    Call super

    Use super to extend a superclass method when overriding.

    Example

    class Animal { func speak() { print("...") } }
    class Dog: Animal {
      override func speak() {
    
    super.speak()
    print("Woof")
    } } let d = Dog() d.speak()
  • Swift self Keyword

    Swift self Keyword

    Use self to reference the current instance, disambiguate names, and in type methods to refer to the type.


    Disambiguation

    Disambiguate means to make something clear or distinct.

    In this instance it is used to make parameter and property names distinct from each other.

    Use self to disambiguate parameter and property names.

    Example

    struct User {
      var name: String
      init(name: String) { self.name = name } // disambiguate
    }
    
    class Counter {
      var value = 0
      func inc() { self.value += 1 } // optional here
      class func resetAll() { print(Self.self) } // refer to the type
    }


    Mutating and self

    Use self to disambiguate parameter and property names in mutating methods.

    Example

    struct Counter {
      var value = 0
      mutating func add(_ value: Int) {
    
    self.value += value // disambiguate
    } } var c = Counter() c.add(3) print(c.value)
  • Swift Class Methods

    Swift Class Methods

    Use static or class to define type methods that belong to the type itself rather than an instance.


    Type Methods

    Use class for overridable type methods; static methods cannot be overridden.

    Example

    class Math {
      class func square(_ n: Int) -> Int { n * n } // overridable in subclasses
      static func cube(_ n: Int) -> Int { n * n * n } // not overridable
    }
    
    print(Math.square(4))
    print(Math.cube(3))


    Override class func

    Use class for overridable type methods; static methods cannot be overridden.

    Example

    class Base {
      class func greet() { print("Base") }
      static func ping() { print("Base ping") }
    }
    
    class Sub: Base {
      override class func greet() { print("Sub") }
      // static func cannot be overridden
    }
    
    Base.greet()
    Sub.greet()
    Base.ping()
  • Swift Class Properties

    Swift Class Properties

    Store state in class properties. Use static for type properties shared across all instances.


    Stored and Computed

    Use var for stored properties and let for constants.

    Example

    class User {
      var name: String // stored
      var greeting: String { "Hello, \(name)" } // computed
      static var appName = "MyApp" // type property
      init(name: String) { self.name = name }
    }

    REMOVE ADS


    Property Observers

    Use willSet and didSet to react to changes on stored properties.

    Example

    class Player {
      var score: Int = 0 {
    
    willSet { print("About to set to \(newValue)") }
    didSet { print("Changed from \(oldValue) to \(score)") }
    } } let p = Player() p.score = 10
  • Swift Class vs Struct

    Swift Class vs Struct

    Choose value semantics (struct) for simple data and reference semantics (class) for shared identity, inheritance, or Objective-C interop.


    Key Differences

    • Copying: Structs copy on assignment and passing; classes pass references.
    • Inheritance: Classes support inheritance; structs do not.
    • Identity: Classes have identity semantics; structs compare by value (when Equatable).

    Example

    struct Point { var x: Int; var y: Int }
    class Counter { var value = 0 }
    
    var p1 = Point(x: 1, y: 2), p2 = p1 // copy
    p2.x = 9 // p1.x remains 1
    
    let c1 = Counter(), c2 = c1 // same instance
    c2.value = 7 // c1.value is 7
  • Swift Structs

    Swift Structs

    Define lightweight value types with stored properties and memberwise initializers.

    Structs are copied on assignment and passing.


    Define a Struct

    Create a struct with stored properties and memberwise initializers, then instantiate and call methods on the instance.

    Example

    struct Point { var x: Int; var y: Int }
    var p1 = Point(x: 1, y: 2)
    var p2 = p1 // copy
    p2.x = 10

    Structs are copied on assignment and passing, so mutations on one reference do not affect the other.

  • Swift Classes/Objects

    Swift Classes/Objects

    Define reference types with stored properties and methods.

    Instances share identity and are passed by reference.


    Define a Class

    Create a class with stored properties and methods, then instantiate and call methods on the instance.

    Example

    class Counter {
      var value = 0
      func inc() { value += 1 }
    }
    
    let c = Counter(); c.inc()


    BankAccount

    Use an initializer to set up state and instance methods to mutate it.

    Example

    class BankAccount {
      var balance: Int
      init(balance: Int) { self.balance = balance }
      func deposit(_ amount: Int) { balance += amount }
    }
    
    let acc = BankAccount(balance: 100)
    acc.deposit(25)
    print(acc.balance) // 125
  • Swift OOP

    Swift OOP

    Understand value vs. reference semantics with structs and classes, and how copying differs between them.


    Object-Oriented Programming

    Swift supports object-oriented programming with classes and protocols.

    Object-oriented programming (OOP) is a programming paradigm based on the concept of “objects”, which can contain data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).

    Swift uses classes and protocols to implement OOP.

    • Classes: Reference types, support inheritance, protocols, access control, and memory management.
    • Structs: Value types, support inheritance, protocols, access control, and memory management.

    Structs vs Classes

    Use structs for value types and classes for reference types.

    Syntax:

    • struct Name { ... }
    • class Name { ... }
    • Value type (struct): Copies on assignment and passing.
    • Reference type (class): Multiple references point to the same instance.

    Example

    struct Point { var x: Int; var y: Int }
    var p1 = Point(x: 1, y: 2)
    var p2 = p1      // copy
    p2.x = 10
    print(p1.x)      // 1
    print(p2.x)      // 10
    
    class Counter { var value = 0 }
    let c1 = Counter()
    let c2 = c1      // reference
    c2.value = 5
    print(c1.value)  // 5
  • Swift Tuples & Type Aliases

    Swift Tuples & Type Aliases

    Group values into lightweight records with tuples, and name complex types using typealias for clarity.


    Tuples

    Tuples bundle multiple values into a single compound value without defining a struct.

    Syntax:

    • let user: (String, Int) = ("Morgan", 30)
    • labels: (name: String, age: Int)

    Example

    let user: (name: String, age: Int) = ("Morgan", 30)
    print(user.name)
    print(user.age)
    
    let http: (code: Int, message: String) = (200, "OK")
    let (code, msg) = http
    print(code)
    print(msg)

    This example creates labeled tuples, accesses fields by name, and destructures into separate variables.



    Type Aliases

    Give a readable name to an existing type using typealias.

    Syntax: typealias Completion = (Result<Void, Error>) -> Void

    Example

    typealias JSON = [String: Any]
    typealias Completion = (Result<Void, Error>) -> Void
    
    func parse(_ data: Data) -> JSON { return [:] }
    
    func save(completion: Completion) {
      completion(.success(()))
    }