Swift for Rails Developers - Notes
Published: 2023-10-08 16:03:38

## Overview

My quick and dirty notes while watching [Swift for Rails Developers](https://www.youtube.com/watch?v=H58Tj-i2aSE)

## Ruby -vs Swift

* Runtimes

* Dynamic -vs- Static

* Mutability

* Optional Values / Null Handling

* Duck Typing

* Monkey Patching 

### Runtimes

* Ruby - IRB console

* Swift - Swift Playground

 * Xcode

 * File - New Playground

### Dynamic -vs- Static

* Ruby - Dynamic

* Swift - Static

 * Can't change the type of something

 * x = 1

 * x = 'String' -> throws error

 * var name: String = 'Joe'

 * var double: Double = 1

### Mutability

 * Swift - let - prefer let over var

  * let name: String = 'Joe'

  * Prefer let (immutable) over var (mutable)

 * Ruby - CONSTANT

### Optional Values / Null Handling

```swift
 let dictionary: [
    "breakfast": "eggs",
    "lunch": "sandwich"
 ]

 let breakfast = dictionary["breakfast"]
'2x ' + breakfast
 ```

* force unwrap

    * let snack = dictionary["snack"]!

    * dangerous, snack doesn't exist, throws error

* null coalescing operator

    * let snack = dictionary["snack"] ?? "chips"

* optional unwrapping with `if let`

    * if let breakfast = dictionary["breakfast"] {
        '2x ' + breakfast
    }

### Duck Typing

 ```ruby
    # Ruby
    class Dog
        def say_hi
            'woof'
        end
    end
    class Cat
        def say_hi
            'meow'
        end
    end

    [Dog.new, Cat.new].each do |animal|
        puts animal.say_hi
    end
 ```

 ```swift
 class Dog {
    func sayHi() -> String {
        return "woof"
    }
 }
 class Cat {
    func sayHi() -> String {
        return "meow"
    }
 }


let animals = [Dog(), Cat()] // Heterogeneous collection

let animals: [Any] = [Dog(), Cat()] // Works but doesn't get us far
```

Instead we can use a *protocol*

```swift
protocol Animal {
    func sayHi() -> String
}

class Dog: Animal {
    func sayHi() -> String {
        return "woof"
    }
}
class Cat: Animal {
    func sayHi() -> String {
        return "meow"
    }
}
class Mouse: Animal {
    func sayHi() -> String {
        return "squeak"
    }
}

let animals: [Animal] = [Dog(), Cat(), Mouse()]

animals.forEach { animal in
    print(animal.sayHi())
}
```

### Extensions (swift) -vs- Monkey Patching (ruby)

Powerfull way to add functionality to existing types from vendors etc.

```swift
protocol Animal {
    func sayHi() -> String
}

class Dog {
}

extension Dog: Animal {
    func sayHi() -> String {
        return "woof"
    }
}
```

# Xcode Intro

## Starting New Project

When building a Turbo Native Application, to start a Swift Project

* xCode

* File - New - Project

* iOS App - Next - Change Swift to Story Board

* Remove Boilerplate

 * App Delegate

 * Scene Delegate

## Navigate Xcode

### Project Navigator

* CMD 0 - Open / Close

* CMD 1 - File Explorer

* CMD 1-9 - Change Mode

* Single Click file open 

* Double CLick file open and leave open

* CMD SHIFT 0 - Quick Open!

### Properties / Inspector

* OPTION CMD 0 - Open / Close

* Usually leave close

## Project File Structure

* AppDelegate.swift - throwback to objective C

 * func main - entry point to application

 * Don't need anything (Yet)
 
 * Future home of push notification registration

* SceneDelegate.swift

 * Gets called every time a new window gets opened.

 * func scene

 * Don't need to modify it really

 * window.backgroundColor = .systemGreen

* Running

 * Click Play in Navigator Bar

 * Runs new Instance of a simulator

## Rails MVC - ios MVC

  * in func scene

```swift
// SceneDelegate.swift
// func scene

let viewController = UIViewController()
window.rootViewController = viewController
```

 * Subclass UIViewController

 ```swift
 class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let label = UILabel()
        label.text = "Hello World"
        label.sizeToFit()
        label.center = view.center
        view.addSubview(label)

    }
 }
 ```

Back in scene

 ```swift
 // SceneDelegate.swift
// func scene

let viewController = MyViewController()
window.rootViewController = viewController
```

Let's try to get more interactive

* Make Button

* Prints Logs when Clicked

```swift
 class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(type: .system)
        button.setTitle("Tap Me", for: .normal)
        button.sizeToFit()
        view.addSubView(button)
        button.center = view.center

        button.addTarget(self, action: #selector(buttonWasTapped), for: .touchUpInside)
    }

    @objc private func buttonWasTapped() {
        print("Button was tapped")
    }
 }
 ```

## Navigation Controller

* Understanding the Navigation Stack

* UIViewController on top NavigationController

* BaseNavigation

 * Index to Show

 * Show to Some Other Show Page

* To Wire Up

 * Get an Instance of Navigation Controller

 * Push UI Controllers

```swift
// SceneDelegate.swift
// func scene

let viewController = UIViewController()
let navigationController = UINavigationController(rootViewController: viewController)
window.rootViewController = viewController
```

```swift
 class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title "My View Controller"
        // ...
    }
 }
 ```

 Create a 2nd View Controller

 ```swift

class SecondViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title "Second View Controller"
        
        let labvel = UILabel()
        label.text = "Second View Controller"
        label.sizeToFit()
        
        view.addSubview(label)
        label.center = view.center
    }
 }
 ```

 How do we get this on the stack

 ```swift
     @objc private func buttonWasTapped() {
        navigationController?.pushViewController(SecondViewController(), animated: true)
    }
 ```

All views by default are transparent, so we can make animations look better.

 ```swift
  class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // ...
    }

  class SecondViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // ...
    }
 ```

## Modals

  * Modals slide up from the bottom, and are not part of the navigation stack

 ```swift
     @objc private func buttonWasTapped() {
        present(SecondViewController(), animated: true)
    }
 ```  

* Modals can also have navigation

 ```swift
     @objc private func buttonWasTapped() {
        let navigationController = UINavigationController(rootViewController: SecondViewController())
        present(navigationController, animated: true)
    }
 ```