0

I am currently writing an app extension for Spotify that allows me to control the playback. I am using the Spotify AppleScript API in combination with the Scripting Bridge in Objective-C. The first thing I would like to ask,does the Scripting API support Key Value Observing? Because when I add an observer I don't get any notifications from the API and when I try to get data manually from the Scripting API I always get nil values,why? I have the following code:

    -(id)init
    {
        self=[super init];
        if(self)
        {
            spotify=[SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
            //Not sure if KVO is implemented,so I use this to get data from the API
            timer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(observeValueForKeyPath) userInfo:nil repeats:YES];
            if([self isSpotifyRunning])
            {
                //Useless?
                [spotify addObserver:self forKeyPath:@"currentTrack" options:NSKeyValueObservingOptionNew context:nil];
                [spotify addObserver:self forKeyPath:@"playerPosition" options:NSKeyValueObservingOptionNew context:nil];
                [spotify addObserver:self forKeyPath:@"playerState" options:NSKeyValueObservingOptionNew context:nil];
                [self.playBackSlider setTarget:self];
                [self.playBackSlider setAction:@selector(sliderDidMove:)];
                if(spotify.playerState==SpotifyEPlSPaused||spotify.playerState==SpotifyEPlSStopped)
                {
                    [self.playButton setStringValue:@"Play"];
                }
                else
                {
                    [self.playButton setStringValue:@"Stop"];
                }
            }

        }
        return self;
    }
    -(void)observeValueForKeyPath
    {
            [self.titleTextField setStringValue:spotify.currentTrack.name];
            [self.artistTextField setStringValue:spotify.currentTrack.artist];
            [self.currentPlayBackPositionTextField setStringValue:[self formatTime:spotify.playerPosition]];
            [self.remainingTimeTextField setStringValue:[self formatTime:spotify.currentTrack.duration]];
            [self.playBackSlider setMaxValue:spotify.currentTrack.duration];
            [self.playBackSlider setDoubleValue:spotify.playerPosition];
            [self.playBackSlider setDoubleValue:spotify.playerPosition];
            [self.currentPlayBackPositionTextField setStringValue:[self formatTime:spotify.playerPosition]];
        if(spotify.playerState==SpotifyEPlSPaused||spotify.playerState==SpotifyEPlSStopped)
        {
            [self.playButton setStringValue:@"Play"];
        }
        else
        {
            [self.playButton setStringValue:@"Stop"];
        }
    }
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if([keyPath isEqualToString:@"currentTrack"])
    {
        [self.titleTextField setStringValue:spotify.currentTrack.name];
        [self.artistTextField setStringValue:spotify.currentTrack.artist];
        [self.currentPlayBackPositionTextField setStringValue:[self formatTime:spotify.playerPosition]];
        [self.remainingTimeTextField setStringValue:[self formatTime:spotify.currentTrack.duration]];
        [self.playBackSlider setMaxValue:spotify.currentTrack.duration];
        [self.playBackSlider setDoubleValue:spotify.playerPosition];
    }
    else if([keyPath isEqualToString:@"playerPosition"])
    {
        [self.playBackSlider setDoubleValue:spotify.playerPosition];
        [self.currentPlayBackPositionTextField setStringValue:[self formatTime:spotify.playerPosition]];
    }
    else if([keyPath isEqualToString:@"playerState"])
    {
        if(spotify.playerState==SpotifyEPlSPaused||spotify.playerState==SpotifyEPlSStopped)
        {
            [self.playButton setStringValue:@"Stop"];
        }
        else
        {
           [self.playButton setStringValue:@"Play"];
        }
    }
}

EDIT: I have set a delegate for the SBApplication Object and I get the following error:

Error Domain=NSOSStatusErrorDomain Code=-600 "procNotFound: no eligible process with specified descriptor" UserInfo={ErrorNumber=-600}

What exactly does that mean? Spotify starts when the SBApplication is created,so why is the SBApplication telling me that it didn't found the process? I also took a look at Info.plist in the Spotify Bundle and it is scriptable, so why is it not working?

Thank you in advance for any help!

ok404
  • 352
  • 2
  • 13
  • As mentioned in my answer : Get rid of `ScriptingBridge`!. The error message says, the process associated with the descriptor wasn't found. – vadian Nov 29 '15 at 11:47
  • Yes,but it makes no sense because Spotify is started when I execute my code. The Scripting Bridge would probably be the easiest way to achieve that task. Now I have to dig into another documentation about AppleScript as I have to write my use case completely in AppleScript. But I will get rid of ScriptingBridge. Thank you! – ok404 Nov 29 '15 at 12:46

1 Answers1

1

The AppleScript scripting dictionary of any application – what you're calling AppleScript API - doesn't support KVO.

Regarding to use the scripting dictionary in Cocoa apps forget ScriptingBridge.

AppleScriptObjC (introduced in 10.6 Snow Leopard) provides a much easier way to interact with AppleScript. You can even write a Cocoa application completely in AppleScriptObjC using AppleScript and a Objective-C like terminology in the same file.

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thank you for your answer. Well then I have to make some changes. – ok404 Nov 29 '15 at 10:59
  • I tried to use the ScriptingBridge in another context(AppDelegate) and it works totally fine. I read trough the App Extensions Programming Guide and found out that App Extensions have limited access to some resources and it seems that app scripting is not supported by app extensions. – ok404 Dec 04 '15 at 07:35
  • 1
    Ok now I got it,the problem was the App Sandbox. Every widget has to be by default in the sandbox. Now I added an exception for app scripting in the entitlements file and it works. – ok404 Dec 04 '15 at 10:47