Swift for Rails Developers - Notes

jrhicks

Jeffrey Hicks

Posted on October 8, 2023

Swift for Rails Developers - Notes

Overview

My quick and dirty notes while watching Swift for Rails Developers

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

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

 let breakfast = dictionary["breakfast"]
'2x ' + breakfast
Enter fullscreen mode Exit fullscreen mode
  • 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
    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
Enter fullscreen mode Exit fullscreen mode
 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
Enter fullscreen mode Exit fullscreen mode

Instead we can use a protocol

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())
}
Enter fullscreen mode Exit fullscreen mode

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

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

protocol Animal {
    func sayHi() -> String
}

class Dog {
}

extension Dog: Animal {
    func sayHi() -> String {
        return "woof"
    }
}
Enter fullscreen mode Exit fullscreen mode

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
// SceneDelegate.swift
// func scene

let viewController = UIViewController()
window.rootViewController = viewController
Enter fullscreen mode Exit fullscreen mode
  • Subclass UIViewController
 class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let label = UILabel()
        label.text = "Hello World"
        label.sizeToFit()
        label.center = view.center
        view.addSubview(label)

    }
 }
Enter fullscreen mode Exit fullscreen mode

Back in scene

 // SceneDelegate.swift
// func scene

let viewController = MyViewController()
window.rootViewController = viewController
Enter fullscreen mode Exit fullscreen mode

Let's try to get more interactive

  • Make Button

  • Prints Logs when Clicked

 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")
    }
 }
Enter fullscreen mode Exit fullscreen mode

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
// SceneDelegate.swift
// func scene

let viewController = UIViewController()
let navigationController = UINavigationController(rootViewController: viewController)
window.rootViewController = viewController
Enter fullscreen mode Exit fullscreen mode
 class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title "My View Controller"
        // ...
    }
 }
Enter fullscreen mode Exit fullscreen mode

Create a 2nd View Controller


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
    }
 }
Enter fullscreen mode Exit fullscreen mode

How do we get this on the stack

     @objc private func buttonWasTapped() {
        navigationController?.pushViewController(SecondViewController(), animated: true)
    }
Enter fullscreen mode Exit fullscreen mode

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

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

  class SecondViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // ...
    }
Enter fullscreen mode Exit fullscreen mode

Modals

  • Modals slide up from the bottom, and are not part of the navigation stack
     @objc private func buttonWasTapped() {
        present(SecondViewController(), animated: true)
    }
Enter fullscreen mode Exit fullscreen mode
  • Modals can also have navigation
     @objc private func buttonWasTapped() {
        let navigationController = UINavigationController(rootViewController: SecondViewController())
        present(navigationController, animated: true)
    }
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
jrhicks
Jeffrey Hicks

Posted on October 8, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related