There are some new property wrappers added to SwiftUI.
I will explain them by showing you the Old way and the New way to complete the same task.
@StateObject
It's similar to @ObservedObject
, when you want to monitor value of Object and reflex the change immediately.
But there's a small different between them.
You should use @StateObject for any observable properties that you initialize in the view that uses it. If the ObservableObject instance is created externally and passed to the view that uses it mark your property with @ObservedObject.
class WrappedModel : ObservableObject {
@Published var name: String = "Hallo"
}
Old way
struct WrappedView : View {
//Should store object somewhere not inside the View
@ObservedObject var model: WrappedModel
var body : some View {
VStack {
Button {
model.name = "Changed"
} label: {
Label(model.name, systemImage: "chevron.left")
}
}
}
}
New way
struct WrappedView : View {
//Can initialized here, the model is kept when View reloaded
@StateObject var model = WrappedModel()
var body : some View {
VStack {
Button {
model.name = "Changed"
} label: {
Label(model.name, systemImage: "hand.point.right")
}
}
}
}
@AppStorage
The wrapper makes usage of UserDefaults
easy as pie.
Old Way
class WrappedModel : ObservableObject {
@Published var name: String
private let nameKey = "app.user.display.name"
init() {
name = UserDefaults.standard.value(forKey: nameKey) as? String ?? "Anonymous"
}
func change(_ name: String) {
UserDefaults.standard.setValue(name, forKey: nameKey)
loadName()
}
func loadName() {
name = UserDefaults.standard.value(forKey: nameKey) as? String ?? "Anonymous"
}
}
struct WrappedView : View {
@StateObject var model: WrappedModel = WrappedModel()
var body : some View {
VStack {
Button {
model.change("Kristine")
} label: {
Label(model.name, systemImage: "hand.point.right")
}
}
}
}
New Way
struct WrappedView : View {
@AppStorage("app.user.display.name") var userName = "Anonymous"
//Or @AppStorage("username", store: UserDefaults(suiteName: "group.com.soyo.user.name")) var userName: String = "Anonymous"
var body : some View {
VStack {
Button {
userName = "Sarah"
//Or UserDefaults.standard.set("Sarah", forKey: "app.user.display.name")
} label: {
Label(userName, systemImage: "hand.point.right")
}
}
}
}
@ScaledMetric
Before working with this wrapper, let take a research about Dynamic Font type.
https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically
User can change their device's font size dynamictically. It's better UX if our app support that.
@ScaledMetric
makes it easy for some hard-code values. Take a look at example:
struct SampleView: View {
@ScaledMetric var imageSize: CGFloat = 100
var body: some View {
Image(systemName: "ant")
.resizable()
.frame(width: imageSize, height: imageSize)
.foregroundColor(.green)
}
}
Before:
After:
Uhm, many more changes will come to SwiftUI soon, like fully support MapView. Just believe our iOS Development will be easier every year.
Happy coding.