Hi, after update to Swift6 and iOS 18 my code is not working anymore. This is code from documentation with onTapGesture (instead of primaryAction) and it's not working. OnTapGesture is not starting when user click.
Menu {
Button(action: {}) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: {}) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: {}) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
} onTapGesture: {
print(" Print on tap gesture")
}
primaryAction isn't working either. I mean, primary action works but menu is not showing inside buttons.
Menu {
Button(action: {}) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: {}) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: {}) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
} primaryAction: {
print("")
}
I also tried to overlay the menu, no effect.
ZStack {
Menu {
Button(action: {}) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: {}) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: {}) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
}
Color.clear
.contentShape(Rectangle())
.onTapGesture {
print("Tap gesture recognized")
}
}
Please help. How to start action when user is clicking on menu?
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
What is current best-practice for supporting an external display in a SwiftUI iOS app in 2024?
(I'm only interested in iOS 17 and/or iOS 18)
The only docs I found require abandoning the SwiftUI App structure and "switching back to a full UIKit App Delegate".
Is this still the only option?
Are there any complete examples on how to accomplish this?
Also is testing external displays with Simulator at all reliable? All experiences I read about say that it never works.
Thanks in advance.
There seems to be a performance issue when scrolling using the track pad with SwiftUI scroll views in macOS 15. This issue is NOT present in macOS 14.
When using the track pad the scrolling is not smooth, but "stutters". However scrolling using the scroll bars is very smooth. The "stuttering" is worse if the SwiftUI ScrollView is in the detail view of a NavigationSplitView.
The problem is not noticeable in scroll views with a small number views, but when the more views inside the scroll view, the more prominent the problem becomes.
I have a simple example app that illustrates the problem here (the example app is a simplification of my app Yammixer): https://github.com/danwaltin/SwiftUIScrollViewPerformance
When running this example app on macOS 14 (Sonoma) on an Intel i7 Mac book pro from 2019 the scrolling is "buttery smooth".
But on macOS 15 (Sequoia) on my Apple Silicon M1 Mac book pro the issue is very obvious.
When using Instruments I see that on macOS 15 "flame graph" shows that 85% of the execution time is in a "_hitTestForEvent" method. If the test app does not use NavigationSplitView about 70% of execution time is in the _hitTestForEvent method.
I am trying out the new TextField selection ability on macOS but it crashes in various different ways with extremely large stack traces. Looks like it is getting into re-entrant function calls.
A similar problem is described on the SwiftUI forums with no responses yet.
Here is my simple example
struct ContentView: View {
@State private var text: String = ""
@State private var selection: TextSelection?
var body: some View {
TextField("Message", text: $text, selection: $selection)
.padding()
}
}
Setting text to a value like "Hallo World" causes an instant crash as soon as you start typing in the TextField.
Setting text empty (as in example above) lets you edit the text but as it crashes as soon as you commit it (press enter).
Any workarounds or fixes?
I'm using UIKit to display a long list of large images inside a SwiftUI ScrollView and LazyHStack using UIViewControllerRepresentable. When an image is loaded, I'm using SDWebImage to load the image from the disk.
As the user navigates through the list and continues to load more images, more memory is used and is never cleared, even as the images are unloaded by the LazyHStack. Eventually, the app reaches the memory limit and crashes. This issue persists if I load the image with UIImage(contentsOfFile: ...) instead of SDWebImage.
How can I free the memory used by UIImage when the view is removed?
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
ForEach(allItems) { item in
TestImageDisplayRepresentable(item: item)
.frame(width: geometry.size.width, height: geometry.size.height)
.id(item.id)
}
}
.scrollTargetLayout()
}
import UIKit
import SwiftUI
import SDWebImage
class TestImageDisplay: UIViewController {
var item: TestItem
init(item: TestItem) {
self.item = item
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
imageView.center = view.center
view.addSubview(imageView)
imageView.sd_setImage(with: item.imageURL, placeholder: nil)
}
}
struct TestImageDisplayRepresentable: UIViewControllerRepresentable {
var item: TestItem
func makeUIViewController(context: Context) -> TestImageDisplay {
return TestImageDisplay(item: item)
}
func updateUIViewController(_ uiViewController: TestImageDisplay, context: Context) {
uiViewController.item = item
}
}
After updating to Sonoma, the following is logged in the Xcode console when an editable text field becomes key. This doesn't occur on any text field, but it seems to happen when the text field is within an NSPopover or an NSSavePanel.
ViewBridge to RemoteViewService Terminated: Error Domain=com.apple.ViewBridge Code=18 "(null)" UserInfo={com.apple.ViewBridge.error.hint=this process disconnected remote view controller -- benign unless unexpected, com.apple.ViewBridge.error.description=NSViewBridgeErrorCanceled}
What does this mean?
After each macOS update come new annoying log messages.
So after updating to Sonoma and Xcode 16, I see the following logged in the Xcode console whenever my app shows an NSPredicateEditor.
Failed to find localization for values: All, of the following are true
I tried setting the "localize" attribute to "never" in the attributes inspector of the corresponding menu/text fields from interface builder, to no avail.
Any suggestions ?
Hello,
After a fair amount of time of trial and error, I have seemingly discovered a bug in iOS 18 with .toolBar with placement:. keyboard)
Feedback: FB15205988
Xcode Version: 16.0 (16A242d)
iOS Version: 18.0 (22A3351)
I'd appreciate help to see if I'm missing something here.
What's happening?
When using .toolBar with (placement: .keyboard), within a NavigationSplitView detail:, encompassed in a TabView, the .toolBar view does not appear above the keyboard.
Within the code, the usage of .toolBar is within ShakeDetail.swift.
When commenting out the TabView within ShakeTabView.swift, or running on iOS 17.5, the .toolbar appears.
Would greatly appreciate help if someone knows a workaround, or a misunderstanding here of course!
Code
Shake.swift
import Foundation
import SwiftUI
struct Shake: Equatable, Identifiable, Hashable {
var name: String
let id = UUID()
static var example = Shake(name: "Banana")
static var many: [Shake] {
return [
Shake(name: "Apple"),
Shake(name: "Banana"),
Shake(name: "Cherry"),
Shake(name: "Berry"),
]
}
}
ShakeDetail.swift
import SwiftUI
struct ShakeDetail: View {
@State var shake: Shake = Shake(name: "")
var body: some View {
List {
TextField("dsf", text: $shake.name, prompt: Text("Enter your favorite shake"))
}
.scrollDismissesKeyboard(.interactively)
.toolbar {
ToolbarItem(placement: .keyboard) {
HStack(alignment: .center) {
Text(shake.name)
.font(.title3)
.foregroundColor(.red)
}
}
}
.toolbarColorScheme(.dark, for: .navigationBar)
.toolbarBackground(
Color.purple,
for: .navigationBar
)
.toolbarBackground(.visible, for: .navigationBar)
}
}
#Preview {
ShakeDetail()
}
ShakeListView.swift
struct ShakeListView: View {
@Binding var shakes: [Shake]
@Binding var selectedShake: Shake?
var body: some View {
List(selection: $selectedShake) {
ForEach(shakes) { shake in
NavigationLink(value: shake) {
Text(shake.name)
.font(.system(.title2, design: .rounded))
.foregroundStyle(.primary)
}
}
}
.navigationTitle("Shakes")
}
}
#Preview {
@Previewable @State var shakes: [Shake] = Shake.many
@Previewable @State var selectedShake: Shake?
ZStack {
ShakeListView(shakes: $shakes, selectedShake: $selectedShake)
}
}
ShakeSplitView.swift
import SwiftUI
import OSLog
public struct ShakeSplitView: View {
@State var selectedShake: Shake?
@State var shakes = Shake.many
@State private var columnVisibility = NavigationSplitViewVisibility.doubleColumn
public init(isShowingFavorites: Bool = false, columnVisibility: SwiftUI.NavigationSplitViewVisibility = NavigationSplitViewVisibility.doubleColumn) {
self.columnVisibility = columnVisibility
}
public var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
ShakeListView(shakes: $shakes, selectedShake: $selectedShake)
} detail: {
if let selectedShake {
// Add shake detail
ShakeDetail(shake: selectedShake)
} else {
Label("Please select a shake", systemImage: "arrow.backward.circle")
.foregroundColor(.purple)
.font(.system(.title2, design: .rounded))
}
}
.navigationSplitViewStyle(.balanced)
}
}
struct FormulaSplitView_Previews: PreviewProvider {
static var previews: some View {
ShakeSplitView()
}
}
ShakeTabView.swift
import SwiftUI
struct ShakeTabView: View {
var body: some View {
/* TabView breaks the ToolbarItem(placement: .keyboard) */
TabView {
ShakeSplitView()
.tabItem {
Image(systemName: "carrot.fill")
Text("Shakes")
}
}
}
}
#Preview {
ShakeTabView()
}
Why is there inconstancy of appearing the keyboard tool bar Item with tab view?
Try to go to second tab and focus the field. Sometimes it does not appear (in my more complex project it does not appear >90% times).
import SwiftUI
struct MainTabView: View {
var body: some View {
TabView {
FirstTabView()
.tabItem { Label("Tab 1", systemImage: "house") }
SecondTabView()
.tabItem { Label("Tab 2", systemImage: "star") }
}
}
}
struct FirstTabView: View {
@State private var text = ""
var body: some View {
NavigationStack {
VStack {
TextField("Enter something 1", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Done") { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) }
}
}
}
}
}
struct SecondTabView: View {
@State private var text = ""
var body: some View {
NavigationStack {
VStack {
TextField("Enter something 2", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Done") { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) }
}
}
}
}
}
#Preview {
MainTabView()
}
I'm attempting to create a standalone watchOS app that fetches the prayer timings of my local mosque in JSON format via an API call. I want the app to fetch the prayer timings in the background, but only once per day, at the start of the day (when prayer timings change, i.e., midnight).
I'm trying to implement this using SwiftUI's backgroundTask modifier as explained in the docs and in this WWDC22 video.
I made sure to enable the Background Modes capability, and this is what my app's Info.plist looks like:
However, I've been unable to get it to work. I would appreciate any assistance I can get and feedback for improvements I can make, even with the Info.plist if anything is incorrect about it. Thank you!
This is what I have so far:
// PrayerTimesCompanionApp.swift
// PrayerTimesCompanion Watch App
import SwiftUI
import WatchKit
@main
struct PrayerTimesCompanion_Watch_AppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.backgroundTask(.appRefresh("TIMINGS_REFRESH")) {
print("Found matching task")
scheduleNextBackgroundRefresh()
}
}
}
// Schedule the next background refresh
func scheduleNextBackgroundRefresh() {
let today = Calendar.current.startOfDay(for: .now)
if let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today) {
WKApplication.shared().scheduleBackgroundRefresh(withPreferredDate: tomorrow, userInfo: "TIMINGS_REFRESH" as NSSecureCoding & NSObjectProtocol) { error in
if error != nil {
fatalError("*** An error occurred while scheduling the background refresh task. ***")
}
print("*** Scheduled! ***")
}
}
}
// ContentView.swift
// PrayerTimesCompanion Watch App
import SwiftUI
struct ContentView: View {
@StateObject var prayerTimeModel = PrayerTimesModel()
var body: some View {
List {
VStack {
VStack(spacing: 15) {
// Table Header
HStack {
Text("Prayer")
.bold()
.frame(maxWidth: .infinity, alignment: .leading) // Align to the left
Text("Iqamah")
.bold()
.frame(maxWidth: .infinity, alignment: .trailing) // Align to the right
}
.padding()
// Table Rows (5 prayers)
ForEach(prayerTimeModel.prayerTimes.data.iqamah, id: \.date) { iqamah in
rowView(prayer: "Fajr", time: iqamah.fajr)
rowView(prayer: "Zuhr", time: iqamah.zuhr)
rowView(prayer: "Asr", time: iqamah.asr)
rowView(prayer: "Maghrib", time: iqamah.maghrib)
rowView(prayer: "Isha", time: iqamah.isha)
}
}
.padding()
}
.padding()
.onAppear {
prayerTimeModel.fetch()
}
}
.edgesIgnoringSafeArea(.top)
}
func rowView(prayer: String, time: String) -> some View {
HStack {
Text(prayer)
.frame(maxWidth: .infinity, alignment: .leading)
Text(time)
.frame(maxWidth: .infinity, alignment: .trailing)
}
}
}
#Preview {
ContentView()
}
// PrayerTimesModel.swift
// PrayerTimesCompanion Watch App
import Foundation
import SwiftUI
// Main struct for the response
struct PrayerTimesResponse: Codable {
let status: String
var data: SalahData
let message: [String]
}
// Struct for the data section
struct SalahData: Codable {
var salah: [Salah]
var iqamah: [Iqamah]
}
// Struct for Salah times
struct Salah: Codable {
let date: String
let hijriDate: String
let hijriMonth: String
let day: String
var fajr: String
let sunrise: String
var zuhr: String
var asr: String
var maghrib: String
var isha: String
enum CodingKeys: String, CodingKey {
case date, hijriDate = "hijri_date", hijriMonth = "hijri_month", day, fajr, sunrise, zuhr, asr, maghrib, isha
}
}
// Struct for Iqamah times
struct Iqamah: Codable {
let date: String
var fajr: String
var zuhr: String
var asr: String
var maghrib: String
var isha: String
let jummah1: String
let jummah2: String
enum CodingKeys: String, CodingKey {
case date, fajr, zuhr, asr, maghrib, isha, jummah1 = "jummah1", jummah2 = "jummah2"
}
}
class PrayerTimesModel: ObservableObject {
@Published var prayerTimes: PrayerTimesResponse = PrayerTimesResponse(
status: "Unknown",
data: SalahData(
salah: [Salah(date: "", hijriDate: "", hijriMonth: "", day: "", fajr: "", sunrise: "", zuhr: "", asr: "", maghrib: "", isha: "")],
iqamah: [Iqamah(date: "", fajr: "", zuhr: "", asr: "", maghrib: "", isha: "", jummah1: "", jummah2: "")]),
message: ["No data available"]
)
// fetches the local mosque's prayer timings via an API call
func fetch() {
guard let url = URL(string: "https://masjidal.com/api/v1/time/range?masjid_id=3OA87VLp") else {
return
}
let task = URLSession.shared.dataTask(with: url) { [self] data, _, error in
guard let data = data, error == nil else {
return
}
// Convert to JSON
do {
var prayerTimes = try JSONDecoder().decode(PrayerTimesResponse.self, from: data)
DispatchQueue.main.async {
self.prayerTimes = prayerTimes
}
}
catch {
print(error)
}
}
task.resume()
}
}
I have implemented a sample video editing timeline using SwiftUI and am facing issues. So I am breaking up the problem in chunks and posting issue each as a separate question. In the code below, I have a simple timeline using an HStack comprising of a left spacer, right spacer(represented as simple black color) and a trimmer UI in the middle. The trimmer resizes as the left and right handles are dragged. The left and right spacers also adjust in width as the trimmer handles are dragged.
Problem: I want to keep the background thumbnails (implemented currently as simple Rectangles filled in different colors) in the trimmer stationary as the trimmer resizes. Currently they move along as the trimmer resizes as seen in the gif below. How do I fix it?
import SwiftUI
struct SampleTimeline: View {
let viewWidth:CGFloat = 340 //Width of HStack container for Timeline
@State var frameWidth:CGFloat = 280 //Width of trimmer
var minWidth: CGFloat {
2*chevronWidth + 10
} //min Width of trimmer
@State private var leftViewWidth:CGFloat = 20
@State private var rightViewWidth:CGFloat = 20
var chevronWidth:CGFloat {
return 24
}
var body: some View {
HStack(spacing:0) {
Color.black
.frame(width: leftViewWidth)
.frame(height: 70)
HStack(spacing: 0) {
Image(systemName: "chevron.compact.left")
.frame(width: chevronWidth, height: 70)
.background(Color.blue)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged({ value in
leftViewWidth = max(leftViewWidth + value.translation.width, 0)
if leftViewWidth > viewWidth - minWidth - rightViewWidth {
leftViewWidth = viewWidth - minWidth - rightViewWidth
}
frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
})
.onEnded { value in
}
)
Spacer()
Image(systemName: "chevron.compact.right")
.frame(width: chevronWidth, height: 70)
.background(Color.blue)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged({ value in
rightViewWidth = max(rightViewWidth - value.translation.width, 0)
if rightViewWidth > viewWidth - minWidth - leftViewWidth {
rightViewWidth = viewWidth - minWidth - leftViewWidth
}
frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
})
.onEnded { value in
}
)
}
.foregroundColor(.black)
.font(.title3.weight(.semibold))
.background {
HStack(spacing:0) {
Rectangle().fill(Color.red)
.frame(width: 70, height: 60)
Rectangle().fill(Color.cyan)
.frame(width: 70, height: 60)
Rectangle().fill(Color.orange)
.frame(width: 70, height: 60)
Rectangle().fill(Color.brown)
.frame(width: 70, height: 60)
Rectangle().fill(Color.purple)
.frame(width: 70, height: 60)
}
}
.frame(width: frameWidth)
.clipped()
Color.black
.frame(width: rightViewWidth)
.frame(height: 70)
}
.frame(width: viewWidth, alignment: .leading)
}
}
#Preview {
SampleTimeline()
}
Hello!
After upgrading to Xcode 16 & Swift 6 & iOS 18 I starting receiveing strange crashes.
Happens randomly in different view and pointing to onGeometryChange action block. I added DispatchQueue.main.async { in hopes it will help but it didn't.
HStack {
...
}
.onGeometryChange(for: CGSize.self, of: \.size) { value in
DispatchQueue.main.async {
self.width = value.width
self.height = value.height
}
}
As far as I understand, onGeometryChange is defined as nonisolated and Swift 6 enforce thread checking for the closures, SwiftUI views are always run on the main thread. Does it mean we can not use onGeometryChange safely in swiftui?
BUG IN CLIENT OF LIBDISPATCH: Assertion failed: Block was expected to execute on queue [com.apple.main-thread (0x1eacdce40)]
Crashed: com.apple.SwiftUI.AsyncRenderer
0 libdispatch.dylib 0x64d8 _dispatch_assert_queue_fail + 120
1 libdispatch.dylib 0x6460 _dispatch_assert_queue_fail + 194
2 libswift_Concurrency.dylib 0x62b58 <redacted> + 284
3 Grit 0x3a57cc specialized implicit closure #1 in closure #1 in PurchaseModalOld.body.getter + 4377696204 (<compiler-generated>:4377696204)
4 SwiftUI 0x5841e0 <redacted> + 60
5 SwiftUI 0x5837f8 <redacted> + 20
6 SwiftUI 0x586b5c <redacted> + 84
7 SwiftUICore 0x68846c <redacted> + 48
8 SwiftUICore 0x686dd4 <redacted> + 16
9 SwiftUICore 0x6ecc74 <redacted> + 160
10 SwiftUICore 0x686224 <redacted> + 872
11 SwiftUICore 0x685e24 $s14AttributeGraph12StatefulRuleP7SwiftUIE15withObservation2doqd__qd__yKXE_tKlF + 72
12 SwiftUI 0x95450 <redacted> + 1392
13 SwiftUI 0x7e438 <redacted> + 32
14 AttributeGraph 0x952c AG::Graph::UpdateStack::update() + 540
15 AttributeGraph 0x90f0 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 424
16 AttributeGraph 0x8cc4 AG::Subgraph::update(unsigned int) + 848
17 SwiftUICore 0x9eda58 <redacted> + 348
18 SwiftUICore 0x9edf70 <redacted> + 36
19 AttributeGraph 0x148c0 AGGraphWithMainThreadHandler + 60
20 SwiftUICore 0x9e7834 $s7SwiftUI9ViewGraphC18updateOutputsAsync2atAA11DisplayListV4list_AG7VersionV7versiontSgAA4TimeV_tF + 560
21 SwiftUICore 0x9e0fc0 $s7SwiftUI16ViewRendererHostPAAE11renderAsync8interval15targetTimestampAA4TimeVSgSd_AItF + 524
22 SwiftUI 0xecfdfc <redacted> + 220
23 SwiftUI 0x55c84 <redacted> + 312
24 SwiftUI 0x55b20 <redacted> + 60
25 QuartzCore 0xc7078 <redacted> + 48
26 QuartzCore 0xc52b4 <redacted> + 884
27 QuartzCore 0xc5cb4 <redacted> + 456
28 CoreFoundation 0x555dc <redacted> + 176
29 CoreFoundation 0x55518 <redacted> + 60
30 CoreFoundation 0x55438 <redacted> + 524
31 CoreFoundation 0x54284 <redacted> + 2248
32 CoreFoundation 0x535b8 CFRunLoopRunSpecific + 572
33 Foundation 0xb6f00 <redacted> + 212
34 Foundation 0xb6dd4 <redacted> + 64
35 SwiftUI 0x38bc80 <redacted> + 792
36 SwiftUI 0x1395d0 <redacted> + 72
37 Foundation 0xc8058 <redacted> + 724
38 libsystem_pthread.dylib 0x637c _pthread_start + 136
39 libsystem_pthread.dylib 0x1494 thread_start + 8
My custom control widget is show up and I can set it to Lock Screen, but it doesn't launch my app when I clicked it. any problem?
in A.swift file, code like below:
@available(iOS 18.0, *)
struct LockScreenAppLaunchWidget: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: "abc") {
ControlWidgetButton(action: LaunchAppIntent()) { // <-- HERE
Label("Something", systemImage: "arrow.up")
}
}
.displayName("Open app")
}
}
@available(iOS 18, *)
struct LaunchAppIntent: AppIntent {
static var title: LocalizedStringResource = "ABC"
static var description: IntentDescription? = "abcd"
static var openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some IntentResult & OpensIntent {
return .result()
}
}
Hello!
Previously, using content from one Swift Package in the UI of another would cause preview failures. Now, with Xcode 16, this issue has been improved, and the preview feature is working, though occasional crashes still occur. I have submitted a report regarding this issue.
I’ve encountered some issues while developing a SwiftUI-based application, particularly when using Xcode’s SwiftUI preview feature, which frequently crashes.
My app supports both macOS and iOS. Due to the differences between the platforms, I’ve had to implement some pages using UIKit and reference a few UIKit-based open-source frameworks. For instance, I’m using the LazyPager library, which only supports iOS. During runtime, I ensure LazyPager is only compiled for iOS by using .product(name: "LazyPager", package: "LazyPager", condition: .when(platforms: [.iOS])), which works as expected.
However, when I use Xcode’s SwiftUI preview mode and select macOS as the target, UIKit-related code still gets compiled, leading to a crash, with the error message indicating an issue related to LazyPager's UIKit dependencies.
Since it’s not feasible to ask the maintainers of these open-source libraries to add #if canImport(UIKit) conditionals to their code, I would like to ask if there’s a better way to resolve this issue, ensuring that SwiftUI preview works properly on macOS.
If you have any suggestions or solutions, I would greatly appreciate your assistance. Thank you so much for your help!
import PackageDescription
let package = Package(
name: "ImportLibrary",
platforms: [
.iOS(.v16),
.macOS(.v13)
],
products: [
.library(
name: "ImportLibrary",
targets: ["ImportLibrary"]),
],
dependencies: [
.package(url: "https://github.com/gh123man/LazyPager", from: "1.1.0"),
],
targets: [
.target(
name: "ImportLibrary",
dependencies: [
"SwiftSoup",
"Kingfisher",
"WaterfallGrid",
.product(name: "LazyPager", package: "LazyPager", condition: .when(platforms: [.iOS])),
"QuickModule"
]
),
.testTarget(
name: "ImportLibraryTests",
dependencies: ["ImportLibrary"]),
]
)
Sincerely,
Best regards
There are a couple of questions in here.
I want to do a basic master-detail split view, as so many SwiftUI apps do. But I want to let the user select an arbitrary subset of items in the list, select them all, and delete them (or do other operations).
Select All, by default, is excruciatingly slow. I have a List view with a selection binding to a Set<PersistentIdentifier>. A ForEach inside renders the items from a SwiftData @Query. I have a couple hundred items, each with a String and Date property. The list item renders the String and nothing else.
I click on an item, hit Command-A, and the app locks up for several seconds while it messes with the selection. It never highlights all the items, but sometimes highlights one (usually different from the one I clicked on).I have an .onChange(of: self.selection) in there to debug the selection. It is called many, many times (2-3 times per second, very slowly), and print the selection count, which starts at 135, and goes down by one, to about 103. If I scroll the list, the selection onChange gets called a bunch more times. Sometimes you see the selection highlights change.
My SwiftUI view hierarchy looks like this:
NavigationSplitView
OrdersList
struct
OrdersList : View
{
var
body: some View
{
List(selection: self.$selection)
{
ForEach(self.orders)
{ order in
NavigationLink
{
Text("Order ID: \(order.vendorOrderID ?? "<none>")")
}
label:
{
Text("Order ID: \(order.vendorOrderID ?? "<none>")")
}
}
.onDelete(perform: self.deleteOrders)
.onChange(of: self.selection)
{
print("Selection changed: \(self.selection.count)")
}
}
}
func
deleteOrders(offsets: IndexSet)
{
withAnimation
{
for index in offsets
{
self.modelContext.delete(self.orders[index])
}
}
}
@State var selection = Set<PersistentIdentifier>()
@Query var orders : [Order]
@Environment(\.modelContext) var modelContext
}
If I use the mouse to select a single item, it behaves as expected. The item is selected, the onChange gets called, and if I choose Edit->Delete, the onDelete handler is called.
If I use the mouse to shift-select multiple items, the behavior is similar to Select All above, but the selection Set count starts at the number of items I selected, and then is slowly whittled down to 0 or 1, with some random item being selected.
I’d like for the Edit->Delete command to work with a keystroke (delete). How can I set the shortcut on an existing menu item without having to reimplement its behavior?
After upgrading to MacOS Sequoia and Xcode 16 my app was behaving oddly. While running the application from Xcode, the app appears to save data, but running the app again from Xcode, the data is not there. If I open the app on the iPhone, run it and create data, the data persists even after I kill the app from the app switcher.
I went many rounds through my code and found nothing that helped. I finally ran the sample app that appears when you begin a new project and that is behaving the same way. There are errors thrown.
error: the replacement path doesn't exist: "/var/folders/qv/m7sk8kcd3713j3l4bg8wt1lw0000gn/T/swift-generated-sources/@_swiftmacro_13SwiftDataTest11ContentViewV5items33_BCE1062261466603F5F86A688789BF68LL5QueryfMa.swift"
error: the replacement path doesn't exist: "/var/folders/qv/m7sk8kcd3713j3l4bg8wt1lw0000gn/T/swift-generated-sources/@_swiftmacro_13SwiftDataTest11ContentViewV5items33_BCE1062261466603F5F86A688789BF68LL5QueryfMa.swift"
error: the replacement path doesn't exist: "/var/folders/qv/m7sk8kcd3713j3l4bg8wt1lw0000gn/T/swift-generated-sources/@_swiftmacro_13SwiftDataTest11ContentViewV5items33_BCE1062261466603F5F86A688789BF68LL5QueryfMa.swift"
is now broken. (but definitely worked when I originally wrote my Document-based app)
It's been a few years.
DocumentBrowserViewController's delegate implements the following func.
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
let newDocumentURL: URL? = Bundle.main.url(forResource: "blankFile", withExtension: "trtl2")
// Make sure the importHandler is always called, even if the user cancels the creation request.
if newDocumentURL != nil {
importHandler(newDocumentURL, .copy)
} else {
importHandler(nil, .none)
}
}
When I tap the + in the DocumentBrowserView, the above delegate func is called (my breakpoint gets hit and I can step through the code) newDocumentURL is getting defined successfully and
importHandler(newDocumentURL, .copy)
gets called, but returns the following error:
Optional(Error Domain=com.apple.DocumentManager Code=2 "No location available to save “blankFile.trtl2”." UserInfo={NSLocalizedDescription=No location available to save “blankFile.trtl2”., NSLocalizedRecoverySuggestion=Enable at least one location to be able to save documents.})
This feels like something new I need to set up in the plist, but so far haven't been able to discover what it is.
perhaps I need to update something in info.plist? perhaps one of:
CFBundleDocumentTypes
UTExportedTypeDeclarations
Any guidance appreciated.
thanks :-)
It looks like iOS 18 app changed the way document-based SwiftUI apps function in a way that breaks our app.
Previously, a ReferenceFileDocument would run its init(configuration:) function before any SwiftUI views would load. Now, it runs it after SwiftUI views load and their onAppear modifiers run.
Because we use a reference-type data model, our views reference a different object than the one loaded from our document's content. Much of our app's functionality is broken, and file saving doesn't work (because the data model writing to disk isn't connected to the views)
I filed a bug report, but this seems like a wild change that should affect more than just us. It wasn't happening earlier in the iOS betas. It feels like it only got added in the last beta, but I'm not sure.
Has anyone else run into this, or have any guidance for how best to deal with this?
My app focuses on a particular day and in the app I have arrow buttons that let you switch to the next or previous day. When one of them is pressed I call WidgetCenter.shared.reloadAllTimelines(). Almost immediately, regardless of how many times they are pressed, I will usually see my widgets immediately update to reflect the new day.
I thought it would be nice to extend this functionality to Lock Screen controls. I had the AppIntent the Lock Screen buttons use call some of the same code that the arrow buttons in my app do, which includes a call to WidgetCenter.shared.reloadAllTimelines().
In the simulator it seemed to work great. I could see my Lock Screen widgets update almost immediately to focus on the new day.
However, when I tried it on an actual device it was a much different story. Usually the first button press will update the widgets but after that it can be much, much longer before the Lock Screen widgets eventually refresh.
It makes sense that the system is probably throttling my requests but is there a way to prevent this so that my Lock Screen controls operate the same way as buttons in my app?
I can't figure out if I've found a VoiceOver problem with Swift Charts or if I'm doing something incorrectly.
I have a loop within a loop showing 2 sets of data in the same chart.
If I touch a month then VO correctly says there are two data series. But if I keep swiping down only data from the first series is read.
ChatGPT said try referencing the outer loop and sure enough that worked if it done in BOTH the label and value.
It sounds really awkward though. For example, "High 89 degrees F High October".
Below the "bad" chart only says something such as "92 degrees F October" when swiping down. The "good" chart will read the high and low temperature data.
VStack {
headerText("BAD")
Chart {
ForEach(processedMonthlyInput) { oneMonth in
ForEach(oneMonth.temperatures, id: \.month) { element in
LineMark(
x: .value("Month", element.month, unit: .month),
y: .value("Temperature", element.tempVal.converted(to: .fahrenheit).value)
)
.accessibilityLabel("\(element.month.formatted(.dateTime.month(.wide)))")
.accessibilityValue(Text("\(element.tempVal.converted(to: tempUnit).formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))))"))
}
.symbol(by: .value("Type", oneMonth.theType))
.foregroundStyle(by: .value("Type", oneMonth.theType))
.interpolationMethod(.catmullRom)
}
}
.frame(maxHeight: paddingAmount)
.padding(.horizontal)
headerText("GOOD")
Chart {
ForEach(processedMonthlyInput) { oneMonth in
ForEach(oneMonth.temperatures, id: \.month) { element in
LineMark(
x: .value("Month", element.month, unit: .month),
y: .value("Temperature", element.tempVal.converted(to: .fahrenheit).value)
)
.accessibilityLabel("\(oneMonth.theType) \(element.month.formatted(.dateTime.month(.wide)))")
.accessibilityValue(Text("\(oneMonth.theType) \(element.tempVal.converted(to: tempUnit).formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))))"))
}
.symbol(by: .value("Type", oneMonth.theType))
.foregroundStyle(by: .value("Type", oneMonth.theType))
.interpolationMethod(.catmullRom)
}
}
.frame(maxHeight: paddingAmount)
.padding(.horizontal)
}