I have created an XPC Service .
the server side code briefly is
NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:#"test.xpcserver"];
listener.delegate = delegate;
[listener resume];
this is installed as a Launch Agent using info.plist as
<?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>Label</key>
<string>TestServer</string>
<key>MachServices</key>
<dict>
<key>test.xpcserver</key>
<true/>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Applications/test.app/Contents/XPCServices/xpcserver.xpc/Contents/MacOS/xpcserver</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
The server is running fine and i can see it running with launchctl list
The client side code is in another app and the code for connect is:
connection = [[NSXPCConnection alloc] initWithMachServiceName:#“test.xpcserver” options:0];
connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:#protocol(xpcserverprotocol)];
[connection resume];
service = [connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) { }];
[service ServerFunc:#“howdy” withReply:^(NSString *result) {
NSLog(#"%#",result);
}];
but not able to connect to server .
any pointers as to what going wrong ?
It's hard to tell from what you've posted. Things to check:
ensure your listener delegate is implementing right function signature for shouldAcceptNewConnection(). If it's not right, you will not get any error. It should be
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)conn { your code here }
Use System Logging (NSLog) or add logging of stdout and stderr for your service. See the launch.sh in this hello_world_xpc example, which generates the plist based on local paths. That example is using the C API, so not exactly what you are looking for.
See old Objective-C hello.tar.gz example here : afewguyscoding.com/2012/07/ipc-easy-introducing-xpc-nsxpcconnection/
(Posting this for future readers, I imagine OP solved their problem by now :) )
Some other things to check:
Make sure your launch agent/deamon is using the -[NSXPCListener initWithMachServiceName], and not +[NSXPCListener serviceListener].
The latter is only useful for regular XPC service (which would be stored in the App's bundle, not in /Library/.... If you mix it up, you'll get a crash report in Console.app that will say:
Configuration error: Couldn't retrieve XPCService dictionary from service bundle.
In your agent/daemon, don't forget to start the main run loop after [listener resume]; with [[NSRunLoop currentRunLoop] run];
In your agent/daemon, make sure there's a strong reference to keep your listener's delegate alive. The -[NSXPCListener delegate] property is declared weak, so it's possible that your listener deallocates immediately after you set it. I would put an NSLog statement in the deinit of your delegate's class, just to be sure.
Related
I am using NSAppleScript for sending email from my OSX application.
It was working fine before but now when I use new XCode 9.0 with Sierra 10.12.6 I have one problem. Application will create email and open Mail window containing email and message content but it wont perform SEND. I can click on send button and everything is fine, mail will send but I would like to avoid manual click-on-send-button action.
I am using entitlements:
<plist version="1.0">
<dict>
<key>com.apple.security.scripting-targets</key>
<dict>
<key>com.apple.mail</key>
<array>
<string>com.apple.mail.compose</string>
</array>
</dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
And this is the code:
NSString *emailString = [NSString stringWithFormat:#"\
tell application \"Mail\"\n\
set newMessage to make new outgoing message with properties {subject:\"%#\", content:\"%#\" & return} \n\
tell newMessage\n\
set visible to false\n\
set sender to \"%#\"\n\
make new to recipient at end of to recipients with properties {name:\"%#\", address:\"%#\"}\n\
tell content\n\
",subjectt, bodyText, #"Company", toAddress, toAddress];
emailString = [emailString stringByAppendingFormat:#"\
end tell\n\
set visible to false\n\
send newMessage\n\
end tell\n\
end tell"];
NSLog(#"%#",emailString);
NSAppleScript *emailScript = [[NSAppleScript alloc] initWithSource:emailString];
[emailScript executeAndReturnError:nil];
So everything is in place but send action is failing.
Again it was working well with older OSX version(s)..
Am I missing entitlement or something?
Thank you!
It looks like you tell the newMessage to send the newMessage (double reference)
Delete newMessage in the send line
Long story short. This is the wrong way. With modern OSX use NSSharingService.
NSArray * shareItems = [NSArray arrayWithObjects:bodyText, nil];
NSArray * recepiants = [NSArray arrayWithObjects:toAddress, nil];
NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
[service setRecipients:recepiants];
[service setSubject:subjectt];
service.delegate = self;
[service performWithItems:shareItems];
When it comes down to sending email with some sort of results from application NSAppleScript is not your friend anymore. OSX is becoming more like iOS nowadays.
I’m playing with Wi-Fi Positioning system (WPS) on the Mac.
Attempting to specify the most exact positioning possible. I’ve noticed that apps such as Maps register in the Location notification of the OS
Yet my code does not seem to generate this effect,
- (void)startStandardUpdates
{
// Create the location manager if this object does not
// already have one.
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
// Set a movement threshold for new events.
locationManager.distanceFilter = 1; // meters
[locationManager startUpdatingLocation];
}
Am I missing something?
Is your app sandboxed?
If it is, you need to enable the "App Data" -> "Location" entitlement in the "Capabilities" tab of your target.
Otherwise the CLLocationManager silently fails to start.
Another interesting point is, that your app only shows up in this list when the CLLocationManager is actually running. Calling stopUpdatingLocation deregisters your app from the location agent.
A minimal entitlement file for a OS X app using Core Location looks like:
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.personal-information.location</key>
<true/>
</dict>
</plist>
The entitlement documentation describes a method in which multiple apps produced by a single development team can share access to a special group container
I've added the entitlement key to a main application and a helper application that is included in the main application bundle. The group directory is never created, and the method in which to get the path – [NSURL containerURLForSecurityApplicationGroupIdentifier:] – doesn't actually exist in any header.
What am I missing to create a shared group container?
UPDATE : added entitlements and group container creation
Here is my entitlements file for the main app and helper application. (replacing TEAM_ID with my actual ID)
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>TEAM_ID.com.pinepointsoftware</string>
</array>
</dict>
</plist>
I've attempted to create the Group Container directory myself based on some information from the dev forums:
NSFileManager *fm = [NSFileManager defaultManager];
NSString *path = [#"~/Library/Group Containers/TEAM_ID.com.pinepointsoftware" stringByExpandingTildeInPath];
if ([fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:NULL]) {
[fm createFileAtPath:[path stringByAppendingPathComponent:#"test.txt"] contents:nil attributes:nil];
}
Executing that inside the main and and helper app create two different directories inside their each sandbox.
I found a sample project from Apple AppSandboxLoginItemXPCDemo, that uses the application groups for XPC. I can get it working by changing the entitlements and bundle identifiers to match my team, but cannot get the Group Containers to be shared.
The "~" will be automatically converted to your container directory in a sandboxed environment.
So one way to do it is to append the "~" with a number of step back ("../") until you reach ~/Library and then you create your group container starting from it.
I'm working on an Cocoa app which is launched/activated using URLs with a custom scheme which is registered in the Info.plist file like so:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>Open myscheme:// URLs</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myscheme</string>
</array>
</dict>
</array>
My question is, once the app is launched or activated, how do I tell what the URL was that launched the app? On iOS, this is easy with the -application:openURL:sourceApplication:annotation: method on the UIApplicationDelegate since it is passed an NSURL instance.
I want to be able to pass in data into my app with URLs like myscheme://do/something/awesome
In your app delegate's -applicationWillFinishLaunching:, do:
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(handleAppleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
And handleAppleEvent:withReplyEvent: should look something like:
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
// do something with the URL string
}
I've read "Service Implementation Guide" from developer.apple.com and a question here from stackoverflow (on how to create a cocoa service), and followed this tutorial: http://www.cocoadev.com/index.pl?MakingServices and read http://homepage.mac.com/simx/technonova/tips/creating_a_service_for_mac_os_x.html
However, I don't seem to be able to get it working. Here's my info.plist addition:
<key>NSServices</key>
<array>
<dict>
<key>NSKeyEquivalent</key>
<dict>
<key>default</key>
<string>L</string>
</dict>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>TESTSERVICE</string>
</dict>
<key>NSMessage</key>
<string>doCapitalizeService</string>
<key>NSPortName</key>
<string>app name</string>
<key>NSSendTypes</key>
<array>
<string>NSStringPBoardType</string>
</array>
</dict>
</array>
Where "app name" is the project and app name.
I've added an interface for handling service calls as such:
#interface my_appService : NSObject
- (void)doCapitalizeService:(NSPasteboard *)pboard
userData:(NSString *)data
error:(NSString **)error;
#end
I've registered my service as such:
my_appService *serviceObject = [[my_appService alloc] init];
[NSApp setServicesProvider:serviceObject];
In applicationDidFinishLaunching (BUT!) for the main app interface, as per the cocoadev tutorial.
I can not find my service anywhere (re)log in did not help. Am I missing something obvious here? Any tips are greatly appreciated; oh, and happy easter, everyone :)