How can I get permanent access to files in macOS? - swift

I am working in app that tells the count of folders and files exist on Desktop in macOS, for making things easier to my issue get solved I made a simple and small project that re create the problem. At first time of lunching the app I am asking the user to select the Desktop, after I am using the selected path for later use case when the app get killed, I do not want ask user each time to select the Desktop each time, it would be very bothering! currently sometimes macOS allows my app to get access to Desktop without showing the permission dialog but sometimes it goes ask each single time, I am trying to make my app authorized for such permission via user and use that permission for later use case in app. For example as you can see Terminal has such permission in the picture:
So here is my code with disabling sandbox:
struct ContentView: View {
#State private var contents: [String]? = nil
var body: some View {
VStack(spacing: 10.0) {
Text("Count of contents of Desktop is:")
Text(contents != nil ? String(contents!.count) : "Unknown").bold()
}
.frame(width: 200.0, height: 200.0)
.padding()
.onAppear {
contents = directoryContentsReader(folderLocation: "/Users/myNameHere/Desktop")
}
}
}
func directoryContentsReader(folderLocation: String) -> [String]? {
do {
return try FileManager.default.contentsOfDirectory(atPath: folderLocation)
}
catch(let error) {
print(error.localizedDescription)
return nil
}
}
And as said before most of the time and randomly I get this below dialog from macOS, I want make this kind permission done one time and for ever when the user use my app, and not bothering user with this dialog any more. How can I get this permission for my app?

Related

SetFocusFilterIntent macOS system extension doesn't run perform() function

I am writing a Focus system extension (sandboxed, Cocoa, entitlements set) for macOS 13/Ventura using Xcode 14.2
I have the extension loading it's UI into the macOS system settings > Focus pane.
so here are the issues:
Even though it is loaded, it doesn't seem to ever run the perform() function when the UI is changed by the user or the user invokes Focus > Do Not Disturb.
What can be done in the perform() function? Like, what is supposed to go there? Nothing seems to work.
import AppIntents
struct MacOSFocus: SetFocusFilterIntent {
static var title: LocalizedStringResource {
return "Focus Settings"
}
// The description as it appears in the Settings app
static var description: LocalizedStringResource? = "Focus Settings" // name under Minus icon in options list
// How a configured filter appears on the Focus details screen
var displayRepresentation: DisplayRepresentation {
return DisplayRepresentation(stringLiteral: "Focus Settings") // name under filter once added to Foucs
}
#Parameter(title: "Show Task Bar", default: false)
var showDefaultTaskBar: Bool
#Parameter(title: "Start Timer")
var startTimer: Bool
func perform() async throws -> some IntentResult {
// This doesnt seem to run
// What can I put here?
// I need to write string data to a text file somewhere or communicate with the host app in some way.
return .result()
}
}
Just trying to get unstuck. Thanks for any help.
Tried adding an NSLog() call in the perform() function for debugging. Even tried using NSSound.beep() just to check that it is getting called. Didn't work.

How to have scene-specific state in SwiftUI?

I'm creating a macOS application that allows you to have multiple windows open. Each one of those windows (or scenes) need their own state, as the document/text/other states of that window are bound to it.
I also want to get that state in the .commands modifier so I can use the state to add keybindings.
The following I tried does not quite work, as all the state is global across all open windows (i.e. keybindings and text affect all instances of the application, while they must be different):
import SwiftUI
#main
struct QCCApp: App {
#StateObject var compiler = UIStateController()
var body: some Scene {
DocumentGroup(newDocument: QCCDocument()) { file in
ContentView()
// A function I've written that combines the file into state
.environmentObject(compiler.attachDocument(file))
}
.commands {
// Able to get the state to the commands area
CommandDelegate(compiler: compiler)
}
Settings {
SettingsView()
}
}
}
The following also doesn't quite work, but the behaviour of the document state is correct. The keybindings can't really be implemented this way:
import SwiftUI
#main
struct QCCApp: App {
var body: some Scene {
DocumentGroup(newDocument: QCCDocument()) { file in
// I implemented an initialiser instead of the `.attachDocument` method
let compiler = UIStateController(targetting: file)
ContentView()
.environmentObject(compiler)
}
.commands {
// I can't reach the `compiler` variable
CommandDelegate(compiler: /* compiler */)
}
Settings {
SettingsView()
}
}
}
Is there a way to have the keybindings specific to one app instance/scene/window? If so, how does one implement such a thing? In the first example, pressing a keybind would affect both windows (and the same goes for other state).

