Since updating to macOS Sequoia, I see this dialog every time I launch my SwiftUI macOS app from Xcode:
Users who installed the app from the App Store don’t see it. And this didn’t happened in previous macOS versions.
Could launching it from Xcode be triggering some extra access requirement? How can I stop this dialog from appearing every time I launch my app? It’s very disruptive to the debugging process.
Post
Replies
Boosts
Views
Activity
In a TabView with the .sidebarAdaptable style, including TabSection elements prevents the default back swipe on the remote from revealing the sidebar. Removing all TabSection elements and using only Tab elements makes it work as expected:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
TabSection("Section") {
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
Tab("Tab", systemImage: "square.fill") { Button(action: {}) { Text("Button") } }
}
}.tabViewStyle(.sidebarAdaptable)
}
}
Am I using it wrong, or is this a bug?
onContinueUserActivity(CSSearchableItemActionType, perform) works as expected on iOS when we search and select an item from Spotlight, but nothing happens when we do the same on a SwiftUI macOS app.
var body: some Scene {
WindowGroup {
MyView()
.onContinueUserActivity(CSSearchableItemActionType, perform: handleSpotlight)
}
}
func handleSpotlight(_ userActivity: NSUserActivity) {
// Is not called...
}
How can we respond to a user clicking a Spotlight result from our apps on macOS?
Adding the openAppWhenRun property to an AppIntent for a ControlWidgetButton causes the following error when the control is tapped in Control Center:
Unknown NSError The operation couldn’t be completed. (LNActionExecutorErrorDomain error 2018.)
Here’s the full ControlWidget and AppIntent code that causes the errorerror:
Should controls be able to open apps after the AppIntent runs, or is this a bug?
The documentation for translationTask(source:target:action:) says it should translate when content appears, but this isn't happening. I’m only able to translate when I manually associate that task with a configuration, and instantiate the configuration.
Here’s the complete source code:
import SwiftUI
import Translation
struct ContentView: View {
@State private var originalText = "The orange fox jumps over the lazy dog"
@State private var translationTaskResult = ""
@State private var translationTaskResult2 = ""
@State private var configuration: TranslationSession.Configuration?
var body: some View {
List {
// THIS DOES NOT WORK
Section {
Text(translationTaskResult)
.translationTask { session in
Task { @MainActor in
do {
let response = try await session.translate(originalText)
translationTaskResult = response.targetText
} catch { print(error) }
}
}
}
// THIS WORKS
Section {
Text(translationTaskResult2)
.translationTask(configuration) { session in
Task { @MainActor in
do {
let response = try await session.translate(originalText)
translationTaskResult2 = response.targetText
} catch { print(error) }
}
}
Button(action: {
if configuration == nil {
configuration = TranslationSession.Configuration()
return
}
configuration?.invalidate()
}) { Text("Translate") }
}
}
}
}
How can I automatically translate a given text when it appears using the new translationTask API?
My app uses Core Data to store user data. It doesn't have login or profile mechanisms, because user data is tied to the user's Apple ID and synced via iCloud using CloudKit.
I've recently added the User Management capability, and checked Runs as Current User to allow each Apple TV user to have access to its own data. Everything works as expected: I press and hold the TV button to select a different user in Control Center; The system shows a switching user UI; And the app is relaunched using the new user's Apple ID and data from iCloud.
The problem is that switching users apparently causes the local Core Data managed database to be reset, removing all data from the previous user. This is inconvenient because every time we switch users, the newly selected user has to wait for CloudKit to download all its data back into the app. And it takes a while, especially for users that have more than a few hundred items in the database.
Ideally, tvOS should maintain the user data on-device, and only switch to a different database according to the active user. This way data would not need to be downloaded from scratch every time a user is switched.
Is there a setting I can configure or a checkbox I can check to enable this behavior, or is this something not possible yet on tvOS?
After updating to iOS 17 and Xcode 15 I noticed that menus on my app get automatically dismissed while CloudKit is syncing data right after opening the app. I know it's CloudKit because this issue doesn't happen when I launch the app in Airplane Mode.
It looks like CloudKit causes something to change (even when there are no data changes since the last usage), which causes the UI to refresh. This refresh causes the menus to be dismissed.
This is frustrating because users have to tap a button two or three times before being able to interact with the menu showed by the button. I'm attaching a screen recording that shows the issue:
https://youtube.com/shorts/Wl9gP2HLktE
I open the app and tap the "+" icon, which shows the menu.
The menu is quickly dismissed.
I tap the "+" icon again, and the menu is shown for a millisecond, and is dismissed again.
I tap the "+" button once again. This time the menu is shown and stays there because CloudKit has finished its initial sync process.
Note that this didn't happened on iOS 16 / Xcode 14, so something must have changed to cause this issue. Is there something I need to update on my code to fix this issue, or is it a bug in the OS or frameworks that will be fixed in a future update?
My app allows users to store URLs for future reference. It also has widgets that display these saved items. I want to make these widgets interactive by opening the linked content in Safari without having to open the main app.
The issue is that UIApplication.shared.open is not available in app extensions. And this is a problem since widgets are app extensions.
Are there alternative methods to accomplish this scenario, or is it currently unsupported?
In an AppIntent we can declare an IntentDescription with a categoryName to group similar intents:
struct OpenArtist: AppIntent {
static var description: IntentDescription = IntentDescription("Opens a specific artist.", categoryName: "artistsSection")
// Additional properties
}
But how can we assign the same category to the "Find AppEntity" intent generated when we declare an EntityPropertyQuery? I want to group it along with the other intents that handle the same entity, but since it is generated by the system, I don't know how to assign a category to it.
My AppEntity has a Bool property, and I would like to query by this property using EntityPropertyQuery. I'm creating an EntityQueryProperty for it with an EqualToComparator as the following (partial code for brevity):
// My AppEntity
struct AlbumAppEntity: AppEntity, Identifiable {
@Property(title: "Hidden")
var isHidden: Bool
// Other properties
}
// My EntityPropertyQuery
static var properties = QueryProperties {
Property(\AlbumAppEntity.$isHidden) {
EqualToComparator { isHidden in
if isHidden {
return NSPredicate(format: "isHidden == YES")
} else {
return NSCompoundPredicate(type: .or, subpredicates: [NSPredicate(format: "isHidden = nil"), NSPredicate(format: "isHidden == NO")])
}
}
}
}
But the problem is that Shortcuts does not recognize it as a boolean parameter in the shortcuts editor. It recognizes it as a numeric field, which returns the error AppIntents.EntityPropertyQueryError error 2 when run:
I also tried different NSPredicate, but the result is always the same:
Property(\AlbumAppEntity.$isHidden) {
EqualToComparator { NSPredicate(format: "isHidden == %d", $0) }
}
Property(\AlbumAppEntity.$isHidden) {
EqualToComparator { NSPredicate(format: "isHidden == %@", NSNumber(value: $0)) }
}
How can I query using boolean properties in a way that Shortcuts recognizes it and provides appropriate fields in the shortcuts editor?
TL;DR
How to create an NSSortDescriptor using a PartialKeyPath instead of a KeyPath?
Or
How to convert a PartialKeyPath to a KeyPath?
Details
I'm using the new query properties from AppIntents to filter my app's data in Shortcuts, but I'm unable to map the sorting attribute it gives me to the sorting attribute Core Data expects in the predicate.
Here's my EntityPropertyQuery implementation:
extension ArtistQuery: EntityPropertyQuery {
static var sortingOptions = SortingOptions {
SortableBy(\ArtistEntity.$name)
}
static var properties = QueryProperties {
Property(\ArtistEntity.$name) {
EqualToComparator { NSPredicate(format: "name = %@", $0) }
ContainsComparator { NSPredicate(format: "name CONTAINS %@", $0) }
}
}
func entities(matching comparators: [NSPredicate],
mode: ComparatorMode,
sortedBy: [Sort<ArtistEntity>],
limit: Int?) async throws -> [ArtistEntity] {
Database.shared.findArtists(matching: comparators,
matchAll: mode == .and,
sorts: sortedBy.map { NSSortDescriptor(keyPath: $0.by, ascending: $0.order == .ascending) })
}
}
And my findArtists method is implemented as follows:
static func findArtists(matching comparators: [NSPredicate],
matchAll: Bool,
sorts: [NSSortDescriptor]) -> [EArtist] {
...
}
As we can see in the entities(matching:) function, I'm using the by attribute from the sortedBy parameter to create the NSSortDescriptor, but it doesn't work because the NSSortDescriptor init expects a KeyPath, not a PartialKeyPath:
Cannot convert value of type 'PartialKeyPath<ArtistEntity>' to expected argument type 'KeyPath<Root, Value>'
So, can I create an NSSortDescriptor using a PartialKeyPath instead of a KeyPath? Or maybe converting a PartialKeyPath to a KeyPath?
iPadOS uses a different selection color when an external keyboard is connected. But the problem is that it doesn't change the text color to white, making it difficult to read:
A simple List with NavigationLink produces this behavior by default:
var body: some View {
List {
ForEach(searchResults) { item in
NavigationLink(destination: ContentDetailView(item: item)) {
ListItemView(item: item)
}
}
}
}
I tried to improve text legibility by changing all Text colors to white when the cell is selected. But this doesn't work because the text becomes even more unreadable when no external keyboard is connected.
Is there a way to change the selection color when an external keyboard is connected? Or maybe detect when an external keyboard is connected to manually change the text color for this specific case?
I have a macOS Share Extension invoked when users tap the Share button in Safari. I'm trying to get the page's URL from the -[NSExtensionItem attachments] attribute, but it comes as an NSSecureCoding object, and I'm unable to read the URL from it.
In the loadView method, I'm filtering and loading the attachments of type public.url:
override func loadView() {
super.loadView()
guard let inputItem = extensionContext?.inputItems.first as? NSExtensionItem else {
print("Didn't received input item from action.")
return
}
var itemProvider: NSItemProvider?
itemProvider = inputItem.attachments?.filter({ $0.registeredTypeIdentifiers.contains("public.url") }).first ?? inputItem.attachments?.filter({ $0.registeredTypeIdentifiers.contains("public.plain-text") }).first
guard let itemProvider = itemProvider else {
print("Didn't received attachments from input item.")
return
}
if itemProvider.canLoadObject(ofClass: URL.self) {
itemProvider.loadItem(forTypeIdentifier: "public.url", completionHandler: onLoadVideoURL)
} else if itemProvider.canLoadObject(ofClass: String.self) {
itemProvider.loadItem(forTypeIdentifier: "public.plain-text", completionHandler: onLoadVideoURL)
} else {
print("This action only supports URL and String.")
}
}
The itemProvider.loadItem method runs for the type identifier public.url, calling the completion handler bellow:
@objc private func onLoadVideoURL(dict: NSSecureCoding?, error: Error?) {
print("URL: \(dict.debugDescription)")
// ...
}
But the content that it prints to the console is:
URL: Optional(<68747470 733a2f2f 73746163 6b6f7665 72666c6f 772e636f 6d2f7175 65737469 6f6e732f 35323231 39373030 2f686f77 2d746f2d 63617374 2d6e7373 65637572 65636f64 696e672d 746f2d6d 6b6d6170 6974656d 2d696e2d 61637469 6f6e2d65 7874656e 73696f6e>)
The same code works as expected on iOS, printing the shared URL to the console.
Do I have to somehow convert this NSSecureCoding to URL or another object? Or should I do this in a completely different way on macOS? The goal is to access the page's URL from the Share Extension activated when the user selects it in the Share Menu.
Using SwiftUI's new Table container, how can I add a context menu that appears when Control-clicking a row?
I can add the contextMenu modifier to the content of the TableColumn, but then I will have to add it to each individual column. And it only works above the specific text, not on the entire row:
I tried adding the modifier to the TableColumn itself, but it shows a compile error:
Value of type 'TableColumn<RowValue, Never, Text, Text>' has no member 'contextMenu'
Here's what I have in terms of source code, with the contextMenu modifier in the content of the TableColumn:
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true)])
private var items: FetchedResults<Item>
@State
private var sortOrder = [KeyPathComparator(\Item.name)]
@State
private var selection = Set<Item.ID>()
var body: some View {
NavigationView {
Table(items, selection: $selection, sortOrder: $items.sortDescriptors) {
TableColumn("Column 1") {
Text("Item at \($0.name!)")
.contextMenu {
Button(action: {}) { Text("Action 1") }
Divider()
Button(action: {}) { Text("Action 2") }
Button(action: {}) { Text("Action 3") }
}
}
TableColumn("Column 2") {
Text($0.id.debugDescription)
}
}
.toolbar {
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
if selection.isEmpty {
Text("Select an item")
} else if selection.count == 1 {
Text("Selected \(items.first(where: { $0.id == selection.first! })!.id.debugDescription)")
} else {
Text("Selected \(selection.count)")
}
}
}
}
So, how can I add a context menu to the entire row inside the Table?
I'm sharing a Core Data store with my action extension, which adds data to it. My app has a SwiftUI view that presents this data, fetched with a FetchRequest:
private struct VideosScrollView: View {
@Environment(\.managedObjectContext) private var viewContext
private var fetchRequest: FetchRequest<Video>
init(sortOrder: String, tagIds: String, showWatched: Bool) {
fetchRequest = Video.getFetchRequest(sortingBy: "addDate",
with: sortOrder,
showWatched: showWatched,
tagIds: tagIds)
}
var body: some View {
ScrollView {
if fetchRequest.wrappedValue.isEmpty {
ContentEmptyView()
} else {
ForEach(fetchRequest.wrappedValue) { item in
VideoCellView(video: item)
}
}
}
}
}
After adding data from the action extension and going back to the app, the view is not updated. Only after CloudKit finished a sync is that the view notices something changed and updates itself.
How can I force my SwiftUI view to update when data changes in the action extension?