问题
I'm trying to add a service to the Finder's context menu using this class:
public class Service {
public func handleServices(pboard:NSPasteboard, userData:String, error:UnsafeMutableBufferPointer<String>) { // not sure about the correct parameters
if (pboard.types?.contains(NSFilenamesPboardType) != nil) {
let fileArray = pboard.propertyListForType(NSFilenamesPboardType)
print(fileArray)
}
}
init () {
NSApp.servicesProvider = self
NSUpdateDynamicServices()
}
}
The service is announced in info.plist as follows:
<key>NSServices</key>
<array>
<dict>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Service Handling Demo</string>
</dict>
<key>NSMessage</key>
<string>handleServices</string>
<key>NSPortName</key>
<string>services</string>
<key>NSSendTypes</key>
<array>
<string>NSFilenamesPboardType</string>
</array>
</dict>
</array>
Finally I have turned on the service in System Preferences/Keyboard/Shortcuts. So I see the service and can call it. But all I get when calling it is
Cannot find service provider for selector handleServices:userData:error: or handleServices:: for service handleServices
回答1:
There are two problems in your code:
Objective-C messages are sent to the service provider, therefore the Swift method must be "Objective-C compatible". This can be achieved by subclassing
NSObject
, or by marking the method with the@objc
attribute.The service handler method has the signature
- (void)handleServices:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error
which is mapped to Swift as
func handleServices(pboard: NSPasteboard!, userData: String!, error: AutoreleasingUnsafeMutablePointer<NSString?>)
So this would be a correct version (which worked in my test):
public class Service {
@objc public func handleServices(pboard: NSPasteboard!,
userData: String!, error: AutoreleasingUnsafeMutablePointer<NSString?>) {
// ...
}
init() {
NSApp.servicesProvider = self
NSUpdateDynamicServices()
}
}
Some more remarks:
if (pboard.types?.contains(NSFilenamesPboardType) != nil) { ... }
is "optional chaining" and checks if the contains()
method could be called on pboard.types
,
in other words it checks only if pboard.types != nil
. What you probably want
is to check if pboard.types != nil
and the contains()
method returns true
.
This can be achieved with the "nil-coalescing operator" ??
:
if (pboard.types?.contains(NSFilenamesPboardType) ?? false) { ... }
Next,
pboard.propertyListForType(NSFilenamesPboardType)
is documented to return an optional array of NSString
s, so you could unwrap
and convert that to a String
array with
if let fileArray = pboard.propertyListForType(NSFilenamesPboardType) as? [String] { ... }
Finally, assigning an error string (to the pointer provided by the caller) would be done with
if (error != nil) {
error.memory = "My error description"
}
回答2:
Received same error (Swift 3.1). Tried all ways in this topic but nothing helped. Found a solution here
Changed code from this
@objc public func handleServices(pboard: NSPasteboard!,
userData: String!, error: AutoreleasingUnsafeMutablePointer<NSString?>)
to this
@objc public func handleServices(_ pboard: NSPasteboard,
userData: String, error:AutoreleasingUnsafeMutablePointer<NSString>)
and it works.
回答3:
Try creating aliases for variables in your method like so.
public func handleServices(pboard:NSPasteboard, userData userData:String, error error:UnsafeMutableBufferPointer<String>)
来源:https://stackoverflow.com/questions/31404026/establish-a-service-in-finder-context