4

I am trying to read the currently playing track of iTunes using ScriptingBridge. I found a couple of other threads here but no one seems to be working for me (macOS Mojave and iTunes 12.9.4).

I have created the iTunes.h file using the following command:

sdef /Applications/iTunes.app | sdp -fh --basename "iTunes" 

I am creating an instance of the Scripting Bridge as follows:

if let itunes: AnyObject = SBApplication(bundleIdentifier: "com.apple.iTunes")
{
  if itunes.isRunning
  {
    Swift.print("iTunes is running...")
  }

  Swift.print(itunes.currentTrack?.name)
  Swift.print(itunes.currentTrack?.album)
}

The code compiles fine. When I run the code, I get the following output:

iTunes is running...
nil
nil

The currentTrack is nil, but iTunes is playing a song. What am I missing here? I already tried disabling the sandbox, but to no avail. I am using macOS Mojave, Xcode 10.2 and iTunes 12.9.4. Also, when iTunes is not running, it will be started? Why?

Would there be another way to get the currently playing song of iTunes?

inexcitus
  • 2,471
  • 2
  • 26
  • 41
  • Stab in the dark, but seems like a scope issue. What if you put the last two lines in the preceding `if` block? – Robert Hartshorn Apr 26 '19 at 13:32
  • 1
    You are right, when no track is playing, this would be correct, but now for testing purposes I do not need to move the code - although a song is playing, it is still printing "nil". – inexcitus Apr 26 '19 at 13:34
  • Isn't there a `currentTrack()` property you can access to traverse to the song that's currently playing? – Robert Hartshorn Apr 26 '19 at 13:37
  • 2
    Yes there is...and I am using it, just take another look at the code above. – inexcitus Apr 26 '19 at 13:39
  • Wah, I need coffee. I'm super interested in this. I will wave the white flag after this comment. Have you tried something like `itunes.currentTrack?.name!`. – Robert Hartshorn Apr 26 '19 at 13:47
  • I'm thinking this comes down to optional chaining (https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html) and therefore you might need to unwrap the `.name` property with `!`. – Robert Hartshorn Apr 26 '19 at 14:01
  • The name property already is of type String! and therefore does not need any unwrapping. If I forcefully unwrap a nil object (currenTrack), the app will crash. Nil is printed because currentTrack is nil, not because the name is nil. – inexcitus Apr 26 '19 at 14:08
  • Did you configure your application for full access on Mojave? Does your app have the required entitlements? – matt Apr 26 '19 at 14:30
  • What do you mean? I have disabled the sandbox temporarily, so I assume that the app has full access. – inexcitus Apr 26 '19 at 14:31
  • 1
    Well, I wouldn't assume that. This is Mojave! Things are much more tightly locked down. Easy way to test that one: try running your app on a lower system. If it works there and not in Mojave, it's Mojave getting in your way. – matt Apr 26 '19 at 14:32
  • I assume you already mapped the protocol before the code you posted executes, like `@objc public protocol iTunesApplication: SBApplicationProtocol { @objc optional var currentTrack: iTunesTrack { get } }`. Either way, it might be helpful if you post the code you're using to bridge the @objc header to Swift. Have a good day sir and I wish you great luck. – Robert Hartshorn Apr 26 '19 at 14:51
  • Did you add `NSAppleEventsUsageDescription` to info.plist? – Willeke Apr 26 '19 at 15:30
  • When iTunes is not running, it will be started to get the current track. – Willeke Apr 26 '19 at 15:31
  • I added the key to the info.plist, but this didn't help. – inexcitus Apr 26 '19 at 15:39
  • I got it working. See my answer below. – matt Apr 27 '19 at 03:10

2 Answers2

11

The key thing is that you must at some point see the dialog that says

MyApp wants access to control “iTunes“. Allowing control will provide access to documents and data in “iTunes“, and to perform actions within that app.

If you have not seen that dialog:

  • In the Entitlements, turn sandboxing off.

  • In the Info.plist, add a Privacy - AppleEvents Sending Usage Description entry with some arbitrary string as its value.

Run the app. If it still doesn't work, then say this in the Terminal:

tccutil reset AppleEvents

and run the app again.

matt
  • 515,959
  • 87
  • 875
  • 1,141
2

The solution provided by matt works.

I noticed that the sandboxing feature can still be enabled, if I add the following key to the entitlements file:

    <key>com.apple.security.temporary-exception.apple-events</key>
    <array>
       <string>com.apple.iTunes</string>
    </array>
inexcitus
  • 2,471
  • 2
  • 26
  • 41