NSService not calling its NSMessage - objective-c

I'm trying to add a Finder service and all looks fine until I want the service to do its job.
This is the method in my AppDelegate.m:
-(void)uploadFromPasteboard:(NSPasteboard *)pboard userData:(NSString *)udata error:(NSString **)err
{
NSString *filename = [pboard stringForType:NSURLPboardType];
dbg(#"file: %#", filename);
}
The plist configuration:
<key>NSServices</key>
<array>
<dict>
<key>NSRequiredContext</key>
<dict/>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Upload File</string>
</dict>
<key>NSMessage</key>
<string>uploadFromPasteboard</string>
<key>NSPortName</key>
<string>Finder</string>
<key>NSSendTypes</key>
<array>
<string>NSURLPboardType</string>
</array>
<key>NSReturnTypes</key>
<array/>
</dict>
</array>
All seems fine, the service is displayed in the service menu, but when I click it, nothing happens, no logs or anything else, like its not called at all.
Could someone point me whats wrong cos I'm starting to pull my hair hardly :(

Are you setting your service provider instance? Like this (from: Providing a Service):
EncryptoClass* encryptor = [[EncryptoClass alloc] init];
[NSApp setServicesProvider:encryptor];
Merely having this method in your app delegate class is not enough. In the standard application set-up, having this in your app delegate might be sufficient:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[NSApp setServicesProvider: self];
}
Also you're specifying "Finder" for NSPortName. This is probably not correct. From Services Properties (emphasis mine):
NSPortName is the name of the port on which the application should
listen for service requests. Its value depends on how the service
provider application is registered. In most cases, this is the
application name. This property is ignored for Automator workflows
being used as services.
My reading of the documentation is that the application whose name is in NSPortName is the application that will be used to handle the service request. If the name of your app isn't "Finder" (and it shouldn't be, for obvious reasons) then your app will never be called by the service.

Related

SSL pinning in React Native

