From f0bb94ebd51d6602581522402bbf23b870afc9d1 Mon Sep 17 00:00:00 2001 From: Boyan Date: Mon, 3 Feb 2025 20:44:15 +0100 Subject: [PATCH] Worked on Swift documentation --- swift/README.md | 717 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 swift/README.md diff --git a/swift/README.md b/swift/README.md new file mode 100644 index 0000000..4d7458f --- /dev/null +++ b/swift/README.md @@ -0,0 +1,717 @@ +

+Swift +

+ +- [General Notes](#general-notes) + - [Swift project](#swift-project) + - [Package.swift](#packageswift) + - [Foundation](#foundation) + - [Syntax](#syntax) + - [Functions, if-else, loops, etc.](#functions-if-else-loops-etc) + - [Var vs. let](#var-vs-let) + - [Language features](#language-features) + - [Pattern matching](#pattern-matching) + - [Classes](#classes) + - [Structs](#structs) + - [Enums](#enums) + - [OOP stuff](#oop-stuff) + - [POP (Protocol-Oriented Programming)](#pop-protocol-oriented-programming) + - [Functional stuffies](#functional-stuffies) + - [Scope and shizz](#scope-and-shizz) + - [Optionals](#optionals) + - [Closures](#closures) + - [Other stuff](#other-stuff) + - [Naming conventions](#naming-conventions) + - ["Main" function? `if __name__ == "__main__":`?](#main-function-if-__name__--__main__) + - [Docstrings](#docstrings) +- [Project-specific Notes](#project-specific-notes) + - [Most likely teammates for Software engineering](#most-likely-teammates-for-software-engineering) + +# General Notes +There are **NO** semicolons in Swift. The language is designed to be concise and readable. Very pythonic in that sense. + +Swift is a statically-typed language. This means that the type of a variable is known at compile time. This is in contrast to dynamically-typed languages like Python, where the type of a variable is determined at runtime. + +Swift deals with memory by using Automatic Reference Counting (ARC). This means that the compiler automatically manages memory for you. This is in contrast to languages like C and C++, where you have to manually manage memory. + +Swift is a multi-paradigm language. This means that it supports multiple programming paradigms, such as object-oriented programming, functional programming, and procedural programming. We'll get into this later. + + +## Swift project + +Creating a new project: + +```bash +swift package init --type executable +``` +`--type` flag specifies the type of the project. In this case, it is an executable. Other options include `library` and `system-module`. + +Running the project (this will build and run the project): +```bash +swift run +``` + +Building the project: +```bash +swift build +``` + +This will create a `.build` directory in the root of the project. The executable will be in `.build/debug/` directory (default filename is `swift`). + + +### Package.swift +This file is the equivalent of `requirements.txt` in Python. It specifies the dependencies of the project. + +E.g. +```swift +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "swift", + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .executableTarget( + name: "swift"), + ] +) + +``` + +To add a dependency, add the following to the `Package.swift` file: +```swift +dependencies: [ + .package(url: " + https://whatever.com", from: "1.0.0") +] +``` + +To import the dependency in the code: +```swift +import Whatever +``` + +To update the dependencies: +```bash +swift package update +``` + +### Foundation + +Foundation is a library that provides basic functionality for Swift. It is similar to the `stdlib` in C. + +To import Foundation: +```swift +import Foundation +``` + +To use a function from Foundation: +```swift +let date = Date() +``` + +To use a class from Foundation: +```swift +let url = URL(string: "https://www.google.com") +``` + +Etc. etc. + +## Syntax + +```swift +// This is a comment +``` + +Variables are declared as follows: +```swift +var x = 5 +let y = 10 +``` + +Types are inferred by the compiler. However, you can specify the type explicitly: +```swift +var x: Int = 5 +let y: Double = 10.0 +``` + +Strings are declared as follows: +```swift +let str = "Hello, world!" +``` + +Arrays are declared as follows: +```swift +let arr = [1, 2, 3, 4, 5] +``` + +Dictionaries are declared as follows: +```swift +let dict = ["key1": "value1", "key2": "value2"] +``` + +Tuples are declared as follows: +```swift +let tuple = (1, "hello", 3.14) +``` + +To access elements of a tuple (unpacking): +```swift +let (a, b, c) = tuple +print(a) // 1 +print(b) // hello +print(c) // 3.14 +``` + +To access elements of an array: +```swift +print(arr[0]) // 1 +``` + + +To access elements of a dictionary: +```swift +print(dict["key1"]) // value1 +``` + +To access the length of an array: +```swift +print(arr.count) +``` + + +### Functions, if-else, loops, etc. + +Functions are defined as follows: +```swift +func functionName(arg1: Type, arg2: Type) -> ReturnType { + // code +} +``` + +> [!TIP] +> In Swift, the underscore (`_`) in function parameters signifies that the parameter name is omitted when calling the function. It essentially means "ignore the external name for this parameter," similar to the `*args` and `**kwargs` in Python. + +--- + +For loops: +```swift +for i in 0..<10 { // Note that 0..<10 is a range, equivalent to range(10) in Python + // code +} +``` + +While loops: +```swift +while condition { + // code +} +``` + +Repeat-while loops (do-while in other languages): +```swift + +repeat { + // code +} while condition +``` + +--- + +If-else statements: +```swift +if condition { + // code +} else if condition { + // code +} else { + // code +} +``` + +Switch statements: +```swift +switch value { +case 1: + // code +case 2: + // code +default: + // code +} +``` + +--- + +**A guard statement** is used to transfer program control out of a scope if one or more conditions aren't met. It is similar to an if-else statement, but it is used to exit early if a condition is not met. + +```swift +guard condition else { + // code +} +``` + +Actual example: +```swift +guard let url = URL(string: "https://www.google.com") else { + print("Invalid URL") + return +} +``` + +In python, this would be: +```python +if not url := URL("https://www.google.com"): + print("Invalid URL") + return +``` + +A guard statement is used to make the code more readable and to avoid the pyramid of doom. + +![Pyramid of doom](../writeups/assets/pyramid.png) + + +Try catch statements: +```swift +do { + try someFunction() +} catch { + // code +} +``` + +Throwing an error: +```swift +func someFunction() throws { // Java-esque throws keyword + if condition { + throw SomeError() + } +} +``` + +A defer statement is used to execute a block of code just before the function returns. It is similar to the `finally` block in Python (outside of the context of exceptions). + +```swift +func someFunction() { + defer { + // code + } + // code +} +``` + +Defer statements are executed in reverse order, i.e., the last defer statement is executed first. + +Practical example: +```swift +func openFile() { + let file = open("file.txt") + defer { + close(file) + } + // code +} +``` + +### Var vs. let + +`var` is used to declare a variable. It is mutable. + +`let` is used to declare a constant. It is immutable. + +```swift +var x = 5 +x = 6 // valid + +let y = 5 +y = 6 // invalid +``` + +We love memory safety <3. This is exactly the same as `const` in JavaScript. +On a sidenote, what is the difference between `let` and `var` in JavaScript? `let` is block-scoped, while `var` is function-scoped. Good segue to- + +## Language features + + +### Pattern matching +OH YES! Pattern matching is a powerful feature in Swift. It is used to match values against patterns. The Python equivalent would probably be `in` or the `re` module. + +```swift +let numbers = [10, 15, 20, 25, 30] + +for num in numbers where num % 10 == 0 { + print(num) // 10, 20, 30 +} +``` + + +### Classes + +Classes and structs are used to define custom data types. + +```swift +class Person { + var name: String + var age: Int + + init(name: String, age: Int) { + self.name = name + self.age = age + } + + func sayHello() { + print("Hello, my name is \(name)") + } +} + +let person = Person(name: "Alice", age: 25) +person.sayHello() +``` + +### Structs +Structs are similar to classes, but they are value types. This means that when you pass a struct to +a function, a copy of the struct is passed, not a reference to the struct. + +```swift +struct Point { + var x: Int + var y: Int +} + +var point1 = Point(x: 1, y: 2) +var point2 = point1 +point2.x = 3 +print(point1.x) // 1 +print(point2.x) // 3 +``` + +### Enums + +Enums are used to define a group of related values. They are similar to enums in C and Java. + +```swift +enum Direction { + case north + case south + case east + case west +} + +let direction = Direction.north + +// We also got associated values (which means that each case can have a value associated with it) +enum Result { + case success(Int) + case failure(String) +} + +let result = Result.success(42) +let result2 = Result.success("Error") // invalid + +``` + + +### OOP stuff + +Inheritance is done using the `:` operator. + +```swift +class Animal { + var name: String + + init(name: String) { + self.name = name + } + + func makeSound() { + print("Animal sound") + } +} + +class Dog: Animal { + override func makeSound() { // Method overriding, similar to Java + print("Woof") + } +} + +let dog = Dog(name: "Rex") +dog.makeSound() +``` + +Extensions are used to add new functionality to existing classes, structs, and *protocols*. This is a way better way to do this than reflection in Java. Safe and concise. + + +```swift +extension Int { + func squared() -> Int { + return self * self + } +} + +let x = 5 +print(x.squared()) // 25 +``` + +### POP (Protocol-Oriented Programming) +What's this? It's a paradigm that combines the best of OOP and functional +programming. It is similar to interfaces in Java. + +```swift +protocol Animal { + var name: String { get set } // Getters and setters (Lombok - @Data) + func makeSound() +} + +class Dog: Animal { + var name: String + + init(name: String) { + self.name = name + } + + func makeSound() { + print("Woof") + } +} +``` + +The reason why POP is so powerful is that it allows you to define default implementations for methods in protocols. This is similar to abstract classes in Java. + +```swift +protocol Animal { + var name: String { get set } + func makeSound() +} + +extension Animal { + func makeSound() { + print("Animal sound") + } +} + +class Dog: Animal { + var name: String + + init(name: String) { + self.name = name + } +} + +let dog = Dog(name: "Rex") +dog.makeSound() // Animal sound +``` + + + + + + +### Functional stuffies + +Swift has a couple of higher-order functions that are similar to Python/Haskell. + +`map` is used to apply a function to each element of an array. + +```swift +let arr = [1, 2, 3, 4, 5] +let newArr = arr.map { $0 * 2 } // $0 is the current element, equv to lambda in Python +print(newArr) // [2, 4, 6, 8, 10] +``` + +`filter` is used to filter elements of an array based on a condition. + +```swift +let arr = [1, 2, 3, 4, 5] +let newArr = arr.filter { $0 % 2 == 0 } +print(newArr) // [2, 4] +``` + +`reduce` is used to combine all elements of an array into a single value. + +```swift +let arr = [1, 2, 3, 4, 5] +let sum = arr.reduce(0) { $0 + $1 } +print(sum) // 15 +``` + +`sorted` is used to sort an array. + +```swift +let arr = [5, 3, 1, 4, 2] +let sortedArr = arr.sorted() + +print(sortedArr) // [1, 2, 3, 4, 5] +``` + +`forEach` is used to iterate over an array. + +```swift + +let arr = [1, 2, 3, 4, 5] +arr.forEach { print($0) } +``` + +`compactMap` is used to remove `nil` values from an array. + +```swift +let arr = [1, nil, 2, nil, 3, nil] +let newArr = arr.compactMap { $0 } +print(newArr) // [1, 2, 3] +``` + +[And so on and so forth.](https://www.appcoda.com/higher-order-functions-swift/) + +### Scope and shizz + +Swift has block scoping. Variables declared inside a block are not accessible outside of it. + +```swift +if true { + let a = 10 + var b = 20 + print(a) // valid + print(b) // valid +} + +// print(a) // invalid, a is out of scope +// print(b) // invalid, b is out of scope +``` + +Function scope vs. global scope: +```swift +func doSomething() { + let localVar = 42 + print(localVar) // valid +} + +// print(localVar) // invalid, localVar is out of scope +``` + +Global variables are declared outside of any function or block. They are accessible from anywhere in the program. + +```swift +let globalVar = 42 + +func doSomething() { + print(globalVar) // valid +} +``` + +Shadowing[^1] is allowed. + +### Optionals + +Memory safety is a big deal in Swift. Optionals are used to handle the absence of a value. An optional is a type that can hold either a value or `nil`. + +```swift +var optionalInt: Int? = 5 +optionalInt = nil +``` + +The question mark `?` is used to denote an optional type. The `!` is used to force unwrap an optional. This is dangerous because if the optional is `nil`, the program will crash. + +```swift +var optionalInt: Int? = 5 +print(optionalInt!) // prints 5 +optionalInt = nil +print(optionalInt!) // crashes +``` + +To safely unwrap an optional, use an if-let statement: +```swift +var optionalInt: Int? = 5 +if let unwrappedInt = optionalInt { + print(unwrappedInt) +} +``` + +Optional chaining is used to access properties and methods of an optional that might be `nil`. If the optional is `nil`, the chain will short-circuit and return `nil`. + +```swift +var str: String? = "Hello, world!" +let count = str?.count +``` + +Nil coalescing operator `??` is used to provide a default value for an optional if it is `nil`. It's like a `get` method in Python. + +```swift +var optionalInt: Int? = nil +let unwrappedInt = optionalInt ?? 0 +``` + + +### Closures + +Closures are self-contained blocks of code that can be passed around and used in your code. They are similar to lambda functions in Python. + +```swift +let closure = { + print("Hello, world!") +} + +closure() +``` + +Aint that nifty? + +## Other stuff + +### Naming conventions + +- Use camelCase for variable names. +- Use PascalCase for type names. +- Use snake_case for function names. +- `_` is used as a wildcard in Swift. It is similar to the `_` in Python/Haskell. + +### "Main" function? `if __name__ == "__main__":`? + +In Swift, the entry point of a program is the `main.swift` file. + +```swift +print("Hello, world!") +``` + +There is no `main` function. Swift is smart enough to figure out that this is the entry point of the program. + +IF you'd like to EXPLICITLY define the entry point, you can do so by annotating the file with `@main`. + +```swift +@main +struct MyProgram { + static func main() { + print("Hello, world!") + } +} +``` + +Note the `struct` keyword. This is because `@main` is an attribute that can only be applied to a struct or a class. + +> [!IMPORTANT] +> The `@main` attribute is only applicable to **libraries**, if you are creating an executable, you don't need to use it. + + +### Docstrings + +Docstrings are used to document functions, classes, and modules. They are enclosed in triple quotes. + +```swift +/** + This is a docstring. +*/ +func someFunction() { + // code +} +``` + + +# Project-specific Notes + +## [Most likely teammates for Software engineering](practical/01_mostLikelyTeams/README.md) + + + + +[^1]: Shadowing is the practice of using the same name for a variable in an inner scope as in an outer scope. The inner variable "shadows" the outer variable. This is useful when you want to use the same name for a variable in different scopes. However, it can lead to confusion and bugs if not used carefully. \ No newline at end of file