iosxcodespeech-recognitioniphone-privateapidictation

Work with private Speech Recognition Frameworks on iOS 8


I need to add Speech Recognition to an App for a Personal Project.

I need the iOS built-in speech recognition framework because it is fast, accurate and it can also recognise your contact names and other information about yourself.

So far, I think I have found the framework which contains the headers for the speech recognition on iOS 8: the SAObjects.framework I got the headers of Github and added them successfully in my Xcode Project. The headers I have tried so far are these:

<SAObjects/SASRecognition.h>
<SAObjects/SASStartSpeechDictation.h>
<SAObjects/SASSpeechRecognized.h>

However, I am not sure how to work with them. For instance, these are two possible methods that can fire a Speech Recognition:

SASStartSpeechDictation *object1 = [SASStartSpeechDictation startSpeechDictation];

SASSpeechRecognized *object2 = [SASSpeechRecognized speechRecognized];

When I debug it though, I cannot find any string in any of these objects. So, obviously something is wrong. Maybe I need to set a notification observer?

Another Solution could be to start a Dictation (through the Keyboard) to a hidden text field (without the keyboard showing). Like the Activator action for Jailbroken devices, if you are familiar with it. But I haven't found any methods that can start the Keyboard dictation, or the Activator action source code to find it out.

Maybe someone has experimented with these things and can give me some help?

Please tell me if you need more information about this question :)

Thanks a lot!


