Swift and SwiftUI
Swift
- Backticks around identifiers are ignored, and allow you to use reserved words as identifiers (ex.
`default`
) - Closure arguments can be accessed using $0, $1, etc.
- \typename.path represents a key path to a property
- typename can be omitted if it can be inferred
- path can include multiple periods, subscripts (if parameter conforms to Hashable), can use optional chaining or force unwrapping
- can use a key path expression whose root type is SomeType and whose path produces a value of type Value, instead of a function or closure of type (SomeType) -> Value
- separate multiple conditions in if statements using , (instead of &&)
- Create a dictionary which groups items by the results of a closure:
let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let studentsByLetter = Dictionary(grouping: students, by: { $0.first! })
// ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]
SwiftUI
Model/State
- Create a model object that inherits from ObservableObject to hold reactive state that views can share
import Combine
- An observable object needs to publish any changes to its data using the @Published attribute, so that its subscribers can pick up the change
- if a property will never change, no need to add @Published
- Use the @StateObject attribute when initializing the model object (ex. in the App instance) to ensure it is initialized only once during the life time of the app
@StateObject private var modelData = ModelData()
- use the .environmentObject modifier to pass the state object down to child views (in the App instance or in previews)
.environmentObject(ModelData())
- use the @EnvironmentObject attribute in child views to receive the state object
@EnvironmentObject var modelData: ModelData
- Use state properties (marked with @State) to hold information that’s specific to a view and its subviews
- always create state as private
- By prefixing a state (or state object) variable with $, you pass a binding which can be updated by the view it's passed into
- Use @Binding on a view property to propagate changes upwards
/* LandmarkDetail.swift */
@EnvironmentObject var modelData: ModelData
...
FavoriteButton(isSet: $modelData.landmarks[landmarkIndex].isFavorite)
/* FavoriteButton.swift */
@Binding var isSet: Bool
Views
- To change alignment in Text view:
Text("Hello world")
.frame(width: 100, alignment: .leading)
Group
can be used to apply modifiers to a bunch of child viewsForEach
renders a bunch of views individually without creating a new container- To use
ForEach
on an array and get the index:
- To use
ForEach(Array(zip(items.indices, items)), id: \.1) { index, item in
...
}
- When iterating using
List
,ForEach
, etc, must provide a keypath for a unique id unless the objects being iterated over implement Identifiable