Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

Posts under SwiftUI tag

200 Posts

Post

Replies

Boosts

Views

Activity

Scrolling through long lists with ScrollView and LazyVstack
What is the correct way to implement scrolling in a looong list that uses ScrollView and LazyVstack Imagine I have some api that returns a longs list of comments with replies The basic usecase is to scroll to the bottom(to the last comment) Most of the time this works fine But, imagine some of the comments have many replies like 35 or more (or even 300) User expands replies for the first post, then presses scroll to bottom. The scrollbar reaches the bottom and I see the blank screen. Sometimes the scrollbar may jump for a while before lazyvstack finishes loading or until I manually scroll up a bit or all the way up and down What should I do in this case? Is this the swiftui performance problem that has no cure? Abstract example: ScrollViewReader { proxy in ScrollView { LazyVStack { ForEach(comments) { comment in CommentView(comment: comment) .id("comment-\(comment.id)") } } } } struct CommentView: View { let comment: Comment @State var isExpanded = false var body: some View { VStack { Text(comment.text) if isExpanded { RepliesView(replies: comment.replies) // 35-300+ replies } } } } ... scroll proxy.scrollTo("comment-\(lastComment.id)", anchor: .bottom)
0
0
16
2h
SwiftUI Slider onEditingChanged is unreliable on iOS 26
For information I stumbled upon a regression with SwiftUI Slider on iOS 26. Its onEditingChanged closure might be called twice when interaction ends, with a final Boolean incorrect value of true provided to the closure. As a result apps cannot reliably rely on this closure to detect when an interaction with the slider starts or ends. I filed a feedback under FB20283439 (iOS 26.0 regression: Slider onEditingChanged closure is unreliable).
6
5
291
2h
SwiftUI WebView Error
I'm using SwiftUI WebView and this error happens when app becomes inactive, the webview changes to blank, and will be in this state all along even if reopen a new webview. When I switch back to WKWebview, everything works fine. environment Xcode 26.1(17B55) on macOS 15.7.1 Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "((target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.rendering AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.networking AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.webcontent))" UserInfo={NSLocalizedFailureReason=((target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.rendering AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.networking AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.webcontent))}> this is the code, pretty simple, in load() function i just call page.load(). WebView(vm.page) .onAppear { Task { await vm.load() } }
0
0
16
3h
Navigation bar fade breaks when using .ignoresSafeArea() on inverted ScrollViews in iOS 26
I’m seeing a strange visual bug in iOS 26 when building chat-style UIs that use an inverted ScrollView or List (via .rotationEffect(.radians(.pi)) and .scaleEffect(x: -1, y: 1)) to anchor messages at the bottom. When I add .ignoresSafeArea() to let the chat bleed behind the navigation bar - the new navigation bar fade (that subtle top-to-bottom gradient Apple added in iOS 26) behaves incorrectly. Instead of fading from the top of the screen toward the nav bar, it fades upward from the bottom of the view, effectively covering the entire screen with the gradient. This only happens when the view is inverted. If I remove .ignoresSafeArea(), the fade looks correct — but then my chat no longer extends behind the nav bar. It looks like the fade effect is being applied in the transformed coordinate space of the inverted scroll view rather than in visual screen space. I haven’t found a reliable workaround besides disabling the fade (which isn’t really possible). Has anyone found a proper solution or a modifier that prevents the fade inversion when using flipped ScrollViews? Would love to know if Apple is aware of this or if there’s a hidden API for disabling that fade effect. I have made a report about this in Feedback Assistant: FB20540755
1
0
122
6h
UIViewRepresentable Coordinator @Binding returns stale value when accessed via context.coordinator but fresh value when accessed via self
I'm encountering unexpected behavior with @Binding in a UIViewRepresentable's Coordinator. The same Coordinator instance returns different values depending on how the property is accessed. Environment: iOS 17+ / Xcode 15+ SwiftUI with UIViewRepresentable Issue: When I access @Binding var test inside the Coordinator: ✅ Via self.test in Coordinator methods: Returns the updated value ❌ Via context.coordinator.test in updateUIView: Returns the stale/initial value Both access the same Coordinator instance (verified by memory address), yet return different values. Minimal Reproducible Example: struct ContentView: View { @State private var test: [Int] = [1, 2, 3, 4, 5] var body: some View { VStack { TestRepresentable(test: $test) Text("State: \(test.description)") } } } struct TestRepresentable: UIViewRepresentable { @Binding var test: [Int] func makeUIView(context: Context) -> UIButton { let button = UIButton(type: .system) button.setTitle("Toggle", for: .normal) button.addTarget( context.coordinator, action: #selector(Coordinator.buttonTapped), for: .touchUpInside ) return button } func updateUIView(_ uiView: UIButton, context: Context) { // Log coordinator instance address let coordAddr = String(describing: Unmanaged.passUnretained(context.coordinator).toOpaque()) print("[updateUIView] Coordinator address: \(coordAddr)") // Log values print("[updateUIView] self.test: \(self.test)") print("[updateUIView] context.coordinator.test: \(context.coordinator.test)") // These should be the same but they're not! // self.test shows updated value // context.coordinator.test shows stale value } func makeCoordinator() -> Coordinator { Coordinator(test: $test) } class Coordinator: NSObject { @Binding var test: [Int] var idx: Int = 0 init(test: Binding<[Int]>) { _test = test } @objc func buttonTapped() { idx += 1 if idx < test.count { test[idx] += 5 } // Log coordinator instance address let selfAddr = String(describing: Unmanaged.passUnretained(self).toOpaque()) print("[buttonTapped] Coordinator address: \(selfAddr)") // Log value - this shows the UPDATED value print("[buttonTapped] self.test: \(test)") } } } Actual Output: [Initial] [updateUIView] Coordinator address: 0x600001234567 [updateUIView] self.test: [1, 2, 3, 4, 5] [updateUIView] context.coordinator.test: [1, 2, 3, 4, 5] [After first tap] [buttonTapped] Coordinator address: 0x600001234567 [buttonTapped] self.test: [1, 7, 3, 4, 5] ✅ Updated! [updateUIView] Coordinator address: 0x600001234567 ← Same instance [updateUIView] self.test: [1, 7, 3, 4, 5] ✅ Updated! [updateUIView] context.coordinator.test: [1, 2, 3, 4, 5] ❌ Stale! [After second tap] [buttonTapped] Coordinator address: 0x600001234567 [buttonTapped] self.test: [1, 7, 8, 4, 5] ✅ Updated! [updateUIView] Coordinator address: 0x600001234567 ← Same instance [updateUIView] self.test: [1, 7, 8, 4, 5] ✅ Updated! [updateUIView] context.coordinator.test: [1, 2, 3, 4, 5] ❌ Still stale! Questions: Why does context.coordinator.test return a stale value when it's the same Coordinator instance? Is this intended behavior or a bug? What's the correct pattern to access Coordinator's @Binding properties in updateUIView? Workaround Found: Using self.test instead of context.coordinator.test in updateUIView works, but I'd like to understand why accessing the same property through context yields different results. Related: I've seen suggestions to update coordinator.parent = self in updateUIView, but this doesn't explain why the same object's property returns different values. Binding documentation states it's "a pair of get and set closures," but there's no explanation of how Context affects closure behavior. Any insights would be greatly appreciated! P.S - https://stackoverflow.com/questions/69552418/why-does-a-binding-in-uiviewrepresentables-coordinator-have-a-constant-read-valu This is the link that I found out online with same problem that I have but not answered well.
0
0
12
7h
Slow rendering List backed by SwiftData @Query
Hello, I've a question about performance when trying to render lots of items coming from SwiftData via a @Query on a SwiftUI List. Here's my setup: // Item.swift: @Model final class Item: Identifiable { var timestamp: Date var isOptionA: Bool init() { self.timestamp = Date() self.isOptionA = Bool.random() } } // Menu.swift enum Menu: String, CaseIterable, Hashable, Identifiable { var id: String { rawValue } case optionA case optionB case all var predicate: Predicate<Item> { switch self { case .optionA: return #Predicate { $0.isOptionA } case .optionB: return #Predicate { !$0.isOptionA } case .all: return #Predicate { _ in true } } } } // SlowData.swift @main struct SlowDataApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([Item.self]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) return try! ModelContainer(for: schema, configurations: [modelConfiguration]) }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } // ContentView.swift struct ContentView: View { @Environment(\.modelContext) private var modelContext @State var selection: Menu? = .optionA var body: some View { NavigationSplitView { List(Menu.allCases, selection: $selection) { menu in Text(menu.rawValue).tag(menu) } } detail: { DemoListView(selectedMenu: $selection) }.onAppear { // Do this just once // (0..<15_000).forEach { index in // let item = Item() // modelContext.insert(item) // } } } } // DemoListView.swift struct DemoListView: View { @Binding var selectedMenu: Menu? @Query private var items: [Item] init(selectedMenu: Binding<Menu?>) { self._selectedMenu = selectedMenu self._items = Query(filter: selectedMenu.wrappedValue?.predicate, sort: \.timestamp) } var body: some View { // Option 1: touching `items` = slow! List(items) { item in Text(item.timestamp.description) } // Option 2: Not touching `items` = fast! // List { // Text("Not accessing `items` here") // } .navigationTitle(selectedMenu?.rawValue ?? "N/A") } } When I use Option 1 on DemoListView, there's a noticeable delay on the navigation. If I use Option 2, there's none. This happens both on Debug builds and Release builds, just FYI because on Xcode 16 Debug builds seem to be slower than expected: https://indieweb.social/@curtclifton/113273571392595819 I've profiled it and the SwiftData fetches seem blazing fast, the Hang occurs when accessing the items property from the List. Is there anything I'm overlooking or it's just as fast as it can be right now?
4
4
1.1k
8h
Tap to Pay on iPhone to from Colombia
Colombia is not yet listed as a Tap to Pay user, but it is in the process of becoming so. We are currently a group of developers at a company in Colombia working on a project to integrate Tap to Pay into our application. After reviewing Apple's documentation, my company is not certified to meet Apple's security requirements, PCI standards, or licensing requirements. However, the payment service provider we have contracted for this is in the process of obtaining the certifications, authorizations, and licenses that Apple specifies. Our team members and managers overseeing this Tap to Pay project have told us that we, as iOS developers, should integrate and use the Proximity Reader API, but we know that we, as developers, are not authorized by Apple to do so. Is the payment service provider the only one who can make this possible, enabling its use with NFC and Proximity Reader? I would like to know if the service provider will provide us with the SDK containing the Proximity Reader API for integration into the project, or if my company will have to implement the Proximity Reader API ourselves?
0
0
15
20h
Keyframe animation crashes with +[_SwiftUILayerDelegate _screen]: unrecognized selector sent to class on iOS 26
We have an UIViewController called InfoPlayerViewController. Its main subview is from a child view controller backed by SwiftUI via UIHostingController. The InfoPlayerViewController conforms to UIViewControllerTransitioningDelegate. The animation controller for dismissing is DismissPlayerAnimationController. It runs UIKit keyframe animations via UIViewPropertyAnimator. When the keyframe animation is executed there’s an occasional crash for end users in production. It only happens on iOS 26. FB Radar: FB20871547 An example crash is below. Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Reason: +[_SwiftUILayerDelegate _screen]: unrecognized selector sent to class 0x20c95da08 Termination Reason: SIGNAL 6 Abort trap: 6 Triggered by Thread: 0 Last Exception Backtrace: 0 CoreFoundation 0x1a23828c8 __exceptionPreprocess + 164 (NSException.m:249) 1 libobjc.A.dylib 0x19f2f97c4 objc_exception_throw + 88 (objc-exception.mm:356) 2 CoreFoundation 0x1a241e6cc +[NSObject(NSObject) doesNotRecognizeSelector:] + 364 (NSObject.m:158) 3 CoreFoundation 0x1a22ff4f8 ___forwarding___ + 1472 (NSForwarding.m:3616) 4 CoreFoundation 0x1a23073a0 _CF_forwarding_prep_0 + 96 (:-1) 5 UIKitCore 0x1a948e880 __35-[UIViewKeyframeAnimationState pop]_block_invoke + 300 (UIView.m:2973) 6 CoreFoundation 0x1a22cb170 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__ + 24 (NSDictionaryHelpers.m:10) 7 CoreFoundation 0x1a245d7cc -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:] + 288 (NSDictionaryM.m:271) 8 UIKitCore 0x1a948e6bc -[UIViewKeyframeAnimationState pop] + 376 (UIView.m:2955) 9 UIKitCore 0x1a7bc40e8 +[UIViewAnimationState popAnimationState] + 60 (UIView.m:1250) 10 UIKitCore 0x1a94acc44 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 684 (UIView.m:17669) 11 UIKitCore 0x1a94ae334 +[UIView(UIViewKeyframeAnimations) animateKeyframesWithDuration:delay:options:animations:completion:] + 224 (UIView.m:17945) 12 MyApp 0x102c78dec static UIView.animateNestedKeyframe(withRelativeStartTime:relativeDuration:animations:) + 208 (UIView+AnimateNestedKeyframe.swift:10) 13 MyApp 0x102aef3c0 closure #1 in DismissPlayerAnimationController.slideDownBelowTabBarTransitionAnimator(using:) + 156 (DismissPlayerAnimationController.swift:229) 14 MyApp 0x102a2d3d4 <deduplicated_symbol> + 28 15 UIKitCore 0x1a7d5ae5c -[UIViewPropertyAnimator _runAnimations] + 172 (UIViewPropertyAnimator.m:2123) 16 UIKitCore 0x1a83e1594 __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_3 + 92 (UIViewPropertyAnimator.m:3557) 17 UIKitCore 0x1a83e1464 __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke + 96 (UIViewPropertyAnimator.m:3547) 18 UIKitCore 0x1a83e1518 __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_2 + 144 (UIViewPropertyAnimator.m:3553) 19 UIKitCore 0x1a83e0e64 -[UIViewPropertyAnimator _setupAnimationTracking:] + 100 (UIViewPropertyAnimator.m:3510) 20 UIKitCore 0x1a83e1264 -[UIViewPropertyAnimator startAnimationAsPaused:] + 728 (UIViewPropertyAnimator.m:3610) 21 UIKitCore 0x1a83de42c -[UIViewPropertyAnimator pauseAnimation] + 68 (UIViewPropertyAnimator.m:2753) 22 UIKitCore 0x1a87d5328 -[UIPercentDrivenInteractiveTransition _startInterruptibleTransition:] + 244 (UIViewControllerTransitioning.m:982) 23 UIKitCore 0x1a87d5514 -[UIPercentDrivenInteractiveTransition startInteractiveTransition:] + 184 (UIViewControllerTransitioning.m:1012) 24 UIKitCore 0x1a7c7931c ___UIViewControllerTransitioningRunCustomTransitionWithRequest_block_invoke_3 + 152 (UIViewControllerTransitioning.m:1579) 25 UIKitCore 0x1a892aefc +[UIKeyboardSceneDelegate _pinInputViewsForKeyboardSceneDelegate:onBehalfOfResponder:duringBlock:] + 96 (UIKeyboardSceneDelegate.m:3518) 26 UIKitCore 0x1a7c79238 ___UIViewControllerTransitioningRunCustomTransitionWithRequest_block_invoke_2 + 236 (UIViewControllerTransitioning.m:1571) 27 UIKitCore 0x1a94ab4b8 +[UIView(Animation) _setAlongsideAnimations:toRunByEndOfBlock:animated:] + 188 (UIView.m:17089) 28 UIKitCore 0x1a7c79070 _UIViewControllerTransitioningRunCustomTransitionWithRequest + 556 (UIViewControllerTransitioning.m:1560) 29 UIKitCore 0x1a86cb7cc __77-[UIPresentationController runTransitionForCurrentStateAnimated:handoffData:]_block_invoke_3 + 1784 (UIPresentationController.m:1504) 30 UIKitCore 0x1a7c43888 -[_UIAfterCACommitBlock run] + 72 (_UIAfterCACommitQueue.m:137) 31 UIKitCore 0x1a7c437c0 -[_UIAfterCACommitQueue flush] + 168 (_UIAfterCACommitQueue.m:228) 32 UIKitCore 0x1a7c436d0 _runAfterCACommitDeferredBlocks + 260 (UIApplication.m:3297) 33 UIKitCore 0x1a7c43c34 _cleanUpAfterCAFlushAndRunDeferredBlocks + 80 (UIApplication.m:3275) 34 UIKitCore 0x1a7c1f104 _UIApplicationFlushCATransaction + 72 (UIApplication.m:3338) 35 UIKitCore 0x1a7c1f024 __setupUpdateSequence_block_invoke_2 + 352 (_UIUpdateScheduler.m:1634) 36 UIKitCore 0x1a7c2cee8 _UIUpdateSequenceRunNext + 128 (_UIUpdateSequence.mm:189) 37 UIKitCore 0x1a7c2c378 schedulerStepScheduledMainSectionContinue + 60 (_UIUpdateScheduler.m:1185) 38 UpdateCycle 0x28c58f5f8 UC::DriverCore::continueProcessing() + 84 (UCDriver.cc:288) 39 CoreFoundation 0x1a2323230 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:2021) 40 CoreFoundation 0x1a23231a4 __CFRunLoopDoSource0 + 172 (CFRunLoop.c:2065) 41 CoreFoundation 0x1a2300c6c __CFRunLoopDoSources0 + 232 (CFRunLoop.c:2102) 42 CoreFoundation 0x1a22d68b0 __CFRunLoopRun + 820 (CFRunLoop.c:2983) 43 CoreFoundation 0x1a22d5c44 _CFRunLoopRunSpecificWithOptions + 532 (CFRunLoop.c:3462) 44 GraphicsServices 0x2416a2498 GSEventRunModal + 120 (GSEvent.c:2049) 45 UIKitCore 0x1a7c50ddc -[UIApplication _run] + 792 (UIApplication.m:3899) 46 UIKitCore 0x1a7bf5b0c UIApplicationMain + 336 (UIApplication.m:5574) // ...
2
1
140
1d
TipKit popoverTip repeatedly showing when switching tabs on iOS 26 (regression from previous versions)
Hi everyone, I’m currently experimenting an issue with TipKit’s popoverTip in combination with a TabView. On iOS 26, the popover tip keeps showing every time I switch tabs, even though it should only appear once. The same code behaves as expected on older iOS versions (for example, iOS 17 or 18), where the tip is displayed only once, as documented. Here’s a simplified example reproducing the issue: import TipKit struct ContentView: View { init() { try? Tips.configure() } var body: some View { TabView { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) .popoverTip(HomeTip(), arrowEdge: .bottom) Text("Hello, world!") } .tabItem { Image(systemName: "house") Text("Home") } .tag(0) NavigationStack { List { Label("Reset Data store", systemImage: "gear") .onTapGesture { try? Tips.resetDatastore() } } .navigationTitle("Explore") } .tabItem { Image(systemName: "sparkles") Text("Settings") } .tag(1) } } private struct HomeTip: Tip { let id = "HomeTip" let title = Text("Test Tool Tip") } } Expected behavior: The tip appears once and does not reappear when switching between tabs. Observed behavior on iOS 26: The tip keeps reappearing every time the user switches back to the tab.
2
0
161
1d
tabViewBottomAccessory in 26.1: View's @State is lost when switching tabs
Any view that is content for the tabViewBottomAccessory API fails to retain its state as of the last couple of 26.1 betas (and RC). The loss of state happens (at least) when the currently selected tab is switched (filed as FB20901325). Here's code to reproduce the issue: struct ContentView: View { @State private var selectedTab = TabSelection.one enum TabSelection: Hashable { case one, two } var body: some View { TabView(selection: $selectedTab) { Tab("One", systemImage: "1.circle", value: .one) { BugExplanationView() } Tab("Two", systemImage: "2.circle", value: .two) { BugExplanationView() } } .tabViewBottomAccessory { AccessoryView() } } } struct AccessoryView: View { @State private var counter = 0 // This guy's state gets lost (as of iOS 26.1) var body: some View { Stepper("Counter: \(counter)", value: $counter) .padding(.horizontal) } } struct BugExplanationView: View { var body: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { Text("(1) Manipulate the counter state") Text("(2) Then switch tabs") Text("BUG: The counter state gets unexpectedly reset!") } .multilineTextAlignment(.leading) } } }
0
0
125
1d
SwiftData Migration: Objects Created in Custom Migration Aren't Persisted or Queryable (Repost)
I'm experiencing a critical issue with SwiftData custom migrations where objects created during migration appear to be inserted successfully but aren't persisted or found by queries after migration completes. The migration logs show objects being created, but subsequent queries return zero results. I'm migrating from schema version V2 to V2_5, which involves: Renaming Person class to GroupData Keeping the same data structure but changing the class name while keeping the old class. Using a custom migration stage to copy data from old to new schema Below is an extract of my two schema and migration plan: Environment: Xcode 16.0, iOS 18.0, Swift 6.0 SchemaV2 enum LinkMapV2: VersionedSchema { static let versionIdentifier: Schema.Version = .init(2, 0, 0) static var models: [any PersistentModel.Type] { [AnnotationData.self, Person.self, History.self] } @Model final class Person { @Attribute(.unique) var id: UUID var name: String var photo: String var requirement: String var statue: Bool var annotationId: UUID? var number: Int = 0 init(id: UUID = UUID(), name: String = "", photo: String = "", requirement: String = "", status: Bool = false, annotationId: UUID? = nil, number: Int = 0) { self.id = id self.name = name self.photo = photo self.requirement = requirement self.statue = status self.annotationId = annotationId self.number = number } } } Schema V2_5 static let versionIdentifier: Schema.Version = .init(2, 5, 0) static var models: [any PersistentModel.Type] { [AnnotationData.self, Person.self, GroupData.self, History.self] } // Keep the old Person model for migration @Model final class Person { @Attribute(.unique) var id: UUID var name: String var photo: String var requirement: String var statue: Bool var annotationId: UUID? var number: Int = 0 init(id: UUID = UUID(), name: String = "", photo: String = "", requirement: String = "", status: Bool = false, annotationId: UUID? = nil, number: Int = 0) { self.id = id self.name = name self.photo = photo self.requirement = requirement self.statue = status self.annotationId = annotationId self.number = number } } // Add the new GroupData model that mirrors Person @Model final class GroupData { @Attribute(.unique) var id: UUID var name: String var photo: String var requirement: String var status: Bool var annotationId: UUID? var number: Int = 0 init(id: UUID = UUID(), name: String = "", photo: String = "", requirement: String = "", status: Bool = false, annotationId: UUID? = nil, number: Int = 0) { self.id = id self.name = name self.photo = photo self.requirement = requirement self.status = status self.annotationId = annotationId self.number = number } } } Migration Plan static let migrationV2toV2_5 = MigrationStage.custom( fromVersion: LinkMapV2.self, toVersion: LinkMapV2_5.self, willMigrate: { context in do { let persons = try context.fetch(FetchDescriptor<LinkMapV2.Person>()) print("=== MIGRATION STARTED ===") print("Found \(persons.count) Person objects to migrate") guard !persons.isEmpty else { print("No Person data requires migration") return } for person in persons { print("Migrating Person: '\(person.name)' with ID: \(person.id)") let newGroup = LinkMapV2_5.GroupData( id: person.id, // Keep the same ID name: person.name, photo: person.photo, requirement: person.requirement, status: person.statue, annotationId: person.annotationId, number: person.number ) context.insert(newGroup) print("Inserted new GroupData: '\(newGroup.name)'") // Don't delete the old Person yet to avoid issues // context.delete(person) } try context.save() print("=== MIGRATION COMPLETED ===") print("Successfully migrated \(persons.count) Person objects to GroupData") } catch { print("=== MIGRATION ERROR ===") print("Migration failed with error: \(error)") } }, didMigrate: { context in do { // Verify migration in didMigrate phase let groups = try context.fetch(FetchDescriptor<LinkMapV2_5.GroupData>()) let oldPersons = try context.fetch(FetchDescriptor<LinkMapV2_5.Person>()) print("=== MIGRATION VERIFICATION ===") print("New GroupData count: \(groups.count)") print("Remaining Person count: \(oldPersons.count)") // Now delete the old Person objects for person in oldPersons { context.delete(person) } if !oldPersons.isEmpty { try context.save() print("Cleaned up \(oldPersons.count) old Person objects") } // Print all migrated groups for debugging for group in groups { print("Migrated Group: '\(group.name)', Status: \(group.status), Number: \(group.number)") } } catch { print("Migration verification error: \(error)") } } ) And I've attached console output below: Console Output
1
0
56
2d
App crashed when switching between Annotation Tab and Group Tab with TabView init(selection:content:)
This app will not crash when switching between these two tabs with TabView init(content:) import SwiftUI import SwiftData struct ContentView: View { @StateObject private var highlightManager = HighlightManager.shared @State private var selectedTab: Int = 0 var body: some View { TabView(selection: $selectedTab) { MapView() .tabItem { Label("Map", systemImage: "map") } .tag(0) // Annotation Tab AnnotationList() .tabItem { Label("Annotation", systemImage: "mappin.and.ellipse") } .tag(1) // Group Tab PeopleList() .tabItem { Label("Group", systemImage: "person.and.person") } .tag(2) } .tutorialOverlay() // Apply the overlay to the root view .environmentObject(highlightManager) .toolbar { ToolbarItem(placement: .confirmationAction) { NavigationLink("Help") { NavigationStack { HelpView(selectedTab: selectedTab) } } } } } }
2
0
106
2d
How to have clickable/tappable buttons where the toolbar is supposed to be?
I'm trying to create a UI with two button bars (top and bottom) inside the detail view of a NavigationSplitView, instead of using the built-in .toolbar() modifier. I'm using .ignoresSafeArea(.container, edges: .vertical) so the detail view can reach into that area. However, in macOS and iOS 26 the top button is not clickable/tappable because it is behind an invisible View created by the non-existent toolbar. Interestingly enough, if I apply .buttonStyle(.borderless) to the top button it becomes clickable (in macOS). On the iPad the behavior is different depending on the iPad OS version. In iOS 26, the button is tappable only by the bottom half. In iOS 18 the button is always tappable. Here's the code for the screenshot: import SwiftUI struct ContentView2: View { @State private var sidebarSelection: String? @State private var contentSelection: String? @State private var showContentColumn = true @State private var showBars = true var body: some View { NavigationSplitView { // Sidebar List(selection: $sidebarSelection) { Text("Show Content Column").tag("three") Text("Hide Content Column").tag("two") } .navigationTitle("Sidebar") } detail: { VStack(spacing: 0) { if showBars { HStack { Button("Click Me") { withAnimation { showBars.toggle() } } .buttonStyle(.borderedProminent) } .frame(maxWidth: .infinity, minHeight: 50, idealHeight: 50, maxHeight: 50) .background(.gray) .transition(.move(edge: .top)) } ZStack { Text("Detail View") } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .init(horizontal: .center, vertical: .center)) .border(.red) .onTapGesture(count: 2) { withAnimation { showBars.toggle() } } if showBars { HStack { Button("Click Me") { withAnimation { showBars.toggle() } } } .frame(maxWidth: .infinity, minHeight: 50, idealHeight: 50, maxHeight: 50) .background(.gray) .transition(.move(edge: .bottom)) } } .ignoresSafeArea(.container, edges: .vertical) .toolbarVisibility(.hidden) } .toolbarVisibility(.visible) } } I'm confused by this very inconsistent behavior and I haven't been able to find a way to get this UI to work across both platforms. Does anybody know how to remove the transparent toolbar that is preventing clicks/taps in this top section of the view? I'm hoping there's an idiomatic, native SwiftUI way to do it.
3
0
236
2d
SwiftData & CloudKit: Arrays of Codable Structs Causing NSKeyedUnarchiveFromData Error
I have SwiftData models containing arrays of Codable structs that worked fine before adding CloudKit capability. I believe they are the reason I started seeing errors after enabling CloudKit. Example model: @Model final class ProtocolMedication { var times: [SchedulingTime] = [] // SchedulingTime is Codable // other properties... } After enabling CloudKit, I get this error logged to the console: 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release CloudKit Console shows this times data as "plain text" instead of "bplist" format. Other struct/enum properties display correctly (I think) as "bplist" in CloudKit Console. The local SwiftData storage handled these arrays fine - this issue only appeared with CloudKit integration. What's the recommended approach for storing arrays of Codable structs in SwiftData models that sync with CloudKit?
6
1
306
2d
Can SKOverlay be used to prompt updates for the same app?
According to Apple's documentation, SKOverlay is designed to recommend other applications to users. I'm seeking clarification on whether it also supports displaying update prompts for the host application itself. Use case: My app (for example, HelloDeveloper) is live at version 2.0, but some users are still on version 1.0. I want to display a soft update prompt that allows users to remain in the app. Question: Is it possible to use SKOverlay with my app's App Store ID to present an update option without requiring users to leave the app?
1
0
113
4d
SwiftUI state is maddening
I honestly thought I was getting somewhere with this, but alas, no. Every time I do anything in my List of ItemRows it jumps back to the top. Here's the setup: DataService.swift: final class DataService { static let shared = DataService() private init() {} let coreData: CoreData = CoreData() let modelData: ModelData = ModelData() } ModelData.swift: @Observable class ModelData: ObservableObject { var allItems: [ItemDetails] var standardItems: [ItemDetails] var archivedItems: [ItemDetails] init() { allItems = [] standardItems = [] archivedItems = [] } func getInitialData() { // Get all items, then split them into archived and non-archived sets, because you can't use `.filter` in a view... allItems = dataService.coreData.getAllItems() standardItems.append(contentsOf: allItems.filter { !$0.archived }) archivedItems.append(contentsOf: allItems.filter { $0.archived }) } } MainApp.swift: // Get access to the data; this singleton is a global as non-view-based functions, including the `Scene`, need to access the model data let dataService: DataService = DataService.shared @main struct MainApp: App { // Should this be @ObservedObject or @StateObject? @ObservedObject private var modelData: ModelData = dataService.modelData // I would use @StateObject if the line was... //@StateObject private var modelData: ModelData = ModelData() // right? // But then I couldn't use modelData outside of the view hierarchy var body: some Scene { WindowGroup { ZStack { MainView() .environment(modelData) } } .onAppear { modelData.getInitialData() } } } MainView.swift: struct MainView: View { @Environment(ModelData.self) private var modelData: ModelData var body: some View { ... ForEach(modelData.standardItems) { item in ItemRow(item) } ForEach(modelData.archivedItems) { item in ItemRow(item) } } } ItemRow.swift: struct ItemRow: View { @Environment(\.accessibilityDifferentiateWithoutColor) private var accessibilityDifferentiateWithoutColor var item: ItemDetails @State private var showDeleteConfirmation: Bool = false var body: some View { // Construct the row view // `accessibilityDifferentiateWithoutColor` is used within the row to change colours if DWC is enabled, e.g. use different symbols instead of different colours for button images. // Add the .leftSwipeButtons, .rightSwipeButtons, and .contextMenu // Add the .confirmationDialog for when I want to ask for confirmation before deleting an item } } Now, the problems: Swipe an item row, tap one of the buttons, e.g. edit, and the list refreshes and jumps back to the top. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor changed. Why did accessibilityDifferentiateWithoutColor change? The setting in Settings > Accessibility > Display & Text Size has not been changed, so why does the row's view think it changed? With a .confirmationDialog attached to the end of the ItemRow (as seen in the code above), if I swipe and tap the delete button the list refreshes and jumps back to the top again. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor, _showDeleteConfirmation changed. Right, it changed for the one row that I tapped the button for. Why does every row get redrawn? I already had to shift from using the colorScheme environment variable to add new asset colours with light and dark variants to cover this, but you can't do that with DWC. Honestly, managing state in SwiftUI is a nightmare. I had zero problems until iOS 26 started removing one or two rows when I scrolled, and the fix for that - using @Statebject/@ObservedObject - has introduced multiple further annoying, mind-bending problems, and necessitated massive daily refactorings. And, of course, plenty of my time islost trying to figure out where a problem is in the code because "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions"...
7
0
202
4d
Drag and Drop Question
This example is based on the latest version of Swift 6.2 for macOS. I have several classes that cannot conform to Codable for various reasons. This, unfortunately, prevents me from using Transferable. If I serialize a class instance into a Data blob and use that for drag-and-drop, it works perfectly — the drag operation succeeds. However, the destination has no way to distinguish between different types of Data blobs. All I’d need is a way to specify a unique identifier and type that I could reference in the drop handler to determine what kind of object I’m working with. Being restricted to Transferable feels quite limiting when your data models can’t conform to Codable. It’s honestly frustrating. Has anyone else run into this issue? Is there a reliable workaround? I tried creating a Transferable wrapper like this: struct CustomObjectTransfer: Codable, Transferable { var data: Data static var transferRepresentation: some TransferRepresentation { // Cannot use '.myGreatSettings' because Main actor–isolated static property 'myGreatSettings' cannot be referenced from a nonisolated context CodableRepresentation(contentType: .init(exportedAs: "com.yourProject.settings")) } } extension UTType { static let myGreatSettings: UTType = UTType("com.yourProject.settings")! } In my list view .draggable ( CustomObjectTransfer(data: myObjectData) ) The UI correctly recognizes the item as draggable. If I misspell the exportedAs identifier, it even throws an error, confirming that the exported type is being recognized by the system. However, the drop destination completely ignores this type: .dropDestination(for: CustomObjectTransfer.self) { items, location in dump(items) return true }isTargeted: { isTargeted in myDestinationIsTargeted = isTargeted } If I switch to using Data.self directly — wrapping the original object data manually — everything works. I can deserialize and validate the data as needed. The problem arises when handling multiple custom drag types. The drop target accepts any data, regardless of its intended type. You only find out the actual type after the drop occurs, during validation — which is too late. By then, isTargeted has already turned true, making the drop appear valid to the user when it actually isn’t. Again anyone else feel this pain? Or is there a workaround that I am missing?
4
0
132
4d