I've encountered an issue where using @Observable in SwiftUI causes extra initializations and deinitializations when a reference type is included as a property inside a struct. Specifically, when I include a reference type (a simple class Empty {}) inside a struct (Test), DetailsViewModel is initialized and deinitialized twice instead of once. If I remove the reference type, the behavior is correct.
This issue does not occur when using @StateObject instead of @Observable. Additionally, I've submitted a feedback report: FB16631081.
Steps to Reproduce
Run the provided SwiftUI sample code (tested on iOS 18.2 & iOS 18.3 using Xcode 16.2).
Observe the console logs when navigating to DetailsView.
Comment out var empty = Empty() in the Test struct.
Run again and compare console logs.
Change @Observable in DetailsViewModel to @StateObject and observe that the issue no longer occurs.
Expected Behavior
The DetailsViewModel should initialize once and deinitialize once, regardless of whether Test contains a reference type.
Actual Behavior
With var empty = Empty() present, DetailsViewModel initializes and deinitializes twice. However, if the reference type is removed, or when using @StateObject, the behavior is correct (one initialization, one deinitialization).
Code Sample
import SwiftUI
enum Route {
case details
}
@MainActor
@Observable
final class NavigationManager {
var path = NavigationPath()
}
struct ContentView: View {
@State private var navigationManager = NavigationManager()
var body: some View {
NavigationStack(path: $navigationManager.path) {
HomeView()
.environment(navigationManager)
}
}
}
final class Empty { }
struct Test {
var empty = Empty() // Comment this out to make it work
}
struct HomeView: View {
private let test = Test()
@Environment(NavigationManager.self) private var navigationManager
var body: some View {
Form {
Button("Go To Details View") {
navigationManager.path.append(Route.details)
}
}
.navigationTitle("Home View")
.navigationDestination(for: Route.self) { route in
switch route {
case .details:
DetailsView()
.environment(navigationManager)
}
}
}
}
@MainActor
@Observable
final class DetailsViewModel {
var fullScreenItem: Item?
init() {
print("DetailsViewModel Init")
}
deinit {
print("DetailsViewModel Deinit")
}
}
struct Item: Identifiable {
let id = UUID()
let value: Int
}
struct DetailsView: View {
@State private var viewModel = DetailsViewModel()
@Environment(NavigationManager.self) private var navigationManager
var body: some View {
ZStack {
Color.green
Button("Show Full Screen Cover") {
viewModel.fullScreenItem = .init(value: 4)
}
}
.navigationTitle("Details View")
.fullScreenCover(item: $viewModel.fullScreenItem) { item in
NavigationStack {
FullScreenView(item: item)
.navigationTitle("Full Screen Item: \(item.value)")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
withAnimation(completionCriteria: .logicallyComplete) {
viewModel.fullScreenItem = nil
} completion: {
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
navigationManager.path.removeLast()
}
}
}
}
}
}
}
}
}
struct FullScreenView: View {
@Environment(\.dismiss) var dismiss
let item: Item
var body: some View {
ZStack {
Color.red
Text("Full Screen View \(item.value)")
.navigationTitle("Full Screen View")
}
}
}
Console Output
With var empty = Empty() in Test
DetailsViewModel Init
DetailsViewModel Init
DetailsViewModel Deinit
DetailsViewModel Deinit
Without var empty = Empty() in Test
DetailsViewModel Init
DetailsViewModel Deinit
Using @StateObject Instead of @Observable
DetailsViewModel Init
DetailsViewModel Deinit
Additional Notes
This issue occurs only when using @Observable. Switching to @StateObject prevents it. This behavior suggests a possible issue with how SwiftUI handles reference-type properties inside structs when using @Observable.
Using a struct-only approach (removing Empty class) avoids the issue, but that’s not always a practical solution.
Questions for Discussion
Is this expected behavior with @Observable?
Could this be an unintended side effect of SwiftUI’s state management?
Are there any recommended workarounds apart from switching to @StateObject?
Would love to hear if anyone else has run into this or if Apple has provided any guidance!
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.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I am trying to do inline to icon and text in tab bar but it is not allowing me to do it in compact, but it showing me in regular mode , but in regular mode tab bar going at top in portrait mode , But my requirement is tab bar required in bottom with icon and text in inline it showed by horizontally but it showing to me stacked vertically, will you guide me on this so that I can push the build to live users.
It looks like I'm one of the rare developers dealing with CarPlay...
I develop a CarPlay extension for my apps. A few things:
especially when using the CarPlay I/O window in iOS Simulator, I get random selection highlightning for list items: I have three list templates in a tab template; once I reselect a list using the tab which has been selected before, the initial list item highlights / returns to normal every refresh of the list content; while this doesn't happen for my real world Sony CarPlay device, I'd rather not see such disturbing highlighting for my users. I do not update the template structs or items here, it is just content like text of detailText I update. Question: how to remove highlightning programmatically - especially for devices with touch screen?
I have one user who reports auto-selection of UI elements while driving; I assume this is some problem with his touch screen, but it may be a general issue too. Question: anyone with similar observations
connecting my iPhone to the stand-alone Car Play simulator doesn't work; I had it working before, so it might be related to a recent iOS beta...
Any hints / observations are welcome. The CarPlay community really seems to be small and I'd like to hear other's experience on the named items.
In SwiftUI's List, on macOS, if I embed a TextField then the text field is presented as non-editable. If the user clicks on the text and waits a short period of time, the text field will become editable.
I'm aware this is generally the correct behaviour for macOS. However, is there a way in SwiftUI to supress this behaviour such that the TextField is always presented as being editable?
I want a scrollable, List of editable text fields, much like how a Form is presented. The reason I'm not using a Form is because I want List's support for reordering by drag-and-drop (.onMove).
Use Case
A view that allows a user to compose a questionnaire. They are able to add and remove questions (rows) and each question is editable. They require drag-and-drop support so that they can reorder the questions.
We have SwiftUI AttributedString with links. But these links are inaccessible via automation scripts.
The text is visible to the automation scripts as XCUIElementTypeStaticText and there is no provision to access the links within.
Is there any workaround available for this case ?
Topic:
UI Frameworks
SubTopic:
SwiftUI
I want to truncate text from head with max 2 lines.
I try the following code
import SwiftUI
struct ContentView: View {
@State var content: String = "Hello world! wef wefwwfe wfewe weweffwefwwfwe wfwe"
var body: some View {
VStack {
Text(content)
.lineLimit(nil)
.truncationMode(.head)
.frame(height: 50)
Button {
content += content
} label: {
Text("Double")
}
.buttonStyle(.borderedProminent)
}
.frame(width: 200, height: 1000)
.padding()
}
}
#Preview {
ContentView()
}
It show result like this, this is not what I want.
now i must use voip + livekit to developing, When incoming offline messages arrive at the device through VoIP, call ConversationManager The method of reporting NewIncomingConversation (uuid: update:) only first time can push new system UI,second or more time will crash, and acrsh stack appears to indicate that callkit has not been called
Topic:
UI Frameworks
SubTopic:
General
I am trying to add custom scheme (CFBundleURLSchemes) to my App Clip.
I launch the app clip via TestFlight to cache it to the device then i try to access the custom scheme URL to launch App Clip but nothing happened.
May I know if it is something I did wrongly or just App Clip does not support Custom Scheme?
In our application we have two usecases for a Hotkey/Shortcut identification API/method.
We have some predefined shortcuts that will ship with our MacOS application. They may or may not change dynamically, based on what the user has already set as shortcuts/hotkeys, and also to avoid any important system wide shortcuts that the user may or may not have changed.
We allow the user to customize the shortcuts/hotkeys in our application, so we want to show what shortcuts the user already has in use system-wide and across their OS experience.
This gives rise to the need for an API that lets us know which shortcut/hotkeys are currently being used by the user and also the current system wide OS shortcuts in use.
Please let me know if there are any APIs in AppKit or SwiftUI we can use for the above
Hi everyone,
we’d appreciate your input on the following use case – thanks in advance!
In our iPhone and Apple Watch app, we’re using the NearbyInteraction API to measure the distance between both devices via UWB.
Setup:
On the iPhone, we start a LiveActivity together with the NISession, to keep the ranging active in the background.
✅ Good news: On iOS 18.4, this works as expected – the NISession stays active in the background as long as the Live Activity is running.
Current issues:
As soon as the Watch app moves to the background, ranging seems to pause and is eventually terminated.
→ Question 1: Is there a way to keep the NISession active on the Watch when the app goes into the background?
Audio playback from background not working:
We'd like to trigger audio playback when certain distance changes are detected. So far, we can only trigger haptic feedback in the background – audio does not play.
→ Question 2: Is it possible to play audio (e.g. using AVAudioPlayer) while a NISession and a LiveActivity are running in the background?
We’d be grateful for any advice or best practices for this combination.
Thanks and best regards!
Topic:
UI Frameworks
SubTopic:
General
In my CarPlay app, I am hiding the navigation bar by using the following:
self.mapTemplate?.automaticallyHidesNavigationBar = true
self.mapTemplate?.hidesButtonsWithNavigationBar = false
I don't want the navigation bar to show unless a user interacts with the map by tapping it.
Strangely, when I present a CPNavigationAlert the navigation bar will often appear and then disappear after the alert is dismissed.
Is there a setting or reason that the navigation bar would be appearing when presenting this alert? I would like to keep the nav bar hidden during this time.
QuickTime recording palette behaves in a way which I want to replicate in my desktop app - specifically the behaviour when switching spaces, it appears on top. Currently, my app appears on all spaces, and even over fullscreen applications BUT it already exists when I switch to the space, this feels disjointed.
I can't find a solution to this behaviour. Here's the Window Collection Behaviours I've tried (on an NSPanel):
FullScreenAuxiliary - appears over fullscreen apps.
CanJoinAllSpaces - appears on all spaces.
These two options make the dock show up on all spaces in the same position, but on each space they already exists.
I've tried this behaviour too:
MoveToActiveSpace - which as per docs would move the window into active space only when its reopened, mine stays open all the time.
I can't find any more information on how QuickTime achieves this.
Topic:
UI Frameworks
SubTopic:
SwiftUI
I have a simple Picker where the options available change by the view state. I would like to have the transition animated but the default animation is not good so I tried setting a .transition() and or an .animation() inside an item on the picker but it is ignored. The same happens if the transition is set on the picker itself since it's always present.
Am I doing it right and is just not posible or is there something else to do?
Code to reproduce the issue:
struct ContentView: View {
@State var list: [String] = [
"Item 4",
"Item 5",
"Item 6",
"Item 7",
"Item 8",
]
@State var selected: String?
@State var toggle: Bool = false
var body: some View {
VStack {
Picker("List", selection: $selected) {
ForEach(list, id: \.self) {
Text($0).tag($0)
// .transition(.opacity)
}
}
.pickerStyle(.segmented)
// .transition(.opacity)
HStack {
Button(action: swapOptions) {
Text("Swap")
}
}
}
.padding()
}
}
extension ContentView {
func swapOptions() {
withAnimation {
toggle.toggle()
switch toggle {
case true:
list = [
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
]
case false:
list = [
"Item 4",
"Item 5",
"Item 6",
"Item 7",
"Item 8",
]
}
}
}
}
``
Topic:
UI Frameworks
SubTopic:
SwiftUI
iOS 18.4.1
When I change a Google type event to an iCloud type, a "Cannot Save Event" prompt box pops up.
We have also received user feedback that recurring events also fail to save. After updating to iOS 18.4 when trying to save changes to an existing repeating event, the message "Cannot Save Event" will appear.
EventKitUI
Topic:
UI Frameworks
SubTopic:
UIKit
At this line of code (SketchTextSelectionManager.swift:378), sometimes there will be crashes based on crashlytics reports. In the reports, it seems like this only happens for RTL text range.
let selection = pdfPage.selection(
from: CGPoint(x: fromStart.x + 1, y: fromStart.y - 1),
to: CGPoint(x: toEnd.x - 1, y: toEnd.y + 1)
)
This is directly calling into PDFKit's PDFPage#selection method: https://developer.apple.com/documentation/pdfkit/pdfpage/selection(from:to:)
Attached the full stacktrace:
Crashed: com.apple.root.user-initiated-qos.cooperative
0 CoreGraphics 0x30598c PageLayout::convertRTLTextRangeIndexToStringRangeIndex(long) const + 156
1 CoreGraphics 0x44c3f0 CGPDFSelectionCreateBetweenPointsWithOptions + 224
2 PDFKit 0x91d00 -[PDFPage selectionFromPoint:toPoint:type:] + 168
3 MyApp 0x841044 closure #1 in SketchTextSelectionManager.handleUserTouchMoved(_:) + 378 (SketchTextSelectionManager.swift:378)
4 MyApp 0x840cb0 SketchTextSelectionManager.handleUserTouchMoved(_:) + 205 (CurrentNoteManager.swift:205)
5 libswift_Concurrency.dylib 0x60f5c swift::runJobInEstablishedExecutorContext(swift::Job*) + 252
6 libswift_Concurrency.dylib 0x63a28 (anonymous namespace)::ProcessOutOfLineJob::process(swift::Job*) + 480
7 libswift_Concurrency.dylib 0x6101c swift::runJobInEstablishedExecutorContext(swift::Job*) + 444
8 libswift_Concurrency.dylib 0x62514 swift_job_runImpl(swift::Job*, swift::SerialExecutorRef) + 144
9 libdispatch.dylib 0x15ec0 _dispatch_root_queue_drain + 392
10 libdispatch.dylib 0x166c4 _dispatch_worker_thread2 + 156
11 libsystem_pthread.dylib 0x3644 _pthread_wqthread + 228
12 libsystem_pthread.dylib 0x1474 start_wqthread + 8
Dear all,
Sorry if the topic has already been commented but I could not be able to find it in over 10,000 picks using the forum search field...
My problem is that with any NSTextField in my app, a click will result in a memory leak. There is no code attached, just bindings to NSNumber properties. How can I fix this ?
Thanks for your help.
Best regards
Chris
Topic:
UI Frameworks
SubTopic:
AppKit
The behavior of the Button in ScrollView differs depending on how the View is displayed modally.
When the View is displayed as a .fullScreenCover, if the button is touched and scrolled without releasing the finger, the touch event is canceled and the action of the Button is not called.
On the other hand, if the View is displayed as a .sheet, the touch event is not canceled even if the view is scrolled without lifting the finger, and the action is called when the finger is released.
In order to prevent accidental interaction, I feel that the behavior of .fullScreenCover is better, as it cancels the event immediately when scrolling. Can I change the behavior of .sheet?
Demo movie is here:
https://x.com/kenmaz/status/1896498312737611891
Sample code
import SwiftUI
@main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State private var showSheet = false
@State private var showFullScreen = false
var body: some View {
VStack(spacing: 16) {
Button("Sheet") {
showSheet.toggle()
}
Button("Full screen") {
showFullScreen.toggle()
}
}
.sheet(isPresented: $showSheet) {
SecondView()
}
.fullScreenCover(isPresented: $showFullScreen) {
SecondView()
}
.font(.title)
}
}
struct SecondView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
ScrollView {
Button("Dismiss") {
dismiss()
}
.buttonStyle(MyButtonStyle())
.padding(.top, 128)
.font(.title)
}
}
}
private struct MyButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration
.label
.foregroundStyle(.red)
.background(configuration.isPressed ? .gray : .clear)
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
I'm currently integrating SwiftUI into an AppKit based application and was curious if the design pattern below was viable or not. In order to "bridge" between AppKit and SwiftUI, most of my SwiftUI "root" views have aViewModel that is accessible to the SwiftUI view via @ObservedObject.
When a SwiftUI views need to use NSViewRepresentable I'm finding the use of a ViewModel and a Coordinator to be an unnecessary layer of indirection. In cases where it makes sense, I've just used the ViewModel as the Coordinator and it all appears to be working ok, but I'm curious if this is reasonable design pattern or if I'm overlooking something.
Consider the following pseudo code:
// 1. A normal @ObservedObject acting as the ViewModel that also owns and manages an NSTableView.
@MainActor final class ViewModel: ObservedObject, NSTableView... {
let scrollView: NSScrollView
let tableView: NSTableView
@Published var selectedTitle: String
init() {
// ViewModel manages tableView as its dataSource and delegate.
tableView.dataSource = self
tableView.delegate = self
}
func reload() {
tableView.reloadData()
}
// Update view model properties.
// Simpler than passing back up through a Coordinator.
func tableViewSelectionDidChange(_ notification: Notification) {
selectedTitle = tableView.selectedItem.title
}
}
// 2. A normal SwiftUI view, mostly driven by the ViewModel.
struct ContentView: View {
@ObservedObject model: ViewModel
var body: some View {
Text(model.selectedTitle)
// No need to pass anything down other than the view model.
MyTableView(model: model)
Button("Reload") { model.reload() }
Button("Delete") { model.deleteRow(...) }
}
}
// 3. A barebones NSViewRepresentable that just vends the required NSView. No other state is required as the ViewModel handles all interactions with the view.
struct MyTableView: NSViewRepresentable {
// Can this even be an NSView?
let model: ViewModel
func makeNSView(context: Context) -> some NSView {
return model.scrollView
}
func updateNSView(_ nsView: NSViewType, context: Context) {
// Not needed, all updates are driven through the ViewModel.
}
}
From what I can tell, the above is working as expected, but I'm curious if there are some situations where this could "break", particularly around the lifecycle of NSViewRepresentable
Would love to know if overall pattern is "ok" from a SwiftUI perspective.
Where from and how does an NSRulerView get its magnification from? I am not using the automatic magnification by NSScrollView but using my own mechanism. How do I relay the zoom factor to NSRulerView?
I am working on creating a custom Popup View based on a .fullscreenCover. The .fullscreenCover is used to place the Popup content on screen on a semi-transparent background.
While this works on iOS 18, there is a problem on iOS 17: When the Popup content contains a .sheet, the background is not transparent any more but opaque.
Image: iOS 17. When showing the Popup an opaque background covers the main content. When tapping on the background it turns transparent.
Image: iOS 18. Everything works as intended. When showing the Popup the main background is covered with a semi-transparent background.
Removing the .sheet(...) from the Popup content solves the problem. It does not matter if the sheet is used or not. Adding it to the view code is enough to trigger the problem.
Using a .sheet within a .fullscreenCover should not be a problem as far as I know.
Is this a bug in iOS 17 or is there something wrong with my code?
Code:
struct SwiftUIView: View {
@State var isPresented: Bool = false
@State var sheetPresented: Bool = false
var body: some View {
ZStack {
VStack {
Color.red.frame(maxHeight: .infinity)
Color.green.frame(maxHeight: .infinity)
Color.yellow.frame(maxHeight: .infinity)
Color.blue.frame(maxHeight: .infinity)
}
Button("Show") {
isPresented = true
}
.padding()
.background(.white)
Popup(isPresented: $isPresented) {
VStack {
Button("Dismiss") {
isPresented = false
}
}
.frame(maxWidth: 300)
.padding()
.background(
RoundedRectangle(cornerRadius: 20)
.fill(.white)
)
.sheet(isPresented: $sheetPresented) {
Text("Hallo")
}
}
}
}
}
struct Popup<Content: View>: View {
@Binding var isPresented: Bool
let content: () -> Content
init(isPresented: Binding<Bool>, @ViewBuilder _ content: @escaping () -> Content) {
_isPresented = isPresented
self.content = content
}
@State private var internalIsPresented: Bool = false
@State private var isShowing: Bool = false
let transitionDuration: TimeInterval = 0.5
var body: some View {
ZStack { }
.fullScreenCover(isPresented: $internalIsPresented) {
VStack {
content()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(
Color.black.opacity(0.5)
.opacity(isShowing ? 1 : 0)
.animation(.easeOut(duration: transitionDuration), value: isShowing)
.ignoresSafeArea()
)
.presentationBackground(.clear)
.onAppear {
isShowing = true
}
.onDisappear {
isShowing = false
}
}
.onChange(of: isPresented) { _ in
withoutAnimation {
internalIsPresented = isPresented
}
}
}
}
extension View {
func withoutAnimation(action: @escaping () -> Void) {
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
action()
}
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI