Before I updated to iOS 18 everything worked fine. I pushed out an update to my application on the App Store and I had no issues. After updating to the latest OS many of my touch events are no longer working and I have no idea why.
Sometimes when the app runs the touch events work fine and other times I can't click on half of my views & buttons. I am completely lost as to what might be happening.
I am having issues all over the application but let's focus on the navigation stack and the toolbar item buttons. I will post some code snippets, I have been unable to replicate this in a small playground project. This is my setup, I have two buttons but lets focus on the home & notifications view.
The custom Router
import SwiftUI
import Foundation
@Observable
class HomeRouter {
var navPath = NavigationPath()
@MainActor
func navigate(to destination: HOME_ROUTES) {
navPath.append(destination)
}
@MainActor
func navigateBack() {
navPath.removeLast()
}
@MainActor
func navigateToRoot() {
navPath.removeLast(navPath.count)
}
}
Home View
import os
import SwiftUI
import CoreLocation
import NotificationCenter
struct Home: View {
@State public var router: HomeRouter
@State private var showDetail = false
@State private var showMoreFields = false
@EnvironmentObject private var session: SessionStore
private var log = Logger(subsystem: "com.olympsis.client", category: "home_view")
init(router: HomeRouter = HomeRouter()) {
self._router = State(initialValue: router)
}
var body: some View {
NavigationStack(path: $router.navPath) {
ScrollView(.vertical) {
//MARK: - Welcome message
WelcomeCard()
.padding(.top, 25)
.environmentObject(session)
// MARK: - Announcements
AnnouncementsView()
.environmentObject(session)
// MARK: - Next Events
NextEvents()
.environmentObject(session)
// MARK: - Hot Events
HotEvents()
.environmentObject(session)
// MARK: - Nearby Venues
NearbyVenues()
.environmentObject(session)
Spacer(minLength: 100)
}
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Text("Olympsis")
.italic()
.font(.largeTitle)
.fontWeight(.black)
}
ToolbarItemGroup(placement: .topBarTrailing) {
Button(action: { router.navigate(to: .messages) }) {
ZStack(alignment: .topTrailing) {
Image(systemName: "bubble.left.and.bubble.right")
.foregroundStyle(Color.foreground)
if session.invitations.count > 0 {
NotificationCountView(value: $session.invitations.count)
}
}
}
Button(action: { router.navigate(to: .notifications) }) {
ZStack(alignment: .topTrailing) {
Image(systemName: "bell")
.foregroundStyle(Color.foreground)
if session.invitations.count > 0 {
NotificationCountView(value: $session.invitations.count)
}
}
}
}
}
.background(Color("background-color/primary"))
.navigationDestination(for: HOME_ROUTES.self, destination: { route in
switch route {
case .notifications:
NotificationsView()
.id(HOME_ROUTES.notifications)
.environment(router)
.environmentObject(session)
.navigationBarBackButtonHidden()
case .messages:
HomeMessagesView()
.id(HOME_ROUTES.messages)
.environment(router)
.environmentObject(session)
.navigationBarBackButtonHidden()
case .full_post_view(let id):
AsyncPostView(postId: id)
.id(HOME_ROUTES.full_post_view(id))
.environmentObject(session)
.navigationBarBackButtonHidden()
}
})
}
}
}
#Preview {
Home()
.environmentObject(SessionStore())
}
The Notifications View
import SwiftUI
struct NotificationsView: View {
@State private var notifications: [NotificationModel] = []
@Environment(HomeRouter.self) private var router
@EnvironmentObject private var session: SessionStore
var body: some View {
ScrollView {
if notifications.count > 0 {
ForEach(notifications, id: \.id){ note in
NotificationModelView(notification: note)
}
} else {
VStack {
Text("No new notifications")
HStack {
Spacer()
}
}.padding(.top, 50)
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button(action:{ router.navigateBack() }) {
Image(systemName: "chevron.left")
.foregroundStyle(Color.foreground)
}
}
ToolbarItem(placement: .principal) {
Text("Notifications")
}
}
.task {
notifications = session.invitations.map({ i in
NotificationModel(id: UUID().uuidString, type: "invitation", invite: i, body: "")
})
}
}
}
#Preview {
NavigationStack {
NotificationsView()
.environment(HomeRouter())
.environmentObject(SessionStore())
}
}
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
Hi,
I am having some spacing issues with the new TabViewStyle.sidebarAdaptable. My app uses @ToolBarContentBuilder for navigationBar elements as there are some custom design requirements. One of these is title text that is set in the principal position.
Simplified example:
var body: some View {
Text("Body")
.toolbar {
toolbar()
}
}
@ToolbarContentBuilder
private func toolbar() -> some ToolbarContent {
ToolbarItem(placement: placement) {
Text("Title")
}
}
Everything with this setup works fine till I use an iPad with iOS 18 where the new toggleable sidebar is present. Upon switching to the sidebar layout the title does not move to the space adjacent to the navigation button (where the tab bar just was) and instead remains in its own bar, below the rest of the navigation.
I've noticed that when navigationTitle is set the the title set in toolbar() does appear in the right place.
var body: some View {
Text("Body")
.toolbar {
toolbar()
}
.navigationTitle("anything")
}
Is this expected behaviour? How can I achieve a single line nav bar with a title set this way?
Happy to provide a sandbox app to reproduce this issue.
Many thanks,
Matt
Our app occasionally crashes when creating a Live Activity using ActivityKit.
Crash log analysis indicates a wild pointer issue originating within ActivityKit.
The crash appears to be linked to the coexistence of multiple Live Activities.
Could this be a compatibility issue with ActivityKit?
We would appreciate any guidance or potential workarounds to resolve this issue.
Looking forward to your response.
Thread 0 Crashed:
0 ActivityKit 0x000000023023d034 0x230235000 + 32820 ( + 13792)
1 ActivityKit 0x000000023023d014 0x230235000 + 32788 ( + 13760)
2 Combine 0x00000001ac5bd168 0x1ac5b5000 + 33128 ( + 292)
3 Combine 0x00000001ac5c0658 0x1ac5b5000 + 46680 ( + 24)
4 Combine 0x00000001ac5d1714 0x1ac5b5000 + 116500 ( + 204)
5 Combine 0x00000001ac5c8da0 0x1ac5b5000 + 81312 ( + 24)
6 Combine 0x00000001ac5e8e98 0x1ac5b5000 + 212632 ( + 2520)
7 Combine 0x00000001ac5d1a4c 0x1ac5b5000 + 117324 ( + 24)
8 Combine 0x00000001ac68316c 0x1ac5b5000 + 844140 ( + 56)
9 Combine 0x00000001ac5d4a4c 0x1ac5b5000 + 129612 ( + 176)
10 Combine 0x00000001ac5bd43c 0x1ac5b5000 + 33852 ( + 392)
11 Combine 0x00000001ac5b7198 0x1ac5b5000 + 8600 ( + 24)
12 Combine 0x00000001ac5ead74 0x1ac5b5000 + 220532 ( + 712)
13 Combine 0x00000001ac5e2320 0x1ac5b5000 + 185120 ( + 24)
14 Combine 0x00000001ac5bfe74 0x1ac5b5000 + 44660 ( + 488)
15 Combine 0x00000001ac5b81b8 0x1ac5b5000 + 12728 ( + 24)
16 Combine 0x00000001ac5b8804 0x1ac5b5000 + 14340 (Just.receive(subscriber:) + 424)
17 Combine 0x00000001ac5f7a7c 0x1ac5b5000 + 273020 (Publishers.Merge.receive(subscriber:) + 820)
18 Combine 0x00000001ac5d9850 0x1ac5b5000 + 149584 (PublisherBox.receive(subscriber:) + 108)
19 Combine 0x00000001ac5b8154 0x1ac5b5000 + 12628 (AnyPublisher.receive(subscriber:) + 64)
20 Combine 0x00000001ac5dc9cc 0x1ac5b5000 + 162252 ( + 548)
21 Combine 0x00000001ac5dc764 0x1ac5b5000 + 161636 (Publishers.HandleEvents.receive(subscriber:) + 620)
22 Combine 0x00000001ac5d9850 0x1ac5b5000 + 149584 (PublisherBox.receive(subscriber:) + 108)
23 Combine 0x00000001ac5b8154 0x1ac5b5000 + 12628 (AnyPublisher.receive(subscriber:) + 64)
24 Combine 0x00000001ac5d92e8 0x1ac5b5000 + 148200 (Publishers.FlatMap.receive(subscriber:) + 416)
25 Combine 0x00000001ac5dc9cc 0x1ac5b5000 + 162252 ( + 548)
26 Combine 0x00000001ac5d9850 0x1ac5b5000 + 149584 (PublisherBox.receive(subscriber:) + 108)
27 Combine 0x00000001ac5b8154 0x1ac5b5000 + 12628 (AnyPublisher.receive(subscriber:) + 64)
28 Combine 0x00000001ac5f062c 0x1ac5b5000 + 243244 (Publishers.CompactMap.receive(subscriber:) + 572)
29 Combine 0x00000001ac5d9850 0x1ac5b5000 + 149584 (PublisherBox.receive(subscriber:) + 108)
30 Combine 0x00000001ac5b8154 0x1ac5b5000 + 12628 (AnyPublisher.receive(subscriber:) + 64)
31 Combine 0x00000001ac5e64e4 0x1ac5b5000 + 201956 (Publishers.SetFailureType.receive(subscriber:) + 552)
32 Combine 0x00000001ac5d92e8 0x1ac5b5000 + 148200 (Publishers.FlatMap.receive(subscriber:) + 416)
33 Combine 0x00000001ac5dc9cc 0x1ac5b5000 + 162252 ( + 548)
34 Combine 0x00000001ac5d9850 0x1ac5b5000 + 149584 (PublisherBox.receive(subscriber:) + 108)
35 Combine 0x00000001ac5b8154 0x1ac5b5000 + 12628 (AnyPublisher.receive(subscriber:) + 64)
36 Combine 0x00000001ac5f062c 0x1ac5b5000 + 243244 (Publishers.CompactMap.receive(subscriber:) + 572)
37 Combine 0x00000001ac5d9850 0x1ac5b5000 + 149584 (PublisherBox.receive(subscriber:) + 108)
38 Combine 0x00000001ac5b8154 0x1ac5b5000 + 12628 (AnyPublisher.receive(subscriber:) + 64)
39 Combine 0x00000001ac5bdd68 0x1ac5b5000 + 36200 (Publishers.ReceiveOn.receive(subscriber:) + 812)
40 Combine 0x00000001ac5f1e10 0x1ac5b5000 + 249360 (Publisher.sink(receiveCompletion:receiveValue:) + 304)
41 ActivityKit 0x00000002302594a0 0x230235000 + 148640 ( + 6064)
42 ActivityKit 0x0000000230258c18 0x230235000 + 146456 ( + 3880)
43 ActivityKit 0x0000000230258410 0x230235000 + 144400 ( + 1824)
44 ActivityKit 0x0000000230258124 0x230235000 + 143652 ( + 1076)
45 ActivityKit 0x0000000230258080 0x230235000 + 143488 ( + 912)
46 ActivityKit 0x000000023026d280 0x230235000 + 230016 ( + 4228)
47 ActivityKit 0x000000023026d39c 0x230235000 + 230300 ( + 4512)
48 libswiftDispatch.dylib 0x00000001ac59e7f4 0x1ac59d000 + 6132 ( + 28)
49 libswiftDispatch.dylib 0x00000001ac5a5a90 0x1ac59d000 + 35472 ( + 16)
50 libswiftDispatch.dylib 0x00000001ac59f97c 0x1ac59d000 + 10620 ( + 188)
51 libswiftDispatch.dylib 0x00000001ac59fa90 0x1ac59d000 + 10896 ( + 28)
52 libswiftDispatch.dylib 0x00000001ac59f5ec 0x1ac59d000 + 9708 ( + 28)
53 libdispatch.dylib 0x00000001ab37feac 0x1ab37c000 + 16044 ( + 20)
54 libdispatch.dylib 0x00000001ab38f428 0x1ab37c000 + 78888 ( + 56)
55 libswiftDispatch.dylib 0x00000001ac59ef38 0x1ac59d000 + 7992 ( + 180)
56 libswiftDispatch.dylib 0x00000001ac59e0dc 0x1ac59d000 + 4316 ( + 56)
57 libswiftDispatch.dylib 0x00000001ac59ec48 0x1ac59d000 + 7240 ( + 396)
58 libswiftDispatch.dylib 0x00000001ac59e188 0x1ac59d000 + 4488 (OS_dispatch_queue.sync(execute:) + 164)
59 ActivityKit 0x000000023026be70 0x230235000 + 224880 ( + 3228)
60 ActivityKit 0x000000023026b400 0x230235000 + 222208 ( + 556)
61 ActivityKit 0x00000002302d10b4 0x230235000 + 639156 ( + 25780)
62 ActivityKit 0x00000002302d0cd0 0x230235000 + 638160 ( + 24784)
63 ActivityKit 0x00000002302d0b94 0x230235000 + 637844 ( + 24468)
64 xxxx 0x0000000100919638 specialized static LiveActivityManager.startActivity(title:) + 169528 (LiveActivityManager.swift:96)
I’m relatively new to SwiftUI, so I’ve got a pretty basic question here. In my watchOS app, I’m using ShareLink to share a string of text to the Mail and Messages apps. Mail shows up in the ShareLink sheet just fine, but Messages doesn’t appear.
What’s odd is that when I run the app in Xcode’s preview or in the Simulator, Messages does show up as a sharing option… So now I’m wondering if this is just a quirk with my device or if Apple doesn’t actually support sharing text to Messages from third-party watchOS apps yet? I know some Apple Watch apps (like Contacts) do support sending text/files to Messages, so I’m curious if anyone’s had a similar experience or knows more about this.
Any insights would be super helpful!
I am trying add Sign in with Apple but when I attempt to capability in my app nothing happens in the list
does apple not able to provide this feature yet in Vision OS or is there any bug or may be ami missing something which does not seems?
I have a SwiftUI based program that has compiled and run consistently on previous macos versions. After upgrading to 15.2 beta 4 to address a known issue with TabView in 15.1.1, my app is now entering a severe hang and crashing with:
"The window has been marked as needing another Update Contraints in Window pass, but it has already had more Update Constraints in Window passes than there are views in the window. .<SwiftUI.AppKitWindow: 0x11d82a800> 0x87 (2071) {{44,0},{1468,883}} en"
Is there a known bug that could be causing this crash or known change in the underlying layout model?
I used .tint(.yellow) to change the default back button color. However, I noticed that the color of the alert button text, except for .destructive, also changed. Is this a bug or Apple’s intended behavior?
this occurs in iOS 18.1 and 18.1.1
Below is my code:
// App
struct TintTestApp: App {
var body: some Scene {
WindowGroup {
MainView()
.tint(.yellow)
}
}
}
// MainView
var mainContent: some View {
Text("Next")
.foregroundStyle(.white)
.onTapGesture {
isShowingAlert = true
}
.alert("Go Next View", isPresented: $isShowingAlert) {
Button("ok", role: .destructive) {
isNextButtonTapped = true
}
Button("cancel", role: .cancel) { }
}
}
thank you!
Generic parameter 'V' could not be inferred ERROR
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let message = self.parent.viewModel.messages[indexPath.row]
// var cell: RCBaseMessageCell? = tableView.dequeueReusableCell(withIdentifier: String( message.type.rawValue)) as? RCBaseMessageCell
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if #available(iOS 16.0, *) {
cell?.contentConfiguration = UIHostingConfiguration {
Text("123").onTapGesture {
print("123")
}
}
return cell
}
on ios 18.1 onTapGesture sometimes will not callback ,but ios 17 16 is ok,how can i fix it
Pretty sure this is a no-no, but asking just in case there's an easy way to make this work
struct DocumentContentView: View {
private static let logger = Logger(
subsystem: "mySubsystem",
category: String(describing: Self.self)
)
var body: some View {
VStack {
Text("Hello")
logger.trace("hello")
}
}
}
This code generates the following compile error at the logger.trace line
buildExpression is unavailable: this expression does not conform to View
I suspect every line of the body var (or any @ViewBuilder - designated code?) needs to 'return' a View. Is this correct? or more importantly any work arounds other than putting some/all of the view contents in a. func()?
Keep ScrollView position when adding items on the top using onAppear / onScrollTargeVisibilityChange
Related Post: Keep ScrollView position when adding items on the top
I was trying to implement a message view using ScrollView + LazyVStack. This view requires pagination so I used onScrollTargeVisibilityChange modifier to detect if the first item appears on screen and a new page needs to be loaded. I learnt from the above post that scrollPositon modifier can help keep the scroll position. I tested the method mentioned in that post -- use a button to add new items to the top -- and it worked.
However, when use onScrollTargeVisibilityChange modifier to add items, the view automatically scrolls to the top and cause the following loop:
first item in the list appears on screen --> load and insert more items to the top --> scroll view scrolls to top --> first item appears --> load more data --> scroll to top --> first item ... --> more data... --> top ...
Until it generates the error ScrollTargetVisibilityChange tried to update multiple times per frame.
Here is the simplified code.
struct Item: Identifiable {
var id: UUID = .init()
var content: String
}
struct ScrollViewTest: View {
@State private var items: [Item] = (0...30).map {Item(content:"\($0)")}.reversed()
@State private var itemID: Item.ID?
@State private var page: Int = 0
var body: some View {
ScrollView {
LazyVStack {
ForEach(items) {item in
Text(item.content)
.frame(height: 30)
}
}
.scrollTargetLayout()
}
.scrollPosition(id: $itemID)
.defaultScrollAnchor(.bottom)
.onScrollTargetVisibilityChange(idType: Item.ID.self) { onScreenItemIDs in
if onScreenItemIDs.first == items.first?.id {
page += 1
let newItems = (page*30+1 ... (page+1)*30).map {Item(content:"\($0)")}
items.insert(contentsOf: newItems.reversed(), at: 0)
}
}
.toolbar {
Button("Add") {
page += 1
let newItems = (page*30+1 ... (page+1)*30).map {Item(content:"\($0)")}
items.insert(contentsOf: newItems.reversed(), at: 0)
}
}
}
}
I want to load data while scrolling without the need of pressing any buttons. How can I solve this problem?
I have a very annoying problem editing a property of one of my model structs: the binding var (subject) property (gender) is updated only once by a selection list.
The first time I use my control, all works correctly; from the second time on, nothing happens (not even an .onChange() placed for debug... it simply doesn't fire up).
EVEN MORE STRANGE BEHAVIOR:
The SAME control, copied inside an Apple sample projects works perfectly (Recipies Sample, downloaded from Apple Developer site). This sample project is, I guess, at least two years old, still with @ObservableObject, @Publish, ...and so on, that I converted to @Observable protocol.
FINAL CHERRY ON THE CAKE: THE SAME sample Project, copied ASIS into a new Xcode projects, doesn't work anymore!
It seems an error buried deep inside Xcode compiler optimizations (maybe to avoid unnecessary views redraw carried too far...).
Anyway: I'm asking for help, because I'm not able to see any reason for this behavior and - just to add a bit of frustration - a working project developed with Xcode 15, without any problem (Stager, available on the App Store), reopenend with Xcode 16 acquires the same odd behavior.
Any Apple developer can help?
Many thanks in advace
Simplified code follows (I made a new project just with the few things needed to show the case)
MODEL
import Foundation
import SwiftUI
enum Gender : String, Codable, CaseIterable, Equatable {
case male = "M"
case female = "F"
case nonbinary = "N"
case unknown = "U"
var description : String {
switch self {
case .male : "Male"
case .female : "Female"
case .nonbinary : "Not binary"
case .unknown : "Unknown"
}
}
var iconName : String {
"iconGender\(self.rawValue)"
}
}
struct Subject : Identifiable, Codable, Equatable, Hashable {
var id : Int
var name : String
var surname : String
var nickName : String // Identificativo alternativo all’anagrafica
var gender : Gender // Sesso del soggetto [ M | F | * ]
var imageName : String {
"foto" + self.nickName.replacingOccurrences(of: " ", with: "") + ".jpg"
}
static func == (lhs: Subject, rhs: Subject) -> Bool {
return (lhs.id, lhs.nickName, lhs.surname, lhs.name) == (rhs.id, rhs.nickName, rhs.surname, rhs.name)
}
static func emptySubject() -> Subject {
return Subject(id: -1, name: "", surname: "", nickName: "", gender: .unknown)
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(nickName)
hasher.combine(surname)
hasher.combine(name)
}
}
CONTROL
import SwiftUI
struct FormPickerGender: View {
@Binding var value : Gender
let isEdit : Bool
@State var presentPicker : Bool = false
var body: some View {
HStack {
Text("Gender:").foregroundStyle(.gray).italic()
if isEdit {
Image(systemName: "text.magnifyingglass")
.foregroundStyle(.tint)
.onTapGesture {
presentPicker = true
}
}
Text("\(value.description)")
}
.sheet(isPresented: $presentPicker, content: {
PickGender(currentGender: $value)
})
}
}
struct PickGender: View {
@Environment(\.dismiss) var dismiss
@Binding var currentGender : Gender
var body: some View {
Text("Gender selection")
.font(.title2)
.foregroundStyle(.tint)
Button("Cancel") {
dismiss()
}
.buttonBorderShape(.capsule)
List {
ForEach(Gender.allCases, id: \.self) { genderCase in
HStack {
Image("\(genderCase.iconName)")
if currentGender == genderCase {
Text(genderCase.description)
.font(.title3)
.foregroundStyle(.blue)
} else {
Text(genderCase.description)
.font(.title3)
}
Spacer()
}
.onTapGesture {
currentGender = genderCase
dismiss()
}
}
.listRowInsets(EdgeInsets(top: 10, leading: 50, bottom: 10, trailing: 50))
}
}
}
struct GenderPreviewWrapper: View {
@State var subject = Subject.emptySubject()
var body: some View {
Form {
FormPickerGender(value: $subject.gender, isEdit: true)
}
}
}
#Preview {
return GenderPreviewWrapper()
}
Just for completion... If instead of $subject.gender, I use a state variable valued with gender and then $stateGender it works, but to create a specific state var for EACH property of a structure, seems to me to nullify the concept itself of struct: why bother to foreseen properties, if you can't manage them as a whole?
Probably the solution will be to create a specific CLASS object of the STRUCT object, just for edit... something like :
static func <STRUCT>.getEditObject() -> classObject
static func <CLASS>.getStructObject() -> structObject
...once again: why have structs?
Is there a way to implement controls for background audio using ActivityKit like in the Apple Music application? I didn't found anything in your documentation about handling actions like this except deep links, but they're not suitable for this use case
I've been encountering significant issues with button responsiveness across my entire SwiftUI application after upgrading to iOS 18. While everything worked as expected on iOS 17, buttons across various views now sometimes fail to respond to initial taps and require a long press to trigger actions.
Here is a sample of a button within a toolbar, similar issues are occurring with other buttons throughout the app:
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button(action: {
DispatchQueue.main.async {
navigationCoordinator.pushScreen(.account)
}
}) {
Image("User")
.resizable()
.scaledToFit()
.frame(width: 24.5, height: 24.5)
}
}
}
Expected Behavior: All buttons in the application should respond to a simple tap without requiring additional user effort, maintaining consistency with their behavior on iOS 17.
Actual Behavior: On iOS 18, the responsiveness of buttons throughout the application is inconsistent; sometimes they react to a simple tap, and other times they only respond to a long press. This erratic behavior disrupts the user experience and hinders basic app functionality.
I am looking to understand whether this is a widespread issue with the new OS version and if there are any fixes or patches anticipated from Apple. Thank you for your assistance!
I have an observable object which is a model a view.
I also have a Tip (from TipKit) with @Parameter and a rule.
The source of truth for the state is in the observable object, however the Tip needs to be updated when state changes. So how do I bind between the two?
The way I was thinking was to sink updates from objectWillChange and check if Tips parameter needs to be updated or add didSet if it a @Published property. But I am not sure this is the best practice.
Schematic example:
class MyModel: ObservableObject {
struct TapSubmitTip: Tip {
@Parameter static var isSubmitButtonEnabled: Bool = false
var title: Text {
Text("Tap \"Submit\" to confirm your selection.")
}
var rules: [Rule] {
#Rule(Self.$isSubmitButtonEnabled) {
$0 == true
}
}
}
let tapSubmitTip = TapSubmitTip()
var objectWillChangeCancallable: AnyCancellable!
// Used by the view to enable or disable the button
var isSubmitButtonEnabled: Bool {
// Some logic to determine if button should be enabled.
}
init() {
objectWillChangeCancallable = objectWillChange.sink { [weak self] void in
guard let self else { return }
if isSubmitButtonEnabled {
TapSubmitTip.isSubmitButtonEnabled = true
}
}
}
...
// Call objectWillChange or update published properties as needed.
...
}
Getting this error:
'Task' is only available in macOS 10.15 or newerSourceKit
LoggerForPreviews.swift(130, 9): Add 'if #available' version check
LoggerForPreviews.swift(129, 24): Add @available attribute to enclosing static method
LoggerForPreviews.swift(5, 20): Add @available attribute to enclosing actor
Does it have something to do with developing in VSCode?
import Foundation
import SwiftUI
// Logger: A concise, globally accessible logging utility for SwiftUI previews
public final actor PreviewLogger: Sendable {
// LogLevel: Defines severity levels for logging
public enum LogLevel: Int, Sendable, CaseIterable {
// Define cases based on LOG_LEVEL_MAP
case trace, debug, verbose, info, notice, warning, error, critical, fatal
// Computed property to get order based on case declaration
private var order: Int {
switch self {
case .trace: return 0
case .debug: return 1
case .verbose: return 2
case .info: return 3
case .notice: return 4
case .warning: return 5
case .error: return 6
case .critical: return 7
case .fatal: return 8
}
}
public var description: String {
// Use capitalized raw value for description
return String(describing: self).uppercased()
}
// Static function to compare log levels
static func >= (lhs: LogLevel, rhs: LogLevel) -> Bool {
return lhs.order >= rhs.order
}
}
// Shared instance for global access
public static let shared = PreviewLogger()
// Current log level
public var logLevelThreshold: LogLevel = .info
private init() {}
// Configure the logger's log level
public func configure(logLevelThreshold: LogLevel) {
self.logLevelThreshold = logLevelThreshold
}
// Helper function to center text within a given width
private func centered(_ text: String, in separator: String) -> String {
let totalLength = separator.count
let textLength = text.count
if textLength >= totalLength {
return text
}
let padding = (totalLength - textLength) / 2
let padString = String(repeating: " ", count: padding)
return padString + text
}
// Main logging function
public func log(
_ message: String,
level: LogLevel = .info,
file: String = #file,
function: String = #function,
line: Int = #line,
callerFile: String? = nil,
callerFunction: String? = nil,
callerLine: Int? = nil
) {
#if DEBUG
guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" else {
return
}
guard level >= logLevelThreshold else { return }
let fileName = (file as NSString).lastPathComponent
let cleanFunction = function.replacingOccurrences(of: "(_:file:function:line:)", with: "")
let levelIcon: String
switch level {
case .trace: levelIcon = "🔍"
case .debug: levelIcon = "🛠️"
case .verbose: levelIcon = "📝"
case .info: levelIcon = "ℹ️"
case .notice: levelIcon = "📢"
case .warning: levelIcon = "⚠️"
case .error: levelIcon = "❌"
case .critical: levelIcon = "🚨"
case .fatal: levelIcon = "💀"
}
let header = "[\(levelIcon) \(level.description)]"
let separator =
"· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·"
let finalSeparator =
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
let centeredHeader = centered(header, in: separator)
var output = """
\n\(separator)
\(centeredHeader)
"""
let locationInfo = "📍 \(fileName):\(line) ➜ \(cleanFunction)"
let centeredLocation = centered(locationInfo, in: separator)
output += "\n\(centeredLocation)"
if let callerFile = callerFile,
let callerLine = callerLine
{
let callerFileName = (callerFile as NSString).lastPathComponent
let callerInfo = "📱 Called from: \(callerFileName):\(callerLine)"
let centeredCallerInfo = centered(callerInfo, in: separator)
output += "\n\(centeredCallerInfo)"
}
output += """
\n\(separator)\n\(message)\n\(finalSeparator)
"""
print(output)
#endif
}
// Static Methods
public static func configure(logLevelThreshold: LogLevel) {
Task {
await shared.configure(logLevelThreshold: logLevelThreshold)
}
}
public static func log(
_ message: String,
level: LogLevel = .info,
file: String = #file,
function: String = #function,
line: Int = #line,
callerFile: String? = nil,
callerFunction: String? = nil,
callerLine: Int? = nil
) {
Task {
await shared.log(
message,
level: level,
file: file,
function: function,
line: line,
callerFile: callerFile,
callerFunction: callerFunction,
callerLine: callerLine
)
}
}
}
Hey everyone,
TL;DR
How do I enable a draggable TableView to drop Audio Files into Apple Music / Rekordbox / Finder?
Intro / skip me
I've been dabbling into Swift / SwiftUI for a few weeks now, after roughly a decade of web development.
So far I've been able to piece together many things, but this time I'm stuck for hours with no success using Forums / ChatGPT / Perplexity / Trial and Error.
The struggle
Sometimes the target doesn't accept the dropping at all
sometimes the file data is failed to be read
when the drop succeeds, then only as a stream in apple music
My lack of understanding / where exactly I'm stuck
I think the right way is to use UTType.fileUrl but this is not accepted by other applications.
I don't understand low-level aspects well enough to do things right.
The code
I'm just going to dump everything here, it includes failed / commented out attempts and might give you an Idea of what I'm trying to achieve.
//
// Tracks.swift
// Tuna Family
//
// Created by Jan Wirth on 12/12/24.
//
import SwiftySandboxFileAccess
import Files
import SwiftUI
import TunaApi
struct LegacyTracks: View {
@State var tracks: TunaApi.LoadTracksQuery.Data?
func fetchData() {
print("fetching data")
Network.shared.apollo.fetch(query: TunaApi.LoadTracksQuery()) { result in
switch result {
case .success(let graphQLResult):
self.tracks = graphQLResult.data
case .failure(let error):
print("Failure! Error: \(error)")
}
}
}
@State private var selection = Set<String>()
var body: some View {
Text("Tracks").onAppear{
fetchData()
}
if let tracks = tracks?.track {
Table(of: LoadTracksQuery.Data.Track.self, selection: $selection) {
TableColumn("Title", value: \.title)
} rows : {
ForEach(tracks) { track in
TableRow(track)
.draggable(track)
// .draggable((try? File(path: track.dropped_source?.path ?? "").url) ?? test_audio.url) // This causes a compile-time error
// .draggable(test_audio.url)
// .draggable(DraggableTrack(url: test_audio.url))
// .itemProvider {
// let provider = NSItemProvider()
// if let path = self.dropped_source?.path {
// if let f = try? File(path: path) {
// print("Transferring", f.url)
//
//
// }
// }
//
// provider.register(track)
// return provider
// } // This does not
}
}
.contextMenu(forSelectionType: String.self) { items in
// ...
Button("yoooo") {}
} primaryAction: { items in
print(items)
// This is executed when the row is double clicked
}
} else {
Text("Loading")
}
// }
}
}
//extension Files.File: Transferable {
// public static var transferRepresentation: some TransferRepresentation {
// FileRepresentation(exportedContentType: .audio) { url in
// SentTransferredFile( self.)
// }
// }
//}
struct DraggableTrack: Transferable {
var url: URL
public static var transferRepresentation: some TransferRepresentation {
FileRepresentation (exportedContentType: .fileURL) { item in
SentTransferredFile(test_audio.url, allowAccessingOriginalFile: true)
}
// FileRepresentation(contentType: .init(filenameExtension: "m4a")) {
// print("file", $0)
// print("Transferring fallback", test_audio.url)
// return SentTransferredFile(test_audio.url, allowAccessingOriginalFile: true)
// }
// importing: { received in
// // let copy = try Self.(source: received.file)
// return Self.init(url: received.file)
// }
// ProxyRepresentation(exporting: \.url.absoluteString)
}
}
extension LoadTracksQuery.Data.Track: @retroactive Identifiable {
}
import UniformTypeIdentifiers
extension LoadTracksQuery.Data.Track: @retroactive Transferable {
// static func getKind() -> UTType {
// var kind: UTType = UTType.item
// if let path = self.dropped_source?.path {
// if let f = try? File(path: path) {
// print("Transferring", f.url)
// if (f.extension == "m4a") {
// kind = UTType.mpeg4Audio
// }
// if (f.extension == "mp3") {
// kind = UTType.mp3
// }
// if (f.extension == "flac") {
// kind = UTType.flac
// }
// if (f.extension == "wav") {
// kind = UTType.wav
// }
//
// }
// }
// return kind
// }
public static var transferRepresentation: some TransferRepresentation {
ProxyRepresentation {
$0.dropped_source?.path ?? ""
}
FileRepresentation(exportedContentType: .fileURL) { <#Transferable#> in
SentTransferredFile(<#T##file: URL##URL#>, allowAccessingOriginalFile: <#T##Bool#>)
}
// FileRepresentation(contentType: .fileURL) {
// print("file", $0)
// if let path = $0.dropped_source?.path {
// if let f = try? File(path: path) {
// print("Transferring", f.url)
// return SentTransferredFile(f.url, allowAccessingOriginalFile: true)
// }
// }
// print("Transferring fallback", test_audio.url)
// return SentTransferredFile(test_audio.url, allowAccessingOriginalFile: true)
// }
// importing: { received in
// // let copy = try Self.(source: received.file)
// return Self.init(_fieldData: received.file)
// }
// ProxyRepresentation(exporting: \.title)
}
}
On an iPad or iPhone running iPadOS / iOS 18.2 (built with Xcode 16.2), run the WritingApp sample code from https://developer.apple.com/documentation/SwiftUI/Building-a-document-based-app-with-SwiftUI from WWDC24
Then add the following struct to the project:
struct NavigationBarToolbar: ToolbarContent {
var body: some ToolbarContent {
ToolbarItem(placement: .secondaryAction) {
Button("Button 1", systemImage: "1.circle") { }
}
ToolbarItem(placement: .secondaryAction) {
Button("Button 2", systemImage: "2.circle") { }
}
ToolbarItem(placement: .secondaryAction) {
Button("Button 3", systemImage: "3.circle") { }
}
ToolbarItem(placement: .secondaryAction) {
Button("Button 4", systemImage: "4.circle") { }
}
ToolbarItem(placement: .secondaryAction) {
Button("Button 5", systemImage: "5.circle") { }
}
}
}
Comment out the original toolbar in the sample project and replace with:
.toolbar(content: NavigationBarToolbar.init)
Run the project and open or create a document
Click on the TitleMenu and choose Rename, in order to rename the file
Type in a new name and press Enter.
Notice how the items of the toolbar disappear
——
This issue has been submitted as feedback with number FB16100225
This issue is linked to the following feedbacks:
FB14855728
FB14855668
FB14849205
FB12343963
FB15164292
Hi there, so I have this chart that's taking in a Date for it's x values and a time interval for their y values. For some reason, the labels aren't centering on each bar, the only fix I see is to add an offset to each label but that seems hacky.
My code:
Chart {
ForEach(weekBreakdownArr, id: \.startDate) { bd in
BarMark(
x: .value("Date", bd.startDate),
y: .value("Duration", bd.durationWorkDone),
width: .fixed(15)
)
.foregroundStyle(Color.redYarn)
.cornerRadius(2)
}
//...
}
// shownXValues are just the start dates in an array
.chartXAxis {
AxisMarks(position: .automatic, values: shownXValues) { val in
AxisValueLabel {
Text("Th")
.useAppFont(size: 12, relativeTo: .body, weight: .regular)
}
}
}
The WatchOS Control Center has an Edit/Done button at the bottom, and in its edit mode, elements can be moved around and added/removed.
Yet, the SwiftUI List doesn't have an edit mode on WatchOS.
My question is: is the edit functionality in Control Center a custom thing, or is that present in some SwiftUI component that I've missed?