I need to check the network connection with NWPathMonitor.
import Foundation
import Network
class NetworkViewModel: ObservableObject {
let monitor = NWPathMonitor()
let queue = DispatchQueue(label: "NetworkViewModel")
@Published var isConnected = false
var connectionDescription: String {
if isConnected {
return "You are connected."
} else {
return "You are NOT connected."
}
}
init() {
monitor.pathUpdateHandler = { path in
DispatchQueue.main.async {
self.isConnected = path.status == .satisfied
}
}
monitor.start(queue: queue)
}
}
import SwiftUI
struct ContentView: View {
@StateObject private var networkViewModel = NetworkViewModel()
var body: some View {
VStack {
}
.onAppear {
if networkViewModel.isConnected {
print("You are connected.")
}
else {
print("You are NOT connected.")
}
}
}
}
So there is nothing special, not at all. Yet, if I test it with a totally new Xcode project for iOS, it fails and return !isConnected. I've tested it with a macOS application. And it fails. I've tested it with an actual device. It fails. I've tested it with an old project. It still does work. I have no mere idea why new Xcode projects all fail to detect the WiFi connection. This is a total nightmare. Does anybody have a clue? thanks.
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've posted a couple times now about major issues I'm having with NSLayoutManager and have written to Apple for code-level support, but no one at Apple has responded to me in more than two weeks. So I'm turning to the community again for any help whatsoever.
I'm fairly certain it's a real bug in TextKit. If I'm right about that, I'd love for anyone at Apple to take an interest. And better yet, if I'm wrong (and I hope I am), I'd be incredibly grateful to anyone who can point out where my mistake lies! I've been stuck with this bug for weeks on end.
The crux of the issue is that I'm getting what seemed to be totally incompatible results from back to back calls to textContainer(forGlyphAt:effectiveRange:) and lineFragmentRect(forGlyphAt:effectiveRange:withoutAdditionalLayout:)... I'd lay out my text into a fairly tall container of standard page width and then query the layout manager for the text container and line fragment rect for a particular glyph (a glyph that happens to fall after many newlines). Impossibly, the layout manager would report that that glyph was in said very tall container, but that the maxY of its lineFragmentRect was only at 14 points (my NSTextView's isFlipped is true, so that's 14 points measuring from the top down).
After investigating, it appears that what is happening under the hood is NSLayoutManager is for some reason laying out text back into the first container in my series of containers, rather than overflowing it into the next container(s) and/or giving me a nil result for textContainer(forGlyphAt:...)
I've created a totally stripped down version of my project that recreates this issue reliably and I'm hoping literally anyone at Apple will respond to me. In order to recreate the bug, I've had to build a very specific set of preview data - namely some NSTextStorage content and a unique set of NSTextViews / NSTextContainers.
Because of the unique and particular setup required to recreate this bug, the code is too much to paste here (my preview data definition is a little unwieldy but the code that actually processes/parses it is not).
I can share the project if anyone is able and willing to look into this with me. It seems I'm not able to share a .zip of the project folder here but am happy to email or share a dropbox link.
Hi! I develop an iOS library and I met an issue with SwiftUI previews in iOS app project with my library integrated. After I open preview, build for preview finishes successfully, but preview itself never appears. I failed to find any error messages or any other indicators of what went wrong or how to fix it. Switching to legacy preview execution seems to fix problem, but I think that is not ideal. Could you help fixing this?
Xcode 16.2, Simulator iPhone 16, iOS 18.2
Project to reproduce -
https://drive.google.com/file/d/1cU6JKwshK_wQfe9YIqcMg3UGWq45OYlx/view?usp=sharing
Preview diagnostics - https://drive.google.com/file/d/1kPcgVSSqreiepGuqhdIoCW2rLSicgsWr/view?usp=sharing
I'm trying to implement the same UI used by the Settings app on iPad: a split view with two columns that are visible at all times.
This code produces the layout i want, but I would like to hide the "toggle sidebar visibility" button that the system introduces.
Is there a SwiftUI API I can use to hide this button? Maybe an alternate way to setup views that tells the system that the button is not necessary?
struct SomeView: View {
var body: some View {
NavigationSplitView(
columnVisibility: .constant(.all),
sidebar: { Text("sidebar") },
detail: { Text("detail") }
)
.navigationSplitViewStyle(.balanced)
}
}
We have encountered a problem on iOS 26. When switching to dark mode, the color of all subviews (font color, background color, etc.) of the Sidebar (Primary View) of UISplitViewController will not change. For example, if it is set to the color of UIColor.label, it will always be black and will not be white in dark mode.
On Xcode, just create a UISplitViewController in Storyboard without changing any settings, and run it directly to see the following:
The title of the Navigation Bar defaults to the label color, and it is still black after switching to dark mode.
There is no such problem in the Secondary View or other places.
This problem has occurred since iOS 26 beta 3, and iOS 26 beta 4 is now the same. But beta 1 and beta 2 have no problem.
I'm not sure if this is a bug, or if there is something that needs to be changed to adapt to iOS 26?
In my project, I am getting some text from backend which could have html tags. For this, I am converting the string to attributed string. However I noticed that when the string has html tags with color in it and when the text is displayed in toolbar, then the text displays with an ellipsis towards the end. Sharing code below:
import SwiftUI
struct ContentViewA: View {
@State private var displayText: AttributedString?
var body: some View {
NavigationStack {
VStack {
Text(displayText ?? "")
}
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button {
} label: {
Text("Done").font(.body.bold())
}
}
ToolbarItem(placement: .principal) {
Text(displayText ?? "")
}
}
.onAppear {
let string = "<div><p><span style=\"color:#FF0000;\">Hello World</span></p></div>"
displayText = string.convertToAttributedString
/// Note: If I don't set the font, then the ellipsis are not displayed in the toolbar, but I need this font style.
displayText?.font = .body.bold()
}
}
}
}
extension String {
var convertToAttributedString: AttributedString? {
guard let data = data(using: .utf8) else { return nil }
var attributedString: AttributedString?
if let nsAttributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil) {
attributedString = try? AttributedString(nsAttributedString, including: \.uiKit)
}
return attributedString
}
}
I am printing displayText in the body of the view and am not seeing ellipsis at the end of the string, but in toolbar, I am seeing ellipsis. I am unable to figure out what's causing this and what can be the fix for it. However, if I avoid setting the font on attributed string, then the ellipsis are not displayed in toolbar. However, I need to set the string to a specific font style.
How can I avoid ellipsis in toolbar and while also setting the required font on the string?
Topic:
UI Frameworks
SubTopic:
SwiftUI
Subject: Need Assistance with App Clip Invocation via URL
Hello Developers,
I’m currently facing an issue with invoking my App Clip through a URL, specifically when the link is shared via iMessage or Email. Instead of launching the App Clip, the URL redirects to the website.
Here’s my current configuration:
Approved App with an App Clip
Universal Links functioning correctly within the App (verified through AASA file hosted on the website)
Associated Domain Entitlements included for both the App and the App Clip
Universal Link is expected to invoke the App Clip if the App isn’t installed
Advanced Experience configured in App Store Connect
The default experience URL from App Store Connect successfully triggers the App Clip, but my custom URL does not.
I suspect I might be missing a crucial configuration step. Has anyone encountered a similar issue or have suggestions on what else I should verify?
Thank you in advance for your help!
Hey,
The new "soft" scroll edge effect is really cool! But it seems to only appear when you add toolbar items.
Is there a way to add it for "custom" views as well, that I place in a safe area inset?
For example, the messages app in iOS 26 does this. There's a text field as a safe area inset as well as a soft scroll edge effect.
Thanks!
Hi,
I have added widgets to my iOS app and I would like to make this feature only accessible to "pro" users that have made a non-consumable in-app purchase.
Currently, I am doing the following: I store an "isUnlocked" property in the Keychain after the purchase is made
I read data to be displayed in the widget and here I also query the Keychain and store whether the widget is unlocked
I have no refresh policy, but only change the widget data on a significant time change
a different view is displayed when the app is locked
Some dummy code snippets:
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
		let entry = readContents()
		let timeline = Timeline(entries: [entry], policy: .never)
		completion(timeline)
}
struct WidgetEntryView: View {
		let entry: Provider.Entry
@Environment(\.widgetFamily) var family
@ViewBuilder
var body: some View {
switch family {
case .systemSmall:
if !entry.isUnlocked {
LockedWidgetView()
} else if let event = entry.event {
SmallWidgetEventView(event: event)
} else {
NoDataWidgetView()
}
...
func applicationSignificantTimeChange(_ application: UIApplication) {
		if #available(iOS 14.0, *) {
				WidgetCenter.shared.reloadAllTimelines()
		}
...
However, 2 unexpected things happen: the view is refreshed intraday (not only at midnight i.e. at significant time change)
sometimes the LockedWidgetView is displayed.
Especially the latter is problematic, because it gives false information to a user that has already made the in-app purchase.
How can I achieve my goal of only displaying info when the user has made the in-app purchase?
Thanks in advance.
P.S. Although it would not have my preference, I would also find it acceptable if the widget is only shown as option to add once the purchase is made. In other words, I was considering changing the Widget itself:
struct MyWidget: Widget {
private var supportedFamilies: [WidgetFamily] = isUnlocked() ? [.systemSmall, .systemMedium] : []
but I believe I cannot re-initialise the widget from the app when the user makes the in-app purchase, because the only refresh option that I have is
WidgetCenter.shared.reloadAllTimelines()
I want a different color, one from my asset catalog, as the background of my first ever swift UI view (and, well, swift, the rest of the app is still obj.c)
I've tried putting the color everywhere, but it does't take. I tried with just .red, too to make sure it wasn't me. Does anyone know where I can put a color call that will actually run? Black looks very out of place in my happy app. I spent a lot of time making a custom dark palette.
TIA
KT
@State private var viewModel = ViewModel()
@State private var showAddSheet = false
var body: some View {
ZStack {
Color.myCuteBg
.ignoresSafeArea(.all)
NavigationStack {
content
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
Image("cute.image")
.font(.system(size: 30))
.foregroundColor(.beigeTitle)
}
}
}
.background(Color.myCuteBg)
.presentationBackground(.myCuteBg)
.sheet(isPresented: $showAddSheet) {
AddView()
}
.environment(viewModel)
.onAppear {
viewModel.fetchStuff()
}
}
.tint(.cuteColor)
}
@ViewBuilder var content: some View {
if viewModel.list.isEmpty && viewModel.anotherlist.isEmpty {
ContentUnavailableView(
"No Content",
image: "stop",
description: Text("Add something here by tapping the + button.")
)
} else {
contentList
}
}
var contentList: some View {
blah blah blah
}
}
First I tried the background, then the presentation background, and finally the Zstack. I hope this is fixed because it's actually fun to build scrollable content and text with swiftUI and I'd been avoiding it for years.
When building with the iOS 26 SDK (currently beta 4), the navigation title is often illegible when rendering a Map view.
For example, note how the title "Choose Location" is obscured by the map's text ("South America") in the screenshot below:
This screenshot is the result of the following view code:
import MapKit
import SwiftUI
struct Demo: View {
var body: some View {
NavigationStack {
Map()
.navigationTitle(Text("Choose Location"))
.navigationBarTitleDisplayMode(.inline)
}
}
}
I tried using the scrollEdgeEffectStyle(_:for:) modifier to apply a scroll edge effect to the top of the screen, in hopes of making the title legible, but that doesn't seem to have any effect. Specifically, the following code seems to produce the exact same result shown in the screenshot above.
import MapKit
import SwiftUI
struct Demo: View {
var body: some View {
NavigationStack {
Map()
.scrollEdgeEffectStyle(.hard, for: .top) // ⬅️ no apparent effect
.navigationTitle(Text("Choose Location"))
.navigationBarTitleDisplayMode(.inline)
}
}
}
Is there a recommended way to resolve this issue so that the navigation title is always readable?
On iPhone .inspector is presented as a sheet so you can use .presentationDetents to determine its detents. However, SwiftUI doesn't update the presentationDetents selection binding in this case. See attached minimum example of the problem - onChange will not run and print when you swipe and change the detent of the inspector sheet.
import SwiftUI
@main
struct TestingApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var showInspector = false
@State var detent: PresentationDetent = .medium
var body: some View {
Button("Toggle Inspector") {
showInspector.toggle()
}
.inspector(isPresented: $showInspector) {
Text("Inspector Content")
.presentationDetents([.medium, .large], selection: $detent)
}
.onChange(of: detent) { _, detent in
print(detent)
}
}
}
#Preview {
ContentView()
}
Dear Sirs,
I'm writing an audio application that should show up to 128 horizontal peakmeters (width for each is about 150, height is 8) stacked inside a ScrollViewReader. For the actual value of the peakmeter I have a binding to a CGFloat value. The peakmeter works as expected and is refreshing correct. For testing I added a timer to my swift application that is firing every 0.05 secs, meaning I want to show 20 values per second. Inside the timer func I'm just creating random CGFloat values in range of 0...1 for the bound values. The peakmeters refresh and flicker as expected but I can see a CPU load of 40-50% in the activity monitor on my MacBook Air with Apple M2 even when compiled in release mode. I think this is quite high and I'd like to reduce this CPU load. Should this be possible? I.e. I thought about blocking the refresh until I've set all values? How could this be done and would it help? What else could I do?
Thanks and best regards,
JFreyberger
I had a problem with my app (or in my setup) and searching the web I found a very simple code where part of my problem occurs.
I create a new Multiplatform App and
paste this code in ContentView.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack {
VStack {
Text("Navigation article")
.font(.title)
.padding()
NavigationLink("Go to the second view", destination: SecondView())
.padding()
}
.navigationTitle("First View")
}
}
}
struct SecondView: View {
var body: some View {
Text("This is the Second view")
.font(.headline)
.padding()
.navigationTitle("Second View")
}
}
run on iPhone/ iOS no problem
run on a Mac/macOS
Going from view 1 to view 2 work, the back arrow on view 2 is there, and it is working but the second time I go to the view 2, the back arrow is gone.
after looking closely I can see the Arrow Underneath the S of SecondView.
I have tried many things and could not make it work.
I post this in a HackingWithSwift forum and somebody tried the code and said it work. so it seems the problem could be related to my setup but I create another user in my computer , same problem and tried it on my another computer, same problem.
Topic:
UI Frameworks
SubTopic:
SwiftUI
[Also submitted as FB19313064]
The .disabled() modifier doesn't visually disable buttons inside a ToolbarItem container on iOS 26.0 (23A5297i) devices. The button looks enabled, but tapping it doesn't trigger the action.
When deployment target is lowered to iOS 18 and deployed to an iOS 18 device, it works correctly. It still fails on an iOS 26 device, even with an iOS 18-targeted build.
This occurs in both the Simulator and on a physical device.
Screen Recording
Code
struct ContentView: View {
@State private var isButtonDisabled = false
private var osTitle: String {
let version = ProcessInfo.processInfo.operatingSystemVersion
return "iOS \(version.majorVersion)"
}
var body: some View {
NavigationStack {
VStack {
Button("Body Button") {
print("Body button tapped")
}
.buttonStyle(.borderedProminent)
.disabled(isButtonDisabled)
Toggle("Disable buttons", isOn: $isButtonDisabled)
Spacer()
}
.padding()
.navigationTitle("Device: \(osTitle)")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem {
Button("Toolbar") {
print("Toolbar button tapped")
}
.buttonStyle(.borderedProminent)
.disabled(isButtonDisabled)
}
}
}
}
}
In creating a sequenced gesture combining a LongPressGesture and a DragGesture, I found that the combined gesture exhibits two problems:
The @GestureState does not properly update as the gesture progresses through its phases. Specifically, the updating(_:body:) closure (documented here) is only ever executed during the drag interaction. Long presses and drag-releases do not call the updating(_:body:) closure.
Upon completing the long press gesture and activating the drag gesture, the drag gesture remains empty until the finger or cursor has moved. The expected behavior is for the drag gesture to begin even when its translation is of size .zero.
This second problem – the nonexistence of a drag gesture once the long press has completed – prevents access to the location of the long-press-then-drag. Access to this location is critical for displaying to the user that the drag interaction has commenced.
The below code is based on Apple's example presented here. I've highlighted the failure points in the code with // *.
My questions are as follows:
What is required to properly update the gesture state?
Is it possible to have a viable drag gesture immediately upon fulfilling the long press gesture, even with a translation of .zero?
Alternatively to the above question, is there a way to gain access to the location of the long press gesture?
import SwiftUI
import Charts
enum DragState {
case inactive
case pressing
case dragging(translation: CGSize)
var isDragging: Bool {
switch self {
case .inactive, .pressing:
return false
case .dragging:
return true
}
}
}
struct ChartGestureOverlay<Value: Comparable & Hashable>: View {
@Binding var highlightedValue: Value?
let chartProxy: ChartProxy
let valueFromChartProxy: (CGFloat, ChartProxy) -> Value?
let onDragChange: (DragState) -> Void
@GestureState private var dragState = DragState.inactive
var body: some View {
Rectangle()
.fill(Color.clear)
.contentShape(Rectangle())
.onTapGesture { location in
if let newValue = valueFromChartProxy(location.x, chartProxy) {
highlightedValue = newValue
}
}
.gesture(longPressAndDrag)
}
private var longPressAndDrag: some Gesture {
let longPress = LongPressGesture(minimumDuration: 0.2)
let drag = DragGesture(minimumDistance: .zero)
.onChanged { value in
if let newValue = valueFromChartProxy(value.location.x, chartProxy) {
highlightedValue = newValue
}
}
return longPress.sequenced(before: drag)
.updating($dragState) { value, gestureState, _ in
switch value {
case .first(true):
// * This is never called
gestureState = .pressing
case .second(true, let drag):
// * Drag is often nil
// * When drag is nil, we lack access to the location
gestureState = .dragging(translation: drag?.translation ?? .zero)
default:
// * This is never called
gestureState = .inactive
}
onDragChange(gestureState)
}
}
}
struct DataPoint: Identifiable {
let id = UUID()
let category: String
let value: Double
}
struct ContentView: View {
let dataPoints = [
DataPoint(category: "A", value: 5),
DataPoint(category: "B", value: 3),
DataPoint(category: "C", value: 8),
DataPoint(category: "D", value: 2),
DataPoint(category: "E", value: 7)
]
@State private var highlightedCategory: String? = nil
@State private var dragState = DragState.inactive
var body: some View {
VStack {
Text("Bar Chart with Gesture Interaction")
.font(.headline)
.padding()
Chart {
ForEach(dataPoints) { dataPoint in
BarMark(
x: .value("Category", dataPoint.category),
y: .value("Value", dataPoint.value)
)
.foregroundStyle(highlightedCategory == dataPoint.category ? Color.red : Color.gray)
.annotation(position: .top) {
if highlightedCategory == dataPoint.category {
Text("\(dataPoint.value, specifier: "%.1f")")
.font(.caption)
.foregroundColor(.primary)
}
}
}
}
.frame(height: 300)
.chartOverlay { chartProxy in
ChartGestureOverlay<String>(
highlightedValue: $highlightedCategory,
chartProxy: chartProxy,
valueFromChartProxy: { xPosition, chartProxy in
if let category: String = chartProxy.value(atX: xPosition) {
return category
}
return nil
},
onDragChange: { newDragState in
dragState = newDragState
}
)
}
.onChange(of: highlightedCategory, { oldCategory, newCategory in
})
}
.padding()
}
}
#Preview {
ContentView()
}
Thank you!
The example code below shows what I am trying to achieve: When the user types a '*', it should be replaced with a '×'.
It looks like it works, but the cursor position is corrupted, even though it looks OK, and the diagnostics that is printed below shows a valid index. If you type "12*34" you get "12×43" because the cursor is inserting before the shown cursor instead of after.
How can I fix this?
struct ContentView: View {
@State private var input: String = ""
@State private var selection: TextSelection? = nil
var body: some View {
VStack {
TextField("Type 12*34", text: $input, selection: $selection)
.onKeyPress(action: {keyPress in
handleKeyPress(keyPress)
})
Text("Selection: \(selectionAsString())")
}.padding()
}
func handleKeyPress(_ keyPress: KeyPress) -> KeyPress.Result {
if (keyPress.key.character == "*") {
insertAtCursor(text: "×")
moveCursor(offset: 1)
return KeyPress.Result.handled
}
return KeyPress.Result.ignored
}
func moveCursor(offset: Int) {
guard let selection else { return }
if case let .selection(range) = selection.indices {
print("Moving cursor from \(range.lowerBound)")
let newIndex = input.index(range.lowerBound, offsetBy: offset, limitedBy: input.endIndex)!
let newSelection : TextSelection.Indices = .selection(newIndex..<newIndex)
if case let .selection(range) = newSelection {
print("Moved to \(range.lowerBound)")
}
self.selection!.indices = newSelection
}
}
func insertAtCursor(text: String) {
guard let selection else { return }
if case let .selection(range) = selection.indices {
input.insert(contentsOf: text, at: range.lowerBound)
}
}
func selectionAsString() -> String {
guard let selection else { return "None" }
switch selection.indices {
case .selection(let range):
if (range.lowerBound == range.upperBound) {
return ("No selection, cursor at \(range.lowerBound)")
}
let lower = range.lowerBound.utf16Offset(in: input)
let upper = range.upperBound.utf16Offset(in: input)
return "\(lower) - \(upper)"
case .multiSelection(let rangeSet):
return "Multi selection \(rangeSet)"
@unknown default:
fatalError("Unknown selection")
}
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
It seems like it is no longer possible to open the main window of an app after the app has been launched by the system if the "Auto Start" functionality has been enabled.
I am using SMAppService.mainApp to enable to auto start of my app. It is shown in the macOS system settings and the app is automatically started - but the main window is not visible.
How can I change this behaviour so the main window of the app is always visible when started automatically?
I have not noticed this behaviour before the release of macOS Sequoia. My app is using Swift 6 and the latest version of macOS and Xcode.
Regards
Topic:
UI Frameworks
SubTopic:
SwiftUI
Looking to see if anyone has experienced this issue, and is aware of any workarounds.
With an app migrating towards SwiftUI Views but still using UIKit for primary navigation, my app makes use of UIHostingController to push SwiftUI Views onto a UINavigationController stack in a lot of areas. With iOS 26, I notice that SwiftUI's Menu view really struggles to present when contained in a UIHostingController. An error is logged to the console on presentation, and depending on the UI, the Menu won't present inside of it's container, or will jump around the screen.
The bug, it seems is based in a private class UIReparentingView and I am curious if anyone has found a work around for this issue. The error reported is:
Adding '_UIReparentingView' as a subview of UIHostingController.view is not supported and may result in a broken view hierarchy. Add your view above UIHostingController.view in a common superview or insert it into your SwiftUI content in a UIViewRepresentable instead.
The simplest way to see this issue is to create a new storyboard based project. From the ViewController present a UIHostingController with a SwiftUI view that has a Menu and then simply tap to open the Menu. Thanks for any input!
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.