Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

Posts under SwiftUI tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

SwiftUI MagnificationGesture broken in iPadOS 15?
The following code is shown on apples documentation page for SwiftUI MagnificationGesture: struct MagnificationGestureView: View { @GestureState var magnifyBy = CGFloat(1.0) var magnification: some Gesture { MagnificationGesture() .updating($magnifyBy) { currentState, gestureState, transaction in gestureState = currentState } } var body: some View { Circle() .frame(width: 100 * magnifyBy, height: 100 * magnifyBy, alignment: .center) .gesture(magnification) } } Try it (on device) in the Swift Playgrounds App by prepending import SwiftUI import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true PlaygroundPage.current.setLiveView(MagnificationGestureView()) or as a compiled app using the app template in Xcode and try to scale the circle to different sizes in succession. On iPadOS 14 everything works as expected, but since iPadOS 15 Beta 2 it hangs after a few movements of the fingers. Does it work for you? What am I doing wrong? I already filed feedback, but the problem remains till the current beta version and I don't know how to get the gestures working again?
5
0
1.6k
Aug ’23
@Published properties and the main thread
I am working on a library, a Swift package. We have quite a few properties on various classes that can change and we think the @Published property wrapper is a good way to annotate these properties as it offers a built-in way to work with SwiftUI and also Combine. Many of our properties can change on background threads and we've noticed that we get a purple runtime issue when setting the value from a background thread. This is a bit problematic for us because the state did change on a background thread and we need to update it at that time. If we dispatch it to the main queue and update it on the next iteration, then our property state doesn't match what the user expects. Say they "load" or "start" something asynchronously, and that finishes, the status should report "loaded" or "started", but that's not the case if we dispatch it to the main queue because that property doesn't update until the next iteration of the run loop. There also isn't any information in the documentation for @Published that suggests that you must update it on the main thread. I understand why SwiftUI wants it on the main thread, but this property wrapper is in the Combine framework. Also it seems like SwiftUI internally could ask to receive the published updates on the main queue and @Published shouldn't enforce a specific thread. One thing we are thinking about doing is writing our own property wrapper, but that doesn't seem to be ideal for SwiftUI integration and it's one more property wrapper that users of our package would need to be educated about. Any thoughts on direction? Is there anyway to break @Published from the main thread?
3
1
4.4k
Jan ’24
fileImporter download progress from files in iCloud Drive
I'm using fileImporter for a Mac app. If I open a file from iCloud Drive that isn't already downloaded, I need to start the download before I can read the contents of the file. I found that I can download the file by using NSFileCoordinator or FileManager.default.startDownloadingUbiquitousItem. These APIs will begin the download, but I have no updates on the progress or if the file has been downloaded. I've tried to use NSMetadataQuery with no luck. Is there a way, either with fileImporter in SwiftUI or an AppKit API that I can receive updates for when a remote file has been downloaded, or do I need to prompt the users to download the file themselves before importing into my app?
1
1
1.3k
Oct ’23
iOS App shows blank screen after migrating to SwiftUI Lifecycle
This question was originally posted to StackOverflow, but I found it more suitable to be placed here. Was working on migrating one of my app from AppDelegate lifecycle to SwiftUI lifecycle according to this question. After following all the steps, The simulator simply shows a blank screen (the app does not launch at all): There is no log in the console. However, if the app is removed from the simulator (or device) and reinstalled, it will launch the new SwiftUI lifecycle correctly. So there seems to be some problem with scene caching that causes iOS to be confused after the migration. Am I missing something during the migration?
2
0
1.9k
6d
Unable to tap SwiftUI menu with XCUITest (ios15)
I have a SwiftUI menu Menu{ .... }, label : { Image(...).accessibility(identifier: "cardMenu") } I used to be able to bring up the menu (before upgrading to xcode 13 (ios15)) like this let app = XCUIApplication() app.launch() app.buttons["cardMenu"].tap() But now i am unable to see the identifier in app.buttons. Can't seem to find the identifier anymore. I've tried looking for the identifier in the other app fields and changing to use text instead of identifer. No luck. These tests used to work prior to the upgrade. Any help would be appreciated
3
1
2.7k
Apr ’24
Jaggy, Laggy, low-fps scrolling on iPhone 13 Pro (Pro Motion Display) for .offset
I created a horizontally scrolling view modifier that essentially wraps the contents and applies a horizontal offset based on a DragGesture GestureState. It looks a little bit like this @GestureState private var dragOffset: CGFloat = 0 content .offset(x: dragOffset, y: 0) .simultaneousGesture(DragGesture(minimumDistance: 20, coordinateSpace: .global) .updating($dragOffset) { (value, gestureState, transaction) in                             gestureState = value.translation.width                         }) The problem I am seeing is that on the Pro Motion Display of the iPhone 13 Pro, the "offset" view does not smoothly slide along with your finger as it drags. Instead is is very jaggy or laggy, probably rendered with maybe 15 fps or so. On the iPhone 11 Pro I used before, the drag is very smooth and on the iOS 15 simulator it is smooth as well. What is wrong here? Thanks for help!
7
2
4.6k
Oct ’23
SwiftUI NavigationView pops back when updating observableObject
Background: I have been stuck on this annoying problem for days, trying different solution and searching apple developer forum, stackoverflow etc for answers; some have had similar problems but no suggested solution had the desired effect. The problem is that when updating an observableObject or environmentObject down the navigation hierarchy view stack, the views get popped back to root. Viewing data from observableObject is fine, but not editing. Scenario is: I navigate to: root -> view1 -> view2. I update the environmentObject in View2 but then I get pushed back to: root -> view1 I have simplified my app in order to make it more understandable. See below: ObservableObject: class DataStore: ObservableObject { static let shared = dataStore() @Published var name : Int = "" } RootView: struct ContentView: View { @StateObject var dataStore = DataStore.shared @State private var isShowingView1 = false var body: some View { NavigationView{ VStack{ Text(dataStore.name) NavigationLink(destination: View1(), isActive: $isShowingView1) { } Button(action: { isShowingView1 = true }) } } } } View1: struct View1: View { @EnvironmentObject var dataStore: dataStore @State private var isShowingView2 = false var body: some View { ScrollView{ VStack(alignment: .center) { Text(dataStore.name) NavigationLink(destination: View2(), isActive: $isShowingView2) { } Button(action: { isShowingView2 = true }){ Text("Go to View2") } } } } } View2: struct View2: View { @EnvironmentObject var dataStore: dataStore var body: some View { ScrollView{ VStack(alignment: .center) { Text(dataStore.name) Button(action: { dataStore.name = "updated value" }){ Text("Update data") } // When updating this environmentObject the viewstack will be pushed back to View1. If view2 had been navigated to view3 and the view3 had been updating the environmentObject, then it would also be pushed back to View1. } } } } Solution: I spent many hours searching for solutions and trying different approaches, but nothing I tried worked. There seemed to be a few other people that had the same problem as I experienced, but the suggested solutions didn't cut it. But then I stumbled on a solution for this problem when trying to implement another feature. So to be frank I am writing here now, not to ask this great community for help, but instead to give back to the community by providing the this solution to others that might need to see this. The solution is really simple implement but was not so easy to come across. If you experience a problem similar to me then you will only need to update your rootView accordingly: RootView Updated: struct ContentView: View { @StateObject var dataStore = DataStore.shared @State private var isShowingView1 = false var body: some View { NavigationView{ VStack{ Text(dataStore.name) NavigationLink(destination: View1(), isActive: $isShowingView1) { } Button(action: { isShowingView1 = true }) } } .navigationViewStyle(.stack) //ADD THIS LINE ABOVE } } This one line .navigationViewStyle(.stack) fixed the problem of popping the viewstack for me. Unfortunately I can't provide you with the logic explanation for this behaviour, but it works and I am satisfied with that. Perhaps you are too, or perhaps you have insight on why this solution actually achieves the desired effect of allowing views down the hierarchy update observableObjects without being popped. Happy coding :)
15
12
16k
Feb ’24
SwiftUI Mac OS toolbar button seems behind other elements
Hi I use a WindowsGroup with a NavigationView and inside of this a list as a sidebar and an HStack for the content. In addition I use a toolbar with several icons. It seems, that the toolbar is behind the content if the button contains only an image. It works correctly if I use an image and a text for the button. Only when I move the mouse pointer over the toolbar icon the icon becomes active and stays active. It doesn't matter where I place the toolbar or whether I use a padding for the content or not. And if I remove the top padding for the content the complete content area goes from edge to edge. I guess that I missed some modifier for the toolbar and/or the content, but can't figure out which one. Thank you for helping Swift version 5.5.1 Xcode 13.1 MacOS Monterey (12.0.1)     var body: some Scene {         WindowGroup {             NavigationView{                 List {                     Text("One")                     Text("Two")                 }                 Text("Hello, world!")                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)                     .background(Color.gray)                     .padding(.all, .zero)                     .padding(.top, 1.0) // removed this in video 2                     .toolbar {                         ToolbarItemGroup(placement: .navigation) {                             Button(action: {                             }, label: {                                 Image(systemName: "sidebar.left").imageScale(.large)                             })                             Button(action: {                             }, label: {                                 HStack{                                     Image(systemName: "sidebar.left").imageScale(.large)                                     Text("")                                 }                             })                         }                     }             }         }         .windowStyle(HiddenTitleBarWindowStyle())     }
2
0
2.8k
Aug ’23
ImportFromDevicesCommands - The operation couldn’t be completed. (Cocoa error 66563.)
I am trying to use import from iPhone option as shown in WWDC session. I added code  WindowGroup {             ContentView()                 .environment(\.managedObjectContext, persistenceController.container.viewContext)                      }         .commands {             ImportFromDevicesCommands()         } ContentView.swift is  List {                 ForEach(items) { item in                     NavigationLink {                         Text("Item at \(item.timestamp!, formatter: itemFormatter)")                     } label: {                         Text(item.timestamp!, formatter: itemFormatter)                     }                 }                 .onDelete(perform: deleteItems)             }             .importsItemProviders([.image,.png,.jpeg,.rawImage], onImport: { providers in                 print("checking reachability")                 return true             }) The importsItemProviders block itself is not executed and not printing anything. In addition I am getting alert The operation couldn’t be completed. (Cocoa error 66563.) Is there anything to add for making this functionality work ?
3
0
1.3k
Oct ’23
WKExtensionsDelegateClassName is Invalid in info.plist
So I am banging my head, I realized my stand along Watch App had a STUPID long name of "App Name - WatchKit App" so I went into my Target and changed the Display Name to "App Name" removing WatchKit App. Well now my app won't validate when uploading to the Appstore. I get the message - Invalid Info.plist key. The key WKExtensionDelegateClassName in bundle App Name.app/Watch/App Name WatchKit App.app is invalid.  My Info.plist has the value of <key>WKExtensionDelegateClassName</key> <string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string> I have confirmed that I have  @WKExtensionDelegateAdaptor(ExtensionDelegate.self) var delegate in my @main for the SwiftUI App. And when I print a few values in my app launch I get the following confirmations: Super Init - ExtensionDelegate Contentview applicationDidFinishLaunching for watchOS Super Init - ExtensionDelegate Optional(My_App_Extension.Setup) Optional(My_App_Extension.Statistics) Optional(My_App_Extension.Other) applicationDidBecomeActive for watchOS update complication I create three classes at launch and print this in the log with print(ExtensionDelegate.shared.Setup as Any) , etc. The other lines are just confirming where I am at app startup. This is a WatchOS8 application and I am running Xcode version Version 13.1 (13A1030d).
2
1
954
2w
In-App Purchase consumable can be purchased just once
I am making an app in SwiftUI using an In-app purchase. In this app, the user should be able to buy points as many times as he wants, so I have used consumable products (in the app store connect). But when I've tried to buy them once again I got the information "This In-App purchase has already been bought. It will be restored for free". I've already searched for a way how to do it but none of the ideas worked for me. Here is my StoreManager class: import Foundation import StoreKit import SwiftUI class StoreManager : NSObject, ObservableObject, SKProductsRequestDelegate { @EnvironmentObject var authViewModel: AuthViewModel @Published var transactionState: SKPaymentTransactionState? @Published var myProducts = [SKProduct]() var request: SKProductsRequest! func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { print("Did receive response") if !response.products.isEmpty { for fetchedProduct in response.products { DispatchQueue.main.async { self.myProducts.append(fetchedProduct) } } for invalidIdentifier in response.invalidProductIdentifiers { print("Invalid identifiers found: \(invalidIdentifier)") } }else{ print("it's empty") } } func getProducts(productIDs: [String]) { let request = SKProductsRequest(productIdentifiers: Set(productIDs)) request.delegate = self request.start() } func request(_ request: SKRequest, didFailWithError error: Error) { print("Request did fail: \(error)") } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .purchasing: transactionState = .purchasing break case .purchased: print("purchased") queue.finishTransaction(transaction) transactionState = .purchased break case .restored: print("restored") transactionState = .restored queue.finishTransaction(transaction) break case .failed, .deferred: queue.finishTransaction(transaction) transactionState = .failed break default: queue.finishTransaction(transaction) break } } } func purchaseProduct(product: SKProduct) { if SKPaymentQueue.canMakePayments() { let payment = SKPayment(product: product) SKPaymentQueue.default().add(payment) } else { print("User can't make payment.") } } func restoreProducts() { SKPaymentQueue.default().restoreCompletedTransactions() } } And I am simply using getProducts with onAppear, and purchase product on button's action. Please help me or if an answer to a similar question already exists send me a link to that thread.
3
0
1.9k
Oct ’23
How to get upload progress when using "await urlSession.upload()"
How to get upload progress when using: let (data, urlResponse) = try await urlSession.upload( for: urlRequest, from: bodyData, delegate: nil // Something I need here maybe? ) I have tried using: func urlSession( _ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { print("fractionCompleted : \(Float(totalBytesSent) / Float(totalBytesExpectedToSend))") } Within the same class but it never fires. I specifically want the async / await capability on the upload. I couldn't get the session.uploadTask to work with the await prefix.
2
1
1.7k
Mar ’24
What is the files and folders permission?
Hi there, I am currently making an app and trying to download content to a user-selected download directory (like how safari does it). However, whenever I set the download directory outside the App's documents, I get the following error when running my code on a real iOS device. downloadedFileMoveFailed(error: Error Domain=NSCocoaErrorDomain Code=513 "“CFNetworkDownload_dlIcno.tmp” couldn’t be moved because you don’t have permission to access “Downloads”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, NSUserStringVariant=(Move), NSDestinationFilePath=/private/var/mobile/Containers/Shared/AppGroup/E6303CBC-62A3-4206-9C84-E37041894DEC/File Provider Storage/Downloads/100MB.bin, NSFilePath=/private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, NSUnderlyingError=0x281d045d0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}, source: file:///private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, destination: file:///private/var/mobile/Containers/Shared/AppGroup/E6303CBC-62A3-4206-9C84-E37041894DEC/File%20Provider%20Storage/Downloads/100MB.bin) The summary of that error is that I don't have permission to access the folder I just granted access to. Here's my attached code (I'm using AlamoFire since it's easier to understand, but I'm having a problem saving files in iOS): import SwiftUI import UniformTypeIdentifiers import Alamofire struct ContentView: View { @AppStorage("downloadsDirectory") var downloadsDirectory = "" @State private var showFileImporter = false var body: some View { VStack { Button("Set downloads directory") { showFileImporter.toggle() } Button("Save to downloads directory") { Task { do { let destination: DownloadRequest.Destination = { _, response in let documentsURL = URL(string: downloadsDirectory)! let suggestedName = response.suggestedFilename ?? "unknown" let fileURL = documentsURL.appendingPathComponent(suggestedName) return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) } let _ = try await AF.download(URL(string: "https://i.imgur.com/zaVQDFJ.png")!, to: destination).serializingDownloadedFileURL().value } catch { print("Downloading error!: \(error)") } } } } .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [UTType.folder]) { result in switch result { case .success(let url): downloadsDirectory = url.absoluteString case .failure(let error): print("Download picker error: \(error)") } } } } To reproduce (Run on a REAL iOS device!): Click the Set downloads directory button to On my iPhone Click the Save to downloads directory button Error occurs Upon further investigation, I found that safari uses the Files and Folders privacy permission (Located in Settings &gt; Privacy &gt; Files and folders on an iPhone) to access folders outside the app sandbox (see the attached image for a visual representation of what I'm talking about). I scoured the web as much as I can and I couldn't find any documentation for this exact permission. I have seen non-apple apps (such as VLC) use this permission, but I cannot figure out how it's granted. I tried enabling the following plist properties, but none of them work (because I later realized these are for macOS only) &lt;key&gt;NSDocumentsFolderUsageDescription&lt;/key&gt; &lt;string&gt;App wants to access your documents folder&lt;/string&gt; &lt;key&gt;NSDownloadsFolderUsageDescription&lt;/key&gt; &lt;string&gt;App wants to access your downloads folder&lt;/string&gt; Can someone please help me figure out how to grant the files and folder permission and explain what it does? I would really appreciate the help. Image of the permission in question:
4
1
9.7k
Oct ’23