Hi! I have a stateful object that should be created in my app entry point and delivered through my component graph:
@main
@MainActor struct MyApp : App {
@State private var myAppState = MyAppState()
var body: some Scene {
...
}
}
The MyApp is a struct value type… in a SwiftUI world that seems to imply it could "go away" and be recreated as the system sees appropriate. We see this with view components all the time (hence the State wrapper to help preserve our instance across the lifecycle of our identity)… but I'm wondering if this State wrapper is even necessary with an App entry point. Could this struct ever go away? Would there be any legit reasons that this struct should go away and recreate over one SwiftUI app lifecycle (other than terminating the app and starting a whole new process of course)?
And what lifecycle is the SwiftUI.State tied to in this example? In a view component our SwiftUI.State is tied to our component identity. In this example… we are tied to app component identity? Is there ever going to be multiple legit app component identities live in the same process?
I'm thinking I could just go ahead and keep using State as a best practice… but is this just overkill or is there a real best practice lurking under here? Any more ideas about that? Thanks!
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
Hi! I believe there might be a small bug in the SwiftData Quakes Sample App.^1 The Quakes app requests a JSON feed from USGS.^2 What seems to be breaking is that apparently earthquake entities from USGS can return with null magnitudes. That is throwing errors from the decoder:
struct GeoFeatureCollection: Decodable {
let features: [Feature]
struct Feature: Decodable {
let properties: Properties
let geometry: Geometry
struct Properties: Decodable {
let mag: Double
let place: String
let time: Date
let code: String
}
struct Geometry: Decodable {
let coordinates: [Double]
}
}
}
which is expecting mag to not be nil.
Here is my workaround:
struct GeoFeatureCollection: Decodable {
let features: [Feature]
struct Feature: Decodable {
let properties: Properties
let geometry: Geometry
struct Properties: Decodable {
let mag: Double?
let place: String
let time: Date
let code: String
}
struct Geometry: Decodable {
let coordinates: [Double]
}
}
}
And then:
extension Quake {
/// Creates a new quake instance from a decoded feature.
convenience init(from feature: GeoFeatureCollection.Feature) {
self.init(
code: feature.properties.code,
magnitude: feature.properties.mag ?? 0.0,
time: feature.properties.time,
name: feature.properties.place,
longitude: feature.geometry.coordinates[0],
latitude: feature.geometry.coordinates[1]
)
}
}
https://gist.github.com/vanvoorden/37ff2b2f9a2a0d0657a3cc5624cc9139
Hi! I'm experimenting with the Entry macro in a SwiftUI app. I'm a little confused about how to stored a defaultValue to prevent extra work from creating this more than once.
A "legacy" approach to defining an Environment variable looks something like this:
struct StoredValue {
var value: String {
"Hello, world!"
}
init() {
print("StoredValue.init()")
}
}
extension EnvironmentValues {
var storedValue: StoredValue {
get {
self[StoredValueKey.self]
}
set {
self[StoredValueKey.self] = newValue
}
}
struct StoredValueKey: EnvironmentKey {
static let defaultValue = StoredValue()
}
}
The defaultValue is a static stored property.
Here is a "modern" approach using the Entry macro:
struct ComputedValue {
var value: String {
"Hello, world!"
}
init() {
print("ComputedValue.init()")
}
}
extension EnvironmentValues {
@Entry var computedValue: ComputedValue = ComputedValue()
}
From the perspective of the product engineer, it looks like I am defining another stored defaultValue property… but this actually expands to a computed property:
extension EnvironmentValues {
var computedValue: ComputedValue {
get {
self[__Key_computedValue.self]
}
set {
self[__Key_computedValue.self] = newValue
}
}
private struct __Key_computedValue: SwiftUICore.EnvironmentKey {
static var defaultValue: ComputedValue {
get {
ComputedValue()
}
}
}
}
If I tried to use both of these Environment properties in a SwiftUI component, it looks like I can confirm the computedValue is computing its defaultValue several times:
@main
struct EnvironmentDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@Environment(\.computedValue) var computedValue
@Environment(\.storedValue) var storedValue
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
And then when I run the app:
ComputedValue.init()
StoredValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
Is there any way to use the Entry macro in a way that we store the defaultValue instead of computing it on-demand every time?
Hi! I'm attempting to run the Quakes Sample App^1 from macOS. I am running breakpoints and confirming the mapCameraKeyframeAnimator is being called:
.mapCameraKeyframeAnimator(trigger: selectedId) { initialCamera in
let start = initialCamera.centerCoordinate
let end = quakes[selectedId]?.location.coordinate ?? start
let travelDistance = start.distance(to: end)
let duration = max(min(travelDistance / 30, 5), 1)
let finalAltitude = travelDistance > 20 ? 3_000_000 : min(initialCamera.distance, 3_000_000)
let middleAltitude = finalAltitude * max(min(travelDistance / 5, 1.5), 1)
KeyframeTrack(\MapCamera.centerCoordinate) {
CubicKeyframe(end, duration: duration)
}
KeyframeTrack(\MapCamera.distance) {
CubicKeyframe(middleAltitude, duration: duration / 2)
CubicKeyframe(finalAltitude, duration: duration / 2)
}
}
But I don't actually see any map animations taking place when that selection changes.
Running the application from iPhone simulator does show the animations.
I am building from Xcode Version 16.2 and macOS 15.2. Are there known issues with this API on macOS?
Hi! I'm seeing some weird animation issues building the Food Truck sample application.^1 I'm running from macOS 15.4 and Xcode 16.3. I'm building the Food Truck application for macOS. I'm not focusing on iOS for now.
The FoodTruckModel adds new Order values with an animation:
// FoodTruckModel.swift
withAnimation(.spring(response: 0.4, dampingFraction: 1)) {
self.orders.append(orderGenerator.generateOrder(number: orders.count + 1, date: .now, generator: &generator))
}
This then animates the OrdersTable when new Order values are added.
Here is a small change to OrdersTable:
// OrdersTable.swift
- @State private var sortOrder = [KeyPathComparator(\Order.status, order: .reverse)]
+ @State private var sortOrder = [KeyPathComparator(\Order.creationDate, order: .reverse)]
Running the app now inserts new Order values at the top.
The problem is I seem to be seeing some weird animation issues here. It seems that as soon as the new Order comes in there is some kind of weird glitch where it appears as if part the animation is coming from the side instead of down from the top:
What's then more weird is that if I seem to affect the state of the Table in any way then the next Order comes in with perfect animation.
Scrolling the Table fixes the animation.
Changing the creationData sort order from reverse to forward and back to reverse fixes the animation.
Any ideas? Is there something about how the Food Truck product is built that would cause this to happen? Is this an underlying issue in the SwiftUI infra?
https://github.com/apple/sample-food-truck
Hi! I'm following along with the sample-food-truck application from WWDC 2022. I'm seeing some weird navigation issues when building the app for iPadOS.
The Table component displays a Select Button for selecting elements and a Done Button disabling that state. These buttons seem to be breaking something about navigation on iOS 18.4.1. It's a nondeterministic issue… but tapping the buttons seems to lead to some corrupt state where the app transitions out of OrdersView and back to TruckView.
This code from FoodTruckModel seems to be making a difference:
Task(priority: .background) {
var generator = OrderGenerator.SeededRandomGenerator(seed: 5)
for _ in 0..<20 {
try? await Task.sleep(nanoseconds: .secondsToNanoseconds(.random(in: 3 ... 8, using: &generator)))
Task { @MainActor in
withAnimation(.spring(response: 0.4, dampingFraction: 1)) {
self.orders.append(orderGenerator.generateOrder(number: orders.count + 1, date: .now, generator: &generator))
}
}
}
}
Commenting out that code and disabling the new Order values coming in seems to fix the issue.
Is there any public documentation for me to learn about the Select and Done buttons? I don't see anywhere for me to learn about how these work and what my ability is to customize their behavior.
Any ideas? I can repro from device and simulator.
https://github.com/apple/sample-food-truck
Hi! I'm seeing what looks like some weird navigation issue in the Food Truck app. It's from the Live Activity that should deep link to a specific point in the app. There seems be some state where the app is not linking to the correct component. Here are my repro steps on iPhone:
Start live activity from OrderDetailView.
Navigate to Sidebar component.
Tap the Live Activity.
App opens TruckView.
The App should be opening the OrderDetailView for the Order that was passed to the Live Activity. This seems to work when the app is not currently on Sidebar.
Any ideas? I'm testing this on iPhone OS 18.4.1. Is this an issue inside NavigationSplitView? Is this an issue with how Food Truck handles deeplinking?