Hi,
In my app, I have an option to remove a contact from a contact group (using the Contacts framework), and it's been working fine till recently users of the macOS version reported that it's not working. I have been using the CNSaveRequest removeMember(contact, from: group) API. The same API works fine on iOS. I'm not sure when it started but it seems to be affecting macOS14.6 as well as 15.1.
I was able to reproduce it in a small test project as well, and have the same experience (the API works on iOS but not on macOS), so it definitely seems like a problem with the framework. Can someone confirm this, and/or suggest a workaround?
Here's the code I run to test it out ...a simple SwiftUI view that has 4 buttons:
Create contact and group
Add contact to group
Remove contact from group
(optional) cleanup by deleting contact and group
It's the 3rd step that seems to fail on macOS, but works fine on iOS.
Here's the code to test it out:
struct ContentView: View {
let contactsModel = ContactsStoreModel()
var body: some View {
VStack (alignment: .center, spacing: 15){
Button ("1. Add Contact And Group") {
print("add contact button pressed")
contactsModel.addTestContact()
if let _ = contactsModel.createdContact {
print("created contact success")
}
}
Button ("2. Add Contact To Group") {
print("add to group button pressed")
contactsModel.addContactToGroup()
}
Button ("3. Remove Contact From Group") {
print("remove from group button pressed")
contactsModel.removeContactFromGroup()
}
Button ("4. Delete Contact and Group") {
print("remove from group button pressed")
contactsModel.deleteContactAndGroup()
}
}
.padding()
}
}
#Preview {
ContentView()
}
@available(iOS 13.0, *)
@objc final class ContactsStoreModel: NSObject, ObservableObject {
let contactStore = CNContactStore()
var createdContact : CNContact?
var createdGroup : CNGroup?
public func addTestContact() {
let storeContainer = contactStore.defaultContainerIdentifier()
let contact = CNMutableContact()
contact.givenName = "Testing"
contact.familyName = "User"
contact.phoneNumbers = [CNLabeledValue(label: "Cell", value: CNPhoneNumber(stringValue: "1234567890"))]
let group = CNMutableGroup()
group.name = "Testing Group"
print("create contact id = \(contact.identifier)")
print("create group id = \(group.identifier)")
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.add(contact, toContainerWithIdentifier: storeContainer)
saveRequest.add(group, toContainerWithIdentifier: storeContainer)
try contactStore.execute(saveRequest)
createdContact = contact
createdGroup = group
} catch {
print("error in store execute = \(error)")
}
}
public func addContactToGroup() {
if let contact = createdContact, let group = createdGroup {
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.addMember(contact, to: group)
try contactStore.execute(saveRequest)
}
catch {
print("error in store execute = \(error)")
}
}
}
public func removeContactFromGroup() {
if let contact = createdContact, let group = createdGroup {
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.removeMember(contact, from: group)
try contactStore.execute(saveRequest)
}
catch {
print("error in store execute = \(error)")
}
}
}
public func addGroupAndContact() {
let storeContainer = contactStore.defaultContainerIdentifier()
let group = CNMutableGroup()
group.name = "Test Group"
print("create group id = \(group.identifier)")
if let contact = createdContact {
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.add(group, toContainerWithIdentifier: storeContainer)
saveRequest.addMember(contact, to: group)
try contactStore.execute(saveRequest)
createdGroup = group
} catch {
print("error in store execute = \(error)")
}
}
}
public func deleteContactAndGroup() {
if let contact = createdContact, let group = createdGroup {
do {
let mutableGroup = group.mutableCopy() as! CNMutableGroup
let mutableContact = contact.mutableCopy() as! CNMutableContact
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.delete(mutableContact)
saveRequest.delete(mutableGroup)
try contactStore.execute(saveRequest)
}
catch {
print("error in deleting store execute = \(error)")
}
}
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I have a SwiftUI List (on macOS) where I want to display a few TextFields in each row, and observe how the focus changes as an individual text field is selected or unselected. This seems to work fine if I just have the List display a pure TextField in each row, but if I embed the TextField inside a Button view, the focus change notifications stop working. I want the 'button' functionality so that the user can tap anywhere on the row and have the text field activated, instead of tapping exactly on the text field boundary.
Here is some code to demonstrate the problem. In the List if you have RowView uncommented out (1), then the focus change notifications work. If you comment that out and uncomment the RowViewWithButton (2), then the button functionality works, but I can't get focus change notifications anymore.
Here is the code to reproduce the issue. Interestingly, when I test this on iOS, it works fine in both cases. But I need the solution for macOS.
import SwiftUI
// test on macOS target
struct ContentView: View {
@State private var textFields = Array(repeating: "", count: 4)
@FocusState private var focusedField: Int?
var body: some View {
List(0..<4, id: \.self) { index in
// 1. works with focus notification changes
RowView(index: index, text: $textFields[index], focusedField: $focusedField)
// 2. button works, but no focus notifications on text field
//RowViewWithButton(index: index, text: $textFields[index], focusedField: $focusedField)
}
}
}
struct RowView: View {
let index: Int
@Binding var text: String
@FocusState.Binding var focusedField: Int?
var body: some View {
HStack {
Text("Row \(index + 1):")
TextField("", text: $text)
.multilineTextAlignment(.leading)
.focused($focusedField, equals: index)
}
.onChange(of: focusedField) { newFocus in
if newFocus == index {
print("TextField \(index) is in focus")
} else {
print("TextField \(index) lost focus")
}
}
.padding(.vertical, 4)
}
}
struct RowViewWithButton: View {
let index: Int
@Binding var text: String
@FocusState.Binding var focusedField: Int?
var body: some View {
Button (action: {
print("RowView - button selected at index \(index)")
focusedField = index
}) {
HStack {
Text("Row \(index + 1):")
TextField("", text: $text)
.multilineTextAlignment(.leading)
.focused($focusedField, equals: index)
}
.onChange(of: focusedField) { newFocus in
if newFocus == index {
print("TextField \(index) is in focus")
} else {
print("TextField \(index) lost focus")
}
}
.foregroundColor(.primary)
}
.buttonStyle(BorderlessButtonStyle())
.padding(.vertical, 4)
}
}
#Preview {
ContentView()
.frame(width: 320, height: 250)
}
I am trying out the new AttributedString binding with SwiftUI’s TextEditor in iOS26. I need to save this to a Core Data database. Core Data has no AttributedString type, so I set the type of the field to “Transformable”, give it a custom class of NSAttributedString, and set the transformer to NSSecureUnarchiveFromData
When I try to save, I first convert the Swift AttributedString to NSAttributedString, and then save the context. Unfortunately I get this error when saving the context, and the save isn't persisted:
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x600003721140> , <shared NSSecureUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
Here's the code that tries to save the attributed string:
struct AttributedDetailView: View {
@ObservedObject var item: Item
@State private var notesText = AttributedString()
var body: some View {
VStack {
TextEditor(text: $notesText)
.padding()
.onChange(of: notesText) {
item.attributedString = NSAttributedString(notesText)
}
}
.onAppear {
if let nsattributed = item.attributedString {
notesText = AttributedString(nsattributed)
} else {
notesText = ""
}
}
.task {
item.attributedString = NSAttributedString(notesText)
do {
try item.managedObjectContext?.save()
} catch {
print("core data save error = \(error)")
}
}
}
}
This is the attribute setup in the Core Data model editor:
Is there a workaround for this?
I filed FB17943846 if someone can take a look.
Thanks.
I am trying out the new AttributedString binding with SwiftUI’s TextEditor in iOS26. I need to save this to a Core Data database. Core Data has no AttributedString type, so I set the type of the field to “Transformable”, give it a custom class of NSAttributedString, and set the transformer to NSSecureUnarchiveFromData
When I try to save, I first convert the Swift AttributedString to NSAttributedString, and then save the context. Unfortunately I get this error when saving the context, and the save isn't persisted:
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x600003721140> , <shared NSSecureUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
Here's the code that tries to save the attributed string:
struct AttributedDetailView: View {
@ObservedObject var item: Item
@State private var notesText = AttributedString()
var body: some View {
VStack {
TextEditor(text: $notesText)
.padding()
.onChange(of: notesText) {
item.attributedString = NSAttributedString(notesText)
}
}
.onAppear {
if let nsattributed = item.attributedString {
notesText = AttributedString(nsattributed)
} else {
notesText = ""
}
}
.task {
item.attributedString = NSAttributedString(notesText)
do {
try item.managedObjectContext?.save()
} catch {
print("core data save error = \(error)")
}
}
}
}
I'm trying to use the new #Playground feature in Xcode, but it keeps crashing in the canvas. I am testing on Xcode 26.0 beta 4, on both macOS 15 and macOS26 beta 4. How do I figure out what's going on?
I also have a few SwiftUI previews in my project, which also crashes if I try to preview them. They're not in any active tab right now in Xcode. But the #Playground feature still doesn't work.
I also don't get a crash when working on a #Playground in a different project.
I've tried cleaning out Derived Data and restarting the Mac / Xcode, all with no avail. How do I track down what's causing this? Do previews and playground run on the same engine?
System Integrity Protection: enabled
Triggered by Thread: 0
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: Namespace OBJC, Code 1,
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x100634ad0 __abort_with_payload + 8
1 libsystem_kernel.dylib 0x100659dc0 abort_with_payload_wrapper_internal + 100
2 libsystem_kernel.dylib 0x100659d5c abort_with_reason + 28
3 libobjc.A.dylib 0x18009bd08 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 112
4 libobjc.A.dylib 0x18009bc98 _objc_fatal(char const*, ...) + 28
5 libobjc.A.dylib 0x18008be14 lookUpImpOrForward + 580
6 libobjc.A.dylib 0x180072f00 _objc_msgSend_uncached + 64
7 ??? 0x340a44048 ???
8 CJournal 0x1001c54c4 __debug_blank_executor_run_user_entry_point + 152
9 PreviewsInjection 0x241623bf4 0x2415ff000 + 150516
10 PreviewsInjection 0x24162481c 0x2415ff000 + 153628
11 PreviewsInjection 0x241624740 __previews_injection_run_user_entrypoint + 12
12 XOJITExecutor 0x25543ad48 __xojit_executor_run_program_wrapper + 1460
13 XOJITExecutor 0x25543725c 0x255435000 + 8796
14 PreviewsInjection 0x24162468c 0x2415ff000 + 153228
15 CJournal 0x1001c4c7c __debug_blank_executor_main + 996
16 ??? 0x1003e13d0 ???
17 dyld 0x100218924 start + 6400
Hi,
I have been working with an implementation of MapKit which show custom annotations with a detailCalloutAccessoryView built using SwiftUI. This has been working fine for many years, but starting with macOS Tahoe, somehow the SwiftUI buttons in this view have stopped being tappable.
I have reproduced the issue in the code below ... same code works fine in macOS14 and macOS15 now doesn't work correctly in macOS26:
import Cocoa
import MapKit
import SwiftUI
class ViewController: NSViewController {
private var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
}
private func setupMapView() {
// Create and configure the map view
mapView = MKMapView()
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.delegate = self
view.addSubview(mapView)
// Pin the map to all edges of the view
NSLayoutConstraint.activate([
mapView.topAnchor.constraint(equalTo: view.topAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
// Create an annotation for San Francisco
let sanFranciscoCoordinate = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)
let annotation = MKPointAnnotation()
annotation.coordinate = sanFranciscoCoordinate
annotation.title = "San Francisco"
annotation.subtitle = "The City by the Bay"
// Add the annotation to the map
mapView.addAnnotation(annotation)
// Center the map on San Francisco
let region = MKCoordinateRegion(center: sanFranciscoCoordinate,
latitudinalMeters: 5000,
longitudinalMeters: 5000)
mapView.setRegion(region, animated: false)
}
}
// MARK: - MKMapViewDelegate
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "CustomAnnotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView
if annotationView == nil {
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
// Create the SwiftUI view for the callout
let calloutView = CalloutContentView()
let hostingView = NSHostingView(rootView: calloutView)
hostingView.frame = NSRect(x: 0, y: 0, width: 200, height: 100)
// Set the SwiftUI view as the detail callout accessory
annotationView?.detailCalloutAccessoryView = hostingView
} else {
annotationView?.annotation = annotation
}
return annotationView
}
}
// MARK: - SwiftUI Callout View
struct CalloutContentView: View {
var body: some View {
VStack(spacing: 12) {
Text("Welcome to San Francisco!")
.font(.headline)
.multilineTextAlignment(.center)
HStack(spacing: 12) {
Button(action: {
print("Directions button tapped")
}) {
Label("Directions", systemImage: "arrow.triangle.turn.up.right.circle.fill")
.font(.caption)
}
.buttonStyle(.borderedProminent)
Button(action: {
print("Info button tapped")
}) {
Label("Info", systemImage: "info.circle.fill")
.font(.caption)
}
.buttonStyle(.bordered)
}
}
.padding()
.frame(width: 200)
}
}
I've looked at other problems with Map and onTap handlers not getting called, but this is a SwiftUI view inside an AppKit MapKit annotation's callout view.
Any idea of how to handle this?
I have a couple of questions about NSSharingService, specifically with NSSharingServiceNameComposeEmail:1) I use [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeMessage], then set the correct parameters and call `performWithItems` on it. This works fine if the Mail app is configured to be the "Default email reader" in Mail.app's Preferences. If I change this to some of the other email clients I have, this doesn't work anymore. I have tried MS Outlook, Airmail and Unibox apps, and this doesn't work in any of them. Is this intentional? Does the sharing service with NSSharingServiceNameComposeMessage only work for Mail.app?2) If that isn't the case, and other apps can be responsible for composing emails as well, what does an app have to do to handle this? If I want to set myself as an email client app, and let others call ComposeMessage, what API should I implement? If anyone has an idea on how to do this, would really appreciate the help.Thanks.
Hi,I'm starting to work on adding a auto-renewable subscription in my app to unlock certain features. I understand the StoreKit and iTC details, but one thing that's not clear to me is *why* we need to do receipt validation for in-app purchases? There seems to be a lot of empasis on doing this, and it gets complicated because you need to either import different libraries to do this client-side (which isn't the recommended option) or do it server-side, which adds a lot of overhead. In my case, I don't have any server running at all, and my whole app relies on CloudKit instead. So I'm wondering why it's so essential in case of IAP. a) if it's for preventing piracy, shouldn't that apply to paid-apps as well? But no one talks about reciept validation for paid-up-front apps.b) when StoreKit's paymentQueue "updatedTransactions" delegate tells us that someone has purchased an IAP, is that something that can be spoofed? I would imagine not, and even so, this would only be possible with jailbroken devices. Is that what people are worried about? How much of a problem is this in the real world? c) is there certain information in the reciept that is essential for subscriptions to work correctly? In my mind, the simplest implementation would be this: display the IAP products, implement the storekit delegate to see when the user purchased the product and mark that user's CloudKit "User" record with the type and date of subscription (this can also be used to restore the IAP on another device) and unlock the features. When the next billing cycle comes, I can wait for StoreKit to tell me whether the user cancelled or continued the subscription, and lock/unlock the feature accordingly. I don't see the necessity of receipt validation in this case. But I might be wrong and misguided about my assumptions. Would love some comments about this.
I am working with a UIKit app, which has an MKMapView in it, which displays a bunch of MKAnnotationViews. For each annotation view, I'm using a SwiftUI view to draw the detailAccessoryView. This works, but whenever a keyboard is presented (even if it's in another view controller), I see a major memory spike in the app. For e.g., it goes from using 130MB to 600MB+ on an iPhone8 (the exact values don't matter of course). This explains a crash that seems to be happening with the same app in production as well, which I reported in this DevForums Thread
From what I can tell, whenever a keyboard is shown, the SwiftUI views seem go repeatedly resize or re-layout themselves, maybe trying to reconfigure their internal layout or something. If I reduce the number of views in my SwiftUI view, it reduces the memory spike; if I remove the SwiftUI view and just use a plain UIView, the spike completely disappears.
My question is: how can I get the SwiftUI views to ignore what's happening with the keyboard in some other view? I've read in some other threads that there is a 'keyboard avoidance' issue with iOS14 and above ... though in my SwiftUI view, there isn't any text field in it's own views. I've tried adding the modifier to the top-level SwiftUI view:
.ignoresSafeArea(.keyboard)
But it doesn't make a difference
Can anyone shed some light on this? Is there another way to avoid keyboard changes in a SwiftUI view?
Hi,
I need to keep supporting iOS16 for my widgets. I'm having a problem using the iOS17 'containerBackground' API.
Ideally I would use a view extension:
extension View {
func adoptableWidgetBackground(_ color: Color) -> some View {
if #available(iOS 17.0, *) {
containerBackground(for: .widget) { color }
}
else {
background(color)
}
}
}
But this gives an error:
Branches have mismatching types 'some View' (result of 'Self.containerBackground(for:alignment:content:)') and 'some View' (result of 'Self.background(_:alignment:)')
If I try to use this directly on the 'body' view modifier like this:
if #available(iOS 17.0, *) {
.containerBackground(for: .widget) {
ContainerRelativeShape().fill(Color.init(UIColor.quaternarySystemFill))
}
} else {
.background(ContainerRelativeShape().fill(Color.init(UIColor.quaternarySystemFill)))
}
This doesn't work either.
Instance member 'containerBackground' cannot be used on type 'View'
How do I use this correctly?
Hi,
I am testing out an update for my app in macOS Sonoma. I first installed the App Store version of my app on the device running macOS Sonoma, and it ran fine. I then installed an updated version of my app through TestFlight (built with macOS Ventura SDK), but when I run this updated version, I get prompted ”MyApp differs from previously opened versions. Are you sure you want to open it?".
Why is this happening? Is this warning only because the app is updated through TestFlight, or do I need to do something to prevent this warning from happening when I update my app through the App Store?
I see this mentioned in an Apple security update::
App Sandbox now associates your macOS app with its sandbox container using its code signature. The operating system asks the person using your app to grant permission if it tries to access a sandbox container associated with a different app. For more information, see Accessing files from the macOS App Sandbox.
My app is already sandboxed, and I'm not trying to access a different app's sandbox container, just my own. For the TestFlight build, it probably also uses the same Release configuration that the App Store build uses. I might have changed my provisioning profiles recently because they expired. Would that affect this and cause a prompt to be showed?
Would love to know more about this prompt and how to avoid it.
Thanks.
Hi,
I am working on creating a EntityPropertyQuery for my App entity. I want the user to be able to use Shortcuts to search by a property in a related entity, but I'm struggling with how the syntax for that looks.
I know the documentation for 'EntityPropertyQuery' suggests that this should be possible with a different initializer for the 'QueryProperty' that takes in a 'entityProvider' but I can't figure out how it works.
For e.g. my CJPersonAppEntity has 'emails', which is of type CJEmailAppEntity, which has a property 'emailAddress'. I want the user to be able to find the 'person' by looking up an email address.
When I try to provide this as a Property to filter by, inside CJPersonAppEntityQuery, but I get a syntax error:
static var properties = QueryProperties {
Property(\CJPersonEmailAppEntity.$emailAddress, entityProvider: { person in
person.emails // error
}) {
EqualToComparator { NSPredicate(format: "emailAddress == %@", $0) }
ContainsComparator { NSPredicate(format: "emailAddress CONTAINS %@", $0) }
}
}
The error says "Cannot convert value of type '[CJPersonEmailAppEntity]' to closure result type 'CJPersonEmailAppEntity'"
So it's not expecting an array, but an individual email item. But how do I provide that without running the predicate query that's specified in the closure?
So I tried something like this , just returning something without worrying about correctness:
Property(\CJPersonEmailAppEntity.$emailAddress, entityProvider: { person in
person.emails.first ?? CJPersonEmailAppEntity() // satisfy compiler
}) {
EqualToComparator { NSPredicate(format: "emailAddress == %@", $0) }
ContainsComparator { NSPredicate(format: "emailAddress CONTAINS %@", $0) }
}
and it built the app, but failed on another the step 'Extracting app intents metadata':
error: Entity CJPersonAppEntity does not contain a property named emailAddress. Ensure that the property is wrapped with an @Property property wrapper
So I'm not sure what the correct syntax for handling this case is, and I can't find any other examples of how it's done. Would love some feedback for this.
Hi,
When using SwiftUI ‘List’ with a large number of elements (4000+), I noticed a significant performance issue if extracting the views inside the ‘ForEach’ block into their own subview class. It affects scrolling performance, and using the scroll handle in the scrollbar causes stutters and beachballs. This seems to happen on macOS only ... the same project works fine on iOS.
Here's an example of what I mean:
List (selection: $multiSelectedContacts) {
ForEach(items) { item in
// 1. this subview is the problem ... replace it with the contents of the subview, and it works fine
PlainContentItemView(item: item)
// 2. Uncomment this part for it to work fine (and comment out PlainContentItemView above)
/*HStack {
if let timestamp = item.timestamp, let itemNumber = item.itemNumber {
Text("\(itemNumber) - \(timestamp, formatter: itemFormatter)")
}
}*/
}
}
struct PlainContentItemView: View {
let item: Item
var body: some View {
HStack {
if let timestamp = item.timestamp, let itemNumber = item.itemNumber {
Text("\(itemNumber) - \(timestamp, formatter: itemFormatter)")
}
}
}
}
Item is a NSManagedObject subclass, and conforms to Identifiable by using the objectID string value.
With this, scrolling up and down using the scrolling handle, causes stuttering scrolling and can beachball on my machine (MacBook Pro M1).
If I comment out the ‘PlainContentItemView’ and just use the HStack directly (which is what was extracted to ‘PlainContentItemView’), the performance noticeably improves, and I can scroll up and down smoothly.
Is this just a bug with SwiftUI, and/or can I do something to improve this?
Hi,
A new "Featuring Nominations" workflow was introduced at WWDC24, to help nominate our apps to be potentially featured on the App Store. When is that expected to arrive? The WWDC video didn't mention any expected release dates. Would love to be able to use this for an upcoming update.
Thanks.
Hi,
I have noticed a major change to a SwiftUI API behavior in iOS18.4beta1 which breaks my app's functionality, and I've started hearing from users running the new beta that the app doesn't correctly work for them anymore.
The problem is with views that contain a List with multiple-selection, and the contextMenu API applied with the ‘primaryAction’ callback that is triggered when the user taps on a row. Previously, if the user tapped on a row, this callback was triggered with the 'selectedItems' showing the tapped item. With iOS18.4beta, the same callback is triggered with ‘selectedItems’ being empty.
I have the code to demonstrate the problem:
struct ListSelectionTestView: View {
@State private var items: [TimedItem] = [
TimedItem(number: 1, timestamp: "2024-11-20 10:00"),
TimedItem(number: 2, timestamp: "2024-11-20 11:00"),
TimedItem(number: 3, timestamp: "2024-11-20 12:00")
]
@State var selectedItems = Set<TimedItem.ID>()
var body: some View {
NavigationStack {
List(selection: $selectedItems) {
ForEach(items) { item in
Text("Item \(item.number) - \(item.timestamp)")
}
}
.contextMenu(forSelectionType: TimedItem.ID.self, menu: {_ in
Button(action: {
print("button called - count = \(selectedItems.count)")
}) {
Label("Add Item", systemImage: "square.and.pencil")
}
}, primaryAction: {_ in
print("primaryAction called - count = \(selectedItems.count)")
})
}
}
}
struct TimedItem: Identifiable {
let id = UUID()
let number: Int
let timestamp: String
}
#Preview {
ListSelectionTestView()
}
Running the same code on iOS18.2, and tapping on a row will print this to the console:
primaryAction called - count = 1
Running the same code on iOS18.4 beta1, and tapping on a row will print this to the console:
primaryAction called - count = 0
So users who were previously selecting an item from the row, and then seeing expected behavior with the selected item, will now suddenly tap on a row and see nothing. My app's functionality relies on the user selecting an item from a list to see another detailed view with the selected item's contents, and it doesn't work anymore.
This is a major regression issue. Please confirm and let me know. I have filed a feedback: FB16593120