I am getting memory leak when using .onSubmit view modifier on TextField.
Here is a simplified reproducible example:
struct ListView: View {
var body: some View {
NavigationStack {
List(0..<100) { i in
NavigationLink("Select \(i)", value: i)
}
.navigationDestination(for: Int.self) { selection in
DetailView()
.navigationTitle("Page: \(selection)")
}
}
}
}
struct DetailView: View {
@State private var viewModel = DetailViewModel()
var body: some View {
TextField(
"",
text: $viewModel.text,
prompt: Text("Type in")
)
.padding()
.onSubmit {
print(viewModel.text)
}
}
}
@Observable
final class DetailViewModel {
var text: String = ""
deinit {
print("Deinit \(Self.self)")
}
}
Navigate to DetailView by selecting a row on ListView page and start typing in TextField, hit the submit button on keyboard and this DetailView with DetailViewModel will be leaked after navigating back to ListView, deinit will not be called.
Commenting out .onSubmit {} part fixes the leak.
What I observed also, is that once I open the same page or other, It will create new DetailView instance with it's view model and previously leaked one will be released, possible indicating that system somehow holds the last active TextField until new one has become active.
I tried calling UIApplication.shared.resignFirstResponder() in onDissapear{} but this does not fix the leak.
Only way the leak is fixed, is by using deprecated TextField initializer:
init(_ titleKey: LocalizedStringKey, text: Binding<String>, onCommit: @escaping () -> Void)
where onCommit is essentially doing the same as onSubmit.
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Posts under SwiftUI tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I Am interested in coding, and built my fist app that is an app that has a picture of Niagara Falls with corner radius of 10, But, every time I start the build, it says: Thread 1: EXC_BAD_ACCESS (code=2, address=0x16b123f20) not sure what to do now.
For my macOS app, I'm trying to change the mouse cursor to a pointing hand while hovering over a specific view. However, when the view is scaled with an animation triggered by hovering (using .scaleEffect() and .animation()), the cursor doesn't change as expected. Is there any workaround to fix this?
This is a sample code:
struct ContentView: View {
@State private var hovering = false
var body: some View {
VStack {
Text("Hover me")
.padding()
.background(hovering ? Color.blue : Color.gray)
.scaleEffect(hovering ? 1.2 : 1.0)
.animation(.linear(duration: 0.2), value: hovering)
.onHover { hovering in
self.hovering = hovering
if hovering {
NSCursor.pointingHand.push()
} else {
NSCursor.pop()
}
}
}
.frame(width: 200, height: 200)
}
}
This is how it works:
As you can see, when the pointer enters the view, the cursor changes momentarily before reverting back to the arrow icon.
I also tried using NSTrackingArea with an NSView placed over the view, but it did not solve the issue. It might be that the combination of .scaleEffect() and .animation() is causing a forced cursor reset (possibly related to the use of NSWindow.disableCursorRects() or something similar). However, I'm not entirely sure.
Any insights or suggestions would be greatly appreciated. Thanks!
Hi everyone,
I'm working on a tvOS app using SwiftUI, and I want to disable the focus effect (the default focus glow/bounce) on a specific Button. According to the documentation:
/// - Parameter disabled: A Boolean value that determines whether this view
/// can display focus effects.
/// - Returns: A view that controls whether focus effects can be displayed
/// in this view.
I used .focusEffectDisabled(true) on the Button, expecting the focus style to be completely disabled for that view. However, this doesn’t seem to have any effect in my tvOS 17+ app – the button still shows the default focus visual effect when focused.
Here’s a simplified example:
Button("Click Me") {
// action
}
.focusEffectDisabled(true)
This still shows the bounce/glow focus effect. Am I missing something, or is this a bug?
Has anyone managed to fully disable the focus effect for a view (especially Button) in tvOS using SwiftUI? Any workarounds or additional modifiers I should apply?
Thanks in advance!
All of a sudden, after iOS 18.4 was released, I am having tons of navigation problems in my app in production. Buttons navigating to empty pages, views seeming to 'freeze', top navigation bar mismatched with the content of the page. It seems that iOS 18.4 broke a critical piece of UIKit + SwiftUI bridging functionality that my project relies on.
My application is written with both UIKit and SwiftUI components. Here is a breakdown of my setup:
UIApplicationDelegate >
UIWindow >
rootViewController of window is a UITabBarController >
each tab is a UINavigationController
rootViewController of nav controller is a UIHostingController >
rootView of the hosting controller is a SwiftUI View
In my SwiftUI views, I have been using NavigationLink for horizontal 'push' style navigation in my SwiftUI views. I do not use NavigationView, I only rely on the bridging capabilities of UINavigationController to action on my NavigationLinks. This has never been an issue, until iOS 18.4 was released. Now, when running iOS 18.4, I am having all sorts of unexpected behavior in the UI. I will break down 2 of these use cases here:
Use case A:
In one of my SwiftUI views, I have a ForEach for which each element's view is a NavigationLink. This is using the NavigationLink(_ destination:,label:) initializer. Navigating forward from here works/looks normal.
However, once I try to navigate backward from that destination (tap the 'Back' button in top left), the view goes blank and the navigation bar at the top of the page (which is maintained by the UINavigationController instance) does not change. If I call popToRootViewController on that nav controller, the navigation bar at the top of the page returns to its normal state, but the view is still blank.
It is not until after I have called popToRootViewController, and then navigate to a different tab of the UITabBarController and return to the initial tab, does the SwiftuI content view (the one with the ForEach) finally redraw and the view hierarchy is restored.
Here is a warning that is logged in the console when I tap the 'Back ' button:
Top view controller's view unexpectedly not in window for navigation transition. Skipping layout. nav = <UINavigationController: 0x1110bbe00>, topVC = <TtGC7SwiftUI19UIHostingControllerV5MyApp10MyPage: 0x106814e00>
EDIT: If I replace the NavigationLink with a call to UINavigationController.pushViewController, I am still seeing the exact same behavior. Pressing back button makes the view empty > need to pop to root view controller and switch tabs in order to restore the view.
Use case B
Another instance of this issue happens whenever I try to use a NavigationLink inside of a view that itself was the destination of a NavigationLink in its parent view (i.e.: Root view > detail view > sub-detail view). For example, take the detail view destination in use case A. I have tapped a NavigationLink from the ForEach and landed on the detail view. Again, so far things work/look normal. Now, if I tap on another NavigationLink from that detail view, the view does not transition to the new page. The top navigation bar does transition, and shows the title and actions associated with this second destination. However, the view of this second destination is not displayed.
It is worth noting that the same warning I mentioned above is also logged when I tap the NavigationLink to navigate to this second destination.
Top view controller's view unexpectedly not in window for navigation transition. Skipping layout. nav = <UINavigationController: 0x109859400>, topVC = <TtGC7SwiftUI19UIHostingControllerVVS_19BridgedPresentation8RootView: 0x300ab8000>
Strangely, if I switch to a different tab of the UITabBarController and back to the initial tab, this second destination's view is successfully rendered. It seems that switching tabs in this UITabBarController is calling something in either SwiftUI or UIKit that is redrawing my views.
Conclusion
This is a serious issue with UIKit + SwiftUI bridging support. I have never had problems like this until devices started running iOS 18.4, and there is nothing in the iOS 18.4 changelog that suggests this was an intentional change. All of a sudden, after updating to the latest iOS version, my app is totally broken.
I want to be clear that I'm not using deprecated NavigationLink methods in these instances. My app's minimum deployment target is iOS 16.
I know that there are more modern navigation APIs like navigation stack, etc. I am looking for answers about my use case: whether it is officially unsupported as of iOS 18.4, whether this setup should be supported and this is indeed some sort of bug in iOS, or anything in-between. I'm happy to provide formatted code if needed for discussion purposes. This is about my entire app's view hierarchy so there are a lot of disparate lines of code that make up this problem.
Hi everyone,
I'm facing an issue where I cannot write a file to a shared App Group container in my tvOS app when running on a real device. My code works perfectly on the simulator, but fails on a physical device with a permissions error. I’ve set up an App Group with a custom identifier (e.g., group.<my.identifier>), and it’s correctly configured in the Capabilities section of Xcode for both my main app and widget targets.
Here’s the code I’m using to save a test file:
func saveTestFile() {
guard let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.<my.identifier>") else {
print("Couldn't access the Group URL.")
return
}
let containerURL = groupURL.appendingPathComponent("Library", isDirectory: true)
if FileManager.default.isWritableFile(atPath: containerURL.path) {
print("Directory IS writable")
} else {
print("Directory IS NOT writable")
}
let fileURL = containerURL.appendingPathComponent("test.txt")
let content = "Hello App Group!"
do {
try content.write(to: fileURL, atomically: true, encoding: .utf8)
print("File test.txt is saved at: \(fileURL.path)")
} catch {
print("Error while saving the file: \(error)")
}
}
Console:
Directory IS NOT writable
Error while saving the file: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test.txt” in the folder “”." UserInfo={NSFilePath=/private/var/mobile/Containers/Shared/AppGroup//Library/test.txt, NSURL=file:///private/var/mobile/Containers/Shared/AppGroup//Library/test.txt, NSUnderlyingError=0x14387fbe0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I’ve tried saving the file in different subdirectories within the App Group container:
Directly in groupURL (root of the container).
In groupURL.appendingPathComponent("Library").
In groupURL.appendingPathComponent("Caches").
Do you have any ideas what is the problem?
Thanks in advance for any help!
Hi there, I got two models here:
Two Models, with Many-To-Many Relationship
@Model
final class PresetParams: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
var positionX: Float = 0.0
var positionY: Float = 0.0
var positionZ: Float = 0.0
var volume: Float = 1.0
@Relationship(deleteRule: .nullify, inverse: \Preset.presetAudioParams)
var preset = [Preset]()
init(position: SIMD3<Float>, volume: Float) {
self.positionX = position.x
self.positionY = position.y
self.positionZ = position.z
self.volume = volume
self.preset = []
}
var position: SIMD3<Float> {
get {
return SIMD3<Float>(x: positionX, y: positionY, z: positionZ)
}
set {
positionX = newValue.x
positionY = newValue.y
positionZ = newValue.z
}
}
}
@Model
final class Preset: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
var presetName: String
var presetDesc: String?
var presetAudioParams = [PresetParams]() // Many-To-Many Relationship.
init(presetName: String, presetDesc: String? = nil) {
self.presetName = presetName
self.presetDesc = presetDesc
self.presetAudioParams = []
}
}
To be honest, I don't fully understand how the @Relationship thing works properly in a Many-To-Many relationship situation. Some tutorials suggest that it's required on the "One" side of an One-To-Many Relationship, while the "Many" side doesn't need it.
And then there is an ObservableObject called "ModelActors" to manage all ModelActors, ModelContainer, etc.
ModelActors, ModelContainer...
class ModelActors: ObservableObject {
static let shared: ModelActors = ModelActors()
let sharedModelContainer: ModelContainer
private init() {
var schema = Schema([
// ...
Preset.self,
PresetParams.self,
// ...
])
do {
sharedModelContainer = try ModelContainer(for: schema, migrationPlan: MigrationPlan.self)
} catch {
fatalError("Could not create ModelContainer: \(error.localizedDescription)")
}
}
}
And there is a migrationPlan:
MigrationPlan
// MARK: V102
// typealias ...
// MARK: V101
typealias Preset = AppSchemaV101.Preset
typealias PresetParams = AppSchemaV101.PresetParams
// MARK: V100
// typealias ...
enum MigrationPlan: SchemaMigrationPlan {
static var schemas: [VersionedSchema.Type] {
[
AppSchemaV100.self,
AppSchemaV101.self,
AppSchemaV102.self,
]
}
static var stages: [MigrationStage] {
[AppMigrateV100toV101, AppMigrateV101toV102]
}
static let AppMigrateV100toV101 = MigrationStage.lightweight(fromVersion: AppSchemaV100.self, toVersion: AppSchemaV101.self)
static let AppMigrateV101toV102 = MigrationStage.lightweight(fromVersion: AppSchemaV101.self, toVersion: AppSchemaV102.self)
}
// MARK: Here is the AppSchemaV101
enum AppSchemaV101: VersionedSchema {
static var versionIdentifier: Schema.Version = Schema.Version(1, 0, 1)
static var models: [any PersistentModel.Type] {
return [ // ...
Preset.self,
PresetParams.self
]
}
}
Fails on iOS 18.3.x: "Failed to fulfill link PendingRelationshipLink"
So I expected the SwiftData subsystem to work correctly with version control. A good news is that on iOS 18.1 it does work. But it fails on iOS 18.3.x with a fatal Error:
"SwiftData/SchemaCoreData.swift:581: Fatal error: Failed to fulfill link PendingRelationshipLink(relationshipDescription: (<NSRelationshipDescription: 0x30377fe80>), name preset, isOptional 0, isTransient 0, entity PresetParams, renamingIdentifier preset, validation predicates (), warnings (), versionHashModifier (null)userInfo {}, destination entity Preset, inverseRelationship (null), minCount 0, maxCount 0, isOrdered 0, deleteRule 1, destinationEntityName: "Preset", inverseRelationshipName: Optional("presetAudioParams")), couldn't find inverse relationship 'Preset.presetAudioParams' in model"
Fails on iOS 17.5: Another Error
I tested it on iOS 17.5 and found another issue: Accessing or mutating the "PresetAudioParams" property causes the SwiftData Macro Codes to crash, affecting both Getter and Setter. It fails with an error:
"EXC_BREAKPOINT (code=1, subcode=0x1cc1698ec)"
Tweaking the @Relationship marker and ModelContainer settings didn't fix the problem.
Hi,
I got a problem with severe hangs when I use code like this on tvOS 18.2
If I try to use HStack instead of LazyHStack inside the scrollview then the problem does not occur any more but then the scroll performance is compromised and the vertical scroll is no longer that smooth. Does someone has any experience with this? Is this SwiftUI problem or am I missing something?
ScrollView {
LazyVStack {
ForEach(0...100, id: \.self) { _ in
ScrollView {
LazyHStack {
ForEach(0...20, id: \.self) { _ in
Color.red.frame(height: 300)
}
}
}
}
}
}
Given a SwiftUI sheet with presentation detent(s):
struct ContentView: View {
@State var isSheetOpen = false
var body: some View {
Button("Open Sheet") {
isSheetOpen = true
}
.sheet(isPresented: $isSheetOpen) {
Color.black
.presentationDetents([.medium])
}
}
}
If you:
open the sheet
close it and rapidly open it again
the second presentation will ignore the detent and open a full screen sheet instead.
Xcode 16.3, iOS 18.4
Bug report FB17115890
Consider the following example of a List view containing sections of rows.
import SwiftUI
struct ListSectionView: View {
@State private var sectionHeaders = ["section1", "section2", "section3"]
var body: some View {
List {
ForEach(sectionHeaders, id: \.self) { sectionHeader in
Section(header: Text(sectionHeader)) {
Text("1")
Text("2")
Text("3")
}
}
.onMove { indices, newOffset in
// ...
}
}
}
}
I would like to reorder the sections within the list by dragging the respective section header to its new position in the list - similar to moving individual rows via onMove-dragging but with the sections instead of the rows.
The above approach does not work. It "activates" moving the rows and then the .onMove code acts on those. The sections themselves are not moved.
How can I move/reorder the sections within the list?
Thanks.
I have been working on a feature, where I have a List in SwiftUI with previous and next data loading, user can scroll up and down to load previous/next page data.
Recently, I faced one accessibility issue while testing voice-over, when user lands on the listing screen and swipe across the screen from navigation and when focus comes on list it should highlight the first item visible.
But when user swipes back:
Should it load the previous data and announce the previous item or it should go back to the navigation items?
If it loads the previous item, what if the user wants to go to the navigation to switch to other actions and vice-versa?
Did anyone come across this kind of issue? What can be the standard expected behavior in this case if list has both previous and next page scroll?
I different tried gestures https://support.apple.com/en-in/guide/iphone/iph3e2e2281/ios, but it isn't working
Hello,
given this following simple SwiftUI setup:
struct ContentView: View {
var body: some View {
CustomFocusView()
}
}
struct CustomFocusView: View {
@FocusState private var isFocused: Bool
var body: some View {
color
.frame(width: 128, height: 128)
.focusable(true)
.focused($isFocused)
.onTapGesture {
isFocused.toggle()
}
.onKeyPress("a") {
print("A pressed")
return .handled
}
}
var color: Color {
isFocused ? .blue : .red
}
}
If I run this via Mac – Designed for iPad, the CustomFocusView toggles focus as expected and cycles through red and blue.
Now if I run this same exact code via Mac Catalyst absolutely nothing happens and so far I wasn't able to ever get this view to accept focused state. Is this expected? I would appreciate if anyone could hint me on how to get this working.
Thank and best regards!
Take a look at this simple code:
import Cocoa
import SwiftUI
struct DemoView: View {
var body: some View {
Text("Click me!")
.onTapGesture {
print("Clicked")
}
}
}
class FlippedView: NSView {
override var isFlipped: Bool {
return true
}
}
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stackView = NSStackView()
stackView.orientation = .vertical
stackView.alignment = .leading
stackView.spacing = 0
stackView.translatesAutoresizingMaskIntoConstraints = false
let hostView = NSHostingView(rootView: DemoView())
stackView.addArrangedSubview(hostView)
let scrollView = NSScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
let flippedView = FlippedView()
flippedView.addSubview(stackView)
scrollView.documentView = flippedView
view.addSubview(scrollView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
}
I need my scroll view to start at the very top, so i put it inside a flipped document view.
But now .onTapGesture does not fire.
Hi, I'm trying to make a weather menu bar app, and I want to have it so that the icon of the app in the menu changes with the actual weather, but the icon isn't showing up. There is still a space in the menu bar where I can click and open the app, it's just that the icon has disappeared. Any ideas to fix it?
Hi, everyone
I have an app already in production that uses SwiftUI's lifecycle (paired with an AppDelegate). Due to some specific behaviour of the app, we decided to migrate the app to use UIKit's lifecycle, adding the corresponding SceneDelegate to the app, as well as modifying the Info.plist file accordingly to accommodate to these new changes.
Although everything seems to work when installing the app from zero, when installing it on top of another version, the screen goes black and the user cannot interact with the app at all unless they reinstall it completely. As I've read online, iOS is reusing the window configuration from the previous execution of the app. I know this because the AppDelegate's application(application:connectingSceneSession:options) is not being called when coming from a previous version of the app.
I would love to know what can I do to make this work because, as you may understand, we cannot ask our user base to reinstall the application.
Thank you very much.
Hi, Im new to SwiftUI and Im trying to implement some drag and drop functionality for some tabs in my application.
Im using .draggable(_) and .dropDestination for this and the issue I have is that as I drag the view, the mouse cursor changes to the copy cursor with the green plus sign and I don't like it but I can't figure out how to avoid it.
Any help would be appreciated.
In the attached code snippet:
struct ContentView: View {
@State private var vText: String = ""
var body: some View {
TextField("Enter text", text: Binding(
get: { vText },
set: { newValue in
print("Text will change to: \(newValue)")
vText = newValue
}
))
}
}
I have access to the newValue of the text-field whenever the text-field content changes, but how do I detect which key was pressed? I can manually get the diff between previous state and the new value to get the last pressed char but is there a simpler way? Also this approach won't let me detect any modifier keys (such as Alt, Ctrl etc) that the user may have pressed.
Is there a pure swift-ui approach to detect these key presses?
I'm using Xcode 14.3.1 on macOS 13.5, and I've managed to reproduce my issue in a trivial application. All the project settings are left at the defaults for a macOS project.
It looks like using a GroupBox breaks the ability of XCTest to find popovers connected to buttons (I suspect any UI element) inside the GroupBox.
The debug console output from the code below lists 15 descendants from my window with the outside-the-GroupBox popover open, and one of them is definitely a popover. With the inside-the-GroupBox popover open, my window only shows nine descendants, and no popover (the rest of the difference is the popover's contents). It's simple enough I don't see what I could be doing wrong:
import SwiftUI
@main
struct GroupBox_Popover_DemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var outsidePopoverPresented: Bool = false
@State var insidePopoverPresented: Bool = false
var body: some View {
VStack {
Button("Outside GroupBox") {
outsidePopoverPresented = true
}
.popover(isPresented: $outsidePopoverPresented,
attachmentAnchor: .point(.leading),
arrowEdge: .leading) {
Popover(selected: .constant("Item A"), isPresented: $outsidePopoverPresented)
}
.padding()
GroupBox {
Button("Inside GroupBox") {
insidePopoverPresented = true
}
.popover(isPresented: $insidePopoverPresented,
attachmentAnchor: .point(.leading),
arrowEdge: .leading) {
Popover(selected: .constant("Item B"), isPresented: $insidePopoverPresented)
}
.padding()
}
}
.padding()
}
}
struct Popover: View {
@Binding var selected: String
@Binding var isPresented: Bool
var body: some View {
VStack(alignment: .leading) {
Picker("", selection: $selected) {
Text("Item A").tag("Item A")
Text("Item B").tag("Item B")
Text("Item C").tag("Item C")
}
.pickerStyle(.radioGroup)
HStack {
Spacer()
Button("Cancel") {
isPresented = false
}
}
}
.padding()
.frame(width: 200)
}
}
Then in my UI tests:
import XCTest
final class GroupBox_Popover_DemoUITests: XCTestCase {
let mainWindow = XCUIApplication().windows
override func setUpWithError() throws {
continueAfterFailure = false
XCUIApplication().launch()
}
func testPopovers() {
let myDescendants = mainWindow.descendants(matching: .any)
mainWindow.buttons["Outside GroupBox"].click()
print("Window descendants with outside popover open:")
print(myDescendants.debugDescription)
mainWindow.popovers.buttons["Cancel"].click()
mainWindow.buttons["Inside GroupBox"].click()
print("Window descendants with inside popover open:")
print(myDescendants.debugDescription)
mainWindow.popovers.buttons["Cancel"].click()
XCTAssert(true, "Test was able to hit cancel on both popovers.")
}
}
Any ideas? Have I missed unchecking some "Ignore anything in a GroupBox" checkbox somewhere?
Hey,
We're loading data from Core Data, and for some reason an error is thrown. This is happening extremely rarely and we haven't been able to reproduce it.
The error thrown has the following description:
Åtgärden kunde inte slutföras. (ScreenGenieCore.EnrolledView.(unknown context at $10087af4c).EnrolledError fel 0.)
It is occurring in an app written in SwiftUI when the user taps a button. The managed object context is initiated in app init and provided to the view using the @environment modifier. So the viewContext should always exist. Still it throws an error saying unknown context ....
Any guidance or possible things to investigate would be much appreciated.
I’ve submitted the following feedback:
FB13820942 (List Outline View Not Using Accent Color on Disclosure Caret for visionOS)
I’d appreciate help on this to see if I’m doing something wrong or indeed it’s the way visionOS currently works and it’s a suggested feedback.