Perform action after coming from a closed state - SwiftUI

I currently have this:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
...
}
and that works when I don't quit the app, it simply is in the background, but when I quit it, the code inside there that is suppoused to check for a time, doesn't work.
I also tried putting it in
.onAppear() {
but that doesn't workout either :/
How can I once the app has been closed, then opened, as soon as it opens run an action?
you could try adding the following code to perform an action when your app starts:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
// do your action here
....
}

WatchOS6 handleActiveWorkoutRecovery not being called after workout crashes

I'm creating a workout app and I'm trying to implement the workoutRecovery logic but the handleActiveWorkoutRecovery function is never being called when an app crashes or is forced closed.
Are there any special circumstances that must be met so that the handleActiveWorkoutRecovery?
func handleActiveWorkoutRecovery() {
NSLog("log: handleActiveWorkoutRecovery");
self.healthInfo.recoverWorkout()
self.healthInfo.workoutRecovery = true
}
Moreover, in Content.swift I have the following code so that in case the login is not working I could see visual feedback that the handleActiveWorkoutRecovery function is being called:
#EnvironmentObject var healthInfo: HealthInfo
var body: some View {
VStack{
if(healthInfo.workoutRecovery){
Text("RECOVERY")
}else{
TestView()
}
}
}
When the app relaunches it never goes to The Text("RECOVERY") view.
Deployment target is 6.1 and I'm using an Apple Watch 4 running 6.1 beta 3
Ive also tested the native apple workout app and that seems to recover as it should

addUIInterruptionMonitor is not getting called on macOS

I want to test my macOS application. It uses your Macbook's camera, and want to handle this in my UITest. However I cannot get it working. Here is my NOT working code. This code triggers to notification, and I'm presented an alert to allow access to my camera, but the closure is not getting called. Thanks fo any help.
There are many solutions for iOS, but I need it on macOS.
let alertHandler = addUIInterruptionMonitor(withDescription: "Camera Permission Alert") { (alert) -> Bool in
if alert.buttons.matching(identifier: "OK").count > 0 {
alert.buttons["OK"].click()
self.app.click()
return true
} else {
return false
}
}
XCTAssertTrue(startButton.waitForExistence(timeout: 1.0))
startButton.click()
XCTAssertTrue(recordButton.waitForExistence(timeout: 20.0))
recordButton.click()
wait(for: 8)
recordButton.click()
removeUIInterruptionMonitor(alertHandler)
}
I managed to make interruption monitor work on macOS by adding an extra interaction after the interaction that triggers the system dialog (be it camera access or else). So in your example I would add an action after startButton.click() (if that is what triggers the camera access dialog).
Example:
func testCamera() {
let alertHandler = addUIInterruptionMonitor(withDescription: "Camera Permission Alert") { (alert) -> Bool in
if alert.buttons.matching(identifier: "OK").count > 0 {
alert.buttons["OK"].click()
self.app.click()
return true
} else {
return false
}
}
useCameraButton.click()
// try to interact with the app by clicking on the app's window
app.windows.first().click()
// at this point the handler should intercept the system interruption
// and blocks further execution until handler does return
// try to use the camera again
useCameraButton.click()
removeUIInterruptionMonitor(alertHandler)
}
Hint about this behaviour in Apple's documentation:
When an alert or other modal UI is an expected part of the
test workflow, don't write a UI interruption monitor. The test won’t
use the monitor because the modal UI isn’t blocking the test. A UI
test only tries its UI interruption monitors if the elements it needs
to interact with to complete the test are blocked by an interruption
from an unrelated UI.
https://developer.apple.com/documentation/xctest/xctestcase/handling_ui_interruptions