Skip to content

Instantly share code, notes, and snippets.

@steventroughtonsmith
Created October 27, 2019 17:02
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save steventroughtonsmith/08cdd95fa2ae8a69beb327ec7b9480a5 to your computer and use it in GitHub Desktop.
Save steventroughtonsmith/08cdd95fa2ae8a69beb327ec7b9480a5 to your computer and use it in GitHub Desktop.
[Catalyst] Registering for & responding to AppleScript events in a Mac Catalyst app
<key>NSAppleScriptEnabled</key>
<true/>
<key>OSAScriptingDefinition</key>
<string>ScriptableTasks.sdef</string>
//
// MRDScripting.h
// RadioIntegration
//
// Created by Steven Troughton-Smith on 26/10/2019.
// Copyright © 2019 High Caffeine Content. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol MRDScriptingDelegate <NSObject>
-(void)playRadioStation:(NSString *)station;
-(void)pause;
-(void)setVolume:(CGFloat)volume;
@end
@interface MRDScripting : NSObject
@property id <MRDScriptingDelegate> delegate;
+ (instancetype)sharedInstance;
+(void)initScripting;
@end
NS_ASSUME_NONNULL_END
//
// MRDScripting.m
// RadioIntegration
//
// Created by Steven Troughton-Smith on 26/10/2019.
// Copyright © 2019 High Caffeine Content. All rights reserved.
//
#import "MRDScripting.h"
@import AppleScriptKit;
@implementation MRDScripting
OSErr myPauseHandler(const AppleEvent *theAppleEvent, AppleEvent *reply, SRefCon handlerRefcon)
{
if ([MRDScripting sharedInstance].delegate && [[MRDScripting sharedInstance].delegate respondsToSelector:@selector(pause)])
[[MRDScripting sharedInstance].delegate pause];
return noErr;
}
OSErr myHandler(const AppleEvent *theAppleEvent, AppleEvent *reply, SRefCon handlerRefcon)
{
NSLog(@"Handling Apple Event");
AEDesc replyObject;
OSErr err;
err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeWildCard, &replyObject);
if (err != noErr) {
NSLog(@"Error decoding %c", replyObject.descriptorType);
}
CFStringRef descRef = UTCreateStringForOSType(replyObject.descriptorType);
NSString * descString = [NSString stringWithString:(__bridge NSString *)descRef];
NSLog(@"Type = %@", descString);
Size sz = AEGetDescDataSize(&replyObject);
if (sz)
{
char *r = malloc(sz);
AEGetDescData(&replyObject, &r[0], sz);
NSString *param = [[NSString alloc] initWithBytes:r length:sz encoding:NSUTF16LittleEndianStringEncoding];
if ([MRDScripting sharedInstance].delegate && [[MRDScripting sharedInstance].delegate respondsToSelector:@selector(playRadioStation:)])
[[MRDScripting sharedInstance].delegate playRadioStation:param];
}
return noErr;
}
OSErr myVolumeHandler(const AppleEvent *theAppleEvent, AppleEvent *reply, SRefCon handlerRefcon)
{
NSLog(@"Handling Apple Event");
AEDesc replyObject;
OSErr err;
err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeWildCard, &replyObject);
if (err != noErr) {
NSLog(@"Error decoding %c", replyObject.descriptorType);
}
CFStringRef descRef = UTCreateStringForOSType(replyObject.descriptorType);
NSString * descString = [NSString stringWithString:(__bridge NSString *)descRef];
NSLog(@"Type = %@", descString);
Size sz = AEGetDescDataSize(&replyObject);
if (sz)
{
if ([descString isEqualToString:@"doub"])
{
CGFloat *r = malloc(sz);
AEGetDescData(&replyObject, &r[0], sz);
CGFloat volume = r[0];
if (volume >= 0.0 && volume <= 1.0)
{
NSLog(@"desired volume = %f", volume);
if ([MRDScripting sharedInstance].delegate && [[MRDScripting sharedInstance].delegate respondsToSelector:@selector(setVolume:)])
[[MRDScripting sharedInstance].delegate setVolume:volume];
}
else
{
return paramErr;
}
}
else if ([descString isEqualToString:@"long"])
{
long *r = malloc(sz);
AEGetDescData(&replyObject, &r[0], sz);
long volume = r[0];
if (volume >= 0.0 && volume <= 1.0)
{
NSLog(@"desired volume LONG = %ld", volume);
if ([MRDScripting sharedInstance].delegate && [[MRDScripting sharedInstance].delegate respondsToSelector:@selector(setVolume:)])
[[MRDScripting sharedInstance].delegate setVolume:(CGFloat)volume];
}
else
{
return paramErr;
}
}
}
return noErr;
}
+(void)initScripting
{
AEInstallEventHandler('HCCR', 'play', myHandler, NULL, false);
AEInstallEventHandler('HCCR', 'paus', myPauseHandler, NULL, false);
AEInstallEventHandler('HCCR', 'volu', myVolumeHandler, NULL, false);
}
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary>
<suite name="HCC Radio suite" code="HCCR" description="Standard terms for HCC Radio scripting">
<command name="play" code="HCCRplay" description="Plays a radio station">
<direct-parameter type="text" optional="no" description="the name of the station to play"/>
</command>
<command name="pause" code="HCCRpaus" description="Pauses playback">
</command>
<command name="set volume" code="HCCRvolu" description="Sets playback volume">
<direct-parameter type="number" optional="no" description="the desired volume, valid from 0.0 to 1.0"/>
</command>
</suite>
</dictionary>
/* Loading your AppKit bundle */
NSError *error = nil;
[[NSBundle bundleWithPath:[[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:@"RadioIntegration.framework"]] loadAndReturnError:&error];
/* After loading your AppKit framework/bundle */
[NSClassFromString(@"MRDScripting") initScripting];
/* In class that wants to register for scripting delegate callbacks */
[[NSClassFromString(@"MRDScripting") sharedInstance] setDelegate:self];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment