Ok so I need to do this:
Wait for command, "Goodnight". Then run an action.
Can someone explain how do accomplish this?
Try this website:
http://www.cocoadev.com/index.pl?NSSpeechRecognizer
And modify as such:
NSSpeechRecognizer *listen;
NSArray *cmds = [NSArray arrayWithObjects:#"goodnight",nil];
listen = [[NSSpeechRecognizer alloc] init];
[listen setCommands:cmds];
[listen setDelegate:self];
[listen setListensInForegroundOnly:NO];
[listen startListening];
[listen setBlocksOtherRecognizers:YES];
- (void)speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(id)aCmd {
if ([(NSString *)aCmd isEqualToString:#"goodnight"]) {
[self performSelector:#selector(goodnightMethod:)];
}
}
Your method for handling good night would be (with accordance to what I have written):
-(void)goodnightMethod:(id)sender {
//Do stuff here...
}
Related
I want to transition all my old usages of -beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo: to the recommended -beginSheetModalForWindow:completionHandler:. How do I define contentInfo: and get it in the completionhandler?
Here is an example of how the old code looks like:
[alert beginSheetModalForWindow:window
modalDelegate:self
didEndSelector:#selector(alertDidEnd:returnCode:contextInfo:)
contextInfo:(void *)CFBridgingRetain(fc)];
The endSelector method looks like this:
- (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
if (returnCode == NSAlertDefaultReturn)
{
FileController *fc = (__bridge FileController *)(contextInfo);
[...]
}
}
}
I guess the new method should look somewhat like this:
[alert beginSheetModalForWindow:window completionHandler:^(NSModalResponse alertReturnCode)
{
if (alertReturnCode == NSAlertFirstButtonReturn)
{
// evaluate contextInfo here ...
}
}];
But I have no clue how to get the contextInfo into the completionhandler.
Any help is appreciated.
There is no context info because the completion handler block can simply look right at the surrounding environment.
NSString* s = #"heyho";
[alert beginSheetModalForWindow:window completionHandler:^(NSModalResponse alertReturnCode) {
if (alertReturnCode == NSAlertFirstButtonReturn)
{
// s is visible here
}
}];
In other words, we don't need to pass a context because we are in a context. If you have a FileController to pass down into the block, just let it pass down into the block.
In my app, I have a download manager. After any of tasks is finished I need to get all data for tableView again and reload it. But I can't get data inside RACObserve signal. Here's my code.
NSArray *activeTasks = [[DownloadManager instance] tasksToProcess];
for (DownloadTask *task in activeTasks) {
[[[self
checkTask:task]
map:^(id value
return [self fetchDownloadedData];
}]
subscribeNext:^(NSArray *models) {
// models returns RACDynamicSignal not NSArray
NSLog(#"%#", models); // <RACDynamicSignal: 0x11611cb50> name:
NSLog(#"checktask next");
} completed:^{
// This is never being executed
NSLog(#"checktask completed");
}];
}
- (RACSignal *)checkTask: (DownloadTask *)task {
return [RACObserve(task, isFinished) map:^id(id _) {
return nil;
}];
}
- (RACSignal *)fetchDownloadedData {
return [[MyCoreDataModel fetchAll] flattenMap:^id(NSArray *models) {
// This is never being executed
return [models filter:^BOOL(MyCoreDataModel *model) {
return model.isDownloaded;
}];
}];
}
- (RACSignal *)fetchAll
{
return [[[MyCoreDataModel findAll] sortBy:#"title"] fetch];
}
Would be great if someone helps me getting where is my mistake is. Thanks in advance.
There are few mistakes:
In map function you use method which returns RACSignal - it's not correct. You should use flattenMap instead.
Complete block will never be called, because RACObserve - hot signal, it only sends next event.
I wrote small example, I hope it helps you.
NSMutableArray<RACSignal *> *signals = [NSMutableArray array];
for (DownloadTask *task in activeTasks) {
RACSignal *signal = [[RACObserve(task, isFinished) ignore:#NO] take:1];
[signals addObject:signal];
}
#weakify(self);
[[[RACSignal merge:signals] flattenMap:^RACStream *(id _) {
#strongify(self);
return [self fetchDownloadedData];
}] subscribeNext:^(NSArray *models) {
}];
Here I created array of signals. Each signal - observing the isFinished property. Also I added ignore:#NO] take:1]; - I think it's more right, because you only need YES value and after that no observe anymore (take:1). Then I merge these signals and each time when someone of them sends finished state, we fetch data.
Please, let me know if something is not understand, I try to explain more clearly.
I am facing app extension close issues , please tell me if anyone know what wrong am doing.I am using action extension after preform some action inside extension i need to return response back.
Sample Code
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
}
// With Error Case
- (void) completeActionWithError: (NSError *) error {
[self.extensionContext cancelRequestWithError: error];
}
With Success Case working fine but some time is not closing,
With Error Case not working above code.
Please let me know what went wrong.Thanks
When you create an action extension, this is the default method which will close the Action Extension View Controller:
- (IBAction)done {
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed in items.
[self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];
}
Since this method is already provided, you should just try calling it from your success method.
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
// Call to "done" method
[self done];
}
I am really stuck at the moment trying to get the grasp of invites in the MultipeerConnectivityFramework.
Right now I have an ipad App acting as the Advertiser and an iphone App acting as Browser.
I have implemented a sharedService for the MultipeerFramework and did the following:
Advertiser
#implementation MultipeerConnectivityService {
MCNearbyServiceAdvertiser *_advertiser;
MCSession *_session;
MCNearbyServiceBrowser *_browser;
}
- (void)automaticAdvertiseWithName:(NSString *)name {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:name];
_session = [[MCSession alloc] initWithPeer:peerID];
_session.delegate = self;
_advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:peerID discoveryInfo:nil serviceType:kServiceType];
_advertiser.delegate = self;
[_advertiser startAdvertisingPeer];
}
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
invitationHandler([#YES boolValue], _session);
NSLog(#"Invitation accepted");
}
Browser
- (void)automaticBrowsingWithName:(NSString *)name {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:name];
_browser = [[MCNearbyServiceBrowser alloc] initWithPeer:peerID serviceType:kServiceType];
_browser.delegate = self;
[_browser startBrowsingForPeers];
}
- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error {
if ([_delegate respondsToSelector:#selector(browser:didNotStartBrowsingForPeers:)]) {
[_delegate browserDidNotStartBrowsingForPeers];
}
}
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info {
[browser invitePeer:peerID toSession:[self getMCSession] withContext:nil timeout:10];
if ([_delegate respondsToSelector:#selector(browser:foundPeer:)]) {
[_delegate browser:browser foundPeer:peerID];
}
}
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID {
if ([_delegate respondsToSelector:#selector(browserLostPeer:)]) {
[_delegate browserLostPeer:peerID];
}
}
- (MCSession *) getMCSession {
return _session;
}
But then I am getting as feedback in the console:
-[MCNearbyServiceBrowser invitePeer:toSession:withContext:timeout:] Bad argument session=nil
When I check for the found Advertisers, everything is OK. My advertiser ipad is being found. But how can I manage the invite?
So I don't get it right now... When I send an invitation by the browser, what session do I have to use? On the iPad I set up the session like you can see in the "automaticAdvertiseWithName" method. but on the iphone I don't do this when calling "automaticBrowsingWithName"... Is that the problem? And don't they have to be the same session to successfully connect them? And how can I successfully invite my advertiser ipad to the browser?
I am confused by the notion of creating a new session when one has already been created by the advertiser.
I am actually not sure, if the delegate didReceiveInvitation is adding the peer to the connectedPeers at all.
- (void)automaticAdvertiseWithName:(NSString *)name {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:name];
self.session = [[MCSession alloc] initWithPeer:peerID];
self.session.delegate = self;
_advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:peerID discoveryInfo:nil serviceType:kServiceType];
_advertiser.delegate = self;
[_advertiser startAdvertisingPeer];
}
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
BOOL accept = YES;
invitationHandler(accept, self.session);
NSLog(#"Invitation accepted: %#", self.session);
}
And when I call the property "connectedPeers" on my session, there are no connected peers at all, even though the delegate found one. Did I make a mistake there?
Your problem is that your session is null at the time you call invitePeer:toSession:withContext:timeout...Anyway you have two options to fix this.
You have two options :
Option 1
- move the peerID creation, session creation and session delegate assignment in a place where its executed at all time. For example in the init code for MultipeerConnectivityService class of if its a UIViewController in the viewDidLoad.
Option 2
- add the following snippet before you call "invitePeer:toSession:withContext:timeout:"
if (!_session) {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:#"Browser"]; // you can customize the name here
_session = [[MCSession alloc] initWithPeer:peerID];
_session.delegate = self;
}
Hope this helps ...good luck!
I'm trying to create a Core Data, document based app but with the limitation that only one document can be viewed at a time (it's an audio app and wouldn't make sense for a lot of docs to be making noise at once).
My plan was to subclass NSDocumentController in a way that doesn't require linking it up to any of the menu's actions. This has been going reasonably but I've run into a problem that's making me question my approach a little.
The below code works for the most part except if a user does the following:
- Tries to open a doc with an existing 'dirty' doc open
- Clicks cancel on the save/dont save/cancel alert (this works ok)
- Then tries to open a doc again. For some reason now the openDocumentWithContentsOfURL method never gets called again, even though the open dialog appears.
Can anyone help me work out why? Or perhaps point me to an example of how to do this right? It feels like something that must have been implemented by a few people but I've not been able to find a 10.7+ example.
- (BOOL)presentError:(NSError *)error
{
if([error.domain isEqualToString:DOCS_ERROR_DOMAIN] && error.code == MULTIPLE_DOCS_ERROR_CODE)
return NO;
else
return [super presentError:error];
}
- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError
{
if(self.currentDocument) {
[self closeAllDocumentsWithDelegate:self
didCloseAllSelector:#selector(openUntitledDocumentAndDisplayIfClosedAll: didCloseAll: contextInfo:)
contextInfo:nil];
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:#"Suppressed multiple documents" forKey:NSLocalizedDescriptionKey];
*outError = [NSError errorWithDomain:DOCS_ERROR_DOMAIN code:MULTIPLE_DOCS_ERROR_CODE userInfo:details];
return nil;
}
return [super openUntitledDocumentAndDisplay:displayDocument error:outError];
}
- (void)openUntitledDocumentAndDisplayIfClosedAll:(NSDocumentController *)docController
didCloseAll: (BOOL)didCloseAll
contextInfo:(void *)contextInfo
{
if(self.currentDocument == nil)
[super openUntitledDocumentAndDisplay:YES error:nil];
}
- (void)openDocumentWithContentsOfURL:(NSURL *)url
display:(BOOL)displayDocument
completionHandler:(void (^)(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error))completionHandler NS_AVAILABLE_MAC(10_7)
{
NSLog(#"%s", __func__);
if(self.currentDocument) {
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:[url copy], #"url",
[completionHandler copy], #"completionHandler",
nil];
[self closeAllDocumentsWithDelegate:self
didCloseAllSelector:#selector(openDocumentWithContentsOfURLIfClosedAll:didCloseAll:contextInfo:)
contextInfo:(__bridge_retained void *)(info)];
} else {
[super openDocumentWithContentsOfURL:url display:displayDocument completionHandler:completionHandler];
}
}
- (void)openDocumentWithContentsOfURLIfClosedAll:(NSDocumentController *)docController
didCloseAll: (BOOL)didCloseAll
contextInfo:(void *)contextInfo
{
NSDictionary *info = (__bridge NSDictionary *)contextInfo;
if(self.currentDocument == nil)
[super openDocumentWithContentsOfURL:[info objectForKey:#"url"] display:YES completionHandler:[info objectForKey:#"completionHandler"]];
}
There's a very informative exchange on Apple's cocoa-dev mailing list that describes what you have to do in order to subclass NSDocumentController for your purposes. The result is that an existing document is closed when a new one is opened.
Something else you might consider is to mute or stop playing a document when its window resigns main (i.e., sends NSWindowDidResignMainNotification to the window's delegate), if only to avoid forcing what might seem to be an artificial restriction on the user.
I know it's been a while, but in case it helps others....
I had what I think is a similar problem, and the solution was to call the completion handler when my custom DocumentController did not open the document, e.g.:
- (void)openDocumentWithContentsOfURL:(NSURL *)url display:(BOOL)displayDocument completionHandler:(void (^)(NSDocument * _Nullable, BOOL, NSError * _Nullable))completionHandler {
if (doOpenDocument) {
[super openDocumentWithContentsOfURL:url display:displayDocument completionHandler:completionHandler];
} else {
completionHandler(NULL, NO, NULL);
}
}
When I added the completionHandler(NULL, NO, NULL); it started working for more than a single shot.