Hey, I have a situation where the app freezes (because it's stuck in an endless update cycle) when I pass down a binding to a NavigationPath.
I have a simple example to reproduce the behavior. Copy the following code and run it in previews, a simulator or a real device. Just tap "Show Middle View" and then "Show Inner View", which will cause the app to freeze, because SwiftUI gets stuck in an update cycle:
import SwiftUI
// This is literally empty
@MainActor final class SomeEnvironmentObject: ObservableObject {}
@MainActor
final class Router: ObservableObject {
@Published var path: NavigationPath = .init()
}
struct ContentView: View {
@StateObject var router = Router()
var body: some View {
NavigationStack(path: $router.path) {
Button("Show Middle View") {
router.path.append(0)
}
.navigationDestination(for: Int.self) { destination in
MiddleView(path: $router.path)
}
}
}
}
struct MiddleView: View {
@EnvironmentObject var someEnvironmentObject: SomeEnvironmentObject
@Binding var path: NavigationPath
var body: some View {
Button("Show Inner View") {
path.append("0")
}
.navigationDestination(for: String.self) { destination in
InnerView(path: $path)
}
}
}
struct InnerView: View {
@Binding var path: NavigationPath
var body: some View {
Text("Inner View")
}
}
#Preview {
ContentView()
.environmentObject(SomeEnvironmentObject())
}
The strange thing is, that the app freezes only, when MiddleView has the environment object:
struct MiddleView: View {
@EnvironmentObject var someEnvironmentObject: SomeEnvironmentObject
// ...
}
Removing that line makes everything work.
I'm pretty sure this is some kind of navigation dependency problem, where I'm capturing too much in the navigationDestination modifier, causing SwiftUI to repeatedly update the views in a cycle. I've read about something similar here: https://hachyderm.io/@teissler/112533860374716961
However, I've tried a variety of combinations of only capturing stuff that's needed in the navigationDestination closure, but nothing works. It still freezes up.
Does anyone have an idea? I assume it's an error on my side, but maybe it could be a bug in SwiftUI? I have no idea how to solve this.
This problem occurs on Xcode 16.0 Beta 5, 16.1 Beta 1 and 15.4, as well as on iOS 17 and iOS 18
Post
Replies
Boosts
Views
Activity
I've been experiencing random crashes in my app and today, I could finally narrow it down enough to start building an example project that consistently crashes. The error is this:
"com.apple.SwiftUI.AsyncRenderer (19): EXC_BREAKPOINT (code=1, subcode=0x102f103f8)"
It's a very strange bug because the SwiftUI view hierarchy has to be in a very specific shape to cause the crash, but if it is, then it crashes 100% of the time:
It's a button inside a List and a simple "@State var number: Int?" that is being animated when the view appears. You can simply paste the following code into a project and run it (device, simulator or even in a preview):
// Must be in Swift 6 Language Mode!
// iOS 18 Beta 4
struct ContentView: View {
@State var number: Int?
var body: some View {
List { // Must be in a List
// It also crashes if button is here inside the List
}
.overlay {
button
}
.task { // Has to be in a task modifier (or .onAppear + Task {}, so it has to be async)
number = 0
}
}
// Has to be in a separate view property
private var button: some View {
Button {} label: { // Has to be this initializer and not Button(number == nil ? "" : "") {}
if number == nil { // There has to be a conditional with "number" in here
Color.clear
}
}
.animation(.default, value: number) // Animation must be inside the List
.alignmentGuide(VerticalAlignment.center) { dimension in
dimension[VerticalAlignment.center]
}
}
}
The crash only happens under the following conditions, as far as I can tell:
This only happens in the Swift 6 language mode (which I have enabled in the example project)
It's related to an .alignmentGuide modifier attached to a button that also has an .animation modifier
The button has to have a conditional view inside its label that checks for the same variable that is being animated
The button must be inside a separate property that is put either inside a List or inside an overlay of a List
The crash is then triggered when the animated value is asynchronously changed (in a .task modifier for example)
The crash happens at least with Beta 3 and 4 of Xcode 16.0
I have also opened a feedback: FB14510236 but I thought I'd post it here as well because this crash was driving me crazy and maybe others might find it helpful for their debugging. Took me hours to find out what's been causing it π
Edit:
Interestingly, when I run this project on an iOS 17 device, it does not crash, but rather print a runtime warning:
"warning: data race detected: @MainActor function at AlignmentGuideCrash/ContentView.swift:27 was not called on the main thread"
Line 27 is:
.alignmentGuide(VerticalAlignment.center) { dimension in
Hey,
in Xcode 16's release notes there is this:
Asset catalogs now provide an inspector property for enabling system color and image accessors for generated asset symbols, which allows Swift packages to opt-in to generating these accessors. (113704993)
What exactly does this mean? Can we make the generated asset symbols public, so that we can share assets between local swift packages in the project?
Unfortunately, I don't see this option in the inspector. I only see "Asset Symbols > Extensions On/Off". Not sure what that means, either.
Thanks
Hey,
I have a setup in my app that I am currently building, that uses @Observable router objects that hold the app's entire navigation state, so that I can easily set it globally and let SwiftUI show the appropriate views accordingly. Each view gets passed in such a router object and there is a global "app" router that the app's root view can access as an entry point:
@Observable @MainActor
final class AppRouter {
static let shared = AppRouter() // Entry point used by the app's root view
var isShowingSheet = false // Navigation state
let sheetRouter = SheetRouter() // Router that's passed to the sheet view. This router could contain other routers for sheets it will show, and so on
}
@Observable @MainActor
final class SheetRouter { // Example of a "nested" router for a sheet view
var path = NavigationPath()
var isShowingOtherSheet = false
func reset() {
path = .init()
isShowingOtherSheet = false
}
}
To open a sheet, I have a button like this:
@Bindable var appRouter = AppRouter.shared
// ...
Button("Present") {
appRouter.sheetRouter.reset() // Reset sheet router
appRouter.isShowingSheet = true // show sheet
}
This seems to work perfectly fine. However, this produces tons of "error" logs in the console, whenever I open the sheet for a second time:
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.isShowingOtherSheet after view is torn down has no effect.
These errors appear when calling the reset() of the sheet view's router before opening the sheet. That method simply resets all navigation states in a router back to their defaults. It's as if the sheetRouter is still connected to the dismissed view from the previous sheet, causing a mutation to trigger these error logs.
Am I misusing SwiftUI here or is that a bug? It's also worth mentioning that these error logs do not appear on iOS 17. Only on iOS 18. So it might be a bug but I just want to make sure my usage of these router objects is okay and not a misuse of the SwiftUI API that the runtime previously simply did not catch/notice. Then I'd have to rewrite my entire navigation logic.
I do have an example project to demonstrate the issue. You can find it here: https://github.com/SwiftedMind/PresentationBugDemo.
I have also filed a feedback for this: FB14162780
STEPS TO REPRODUCE
Open the example project.
Open the sheet in the ContentView twice by tapping "Present"
Close that sheet
Open it again.
Then the console will show these error logs from above.
I'd appreciate any help with this.
Cheers
Hey,
the TipViewStyle protocol seems to still be nonisolated, which makes using it in the Swift 6 language mode impossible, I believe (at least without using something like MainActor.assumeIsolated):
@available(macOS 14.0, iOS 17.0, tvOS 17.0, visionOS 1.0, watchOS 10.0, *)
public protocol TipViewStyle {
// ...
}
I cannot use any views inside the makeBody method.
Other style protocols, like ButtonStyle are correctly isolated to the main actor:
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@MainActor @preconcurrency public protocol ButtonStyle {
// ...
}
Is this just an oversight or am I missing something and this is actually intentional?
Thanks!
Hey,
I've opened an existing project with Xcode 16 Beta 1 (I'm also running macOS Sequoia).
None of my previews work anymore. They simply crash with an error I don't quite understand:
JITError
==================================
| NoBuiltTargetDescriptionCouldBeFound
|
| translationUnit: PreviewTranslationUnit(moduleNamePrefix: "Previews_ErrorView", sourceIdentifier: file:///Users/dennis/Code/Project/Project/Components/ErrorView.swift -> ErrorView.swift, parseTree: ParseTree(version: 1848, statements: 5, providers: 0), update: nil, changesContextMemoizer: PreviewsPipeline.PreviewTranslationUnit.(unknown context at $3561fdc4c).ChangesContextMemoizer(parseTree: ParseTree(version: 1848, statements: 5, providers: 0), sourceIdentifier: file:///Users/dennis/Code/Project/Project/Components/ErrorView.swift -> ErrorView.swift, cachedValue: os.OSAllocatedUnfairLock<Swift.Optional<PreviewsModel.ParseTree.PreviewChangesContext>>(__lock: Swift.ManagedBuffer<Swift.Optional<PreviewsModel.ParseTree.PreviewChangesContext>, __C.os_unfair_lock_s>)), registryDeclarationMemoizer: PreviewsPipeline.PreviewTranslationUnit.(unknown context at $3561fdbec).RegistryDeclarationMemoizer)
|
| builtTargetDescriptions:
The app builds and runs fun on a simulator and on device.
Any ideas what this error means? Is this just an early Beta 1 bug or might there be anything I can do in my project to fix this?
The WWDC24 demo projects all work fine and their previews are working as well.
Thanks!
Hey,
I'm currently working on adding CloudKit support to the GRDB SQLite database in my app. CKSyncEngine, though still a bit tricky to wrap your head around, is amazing!
I have most of the basic setup implemented and some very easy cases already work, which is really exciting!
However, I do have some questions regarding data consistency across devices. I'm not sure though, that these questions are actually "correct" to ask. Maybe my entire approach is inherently flawed.
Say we add two records to the pending changes of the sync engine:
// I'm simplifying CKRecord.ID to be a String here
syncEngine.state.add(pendingRecordZoneChanges: [.saveRecord("1"), .saveRecord("2")]
Let's also say that both records are tightly connected. For example, they could be in a one-to-one relationship and must always be added to the database together because the app relies on the existence of either none or both.
After that call, at some later point, the system will call the sync engine's delegate nextRecordZoneChangeBatch(_:syncEngine:) for us to batch the changes together to send to iCloud.
First question: Can we guarantee that records "1" and "2" always land in the exact same batch, and are never separated?
Looking at the example code, there are two line that worry me a bit:
// (Sample project: `SyncedDatabase.swift, lines 132-133`)
let scope = context.options.scope
let changes = syncEngine.state.pendingRecordZoneChanges.filter { scope.contains($0) }
The scope could lead to one of the two records being filtered out. However, my assumption is that the scope will always be .all when the system calls it from an automatically managed schedule, and only differs when you manually specify a different value through calling syncEngine.sendChanges(_:). Is that correct?
Now back to the example. Say we successfully batched records "1" and "2" together and the changes have been sent to iCloud. Awesome!
What happens next? Other connected devices will, at some point, fetch those changes and apply them to their respective local databases.
Second question: Can we guarantee that iCloud sends the exact same batches from earlier to other devices and does not create new batches from all the changes?
I'm worried that iCloud might take all stored changes and "re-batch" them for whatever reason (efficiency, for example). Because that could cause records "1" and "2" to be separated into different batches. A second device could end up receiving "1" and, for at least some period of time, will not receive "2", which would be an "illegal" state of the data.
I'd appreciate any help or clarification on this :)
Thanks a lot!
Hey,
I've been using watchOS 7 for the past week and it mostly works fine.
There's one strange thing, though. About once a day, the watch connects to our wifi as a new device or something. There's a feature on the router informing about new devices connecting so I noticed that. This hasn't happened before the beta.
One thing to note: The "as a new device" part is an interpretation by me. I don't know if it's actually like this. At least it gets a new ip address every time. Also, known devices that are reconnecting don't usually cause the router to notify us.
Any idea what's causing this?
Thanks!
Hey,
I love the new changes in SwiftUI! Though, I have one question about the use of Core Data:
When you create a new SwiftUI project and choose the new default life cycle "SwiftUI App", there's no option to use Core Data anymore. It's grayed out.
Are there any reasons behind this? And does this mean, Core Data isn't intended for use with SwiftUI anymore? Or will it be enabled later or something?
If Core Data isn't meant to be used anymore in SwiftUI, what are alternatives to properly store persistent data?
Thanks and have a great day!