WorkflowKit.framework

From iPhone Development Wiki
Jump to: navigation, search
WorkflowKit.framework
Private Framework
Availabile 13.0 – present
Headers [headers.cynder.me]


WorkflowKit is the framework that acts as a backend to the Shortcuts app. It provides around 80% functionality of the Shortcuts app including (but not limited to) actions (though ActionKit also powers a lot of them), handles how shortcuts are imported, how they're stored, etc. It was added to iOS in iOS 13, although iOS 12 Shortcuts's embedded WorkflowAppKit framework does bare a lot of similarities, with some code being reused.

For examples on how to use this framework, see the Example Code section of this page.

WFWorkflowRecord

WFWorkflowRecord is how shortcuts are stored. The biggest thing is actions: Every action has an action identifier to identify an action (WFWorkflowActionIdentifier), and some have parameters of what is in said action (WFWorkflowActionParameters). However, it also handles other data about the shortcut, such as its name and minimum client version it can be imported on.

WFBundledActionProvider

WFBundledActionProvider is what provides WorkflowKit with what actions it loads.

WFAction

WFAction is basically the class that every shortcuts action uses. It should be noted that actions aren't all WFAction, but rather their own class that inherit from it (For example, WFExitShortcut in ActionKit).

WFGallerySessionManager

Handles some of gallery, as well as uploading (WFGallerySessionManager uploadWorkflow).

WFShortcutExtractor

Exclusive to iOS 15+. Handles extracting a shortcut being imported, both supporting unsigned and signed shortcuts. To allow unsigned shortcuts, enable allowsOldFileFormat. For iOS 13/14 shortcut importing, see WFSharedShortcut.

Example Code

Replacing Return to Homescreen with Exit Shortcut upon shortcut import

%hook WFSharedShortcut
-(id)workflowRecord {
// WFWorkflowRecord handles how shortcuts are stored and hooking it will affect every shortcut loaded - not only is this bad for performance, but if you make a mistake, every shortcut will be affected, hence it's best to avoid hooking it when possible. Here, we hook WFSharedShortcut and affect its workflow record instead
    id rettype = %orig;
    NSArray *origShortcutActions = [rettype actions];
    NSMutableArray *newMutableShortcutActions = [origShortcutActions mutableCopy];
    int shortcutActionsObjectIndex = 0;
    
    for (id shortcutActionsObject in origShortcutActions) {
        if ([shortcutActionsObject isKindOfClass:[NSDictionary class]]){
// All shortcuts have an identifier to help identify the action (is.workflow.actions.returntohomescreen is Return to Homescreen, is.workflow.actions.exit is Exit Shortcut). You can find the default action identifiers in /System/​Library/​PrivateFrameworks/​WorkflowKit.framework/WFActions.plist
            if ([[shortcutActionsObject objectForKey:@"WFWorkflowActionIdentifier"] isEqualToString:@"is.workflow.actions.returntohomescreen"]) {
            NSMutableDictionary *mutableShortcutActionsObject = [shortcutActionsObject mutableCopy];
// While not demo'ed in this example, actions also have WFWorkflowActionParameters. For example, the file path in Get File is the WFGetFilePath parameter. These can also be found in WFActions.plist, or by extracting an unsigned .shortcut/.wflow file and looking to see the parameters of the action in the shortcut.
    
            [mutableShortcutActionsObject setValue:@"is.workflow.actions.exit" forKey:@"WFWorkflowActionIdentifier"];
    
            NSDictionary *newShortDict = [[NSDictionary alloc] initWithDictionary:mutableShortcutActionsObject];
            newMutableShortcutActions[shortcutActionsObjectIndex] = newShortDict;
            }
        }
        shortcutActionsObjectIndex++;
    }
    
    shortcutActionsObjectIndex = 0;

    [rettype setActions:newMutableShortcutActions];
    return rettype;
}
%end

Iterating through all actions being loaded

%hook WFBundledActionProvider
-(id)createAllAvailableActions {
	id createactions = %orig; //__NSSetM
	for (id newCreateAction in createactions) {
		NSLog(@"Shortcut Action identifier: %@",[newCreateAction identifier]);
		if ([[newCreateAction identifier]isEqualToString:@"is.workflow.actions.exit"]){
			// the action being iterated has the action identifier of is.workflow.actions.exit - this means we found the exit shortcut action!
			NSLog(@"Exit action: %@", newCreateAction);
		}
	}

	return createactions;
}
%end

References