I'm working on a file provider extension that maps to a cloud service. The remote service represents files using paths, directories and filenames (unsurprising). However, the NSFileProviderReplicatedExtension wants persistent identifiers for every item (file or folder).
What's the best practice here? I naively thought I could just use the URL as the identifier, but that breaks down as soon as items are moved - the URL changes, which would mean the identifier does too.
Is it necessary to maintain some kind of mapping in the extension? Does this have to be persisted somehow, or can it be in-memory only? If it's persistent, how would one typically do this?
Thanks for any insight!
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I have included sample code below. In my MenuExtraView, I've added a dropdown Menu to access a quit option. When I render this view as the application's main window, everything works fine. But when the view is used as an item in the NSStatusItem.menu, the dropdown doesn't work despite the view being rendered correctly.
Any ideas or things I'm simply doing wrong here?
I know xcode 14 will include MenuBarExtra directly in SwiftUI, however, I'm currently targeting macos 12.
import AppKit
import SwiftUI
@main
struct MenuExtraApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
MenuExtraView()
// EmptyView().frame(width: .zero)
}
}
}
struct MenuExtraView: View {
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .center) {\
VStack(alignment: .leading) {
Text(verbatim: "Hello World").font(.subheadline)
}
Spacer()
Menu {
Button("Quit") {
NSApplication.shared.terminate(nil)
}.keyboardShortcut("q")
} label: {
Image(systemName: "gearshape").imageScale(.large)
}
.menuStyle(BorderlessButtonMenuStyle())
.menuIndicator(.hidden)
.fixedSize()
}.padding()
Spacer()
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
var statusItem: NSStatusItem?
func applicationDidFinishLaunching(_ aNotification: Notification) {
let hostingView = NSHostingView(rootView: MenuExtraView())
hostingView.frame = NSRect(x: 0, y: 0, width: 350, height: 400)
let menuItem = NSMenuItem()
menuItem.view = hostingView
let menu = NSMenu()
menu.addItem(menuItem)
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem?.menu = menu
statusItem?.button?.image = NSImage(systemSymbolName: "gearshape.circle", accessibilityDescription: "Logo")
}
}
I had assumed that the easy way to ensure serialized access to methods in Swift was to use the new structured concurrency approaches, specifically the actor type.
However, it does not seem to work as expected when the method also uses structured concurrency, i.e. contains async methods.
For example:
actor MyActor {
func doSomething(_ val: Int) async {
print("Started doing something \(val)")
try! await Task.sleep(nanoseconds: 500_000_000)
print("Finished doing something \(val)")
}
}
let actor = MyActor()
for i in 1...5 {
Task { await actor.doSomething(i) }
}
I would have expected this to serialize the code in doSomething, producing pairs of Started N and Finished N. Instead, I get something like:
Started 1
Started 2
Started 3
Finished 2
Finished 1
Finished 3
Given this, it appears to be unblocking access at the first suspension point (e.g. at the Task.sleep()).
Is there an alternative approach I should be using the achieve the desired outcome?
I am attempting to communicate with a FileProviderExtension (NSFileProviderReplicatedExtension) using XPC.
I want to allow the controlling application to manage how the extension communicates with the remote service - e.g. tell the extension to sign in (and provide credentials to do so), sign out, etc.
I have implemented the NSFileProviderServicing protocol in the extension and provided an NSFileProviderServiceSource (following the FruitBasket sample code).
However, I can't see how to get a general connection to the service from the controlling application. The only method I can find is FileManager().fileProviderServicesForItem(at url: URL), which requires a URL for an item managed by the extension. Given the controlling application wants to perform actions like sign in, there is no item in scope and thus no URL.
I tried using NSFileProviderManager.documentStorageURL, but this is not available on macos.
Any idea how to get the connection without a file URL? Or how to get a general purpose URL that will suffice? Or is there another IPC mechanism I should use?
I can't seem to find any references to this in the docs, which is odd because it seems like it would be a fairly common consideration.
When defining an NSXPCInterface for an object to be proxied, can XPC support methods that throw exceptions - coding and passing those exceptions back to the peer?
Given NSError does not implement NSSecureCoding, I can't see how it would work. But perhaps there is some other approach I don't know about.
And assuming there is no support for propagating exceptions across XPC, how does one typically handle the need to return errors from XPC interfaces?
I'm just learning, so possibly missing something obvious. I have a basic login form with two textfields. As soon as I enter some text in either field, a semi-transparent box appears below the textfield, overlapping other content. It will disappear if escape is pressed, something like an autocomplete drop-down might.
Any idea what is creating this, and how to not do that? Screenshot and code below.
Also... if anyone knows how to make the button taller (i.e. padding around the text but inside the button), that would be nice. It's awfully narrow. I tried adding padding on the Text element, but that doesn't expand the button frame.
struct SignInView: View {
@State var email: String = ""
@State var password: String = ""
var body: some View {
VStack {
VStack(alignment: .leading) {
Text("Email").font(.callout).bold()
TextField("bobby@dontworry.com", text: $email)
.disableAutocorrection(true)
.textFieldStyle(RoundedBorderTextFieldStyle())
Text("Password").font(.callout).bold().padding(.top)
SecureField("******************", text: $password)
.disableAutocorrection(true)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
print("Sign In")
}) {
HStack {
Spacer()
Text("Sign In")
Spacer()
}
}
.disabled(email.isEmpty || password.isEmpty)
.padding(.top)
}.padding()
}.padding()
}
}
I have a very simple example project (one file below) which demonstrates an issue: when I start typing into either of the fields, a mysterious, transparent box appears below and extends outside the window bounds. This occurs even in a SwiftUI preview, as shown here:
How can I debug this, to determine what is creating that mysterious box? It seems to only occur when there is both TextField and SecureTextField, so I'm wondering if it's some kind of password assistant, or something like that. I'd like to find a way to track it down, and ultimately get rid of it, but I have no idea where to even begin looking.
import SwiftUI
@main
struct TestApp: App {
var body: some Scene { WindowGroup { ContentView() } }
}
struct ContentView: View {
@State var email: String = ""
@State var password: String = ""
var body: some View {
VStack {
TextField("Email Address", text: $email)
SecureField("******************", text: $password)
}.padding().frame(minWidth:150, minHeight: 300)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
It's not clear in the documentation whether a NSFileProviderReplicatedExtension needs to persist the identifiers it creates for items, so they remain stable over restarts.
Can anyone clarify?
In my application, the remote service only provides a file path, not an identifier that is stable over moves. I can generate an identifier (e.g. a UUID) and maintain a dictionary, but I'm unclear if this would need to be persistent and how/where the extension would store it.
Alternatively I could just use the path as an identifier, but I'm unsure how the extension would handle that given it wouldn't be stable when items are moved.
Given many remote services would only provide file paths and not a stable identifier, I'm hopeful that someone has considered the best practice for file provider extensions that work in these situations and can advise!