Hey,
The new "soft" scroll edge effect is really cool! But it seems to only appear when you add toolbar items.
Is there a way to add it for "custom" views as well, that I place in a safe area inset?
For example, the messages app in iOS 26 does this. There's a text field as a safe area inset as well as a soft scroll edge effect.
Thanks!
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hey, I've been having a problem with scroll views in combination with the .geometryGroup() modifier.
I have filed a Feedback (FB17698293) but I also wanted to post this here in case someone maybe has a better workaround for the problem.
Problem
Whenever you conditionally insert a ScrollView inside a VStack that is modified with a .geometryGroup() modifier, the scroll view content offset resets itself after the insertion animation is done, even if you started scrolling inside the scroll view during the animation and haven't let go of the screen. This happens consistently and is fully reproducible (see below), both using a simulator and a real device.
Unfortunately, this is a very annoying glitch that ruins a lot of cool UX components that rely on .geometryGroup().
The weird thing is that the glitch entirely disappears, if you add a simple, non-zero (but greater than 1) .padding() modifier to the VStack (.padding().geometryGroup()).
I have no idea why this fixes the glitch, but it does. However, adding a padding is not feasible in many situations, so this workaround is not ideal.
Steps to reproduce
Launch the code below (using a simulator or a real device) and tap "Toggle Expansion" to insert the scroll view.
As the view is animating in, drag the scroll content and hold it scrolled away from the top.
Wait for the animation to complete. The scroll view will reset the content offset, even though the drag gesture is still active (i.e. you haven't lifted your finger to release the scroll view)
On a real device, this sometimes even leads to an even worse visual artifact where the scroll view is rendered twice for a few frames; once with the correct offset, and once with the reset offset.
I wanted to include a link to a gif/video showing the glitch, but it tells me that imgur is not allowed on the forums.
Expected Behavior
I want the scroll view to respect the content offset, even if I started changing it mid-animation.
Xcode Version
I am using Xcode 16.4 (16F6) but this problem has been occurring since the .geometryGroup() modifier has been release. I was only now able to pinpoint this problem exactly, so I'm filing this feedback.
Code
The entire code that reproduces the problem:
import SwiftUI
struct ContentView: View {
@State private var isExpanded: Bool = false
var body: some View {
VStack {
if isExpanded {
ScrollView {
Text(loremIpsum)
}
}
Button("Toggle Expansion") {
isExpanded.toggle()
}
}
// .padding(10) // Adding a non-zero padding makes the glitch disappear
.frame(maxWidth: .infinity)
.geometryGroup()
.animation(.default, value: isExpanded)
}
}
#Preview {
ContentView().preferredColorScheme(.dark)
}
// MARK: - Mock Data
let loremIpsum = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt \
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco \
laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt \
mollit anim id est laborum.
"""
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hey,
I've been having a lot of problems with Xcode 16 not seeing changes made to code in local Swift packages (the packages are inside the root directory of the project).
Whenever I make any change like renaming a variable or type, or adding new methods or whatever, autocomplete doesn't see those changes and when I type in that new type/variable manually it gives me an error. However, building the project still works fine, even with the errors never going away.
The only way for it to notice the code changes is to clean the project and build it again which takes a long time.
At first, I thought this was connected to the new "Explicitly built modules" feature in Xcode 16, but I turned it off and it still happens.
Any ideas what I can try? I'm on the latest Xcode version, but this problem has been happening since Xcode 16 originally came out.
Thanks!
Dennis
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
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!