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 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
I'm having a lot of performance issues with SwiftUI Lists and I can't seem to find any solution. Currently, if you have a List with 10_000 items and try to filter it, the app hangs for 1 second at the beginning of filtering, looking at Instruments it seems to be an issue with the underlying UITableView to perform the animations.
Here's a very tiny example to demo the issue:
struct Item: Identifiable, Hashable {
let title: String
var id: String { title }
}
@main
struct ListFilteringDemoApp: App {
private let allItems = (0...10_000).map { _ in Item(title: UUID().uuidString) }
@State var searchQuery: String = ""
var filteredItems: [Item] {
if searchQuery.isEmpty {
// No filter applied
return allItems.filter { _ in true }
} else {
return allItems.filter { item in
item.title.localizedCaseInsensitiveContains(searchQuery)
}
}
}
var body: some Scene {
WindowGroup {
NavigationView {
List(filteredItems) { item in
Text(item.title).id(item)
}
.navigationTitle("Filtering")
.searchable(text: $searchQuery, placement: .navigationBarDrawer(displayMode: .always))
}
}
}
}
I know that having 10_000 items in an array is not common. But using Core Data with a @FetchRequest, having 10_000 items is very easy to achieve for the kind of app that I'm building, and the issue is the same.
The issue is present on device (iPhone 12 Pro, iOS 15.1), macOS and iOS Simulator.
I've two questions:
Is there a better way to filter a list while searching?
If the answer is no, is there any way to prevent the animations on a List while being filtered to try to improve the performance?
Thanks!
PS: For Apple folks, I've opened a feedback which includes a sample project to reproduce the issue and a saved Instruments .trace. Number: FB9763003
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 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 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
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! 🙏