Solution

  • So, I managed to find an answer myself. I luckily found a Github repo with some helpful code: https://github.com/erica/useful-things

    The code I found is under the appstore unsafe pack/DictationHelper directory. This code helps to use the UIDictationController and start and stop the Dictation, and get the text value. Of course, without any Text Fields...

    Important: In order for this to work, you need to have the headers of the UIKit framework, link the framework to the Target and import them in the Project!

    However, I modified the code a bit, because the sample code is only available to speak for a specific duration. I needed to stop speaking by pressing a button. This is the modified code, for anyone who might be interested in the future:

    DicationHelper.h:

    /*
    
     Erica Sadun, http://ericasadun.com
    
     NOT APP STORE SAFE BUT HANDY
     Siri-ready devices only. Will not work in simulator.
    
     Example:
    
     [SpeechHelper speakModalString:@"Please say something"];
     [[DictationHelper sharedInstance] dictateWithDuration:5.0f completion:^(NSString *dictationString) {
     if (dictationString)
     NSLog(@"You said:'%@'", dictationString);
     else
     NSLog(@"No response");}];
    
     //-> OR: (My modification)
    
    
     [SpeechHelper speakModalString:@"Please say something"];
     [[DictationHelper sharedInstance] startDictation:0 completion:^(NSString *dictationString) {
     if (dictationString)
     NSLog(@"You said:'%@'", dictationString);
     else
     NSLog(@"No response");}];
    
     // Then you need to call this to stop the Dictation: [[DictationHelper sharedInstance] stopDictation]
    
     */
    
    #import <UIKit/UIKit.h>
    //#import <Foundation/Foundation.h>
    
    extern NSString *const DictationStringResults;
    
    typedef void (^DictationBlock)(NSString *dictationString);
    
    @interface DictationHelper : NSObject
    + (instancetype) sharedInstance;
    - (void) dictateWithDuration: (CGFloat) duration;
    - (void) dictateWithDuration: (CGFloat) duration completion:(DictationBlock) completionBlock;
    
    -(void) startDictation:(CGFloat) whatever completion:(DictationBlock) completionBlock;
    -(void) stopDictationWithFallback;
    
    @property (nonatomic, readonly) BOOL inUse;
    @end
    

    DictationHelper.m

    /*
    
     Erica Sadun, http://ericasadun.com
    
     NOT APP STORE SAFE BUT HANDY
     Siri-ready devices only. Will not work in simulator.
    
     */
    
    
    #import "DictationHelper.h"
    #define MAKELIVE(_CLASSNAME_)   Class _CLASSNAME_ = NSClassFromString((NSString *)CFSTR(#_CLASSNAME_));
    
    NSString *const DictationStringResults = @"Dictation String Results";
    
    static DictationHelper *sharedInstance = nil;
    
    @class UIDictationController;
    
    @interface UIDictationController
    + (UIDictationController *) sharedInstance;
    - (void) startDictation;
    - (void) stopDictation;
    - (void) preheatIfNecessary;
    @end;
    
    @interface DictationHelper () <UITextFieldDelegate>
    @end
    
    @implementation DictationHelper
    {
        UITextField *secretTextField;
        id dictationController;
        DictationBlock completion;
        BOOL handled;
    }
    
    - (void) preheat
    {
        if (!secretTextField)
        {
            secretTextField = [[UITextField alloc] initWithFrame:CGRectZero];
            UIWindow *window = [[UIApplication sharedApplication] keyWindow];
            [window addSubview:secretTextField];
            secretTextField.inputView = [[UIView alloc] init];
            secretTextField.delegate = self;
        }
    
        if (!dictationController)
        {
            MAKELIVE(UIDictationController);
            dictationController = [UIDictationController sharedInstance];
            [dictationController preheatIfNecessary];
        }
    }
    
    + (instancetype) sharedInstance
    {
        if (!sharedInstance)
        {
            sharedInstance = [[self alloc] init];
            [sharedInstance preheat];
        }
        return sharedInstance;
    }
    
    
    - (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    {
        NSString *tftext = textField.text;
        tftext = [tftext stringByReplacingCharactersInRange:range withString:string];
        [[NSNotificationCenter defaultCenter] postNotificationName:DictationStringResults object:tftext];
    
        if (completion) completion(tftext);
    
        // Treat this dictation as handled
        handled = YES;
        _inUse = NO;
        completion = nil;
    
        // Resign first responder
        [textField resignFirstResponder];
    
        return YES;
    }
    
    - (void) fallback
    {
        // 1. Test completion
        if (!completion) return;
    
        // 2. Check for handled
        if (handled)
        {
            _inUse = NO;
            handled = NO;
            return;
        }
    
        // 3. Assume the dictation didn't work
        completion(nil);
    
        // 4. Reset everything
        handled = NO;
        _inUse = NO;
        completion = nil;
    
        // 5. Resign first responder
        [secretTextField resignFirstResponder];
    }
    
    -(void) startDictation:(CGFloat) whatever completion:(DictationBlock) completionBlock{
        if (completionBlock) completion = completionBlock;
    
        if (_inUse)
        {
            NSLog(@"Error: Dictation Helper already in use");
            return;
        }
    
        _inUse = YES;
        handled = NO;
    
        secretTextField.text = @"";
        [secretTextField becomeFirstResponder];
    
        [[UIDevice currentDevice] playInputClick];
        [dictationController startDictation];
    }
    
    - (void) dictateWithDuration: (CGFloat) numberOfSeconds
    {
        if (_inUse)
        {
            NSLog(@"Error: Dictation Helper already in use");
            return;
        }
    
        _inUse = YES;
        handled = NO;
    
        secretTextField.text = @"";
        [secretTextField becomeFirstResponder];
    
        [[UIDevice currentDevice] playInputClick];
        [dictationController startDictation];
        [self performSelector:@selector(stopDictation) withObject:nil afterDelay:numberOfSeconds];
        [self performSelector:@selector(fallback) withObject:nil afterDelay:numberOfSeconds + 1.0f];
    }
    
    - (void) dictateWithDuration: (CGFloat) duration completion:(DictationBlock) completionBlock
    {
        if (completionBlock) completion = completionBlock;
        [self dictateWithDuration:duration];
    }
    
    - (void) stopDictation
    {
        [dictationController stopDictation];
    }
    
    - (void) stopDictationWithFallback
    {
        [self performSelector:@selector(stopDictation) withObject:nil afterDelay:0.0];
        [self performSelector:@selector(fallback) withObject:nil afterDelay:1.0f];
    }
    
    @end
    
    #undef MAKELIVE