How to incorporate SSL pinning in React Native using axios for api calls.
I came across TrustKit but my continues efforts to use the same ends up crashing my app. I tried both the ways of adding TrustKit.
Via Info.list
<key>TSKConfiguration</key>
<dict>
<key>TSKSwizzleNetworkDelegates</key>
<true/>
<key>TSKPinnedDomains</key>
<dict>
<key>yourDomain.com</key>
<dict>
<key>TSKPublicKeyHashes</key>
<array>
<string>public key 1</string>
<string>public key 2</string>
</array>
<key>TSKPublicKeyAlgorithms</key>
<array>
<string>TSKAlgorithmRsa2048</string>
</array>
<key>TSKIncludeSubdomains</key>
<true/>
<key>TSKEnforcePinning</key>
<true/>
</dict>
</dict>
</dict>
Via AppDelegate.mm within didFinishLaunchingWithOptions
// Override TrustKit's logger method, useful for local debugging
void (^loggerBlock)(NSString *) = ^void(NSString *message)
{
NSLog(#"TrustKit log: %#", message);
};
[TrustKit setLoggerBlock:loggerBlock];
NSDictionary *trustKitConfig =
#{
// Swizzling because we can't access the NSURLSession instance used in React Native's fetch method
kTSKSwizzleNetworkDelegates: #YES,
kTSKPinnedDomains: #{
#"busdue.com" : #{
kTSKIncludeSubdomains: #YES, // Pin all subdomains
kTSKEnforcePinning: #YES, // Block connections if pinning validation failed
kTSKDisableDefaultReportUri: #YES,
kTSKPublicKeyHashes : #[
#"dz0GbS1i4LnBsJwhRw3iuZmVcgqpn+AlxSBRxUbOz0k=",
#"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Fake backup key but we need to provide 2 pins
],
},
}};
[TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
[TrustKit sharedInstance].pinningValidatorCallback = ^(TSKPinningValidatorResult *result, NSString *notedHostname, TKSDomainPinningPolicy *policy) {
if (result.finalTrustDecision == TSKTrustEvaluationFailedNoMatchingPin) {
NSLog(#"TrustKit certificate matching failed");
// Add more logging here. i.e. Sentry, BugSnag etc
}
};
I always end up getting this crash (https://i.stack.imgur.com/673OW.png)
Any suggestions are appreciated.

iOS 8 requestWhenInUseAuthorization no Popup

I tried to make my AppProject iOS 8 ready. I had read a lot about
[_locationManager requestWhenInUseAuthorization];
and the entry in plist
NSLocationWhenInUseUsageDescription
So I changed all the necessary code lines.
It works fine, but now I have copied my project again from my iOS 7 base to include new features. But when I make the changes for the iOS8 Location Privacy the Popup doesn't appear anymore.
My code worked until I copied.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>tolle sache </string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>fapporite.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
and here is my call
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
_UserLocation = [[CLLocation alloc]init];
_locationManager = [[CLLocationManager alloc]init]; // initializing locationManager
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest; // setting the accuracy
[_locationManager requestWhenInUseAuthorization]; // iOS 8 MUST
[_locationManager startUpdatingLocation]; //requesting location updates
NSLog(#"passed initwithcode");
}
return self;
}
How can I fix this?
From the documentation
NSLocationWhenInUseUsageDescription (String - iOS) describes the
reason why the app accesses the user’s location normally while running
in the foreground. Include this key when your app uses location
services to track the user’s current location directly. This key does
not support using location services to monitor regions or monitor the
user’s location using the significant location change service. The
system includes the value of this key in the alert panel displayed to
the user when requesting permission to use location services.
This key is required when you use the requestWhenInUseAuthorization
method of the CLLocationManager class to request authorization for
location services. If the key is not present when you call the
requestWhenInUseAuthorization method without including this key, the
system ignores your request.
This key is supported in iOS 8.0 and later. If your Info.plist file
includes both this key and the NSLocationUsageDescription key, the
system uses this key and ignores the NSLocationUsageDescription key.
Read about it here.
I find that the easiest way to add this key to your info.plist is to right click you info.plist and choose
Open As->Source Code
and then add the following in the end before </dict></plist>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
If you want you can add a text in between <string></string> that describes to the user why you want to use his/hers location. This text will show up under the default text in the alert.
Try writing a NSLocationWhenInUseUsageDescription in Info.plist
iOS 8.3, Xcode 6.3.1, ARC enabled
The question has been resolved, but I have (2) notes to add from my recent involvement with CLLocationManager.
1) You must have the following keys entered into your *.plist file:
Most commonly used keys have generic more descriptive names, such as "Privacy - Location Usage Description", which really is the "NSLocationUsageDescription" key.
To see the "raw" key names go to "*-Info.plist" and right click in the Navigator area where the keys are listed, see below:
And you get the following results:
The three keys that are related to this article are:
2) Make certain that you allocate and initialize your implementation of CLLocationManager before you try to request authorization or update location.
*.h file:
#interface SomeController : UIViewController <CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
*.m file:
- (IBAction)locationButton:(UIButton *)sender
{
if (self.locationManager == nil)
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
}
else
{
nil;
}
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)])
{
[self.locationManager requestWhenInUseAuthorization];
}
else
{
nil;
}
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];
}
Hope this saves someone time! Thanks.
Here's a little gotcha. Make sure you're adding the NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription keys to the main bundle plist and not one of your test target plists!
Had the same problem caused because I was instantiating CLLocationManager in a local var inside a method, solved it making the CLLocationManager a class property.
After a while I found the solution here, but I'm leaving it here since this is the first result in google, hope I save you some time:
requestWhenInUseAuthorization() not Work in iOS 8 With NSLocationWhenInUseUsageDescription Key in Info.plist
Pertaining to the Apple Watch:
You need to put the requestWhenInUseAuthorization key in the iPhone's Info.plist, not the WatchKit App's.
This has bitten me twice now.

Cocoa protocol handler using NSAppleEventManager and kInternetEventClass/kAEGetURL

