I'm observing a weird issue with SwiftUI Lists on macOS (they do work as expected on iOS). The problem is that the List calls the init & body of every row, even if those rows are not on screen (and might never be shown).
If I replace the List with a ScrollView + LazyVStack, it does work as expected (only those rows which are going to be rendered get their init & body called). Of course, this is is not an ideal workaround because you loose the built-in benefits of using a List (mainly selection in my case).
I did expect that under the hood, SwiftUI would use the same mechanism as NSTableView (which loads cells on demand). Historically I'm an iOS dev, so I'm used to cellForRowAtIndexPath coming from UITableView.
Here's a quick gist demonstrating the issue:
import SwiftUI
@main
struct SwiftUIListVSLazyVStackApp: App {
let numbers = (0...100).map { $0 }
var body: some Scene {
WindowGroup {
/*
List calls the init & the body of *every* element on the list, even if it's not being displayed.
This is unexpected because a tableView usually only loads the visible cells as needed (cellForRowAtIndexPath)
*/
List(numbers) { number in
RowView(for: number, example: "list")
}
/*
A combination of ScrollView + LazyVStack achieves what's expected from the list. Only calls the init & body of
the element that's going to be displayed.
*/
// ScrollView {
// LazyVStack(alignment: .leading, spacing: 8) {
// ForEach(numbers) { number in
// RowView(for: number, example: "stack")
// }
// }
// }
}
}
}
struct RowView: View {
private let number: Int
private let example: String
init(for number: Int, example: String) {
print("Init \(example): \(number)")
self.number = number
self.example = example
}
var body: some View {
let _ = print("Body \(example): \(number)")
Text("\(number)")
.onAppear{ print("Appear \(example): \(number)") }
}
}
extension Int: Identifiable {
public var id: Int { self }
}
GitHub gist: https://gist.github.com/xmollv/7ecc97d8118c100e85698c5ff09a20dc
And a video to better show the issue if you can't run the code:
https://gist.github.com/xmollv/7ecc97d8118c100e85698c5ff09a20dc?permalink_comment_id=4140623#gistcomment-4140623
Twitter thread: https://twitter.com/xmollv/status/1517158777882955779
Any help is very much appreciated!
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm facing a weird issue with contextMenu(forSelectionType:menu:primaryAction:) attached to a List. It works fine if you enable edit mode, and start selecting the rows by tapping, but if you have a button that what it does is manually modify the selection, the returned rows when the contextMenu is invoked is incorrect.
Furthermore, if you use the select all button, but actually scroll to the bottom of the list, the returned values is correct, so it seems that unless the cell is rendered, the contextMenu won't return it.
Does anybody know if I'm doing something wrong? Here's a quick example to reproduce the issue:
struct ContentView: View {
let rows = (0..<100).map{ "Row: \($0)" }
@State var selection: Set<String> = []
var body: some View {
List(selection: $selection) {
ForEach(rows, id: \.self) { row in
Text(row).tag(row)
}
}.contextMenu(forSelectionType: String.self) { contextMenuRows in
Button("Number of rows in the contextMenu: \(contextMenuRows.count)") {}
}.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
if selection.isEmpty {
Button("Select All") { selection = Set(rows) }
} else {
Button("Deselect All") { selection = [] }
}
}
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
}
}
}
Make sure to embed the ContentView inside a NavigationView to be able to see the navigation bar.
Video demo showing the issue: https://imgur.com/a/fxKk5Cs
Works fine when selecting manually
When selecting all only displays the first 9 rows
After scrolling, all rows are available to the contextMenu
I'm facing a weird alignment issue with a floating menu when the keyboard appears and I've no idea if there's a workaround. To reproduce the issue, simply copy and paste this code:
struct ContentView: View {
@State var searchQuery = ""
var customToolbarButton: some View {
VStack {
Text("Example").font(.footnote)
Image(systemName: "arrowtriangle.down.fill").imageScale(.small)
}.foregroundColor(.accentColor)
}
var body: some View {
NavigationView {
Rectangle().fill(.red)
.safeAreaInset(edge: .bottom) {
HStack(alignment: .center) {
customToolbarButton
Spacer()
Menu { } label: {
customToolbarButton
}
}
.frame(height: 52)
.padding(.horizontal)
.background(Color.white)
.clipShape(Capsule(style: .continuous))
}
}.searchable(text: $searchQuery)
}
}
When the keyboard appears and moves the HStack up, the views that are a Menu are wrongly placed, all other views seem fine. Any idea if there's a fix?
Video to illustrate the issue: https://twitter.com/xmollv/status/1616851571832229889
We've recently (since yesterday) have been experiencing an issue where builds submitted to TestFlight via Xcode Cloud get queued for ~3 hours.
Here's an example:
The build itself takes ~20 mins:
Is anyone experiencing such issue?
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
App Store Connect
TestFlight
Xcode Cloud
For ages, we've been using appStoreReceiptUrl to detect if the app was installed from TestFlight or not, but now that's deprecated. Since we have a strict policy of no warnings on the project, we need to find a way to check if the app was installed from TestFlight or from the App Store. Does anyone know what's the new way to do so?
I thought about using MarketplaceKit.AppDistributor.testFlight but to use MarketplaceKit you need to jump through hoops that our app really doesn't need to - we don't distribute outside of the App Store.
Any ideas are much appreciated! 🙏
Topic:
App Store Distribution & Marketing
SubTopic:
TestFlight
Tags:
Foundation
App Store
TestFlight
StoreKit
I have an app that has a main target, a Share Extension and an App Extension.
I save data from any of the extensions, but the UI is never updated on the main app when going back into it if the app was on the background (alive, not terminated).
If the app was terminated, it all works as expected because it loads everything from scratch. If the app was on the background, it's not updated until a save from an unrelated part of the code occurs.
It feels like @FetchRequest's is not aware of remote (meaning from other processes) changes, is this something that has to be done manually?
I have both suggested options enabled:
storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
storeDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
When using UIKit with an NSFetchedResultsController, you could simply call performFetch to force the refresh of the controller when the app became active, but I don't see a way to force a refresh on a @FetchRequest.
I'm wondering if there's any way to force the UIViewController presented by a Share Extension to use the UISheetPresentationController introduced in iOS 15?
Right now, the only way they're presented is modally, occupying almost the entire screen. Most share extensions don't need that much space and would allow you to have more context from where you shared (the originating app).
I'm really struggling with having two new In-App Purchases approved. I've added two new IAPs in the V2 of my app, but for some reason they're never approved, they keep coming back to me with the "Developer Action Needed" error and "We have returned your IAP product/s to you as the required binary was not submitted. When you are ready to submit the binary, please resubmit the IAPs with the binary."
I have, indeed, included the code to handle them on the binary being submitted (working fine on dev & TestFlight). I remember that when I added the first ever purchase I had to include it where you select which binary needs to be reviewed, but that option never appears. Here's how it's looking:
There's nowhere to select any In-App Purchase to be reviewed with that binary.
I've peeked at the console and I'm seeing 404's for a URL that might be exactly what I need, is the App Store Connect just broken right now? I've tried everything and I haven't been able to find what's shown here: https://help.apple.com/app-store-connect/#/dev1986a0e5c
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
App Review
App Store Connect
In-App Purchase
Subscriptions
Since a couple of days ago, the tab for Xcode Cloud on App Store Connect never ends up loading. The console seems to log some errors about the ci, so I'd assume something's broken? Works fine from Xcode though, and the builds do go though, might be just a Javascript rendering issue.
For the past few days we've been observing a degraded performance on Xcode Cloud. Some builds get queued for hours and never start. I just cancelled one that's been queued for an hour and a half:
Is this a known issue?
I'm facing a weird issue with SwiftData. I want to have one database that's local to the device and one that syncs to iCloud. In this example, LTRLink should be synced via iCloud while LTRMetadata should stay on-device only. I've it configured like the following:
let schema = Schema([LTRLink.self, LTRMetadata.self])
let cloudkitConfiguration = ModelConfiguration("Remote",
schema: schema,
url: FileManager.remoteDatabaseFolderURL.appending(path: "Remote.sqlite"),
cloudKitDatabase: .private("iCloud.com.xavimoll.abyss3"))
let localConfiguration = ModelConfiguration("Local",
schema: schema,
url: FileManager.localDatabaseFolderURL.appending(path: "Local.sqlite"),
cloudKitDatabase: .none)
return try ModelContainer(for: schema,
configurations: [cloudkitConfiguration, localConfiguration])
For some reason, when I create the iCloud schema, both models end up appearing as records on iCloud. I create the schema like this:
let schema = Schema([LTRLink.self, LTRMetadata.self])
let cloudkitConfiguration = ModelConfiguration("Remote",
schema: schema,
url: FileManager.remoteDatabaseFolderURL.appending(path: "Remote.sqlite"),
cloudKitDatabase: .private("iCloud.com.xavimoll.abyss3"))
#if DEBUG
// Needed to create the schema on iCloud
try autoreleasepool {
let desc = NSPersistentStoreDescription(url: cloudkitConfiguration.url)
let opts = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudkitConfiguration.cloudKitContainerIdentifier!)
desc.cloudKitContainerOptions = opts
desc.shouldAddStoreAsynchronously = false
if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [LTRLink.self]) {
let container = NSPersistentCloudKitContainer(name: "Remote", managedObjectModel: mom)
container.persistentStoreDescriptions = [desc]
container.loadPersistentStores {_, err in
if let err {
fatalError(err.localizedDescription)
}
}
try container.initializeCloudKitSchema()
if let store = container.persistentStoreCoordinator.persistentStores.first {
try container.persistentStoreCoordinator.remove(store)
}
}
}
#endif
let localConfiguration = ModelConfiguration("Local",
schema: schema,
url: FileManager.localDatabaseFolderURL.appending(path: "Local.sqlite"),
cloudKitDatabase: .none)
return try ModelContainer(for: schema,
configurations: [cloudkitConfiguration, localConfiguration])
The logic to initialize the CloudKit schema follows the documentation found here: https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices#Initialize-the-CloudKit-development-schema
It looks like setting cloudKitDatabase: .none on the init for the ModelConfiguration doesn't do anything, and ends up being synced with iCloud either way. When I go to the iCloud console, I see the following:
Does anyone know if there's any workaround that would allow me to have two databases where only one of them syncs to iCloud when using SwiftData?
Hello! I'm facing a strange behavior on macOS related to Ask Each Time, which works fine on iOS. I've an App Intent that declares a parameter like so:
@Parameter(
title: "Tags",
description: "Tags to add to the link.",
optionsProvider: TagsOptionsProvider()
)
var tags: [String]?
The TagsOptionProvider is like this:
struct TagsOptionsProvider: DynamicOptionsProvider {
@Dependency
private var modelCoordinator: ModelCoordinator
@MainActor
func results() async throws -> [String] {
return modelCoordinator.tags().compactMap { $0.name }
}
}
Now, the issue comes if I create a shortcut where for the tags parameter the user selects the magic variable Ask Each Time. On iOS, when the user is presented with the selector, they can simply tap 'Done' without selecting any value (the user does not want to include any tag). The problem is that on macOS the 'Done' button is disabled if there's no selection. See both behaviors:
iOS:
macOS:
Question:
Is there a way to let macOS continue even if the user doesn't select any of the available options like on iOS? I've tried declaring the tags para meter as Optional (like on the screenshot) and non-optional, both cases show the same behavior.
Environment:
iOS 18.5
macOS 15.5
I needed to verify something on the CloudKit console but it doesn't seem to be working even though the system status says that it's operational: http://developer.apple.com/system-status/
Is anyone experiencing the same issue? I can't load the databases, zones nor saved queries.
For Apple folks, here's the feedback number: FB9756611
Hello! I'm encountering a weird issue on TestFlight/AppStoreConnect. I've builds that are in the "Processing" state since the 19th (that's 5 days ago from the date of this post).
I've tried to generate new builds to see if it was just a fluke during those days, but new builds generated today also get stuck at processing. It's starting to become an issue because everybody on our team that's not a developer can't test the latest changes...
Is there any way that I can get them unstuck?
Thanks!
I have an app that uses NSPersistentCloudKitContainer to store all the data so it's backed by iCloud and synced between devices. Such app has a Share Extension, that can be triggered both from iOS (and iPadOS) and macOS (Catalyst).
I was wondering if it's safe to instantiate an NSPersistentCloudKitContainer from a Share Extension due to it being very short lived. At the moment, I'm sending the data straight to iCloud instead of instantiating an NSPersistentCloudKitContainer, but it feels wrong because I'm using the keys that NSPersistentCloudKitContainer created internally (CD_MyEntity, CD_myProperty, etc.) to send it to iCloud and then being correctly pulled by my clients.
The only concern that I have is that bringing up an NSPersistentCloudKitContainer that has to pull data, might delay or even loose the data that I'm saving right now because it gets killed after some amount of time since the share action has been completed.
Any insights will be much appreciated, because if it's safe to use an NSPersistentCloudKitContainer from a Share Extension, I could remove a ton of fragile code! 🙏