Swift for Rails Developers - Notes
Jeffrey Hicks
Posted on October 8, 2023
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
-
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
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
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.
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
// SceneDelegate.swift
// func scene
let viewController = UIViewController()
window.rootViewController = viewController
- 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)
}
}
Back in scene
// SceneDelegate.swift
// func scene
let viewController = MyViewController()
window.rootViewController = viewController
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")
}
}
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
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title "My View Controller"
// ...
}
}
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
}
}
How do we get this on the stack
@objc private func buttonWasTapped() {
navigationController?.pushViewController(SecondViewController(), animated: true)
}
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
// ...
}
Modals
- Modals slide up from the bottom, and are not part of the navigation stack
@objc private func buttonWasTapped() {
present(SecondViewController(), animated: true)
}
- Modals can also have navigation
@objc private func buttonWasTapped() {
let navigationController = UINavigationController(rootViewController: SecondViewController())
present(navigationController, animated: true)
}
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
November 29, 2024