I work on an iOS app using SwiftUI and SwiftData. I added a computed property to one of my models - Parent - that uses relationship - array of Child models - data and I started getting strange problems. Let me start with models:
@Model
final class Parent {
var name: String
@Relationship(deleteRule: .cascade, inverse: \Child.parent)
var children: [Child]? = []
var streak: Int {
// Yes, I know that's not optimal solution for such counter ;)
guard let children = children?.sorted(using: SortDescriptor(\.date, order: .reverse)) else { return 0 }
var date = Date.now
let calendar = Calendar.current
for (index, child) in children.enumerated() {
if !calendar.isDate(child.date, inSameDayAs: date) {
return index
}
date = calendar.date(byAdding: .day, value: -1, to: date) ?? .now
}
return children.count
}
init(name: String) {
self.name = name
}
}
@Model
final class Child {
var date: Date
@Relationship(deleteRule: .nullify)
var parent: Parent?
init(date: Date, parent: Parent) {
self.date = date
self.parent = parent
}
}
At first everything works as expected. The problem arises once I try to remove one of child from the parent instance. I remove the value from context and save changes without any problems, at least not ones that can be caught by do { } catch. But instead of refreshing UI I get an signal SIGABRT somewhere inside SwiftData internals that points to the line where I'm trying (inside View body) get a child from a Query:
struct LastSevenDaysButtons: View {
@Environment(\.modelContext)
private var modelContext
@Query
private var children: [Child]
private let dates: [Date]
private let parent: Parent
init(for parent: Parent) {
self.parent = parent
var lastSevenDays = [Date]()
let calendar = Calendar.current
let firstDate = calendar.date(byAdding: .day, value: -6, to: calendar.startOfDay(for: .now)) ?? .now
var date = firstDate
while date <= .now {
lastSevenDays.append(date)
date = calendar.date(byAdding: .day, value: 1, to: date) ?? .now
}
dates = lastSevenDays
let parentId = parent.persistentModelID
_children = Query(
filter: #Predicate {
$0.parent?.persistentModelID == parentId && $0.date >= firstDate
},
sort: [SortDescriptor(\Child.date, order: .reverse)],
animation: .default
)
}
var body: some View {
VStack {
HStack(alignment: .top) {
ForEach(dates, id: \.self) { date in
// Here is the last point on stack from my code that I see
let child = children.first { $0.date == date }
Button {
if let child {
modelContext.delete(child)
} else {
modelContext.insert(Child(date: date, parent: parent))
}
do {
try modelContext.save()
} catch {
print("Can't save changes for \(parent.name) on \(date.formatted(date: .abbreviated, time: .omitted)): \(error.localizedDescription)")
}
} label: {
Text("\(date.formatted(date: .abbreviated, time: .omitted))")
.foregroundStyle(child == nil ? .red : .blue)
}
}
}
}
}
}
The LastSevenDaysButtons View is kind of deep in a View hierarchy:
RootView -> ParentList -> ParentListItem -> LastSevenDaysButtons
However once I move insides of ParentList to RootView application works just fine, although I see and warning: === AttributeGraph: cycle detected through attribute 6912 ===.
What could be that I do wrong in here? I believe it must something I'm missing here, but after 2 days of debug, trial and errors, I can't think clearly anymore.
Here is the minimal repro I managed to create: Signal SIGABRT on accessing values from SwiftData query
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Post
Replies
Boosts
Views
Activity
I have a setup:
Collection view with compositional layout
a self sizing cell inside
a subview inside the cell
and unrelated view outside the collection view
I would like to:
modify the layout (constraints) of the cell inside the collection view with UIView.animate
trigger an animated layout update of collection view
synchronize the position of an unrelated view to the position of one of the subviews of a collection view cell
What I tried:
UIView.animate(withDuration: 0.25) {
cellViewReference.updateState(state: state, animated: false)
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutIfNeeded()
someOtherViewOutsideCollectionView.center = cellViewReference.getPositionOfThatOneViewInWindowCoordinateSystem()
}
What I'm expecting:
after invalidateLayout, the layout update of the collection view is merely scheduled, but not yet performed
layoutIfNeeded forces an update on the collectionViewLayout + update on the frames of the views inside the UICollectionViewCells
all the frames become correct to what they will look like after the animation is performed
I call getPositionOfThatOneViewInWindowCoordinateSystem and it gives me the position of the view after the uicollectionview AND the cell's layout has updated
What happens instead:
getPositionOfThatOneViewInWindowCoordinateSystem returns me an old value
I am observing that the bounds of the cell didn't actually change during layoutIfNeeded
And moreover, the bounds change without animation, instantly
Question:
how to animate self sizing cell size change due relayout
how to synchronize outside views with collection views
Hello!
I have a destination navigation which is TabVIew where each tab item is ScrollView. And when scrolling content of any of tab items is underneath navigation bar its background is always hidden. But at the same time tab bar background is toggled depending on scrolling content position.
I expected it would work with TabView the same as with any other view.
Is it supposed to work like that?
VisionOS developers want to modify the color of the button when they detect the eyes focusing on it. May I ask if there are any relevant methods that can be implemented? At present, there is a default highlight color when the eyes focus on the button, but this color is too dim. Not obvious enough, how can I modify it?
thank you!
I create a notification with an image attachment:
let center = UNUserNotificationCenter.current()
center.delegate = self
let content = UNMutableNotificationContent()
// some more stuff…
let paths = NSSearchPathForDirectoriesInDomains(
FileManager.SearchPathDirectory.documentDirectory,
FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory = paths[0] as NSString
let fileExtPNG = "#" + "\(imageName)" + " photo.png"
let fileNamePNG = documentsDirectory.appendingPathComponent(fileExtPNG) as String
url = URL(fileURLWithPath: fileNamePNG)
let attachment = try UNNotificationAttachment(identifier: "Image", url: url, options: nil)
content.attachments = [attachment]
I then add the request:
let request = UNNotificationRequest(identifier:requestIdentifier, content: content, trigger: nil)
center.removePendingNotificationRequests(withIdentifiers: [requestIdentifier])
center.add(request) {(error) in }
Problem: when I later test (once notification has been registered), the file do not exist anymore at the url.
I've commented out the add request to confirm.
I have a work around, by creating a temporary copy of the file at the URL and pass it in the attachment.
Then, everything works fine and the copy is deleted.
But that's a bit bizarre. What am I missing here ?
Is it the default behavior that the standard back swipe (interactivePopGestureRecognizer) does not work when running a designed for iPhone app on an iPad?
To my knowledge, all apps behave this way.
Are there any workarounds?
I was hoping for an update of SwiftData which adopted the use of shared and public CloudKit containers, in the same way it does for the private CloudKit container.
So firstly, a big request to any Apple devs reading, for this to be a thing!
Secondly, what would be a sensible way of adding a shared container in CloudKit to an existing app that is already using SwiftData?
Would it be possible to use the new DataStore method to manage CloudKit syncing with a public or shared container?
I opened a feedback ticket (FB16508762) but maybe someone in the community already found a workaround while the feedback reaches the maintainers.
When I put a Button inside a ScrollView, the tap animation stops working reliably and works only when the user taps and holds the button for a short time. The reasons, I believe is related to the fact that isPressed of configuration does not change and the default button styles use it to animate the tap.
import SwiftUI
struct DebuggingButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.onChange(of: configuration.isPressed, { oldValue, newValue in
print("Is pressed: \(oldValue) -> \(newValue)")
})
}
}
struct ContentView: View {
var body: some View {
VStack {
Text("Buttons inside scroll view respond to taps as expected, however isPessed value of the configuration do not change unless the user press and hold it. Try to press the promiment button quickly or use the debug button and observe the console log.")
ScrollView {
VStack {
Button("Button Inside ScrollView") {
print("Button tapped")
}
.buttonStyle(.borderedProminent)
Button("Button Inside ScrollView (printing isPressed)") {
print("Button tapped")
}
.buttonStyle(DebuggingButtonStyle())
}
}
.border(FillShapeStyle(), width: 2)
Spacer()
Text("For reference, here is a button outside of a ScrollView. Tap the promiment button to observe how the button is expected to animate in respnse to a press.")
VStack {
Button("Button Outside ScrollView") {
print("Button tapped")
}
.buttonStyle(.borderedProminent)
Button("Button Outside ScrollView (printing isPressed)") {
print("Button tapped")
}
.buttonStyle(DebuggingButtonStyle())
}
}
.padding()
}
}
My usage of TextField.focused() works fine in Xcode 14.3.1 but is broken as of Xcode 15. I first noticed it in the second beta and it's still broken as of the 4th beta.
Feedback / OpenRadar # FB12432084
import SwiftUI
struct ContentView: View {
@State private var text = ""
@FocusState var isFocused: Bool
var body: some View {
ScrollView {
TextField("Test", text: $text)
.textFieldStyle(.roundedBorder)
.focused($isFocused)
Text("Text Field Is Focused: \(isFocused.description)")
}
}
}
The pitch slider is not supported on tvOS yet it displays when using the Map() view. Does anyone know how to hide it? It's really getting in the way of my UI.
Starting from Sequoia release 15.2 apps crash with following call stack, when adding static text controls. First call to [NSTextField setStringValue] causes following crash
0 libobjc.A.dylib 0x19f2f5820 objc_msgSend + 32
1 AppKit 0x1a3355460 -[NSCell _objectValue:forString:errorDescription:] + 144
2 AppKit 0x1a3355348 -[NSCell setStringValue:] + 48
3 AppKit 0x1a33af9fc -[NSControl setStringValue:] + 104
4 AppKit 0x1a3d1f190 -[NSTextField setStringValue:] + 52
It happens on specific MacBook Pro models(16 in MacBook Pro).
Crash analysis found that isa pointer of the object was corrupted for the object NSCell. Enabled Zombies in debugging, not found any issue. Also tried address sanitizer. Since the issue started with a recent release of macOS, any changes in the Appkit in the recent releases trigger the crash? Any help/suggestions would be appreciated.
In a macOS Swift app I'm trying to get mouse events to not coalesce. This used to work in an Obj-C app by calling [NSEvent setMouseCoalescingEnabled:NO]. setMouseCoalescingEnabled is not exposed in the Swift version of NSEvent. I built a bridge over to a .mm file that calls [NSEvent setMouseCoalescingEnabled:NO] and checked NSEvent.isMouseCoalescingEnabled in Swift after calling and it returns false saying that coalescing is disabled. The mouse is still skipping points (func mouseDragged(with theEvent: NSEvent)) when it is dragged across the window fast. I'm calling [NSEvent setMouseCoalescingEnabled:NO] in applicationDidFinishLaunching().
Hello.
I have a scenario where a hover effect is being shown for a button that is disabled. Usually this doesn't happen but when you wrap the button in a Menu it doesn't work properly.
Here is some example code:
struct ContentView: View {
var body: some View {
NavigationStack {
Color.green
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Menu("Menu") {
Button("Disabled Button") {}
.disabled(true)
.hoverEffectDisabled() // This doesn't work.
Button("Enabled Button") {}
}
}
}
}
}
}
And here is what it looks like:
This looks like a SwiftUI bug. Any help is appreciated, thank you!
I have a UITextField with UITextContentType equal to oneTimeCode.
It works as expected if the message is in English and the keyword "OTP" exists.
It doesn't work if the message is in Greek and the keyword "OTP" is translated also in greek.
Is the OTP keyword really needed? Is there any alternative? Which are the keywords for any case? Are these keywords only in English?
Thanks in advance!
I would like to get user's heart rate zones values in my watchOS app, is there any way to get it from HealthKit?
Furthermore, is it possible to display the Apple heart rate zones widget in my watchOS app?
I'm creating an app which gamifies Screen Time reduction. I'm running into an issue with apples Screen Time setting where the user can disable my apps "Screen Time access" and get around losing the game.
Is there a way to detect when this setting is disabled for my app? I've tried using AuthorizationCenter.shared.authorizationStatus but this didn't do the trick. Does anyone have an ideas?
My app uses the SwiftUI Map control to display annotations. When annotations contain buttons AND the map has an .onTapGesture modifier, annotation button taps aren’t always recognized.
Given the following ContentView, run it on an iOS device or simulator. Tap the buttons. Since iOS 18.0, some of the buttons won't respond to tap. I've also tried using the onTapGesture instead of a button, and that shows the same issue.
This was not a problem under iOS 17.x: it has only shown up for my users since updating to iOS 18. Additionally, this issue does not occur when running on either macOS 15.0 or visionOS 2.0 when running in "designed for iPad" mode.
Note that there was previously a tap issue on VisionOS 1.x (designed for iPad), where no tap gestures were received by annotations. I'm curious if this issue could be related.
I have filed a report in Feedback Assistant (FB15273909).
struct ContentView: View {
private let center = CLLocationCoordinate2D(latitude: 37.77925, longitude: -122.41924)
@State private var label: String = "tap a button"
@State private var locations: [Location] = []
var body: some View {
Map {
ForEach(locations) { location in
Annotation(location.name, coordinate: location.coordinate) {
Button(location.name) {
print("\(location.name) tapped")
label = "\(location.name) tapped"
}
.buttonStyle(.borderedProminent)
}
.annotationTitles(.hidden)
}
}
.onTapGesture { point in
print("Map tapped")
label = "map tapped"
}
.safeAreaInset(edge: .top) {
Text(label)
.padding()
.background(.thinMaterial)
.clipShape(.rect(cornerRadius: 10))
}
.navigationTitle("Test Page")
.navigationBarTitleDisplayMode(.inline)
.task {
for index in 1...16 {
locations.append(Location(name: "location \(index)",
coordinate: CLLocationCoordinate2D(latitude: center.latitude + Double.random(in: -0.02...0.02),
longitude: center.longitude + Double.random(in: -0.02...0.02))))
}
}
}
private struct Location: Identifiable {
let id: UUID = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
}
}
#Preview {
ContentView()
}
I'm getting the following error in my SwiftUI code:
"Main actor-isolated property 'avatarImage' can not be referenced from a Sendable closure"
I don't understand how to fix it.
This happens in the following code:
You can copy-paste this into an empty project and make sure to have Swift 6 enabled under the Build Settings > Swift Language Version
import PhotosUI
import SwiftUI
public struct ContentView: View {
@State private var avatarItem: PhotosPickerItem?
@State private var avatarImage: Image?
@State private var avatarData: Data?
public var body: some View {
VStack(spacing: 30) {
VStack(alignment: .center) {
PhotosPicker(selection: $avatarItem, matching: .images) {
if let avatarImage {
avatarImage
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.foregroundColor(.gray)
.background(.white)
.clipShape(Circle())
.opacity(0.75)
.overlay {
Image(systemName: "pencil")
.font(.title)
.shadow(radius: 5)
}
} else {
Image(systemName: "person.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
.foregroundColor(.gray)
.background(.white)
.clipShape(Circle())
.opacity(0.75)
.overlay {
Image(systemName: "pencil")
.font(.title)
.shadow(radius: 5)
}
}
}
}
}
.onChange(of: avatarItem) {
Task {
if let data = try? await avatarItem?.loadTransferable(
type: Data.self
) {
if let processed = processImage(data: data) {
avatarImage = processed.image
avatarData = processed.data
} else {
}
}
}
}
}
private func processImage(data: Data) -> (image: Image?, data: Data?)? {
guard let uiImage = UIImage(data: data)?.preparingForDisplay() else {
return nil
}
// Check original size
let sizeInMB = Double(data.count) / (1024 * 1024)
// If image is larger than 1MB, compress it
if sizeInMB > 1.0 {
guard let compressedData = uiImage.compress() else { return nil }
return (Image(uiImage: uiImage), compressedData)
}
return (Image(uiImage: uiImage), data)
}
}
#Preview {
ContentView()
}
public extension UIImage {
func compress(to maxSizeInMB: Double = 1.0) -> Data? {
let maxSizeInBytes = Int(
maxSizeInMB * 1024 * 1024
) // Convert MB to bytes
var compression: CGFloat = 1.0
let step: CGFloat = 0.1
var imageData = jpegData(compressionQuality: compression)
while (imageData?.count ?? 0) > maxSizeInBytes, compression > 0 {
compression -= step
imageData = jpegData(compressionQuality: compression)
}
return imageData
}
}
Generic parameter 'V' could not be inferred ERROR
macOS: 15.0
macFUSE: 4.8.3
I am using rclone + macFUSE and mount my netdisk where it has created three subdirectories in its root directory: /user, /share, and /group.
When I save a file to /[root]/user using NSSavePanel and name it test.txt, I expect the file to be saved as:
/[root]/user/test.txt
However, occasionally, the delegate method:
- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError {
}
returns an incorrect path:
/[root]/test.txt
This issue only occurs when selecting /user. The same operation works correctly for /share and /group.
Is there any logs I could provide to help solving this issue?
Many thanks!