Hello. I am re-writing our way of storing data into Core Data in our app, so it can be done concurrently.
The solution I opted for is to have a singleton actor that takes an API model, and maps it to a Core Data object and saves it.
For example, to store an API order model, I have something like this:
func store(
order apiOrder: APIOrder,
currentContext: NSManagedObjectContext?
) -> NSManagedObjectID? {
let context = currentContext ?? self.persistentContainer.newBackgroundContext()
// …
}
In the arguments, there is a context you can pass, in case you need to create additional models and relate them to each other. I am not sure this is how you're supposed to do it, but it seemed to work.
From what I've understood of Core Data and using multiple contexts, the appropriate way use them is with context.perform or context.performAndWait.
However, since my storage helper is an actor, @globalActor actor Storage2 { … }, my storage's methods are actor-isolated.
This gives me warnings / errors in Swift 6 when I try to pass the context for to another of my actor's methods.
let context = …
return context.performAndWait {
// …
if let apiBooking = apiOrder.booking {
self.store(booking: apiBooking, context: context)
/* causes warning:
Sending 'context' risks causing data races; this is an error in the Swift 6 language mode
'self'-isolated 'context' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
Access can happen concurrently
*/
}
// …
}
From what I understand this is because my methods are actor-isolated, but the closure of performAndWait does not execute in a thread safe environment.
With all this, what are my options? I've extracted the store(departure:context:) into its own method to avoid duplicated code, but since I can't call it from within performAndWait I am not sure what to do.
Can I ditch the performAndWait? Removing that makes the warning "go away", but I don't feel confident enough with Core Data to know the answer.
I would love to get any feedback on this, hoping to learn!
Post
Replies
Boosts
Views
Activity
I do not understand what is happening here.
In this example, the toolbar has three Button and one Menu inside an HStack, where the every button has a .buttonStyle(_:) set.
ToolbarItem(placement: .bottomBar) {
HStack {
Button {} label: { Label("Foo", systemImage: "square") }
.buttonStyle(.bordered)
Button {} label: { Label("Bar", systemImage: "circle") }
.buttonStyle(.borderless)
Menu {
Button {} label: { Label("One", systemImage: "figure.arms.open") }
Button {} label: { Label("Two", systemImage: "figure.2.arms.open") }
} label: { Label("Baz", systemImage: "star") }
Button {} label: { Label("Quux", systemImage: "triangle") }
.buttonStyle(.borderedProminent)
}
}
Please note: the menu has a star icon.
This causes only the menu button to appear, but with the first button's icon:
If a single button have has its styles removed, the toolbar appears as expected (in this example all button styles are removed):
This example uses the bottom bar, but also happens when using placement: .navigation, placement: .topBarLeading, placement: .topBarTrailing.
I am trying to figure out how to set a button's label icon to match the button style when inside a list.
These four buttons display differently depending on context – if put inside a List or a VStack:
Button(role: .destructive) {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.automatic)
Button(role: .destructive) {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.bordered)
Button {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.borderedProminent)
Button(role: .destructive) {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.borderedProminent)
Inside a List, which looks weird in my opinion.
For reference, this is what they look like inside a VStack, which is what I'd expect:
I am not sure if this is intentional or a bug. If there are any workaround which do not explicitly set a specific color, like .foreground(.red), please let me know.
When a NavigationSplitView's sidebar has a default selected value, the detail's toolbar item disappears when resizing the view from compact to regular size.
@State private var selectedId: String? = "anything"
NavigationSplitView {
List(selection: $selectedId) {
Text("Sidebar")
}
} detail: {
Text("Detail")
.toolbar {
Button("Button") {}
}
}
There is no way to get the toolbar item back, other than reloading the view. I have no idea why this happens.
In my example I have a dummy Text("Sidebar") without a value, but the problem occurs regardless of its content.
Tested on Simulator iPadOS 18.0, and I've seen this happen in our production app.
With iOS 18.0, this snippet of code that has a NavigationSplitView inside a NavigationStack will not display the sidebar until the navigation transition is completed.
NavigationStack {
NavigationLink("Link") {
NavigationSplitView {
Text("Example")
} detail: {
Text("Does not appear")
}
}
}
Directly after pressing the link, a toolbar briefly appears. Once the transition is completed, the split view's sidebar appears and the toolbar disappears. The detail view does not visually appear at all.
The same problem occurs when using .navigationDestination(…), which I am in our production code.
I've tested this in Xcode 16.0 Previews for iOS 18.0, iPhone SE Simulator iOS 18.0, iPadOS 18.0 when horizontal size class is compact, and on my personal iPhone 13 mini iOS 18.0 I experienced this problem on our production app.
Is there any workaround to not have this delay? Our app is heavily built around this nested approach, and has been working perfectly in previous iOS versions.
I have a peculiar situation, where the first time I present a sheet from a Section that has the header: set, the sheet disappears by itself the first time it is presented.
@State private var show = false
// …
List {
Section {
Text("foo")
} header: {
Text("bar")
}
.sheet(isPresented: $show) {
Text("baz")
}
// just to enable
Button("toggle") {
show = true
}
}
In Xcode I get this warning when I present the sheet (taken from our live app):
Attempt to present <_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView_: 0x10a819e00> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x10a020600> (from <_TtGC7SwiftUI32NavigationStackHostingControllerVS_7AnyView_: 0x10a0cba00>) while a presentation is in progress.
Tested on iOS 17.4.1, iPadOS 17.4.0 (Simulator), Xcode 15.3 Previews.
Circumstances
The circumstances are as following: a Section has to be in a List, and have content:, and header: or footer: set to something, and have the .sheet(…) set on the section itself.
The problem does not occur with these sections:
Section {
Text("…")
}
Section {
} footer: {
Text("…")
}
Section {
Text("…")
} header: {
}
… but the following views have the sheet disappear the first time it is presented:
Section {
Text("…")
} header: {
Text("…")
}
Section {
Text("…")
} footer: {
Text("…")
}
Section {
Text("…")
} header: {
Text("…")
} footer: {
Text("…")
}
Is this a known issue, and are there any known workarounds to present from a Section?
My best guess is to move the .sheet(…) to the parent container, but I'll have to restructure part of my code quite a bit to do so 😕
I wonder if it is possible to modify my application's accessibility text size from within the app, similar to setting the brightness with UIScreen.main.brightness = myBrightness.
I've attempted setting .dynamicTypeSize(…) on my main view, but that setting does not get propagated when displaying sheets or popovers.
When I dismiss a popover and quickly try to navigate to another view by tapping a NavigationLink, both the current view's and the new view's bottom bars momentarily display on-top of each other.
Specifically, this only occurs if I first dismiss the popover by tapping outside of it, then tapping a link before the popover finishes it's dismissal animation. However, this does not happen if i directly tap a visible NavigationLink when the popover is open (weird).
This issue almost always happens when using a List with NavigationLinks inside it, where both toolbars appear momentarily. I assume this is because each row does not act as a button, and you inherently need to tap twice to navigate from a List.
Is this issue known, and are there workarounds? My current solution, in my own code, to not show this to users is by disabling the List when my popover is showing (….disabled(showSettings)).
This code demonstrates my problem:
struct ListNavigationPopoverDuplicateBottomBar: View {
@State private var popover = false
var body: some View {
NavigationStack {
NavigationLink {
Text("").toolbar {
// new view's bottom bar item
ToolbarItem(placement: .bottomBar) { Text("Second toolbar") }
}
} label: {
Text("Link")
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
// button to show popover. (same problem regardless if placed here or directly in the main view)
Button("Popover") {
popover = true
}
.popover(isPresented: $popover) {
Text("A popover")
}
}
ToolbarItem(placement: .bottomBar) {
// first bottom bar
Text("First toolbar")
}
}
}
}
}
Steps to reproduce:
Open popover
Tap empty space (do not tap the NavigationLink)
Quickly tap the NavigationLink before the popover dismissal animation finishes
First post in this forum, please tell me if this is not the right place for this.