I want to store some cryptographic keys in the keychain. I've been able to commit a few in my application, but when I go to search for them, I get a lot of extraneous results. This happens even if I try to use kSecAttrAccessGroup in my query like so:let keychainQuery: [String: Any] = [kSecClass as String: kSecClassKey,
kSecAttrAccessGroup as String: "7X6QFLT4GF.com.example.myApp",
kSecReturnAttributes as String: true,
kSecMatchLimit as String: kSecMatchLimitAll]
var keychainItems: CFTypeRef?
let keychainSearchStatus = SecItemCopyMatching(keychainQuery as CFDictionary, &keychainItems)keychainItems winds up containing things like my iMessage keys. The baffling part is these items don't even have an agrp key in their dictionary, so how are they matching my query for a specific group?Every reasonable filter I have tried returns either no results OR the nine extraneous keys OR my keys plus the nine extraneous keys. I tried filtering by key length (all the keys I have stored happen to be 521-bit ECDSA), and I got only my keys, so I know filtering works at all.What is the right way to find only items which my application has added?Separately, when I'm using Keychain Access, should I see things my application adds? I currently do not, and I'm not sure if that is relevant.
Post
Replies
Boosts
Views
Activity
I'm building a small application which logs in to a remote server over an HTTP-based API. Authentication is via a username and password in an HTTP PUT. Nice and simple. I want to offer users the ability to save credentials, and I want the saved credentials to show up in Keychain Access, ideally just like credentials saved in Safari. My question is about how to recognize when the user has made the decision not to save credentials for a site.When Safari prompts you to make a credential saving decision and you tell it "Never for This Website", it appears to save a Keychain item with the username "Passwords not saved". The item's name is in the form "<server> (Passwords not saved)". It looks to me like any other saved credential. Is there something special I've missed about these items, or is the username "Passwords not saved" just recognized and handled in a special way?
I'm building a macOS application to interact via a web API with a server another company runs. I have no existing users, so I can target an arbitrary version of macOS (currently 10.15; I'm running 10.15.4). This server provides thousands of objects as JSON. Sounds like a perfect match for JSONSerialization and NSBatchInsertRequest, as discussed in the WWDC 2019 session Making Apps with Core Data.The problem I have is most of the JSON keys have dashes in them, and Core Data does not allow dashes in attribute names. Thus, those attributes don't make it into my Core Data objects. Before trying to move to Core Data, I was using structs with a CodingKeys enum combined with JSONDecoder to pull the data in as Swift objects.An example of the structs I have been using:struct objectMetadata: Codable {
let lock:String
let lastModifyTime:Date
let lastModifier:String
let creationTime:Date
let creator:String
enum CodingKeys : String, CodingKey {
case lock
case lastModifyTime = "last-modify-time"
case lastModifier = "last-modifier"
case creationTime = "creation-time"
case creator
}
}I assume I have to do something similar here. How would I control the exact way a JSON blob is converted to an instance of an entity?
Background
==============================
I'm building a small application currently targeting macOS 10.15, but might add iPadOS support in the future. This application is an API client for a server from another company. I have no control over the format of the returned data.
This server stores and processes simple data objects, most of which would be created by the user. Each object is canonically identified by a UUID. A single server can never have two items with the same UUID, so it's great for uniquely tracking objects within a single instance of the server.
The problem is a single organization may have as many as ~30 instances of this server. While they mostly store data the user specifies, they come with around 8000 pre-defined items. The pre-defined items have static UUIDs, which are the same from one instance to another. These pre-defined items are mutable, so a single UUID may not represent the same object configuration from one server instance to another.
The server has a concept of sessions and the API exposes a way to track what changed between a previous session and the current session in its database. I would like to use Core Data to cache the objects on the user workstation so I don't have to download everything every time I connect.
Right now, I am building a class which represents the connection to a server. Eventually, I expect to be able to open multiple instances of the object, and I would like them to not step on each others' toes.
My Questions
==============================
I'm thinking about building the NSPersistentContainer as an instance variable inside my class, to be initialized once the API connection is successful. Does that sound rational, or should I track the servers as entities in a single NSPersistentContainer for the whole application?
If I use multiple NSPersistentContainers as above, should I use a different name per instance?
Do I need to handle the NSPersistentStoreDescription URL myself to ensure uniqueness, or is that generated automatically somehow? If it's automatic, how is it generated?
I am building a small macOS app to connect to a web API on a server made by another company. This is an entirely new application. I don't have beta access, so I can only target up to 10.15.5.
I also don't have control over the server(s) my application will connect to. This server application generates self-signed certificates for the web API, and the vendor doesn't support using a certificate signed by a CA (even a private one). For now, I'm using code like this to hard-code a set of IPs my application trusts implicitly:
class cpmSessionDelegate:NSObject, URLSessionDelegate {
let trustedHosts:[String] = ["10.20.30.40"]
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
switch challenge.protectionSpace.authenticationMethod {
case NSURLAuthenticationMethodServerTrust:
if trustedHosts.contains(challenge.protectionSpace.host) {
let credential = URLCredential(trust:challenge.protectionSpace.serverTrust!)
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential,credential)
}
default:
challenge.sender!.continueWithoutCredential(for: challenge)
}
}
}
This is clearly terrible. Is there a way to tell URLSession I want it to present untrusted certificates to the user and ask for a trust decision? I haven't run across anything like that, but I might be missing it.
If I can't get the system to handle the certificate display and trust prompt, I know I can add certificates to the keychain and set trust properties myself. Is there a standard control to use to show the certificate to the user for verification? Or do I need to use SecCertificateCopyValues and build my own UI in an NSAlert or something?
BACKGROUND
I am building an application for macOS. No existing users, so I can target arbitrary versions, but I'm not a paying developer yet, so no beta access.
This application works with an API provided by a remote server I do not control. This API provides a lot of objects with various types (for the purposes of this discussion, let's limit them to user and group). Each object is identified by a UUID. The type of the object is an attribute within the object, not some intrinsic aspect of how the object is structured. I can't tell what kind of object a given UUID represents ahead of time.
Groups have a 'members' attribute which is given to me as a list of UUIDs. Groups may contain other groups. The list of UUIDs comes in a stable order from fetch to fetch, but that order is not necessarily predictable (I think it depends on the order the user added the objects to the group). Further, when I get the list of all groups, they come in an order which is also stable, but also unpredictable.
I'm trying to import these objects into Core Data. The problem I'm running into is when groups reference each other. Sometimes, the inner group comes before the outer group in the API call result, and everything is fine. For other group pairs, sometimes the outer group comes first, so the inner group won't exist when I'm trying to build the reference. Effectively a race condition, even though the data is processed serially.
MY QUESTIONS
I'm thinking about running a fetch for every member UUID, creating a new entity if there is no result, running a fetch before creating any group, and updating the object if one already exists. That seems like it would be really expensive, though. In this particular situation, the number of groups within groups is always going to be far lower than the number of other things within groups, and I can fetch the user objects separately and commit them before the groups, thereby ensuring all users exist before groups try to reference them. Most data sets I expect to deal with have tens of thousands of users, and hundreds of groups with 10-2000 users each. Are fetch requests a lot cheaper than I expect?
I'm also thinking about blindly creating new entities for each member reference, then solving the duplicates when I commit (if one conflicting object only has a UUID and the other has a type, discard the one with no type). This seems like a much more robust solution, as the order of import would no longer matter for anything, allowing async import from the API. The problem is I haven't yet found details or sample code on how to make a custom NSMergePolicy. Is there sample code I have just missed?
I'm on macOS 10.15.6. My application is 100% Swift right now, but I'm comfortable enough with Objective-C if that is needed.
When I visit a page using a self-signed certificate in Safari, I get the expected "This Connection Is Not Private" message. If I hit Show Details > visit this website > Visit Website, the certificate is added to my Keychain. It has a little blue circle with a white + in it. When I double-click the certificate, it gives me the details. One of them, just under the expiration date at the top, is:
This certificate is marked as trusted for "<IP address>" Is there a public API to add certificates like this with my own software? Or to add this property to an existing certificate?
I'm making a tool which talks to an API over HTTPS. Some of the servers running this API use the same certificate for a web UI to manage the application. When I visit the page in Safari and trust the certificate there, my application trusts it with no further fuss. I would like the same behavior in the other direction. If a user happens to connect to the server with my application first, I would like to add the certificate to the Keychain in such a way that Safari would also be able to use it for the same server.
I'm developing using Xcode 12.3 (12C33) on macOS 11.1 (20C69).
My application has a sidebar drawn using NSOutlineView (kind of vaguely a source list) and a main pane also drawn using NSOutlineView. The main pane's NSOutlineView's fields are drawn using SwiftUI. I'm trying to drag a data element from the sidebar into a field in the main pane's outline view.
In the sidebar NSOutlineViewDataSource, I have this function:
func outlineView(
_ outlineView: NSOutlineView,
pasteboardWriterForItem item: Any)> NSPasteboardWriting?
{
sidebarControllerLogger.trace("Drag operation started.")
guard let workingItem = item as? MyEntity
else {
sidebarControllerLogger.debug("Dragging something which isn't an object! Description: \(String(describing: item), privacy: .public)")
return nil
}
return workingItem.name! as NSString
}
As you might guess, "MyEntity" is a Core Data entity. I don't think that should affect this, but I mention it just in case. The drag works. I can drag out of the application into TextEdit, and I get the object's name.
My drop target is, as mentioned earlier, a SwiftUI view:
VStack{
ForEach(fieldArray, id:\.self) { item in
Text(item.name!)
}
}
.padding(3)
.onDrop(of: ["public.text"], isTargeted: nil) { providers in
mainViewLogger.trace("Entered drop handler block.")
for provider in providers {
print("Provider found. It provides types \(String(describing: provider.registeredTypeIdentifiers))")
provider.loadItem(forTypeIdentifier: "public.text", options: nil) { item, error in
print("item: \(String(describing: item))")
print("error: \(String(describing: error))")
}
}
return false
}
The of: seems to work. The drag gets the little green + orb when over that field and it goes away when over other fields. The drop is giving me NSItemProvider objects for each dragged item. The problem is the NSItemProvider objects don't seem to have any registered types. Here's what I get when I drag a single item from the sidebar to the field which accepts drops:
2021-01-21 21:52:21.680925-0600 MyApp[8505:306504] [Sidebar Controller] Drag operation started.
2021-01-21 21:52:23.334928-0600 MyApp[8505:306504] [Main View] Entered drop handler block.
Provider found. It provides types []
item: nil
error: Optional(Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load representation of type public.text" UserInfo={NSLocalizedDescription=Cannot load representation of type public.text})
I have tried this with a full delegate implementation where I ask the DropInfo for all the NSItemProviders with "public.text" representations. I get a list of providers. When I ask them for their "public.text", I get the same "Cannot load representation of type public.text".
• I could understand maybe dragging an NSString to the pasteboard doesn't spit out a "public.text" object. That's easy enough to handle. If I'm understanding the object properties properly, though, it doesn't have any representations. How does that happen?
• Why is my drop target lighting up if none of the NSItemProviders I get can provide the single type I say the drop target accepts? If I change it to some other type, the drop target no longer lights up, and no longer accepts the drop.
• Does anybody have any suggestions on what to try next?
First of all, I'm building my application with Xcode 12.4 (12D4e) and running it on macOS 11.2.3 (20D91).
Most of this background shouldn't be relevant to my issue, but it should be working, so clearly I'm missing something. My application downloads a set of objects from a server (think the users, computers, and groups in an Active Directory database) and displays them in a view-based NSOutlineView in the sidebar. I'm using Core Data. As objects are downloaded, I put them in the managed object context, then on the UI side I have a set of NSFetchedResultsController objects to pull the objects for display in the sidebar. I have three manual sidebar supercategories with a separate FRC for each. I am trying to make a delegate for my FRCs which will insert the items into the sidebar as they are downloaded.
My NSFetchedResultsControllerDelegate conformance looks like this:
Swift
extension ObjectSidebarController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsControllerNSFetchRequestResult) {
print("NSFRCD beginning updates.")
(myScrollView.documentView! as! NSOutlineView).beginUpdates()
}
func controller(
_ controller: NSFetchedResultsControllerNSFetchRequestResult,
didChange sectionInfo: NSFetchedResultsSectionInfo,
atSectionIndex sectionIndex: Int,
for type: NSFetchedResultsChangeType)
{
let myOutlineView:NSOutlineView = myScrollView.documentView! as! NSOutlineView
switch type {
case .insert:
print("Inserting a section.")
print("Index: \(sectionIndex)")
let parent = myOutlineView.child(topLevelSectionIndex!, ofItem: nil)!
print("inParent: \(String(describing: parent))")
myOutlineView.insertItems(
at: IndexSet([sectionIndex]),
inParent: parent,
withAnimation: .slideLeft)
case .delete:
print("Deleting a section.")
default:
return
}
}
func controller(
_ controller: NSFetchedResultsControllerNSFetchRequestResult,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?)
{
return
}
func controllerDidChangeContent(_ controller: NSFetchedResultsControllerNSFetchRequestResult) {
print("NSFRCD ending updates.")
(myScrollView.documentView! as! NSOutlineView).endUpdates()
}
}
ObjectSidebarController has an IBOutlet myScrollView which references the scroll view in the sidebar. I build the NSOutlineView programmatically. Again, the NSOutlineView is view-based, not cell-based.
I have a bunch of print statements in my NSOutlineViewDataSource and NSOutlineViewDelegate letting me know when their methods are called. When I make the myOutlineView.insertItems call, I see the data source being asked for the supercategory I just inserted the new section into, and for child 0 of that supercategory:
text
Inserting a section.
Index: 0
Entering outlineView(_: child:2 ofItem:nil)
inParent: supercategory
Entering outlineView(_: child:2 ofItem:nil)
Entering outlineView(_: child:0 ofItem:Optional(supercategory))
About to return: _NSDefaultSectionInfo: 0x600002eb6f80
Entering outlineView(_: child:2 ofItem:nil)
Entering outlineView(_: child:0 ofItem:Optional(supercategory))
About to return: _NSDefaultSectionInfo: 0x600002eb6f80
NSFRCD ending updates.
But it doesn't ask the NSOutlineViewDelegate for the views. I have confirmed my code is called in the main thread, not a background thread. When I collapse the supercategory and open it back up, I get this in my logs:
text
Entering outlineView(_: numberOfChildrenOfItem:Optional(supercategory))
Entering outlineView(_: child:0 ofItem:Optional(supercategory))
About to return: _NSDefaultSectionInfo: 0x600002eb6f80
Entering outlineView(_: viewFor:Optional(NSTableColumn: 0x6000022152d0 identifier: objectName width: 200) item:_NSDefaultSectionInfo: 0x600002eb6f80)
So it seems to me I need to do something to tell the NSOutlineView that I want it to get the views for the supercategory and its child. I have tried calling NSOutlineView.reloadItem(_:reloadChildren:) immediately after inserting the item passing it the same object I just did for the insert's parent, but no change in logs or behavior.
Does anybody have any idea what I'm doing wrong? Based on the documentation, it looks like this should work.
I'm building an application for macOS. Currently targeting 11 and up. I am running 11.3.1 (20E241) and Xcode 12.5 (12E262).
My application interacts with a remote server and downloads a lot of objects from it. These objects have a lot of properties, one of which is a color. The vendor has a list of about 35 named colors which are allowed, and those are the only values they accept. My application is meant to edit these objects, then push the changes to the server.
I'm currently trying to build a picker to let users select the color they want the object to be. I've built the colors in my Assets.xcassets, and I can use them in SwiftUI like so:
Swift
struct ObjectNamePlusIcon: View {
let object:genericObject
var body: some View {
HStack {
ObjectIcon(forObject: object).foregroundColor(Color(object.color ?? "black"))
Text(object.name)
}
.lineLimit(1)
.padding(2)
}
}
The icon shows up and is colored appropriately. I tried adding this to my object editor, though, and it isn't working:
Swift
Picker("Color:", selection: pickerColor) {
ForEach(namedColors, id: \.self) { colorName in
HStack {
Image(systemName: "square.fill")
.foregroundColor(Color(colorName))
Text(colorName)
}
}
}
namedColors is a list I have manually built of all of the allowed color names. With the above code, I get a menu, and the items have the square swatches, but all the swatches are black rather than the color they're meant to be. If I add .pickerStyle(RadioGroupPickerStyle()) to the end of the picker definition, I get a big radio group with swatches and names exactly how I want them.
I've tried this with the various renderingMode options on the image, tried turning the individual items into Buttons and wrapping them in a Menu, tried switching from HStack{Image,Text} to Label items, everything I can think of.
I know menus in general can have colored images in them. Safari has color favicons in the history list, for example. Am I missing something? Is this just something SwiftUI doesn't handle?
Background
I'm working with Xcode 12.5.1 (12E507) and macOS 11.5.1. I am trying to show a tree of NSManagedObject instances in an NSOutlineView. Here's the general schema:
The root of the tree I'm showing (object nil in the NSOutlineView) is always a Layer. Its list of rules can contain a mix of Rule objects and Section objects (thus the PolicyItem entity which Rules and Sections inherit from). Section objects can contain further Rule objects. Rules may contain a reference to another Layer, but generally don't.
The canonical data is on a remote server accessed via an API. The server does not allow cycles (so a Layer contains a Section contains a Rule, but that Rule cannot reference the Layer it is under), so no need to account for that.
I'm trying to handle rearranging rules. I have the drag-and-drop part working in the UI, and I am able to make the API call to rearrange the rule. The rule gets properly moved in the Core Data store, but on a background thread.
Layer.rules can be an ordered relationship (with ordering managed by Core Data), or I can manage the ordering myself with an integer to track the position of each PolicyItem. Same for Section.rules.
My Question
How should I recognize the changes in the data store and update the UI?
For unordered things, NSFetchedResultsController is pretty great. I've hooked it up to NSOutlineView.insertItems, .removeItems, and so on. I'm not sure how to get it to work with ordered data, though.
Maybe use manual ordering, add a relationship from each rule to its parent layer (regardless of intervening section), run the FRC to fetch rules whose parent layer is the layer I want, then sort by [Rule.inRulesOfSection?.positionInt? ?? 0, Rule.positionInt]? That seems really *****, and I don't think it would handle changes in a layer in a rule.
With the upcoming async/await, I could redo the API interaction and update the positions in my drop handler when the call returns. That only helps for drag-and-drop, though. If somebody else makes a change on the server while the client is disconnected, it wouldn't be reflected on the client until the client's view is reloaded.
Is this something KVO should be able to do? I tried setting it up, but had trouble adding observers to any of the Sections.
Should I instead focus on NSManagedObjectContext change notifications? It looks like those notifications only contain information about the new state, not about the transition from old to new. That is, if I move a Rule from one Section to another, it looks like I would get the Rule and both Sections in the NSUpdatedObjectsKey, but it wouldn't tell me the rule was removed from one and added to the other. Or if it was rearranged, I would see the new position, but I don't see how I would find the old position.
Is there some other option I've missed?
Background:
I'm targeting macOS 11 and up, running macOS 12.4 (21F79), and working with Xcode 13.4 (13F17a).
I am building an application to show a list of VMs running under my account on a hosting provider. The VMs have a lot of simple properties like a UUID, a name, a comment field, a power state, and so on. They also have a few properties which are lists of relationships to other things. For example, a single VM can have several disks or several network adapters.
The VMs can be organized in folders, so I'm trying to use NSOutlineView bound to a tree controller to show the downloaded data. The problem I have is that when I scroll my outline, it has significant animation hitches. Instruments tells me they are due to expensive commits.
I originally built the NSOutlineView programmatically, but I've switched to building it in Interface Builder to get the NSTableCellView reuse behavior without needing to subclass and write my own implementations. I have an NSView subclass for the disk column's views and the network adapter column's views. These views present an objectValue property, which gets modified as expected. They then contain a SwiftUI VStack containing a ForEach which renders a row for each disk/interface. View reuse is working, and the internal SwiftUI view properly observes the field for new or removed disks/interfaces. It also properly observes the disk/interface objects themselves for changes (e.g, renaming).
Scrolling still has hitches. The hitches get far worse when I bump the number of columns up to the 20 or so I want to use in the real application.
I have built a small example project with an object factory instead of an API connection. The full application has a set of different icons representing different types of disk and network for different performance tiers. For now, I have simplified that to a single icon for each:
https://github.com/Zimmie0/TableInOutlineExample
My questions:
Is there a better way to show a list of items with an image and a string of text per item inside a single NSOutlineView or NSTableView cell?
Maybe instead of the SwiftUI stuff in the reusable views, I should be building an NSArrayController, an NSTableView with a single column, and using bindings and value transformers (to get an image from the text field indicating the resource's performance tier)? I'm not sure how to do that, though. I specifically don't want a scroll view and a clip view inside the top-level table cells. The top-level table rows should be tall enough objects are never hidden. I'm not sure how to build an NSTableView in interface builder (to get the NSTableCellView bindings and so on) without the scroll view and clip view around it. Also not sure how to tell the system to show the view from some XIB.
Could the problem be related to usesAutomaticRowHeights? I'm aware that hurts performance, but I don't know to what extent. I can determine the row height myself easily enough, but I'm not sure how to tell the outline view that a given row's height needs to change in response to a disk or network adapter being added or removed.
This is basic enough I doubt the versions matter, but I'm running macOS 12.5.1 and Xcode 13.4.1, with my build targeting macOS 11. I'm trying to add contextual menus to some views I'm drawing with SwiftUI, but the menus aren't activating how I expect. Here's a trivial reproduction of the issue:
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.overlay(
Rectangle()
.strokeBorder(Color.primary, lineWidth: 0.5))
.frame(width: 200, height: 200)
.overlay(
Rectangle()
.strokeBorder(Color.primary, lineWidth: 0.5))
.contextMenu {
Button {
print("Nothing to see here yet...")
} label: {
Text("A Menu Item")
}
}
.padding()
}
}
The padding is just to provide some visual separation between the frame line and the edge of the window. When I right-click or control-click the text, I get the menu as expected. Anywhere outside the text's rectangle and inside the frame's rectangle, I get nothing. I expect to get the contextual menu.
If I add a background (e.g, .background(Rectangle().fill(Color.yellow))), the contextual menu works over the whole frame. If I change the background's color to Color.clear, the menu is back to working only on the text.
What is going on? Why does it work this way? The change in behavior from Color.yellow to Color.clear makes me think it's some kind of optimization where SwiftUI decides it doesn't need to do work there, but it clearly still knows about the frame as a whole.
The specific use case relates to one of my earlier threads. I'm drawing a table with variable-height rows depending on the number of items in the row's fields. Sometimes, a row has only one value in one field and many values in a second field, so it is drawn taller, with empty space in the first field. I want the contextual menu to work in the whole field, not just when clicking exactly on an object.
I also want to have a contextual menu when I right-click exactly on an object with some additional entries. For example, "Copy this object's UUID", and "Remove this object" when I click on an object, and "Add an object" menu item when I click on an object or when I click in the empty space in the field. Since SwiftUI is composable, I'm able to build the general menu items once and reference them in a section of a particular object's contextual menu.
Any ideas on how to get the contextual menu working across the whole frame rather than just the text? I could implement one version of the general menu items as an NSMenu with selectors and all, then another version of the general menu items in SwiftUI, but this seems like duplication of effort. Surely that can't be the right way to solve the problem.
I'm building a document-based application. I'm currently using macOS 12.5.1 and Xcode 13.4.1, and targeting macOS 11.
My documents are Core Data stores in the tens of megabytes with complex sets of relationships. It's data fetched from a management system which manages tens to thousands of subordinate systems. One of the major entities is a big table of information about one of these subordinate systems. My target audience would want to be able to open several of these tables and quickly switch between them. Think of the document like a project folder in Xcode which contains a lot of sub-documents.
My question: Is there a way to customize the behavior of window-level tabs (View > Show Tab Bar)? I want to have one document per window with a subordinate table per tab.
I'm using Xcode 14.3.1 on macOS 13.5, and I've managed to reproduce my issue in a trivial application. All the project settings are left at the defaults for a macOS project.
It looks like using a GroupBox breaks the ability of XCTest to find popovers connected to buttons (I suspect any UI element) inside the GroupBox.
The debug console output from the code below lists 15 descendants from my window with the outside-the-GroupBox popover open, and one of them is definitely a popover. With the inside-the-GroupBox popover open, my window only shows nine descendants, and no popover (the rest of the difference is the popover's contents). It's simple enough I don't see what I could be doing wrong:
import SwiftUI
@main
struct GroupBox_Popover_DemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var outsidePopoverPresented: Bool = false
@State var insidePopoverPresented: Bool = false
var body: some View {
VStack {
Button("Outside GroupBox") {
outsidePopoverPresented = true
}
.popover(isPresented: $outsidePopoverPresented,
attachmentAnchor: .point(.leading),
arrowEdge: .leading) {
Popover(selected: .constant("Item A"), isPresented: $outsidePopoverPresented)
}
.padding()
GroupBox {
Button("Inside GroupBox") {
insidePopoverPresented = true
}
.popover(isPresented: $insidePopoverPresented,
attachmentAnchor: .point(.leading),
arrowEdge: .leading) {
Popover(selected: .constant("Item B"), isPresented: $insidePopoverPresented)
}
.padding()
}
}
.padding()
}
}
struct Popover: View {
@Binding var selected: String
@Binding var isPresented: Bool
var body: some View {
VStack(alignment: .leading) {
Picker("", selection: $selected) {
Text("Item A").tag("Item A")
Text("Item B").tag("Item B")
Text("Item C").tag("Item C")
}
.pickerStyle(.radioGroup)
HStack {
Spacer()
Button("Cancel") {
isPresented = false
}
}
}
.padding()
.frame(width: 200)
}
}
Then in my UI tests:
import XCTest
final class GroupBox_Popover_DemoUITests: XCTestCase {
let mainWindow = XCUIApplication().windows
override func setUpWithError() throws {
continueAfterFailure = false
XCUIApplication().launch()
}
func testPopovers() {
let myDescendants = mainWindow.descendants(matching: .any)
mainWindow.buttons["Outside GroupBox"].click()
print("Window descendants with outside popover open:")
print(myDescendants.debugDescription)
mainWindow.popovers.buttons["Cancel"].click()
mainWindow.buttons["Inside GroupBox"].click()
print("Window descendants with inside popover open:")
print(myDescendants.debugDescription)
mainWindow.popovers.buttons["Cancel"].click()
XCTAssert(true, "Test was able to hit cancel on both popovers.")
}
}
Any ideas? Have I missed unchecking some "Ignore anything in a GroupBox" checkbox somewhere?