This is the Cocoa version of this question:
AEInstallEventHandler handler not being called on startup
Here's my Info.plist protocol registration:
...
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>My Protocol</string>
<key>CFBundleURLIconFile</key>
<string>myicon</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
Here's where I set the method to listen for the kInternetEventClass/kAEGetURL event when a browser link is clicked with the link "myapp://unused/?a=123&b=456":
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(getURL:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
...
}
Here's the handler method:
- (void)getURL:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply
{
[[[event paramDescriptorForKeyword:keyDirectObject] stringValue] writeToFile:#"/testbed/complete_url.txt" atomically:YES];
}
Here's the test web link:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
</head>
<body>
Open My App
</body>
</html>
This all works great if the application is already running.
The handler method is called and the complete url is captured.
However, if the app is not yet running the same link will launch the app, but the handler will not be invoked — which makes sense since the handler had not yet been bound to the event.
Those arguments in the URL are important for our application to coordinate with the webapp. Although the vast majority of the time our application will already be running when this click occurs, it is reasonable to expect that in some cases it will not.
I've tried inspecting the environment and process invocation arguments and I do not see the URL in either of them.
Anyone know how we can capture this URL reliably, even when our application isn't already running when the browser click happens?
Apple’s SimpleScriptingPlugin sample registers the handler in applicationWillFinishLaunching:, which is probably cleaner than using init. (And like mikker said, the handler gets called before applicationDidFinishLaunching:.)
I just faced this exact problem. I don't know if it's the proper solution but you can register the event handler in the app delegate's init-method instead.
// in AppDelegate.m
- (id)init
{
self = [super init];
if (self) {
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
return self;
}
One thing to note though is that handleURLEvent:withReplyEvent get's called before applicationDidFinishLaunching: if the app is started by an URL Scheme.

Custom UTI Not opening in App

I am trying to create a new document UTI for my App so that people can share points of interest with others. From what I can understand on SO, Tutorials, and from Apple's documentation, you need to do the following:
Create a Document Type in the .plist
Create an Exported UTI that corresponds with it
Use the method: -(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
From what I understand, as long as you did those right, you should be able to open the file through Mail without any problems. Unfortunately it isn't working for my own custom UTIs. I DO see my App in the list of "Open with..." in Mail, but when I select it, my App doesn't open at all. It just dosen't do anything at all not only when the App isn't open, but when the App is open. Mail stays up and nothing happens at all. I also checked the console using "Organizer" and there is absolutely nothing that happens.
Originally I thought my plist was wrong, so I tested opening a public UTI (I added the com.adobe.pdf document type) and my app launched just fine (though it promptly crashed because I don't actually support PDFs ;)). But the point was that it launched without any problem.
The only thing I can think of that might be a problem is HOW I am creating the file. I am creating the file in an email by the use of this method (also in the App to export):
MFMailComposeViewController *picker = [[[MFMailComposeViewController alloc] init] autorelease];
[picker setSubject:[NSString stringWithFormat:#"My place: %#",POIName]];
[picker addAttachmentData:customPOIData mimeType:#"application/customPOI" fileName:[NSString stringWithFormat:#"%#.icp",POIName]];
[picker setMessageBody:#"Check out this great place I found!" isHTML:NO];
[picker setMailComposeDelegate:self];
[self presentModalViewController:picker animated:YES];
Is there anything wrong with that?
Also, here is my plist code:
CFBundleDocumentTypes:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeIconFiles</key>
<array/>
<key>CFBundleTypeName</key>
<string>Custom POI</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.imadev.icp</string>
</array>
</dict>
</array>
UTExportedTypeDeclarations:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Custom POI</string>
<key>UTTypeIdentifier</key>
<string>com.imadev.icp</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>icp</string>
</array>
<key>public.mime-type</key>
<string>application/customPOI</string>
</dict>
</dict>
</array>
Thanks a lot for any help!!
-Mark
I finally figured out what was wrong by pulling apart all the code.
When I change "public.filename-extension" to a string and not an array of strings it works. Don't ask me why... I think it's strange we can't use an array of file extensions. But apparently that was it.
Any ideas as to why that happened?
In my case, I could keep filename-extension as an array without problems. Instead, it was the 'public.data' that was the issue. I switched to 'public.image' and then had jpg, jpeg, and png as file extensions, and it suddenly worked.

How does one properly lay out a standalone system service in Xcode?

I've recently become interested in writing some system services for OS X but since I have no application to advertise the services under I must resort to writing standalone system services. Apple's documentation on System Services is spartan as it is, but its documentation on standalone services is non-existant.
What I have thus far is an Xcode project built from the bundle package, with the two sources HashifyService.h and HashifyService.m. Here is the test code I have:
- (void) doServiceWork:(NSPasteboard *)pboard
userData:(NSString *)userData
error:(NSString **)error {
NSLog(#"Actually in the service now");
NSString *pboardString;
NSArray *types;
NSLog(#"do test magic service! (pboard: %#, types: %#)", pboard, [pboard types]);
NSString* outputString = #"It Worked";
types = [NSArray arrayWithObject:NSStringPboardType];
[pboard declareTypes:types owner:nil];
[pboard setString:outputString forType:NSStringPboardType];
[outputString release];
return;
}
and this is the NSServices entry in my Info.plist:
<dict>
<key>NSMenuItem</key>
<dict>
<key>Menu item title</key>
<string>HashifyTest</string>
</dict>
<key>NSMessage</key>
<string>doServiceWork</string>
<key>NSPortName</key>
<string>HashifyService</string>
<key>NSReturnTypes</key>
<array>
<string>NSStringPboardType</string>
</array>
<key>NSSendTypes</key>
<array>
<string>NSStringPboardType</string>
</array>
</dict>
I then build the service bundle and place it in ~/Library/Services/ where it is appropriately detected and I am given the option to use the service. Upon activating the service, however, an error occurs and is logged to Console:
..../Hashify.service/Contents/MacOS/Hashify: cannot execute binary file
What am I doing wrong?
You need a main() function. That should register the service using NSRegisterServicesProvider() and enter the run loop. That is in the documentation.