tingraldi/SwiftScripting

Issue with iTunes and SBObject

sedroc opened this issue · 13 comments

Hi, I was using your tool to create a header for iTunes scripting. I am not sure if it is a bug or if I am doing something wrong.

In the iTunes.h file the selection property has type SBObject and can be converted to a SBElementArray in Obj-C. It is then possible to iterate over all the items selected by the user.

// The application program
@interface iTunesApplication : SBApplication

[...]

@property (copy, readonly) SBObject *selection;  // the selection visible to the user

[...]

@end

In the created iTunes.swift file selection has type Int and I do not see how to use this to iterate over the selection of items in iTunes.

// MARK: iTunesApplication
@objc public protocol iTunesApplication: SBApplicationProtocol {
[...]

    optional var selection: Int { get } // the selection visible to the user

[...]
}

Thank you so much for this project. It makes working with Swift & SB feasible again.

I've reproduced this issue on El Capitan. I'm not seeing it on Yosemite. In fact, many of the property/return types are missing or erroneously set to Int when running sbhc.py on El Capitan. This may be related to the dyld warning that is emitted when running sbhc.py.

dyld: warning, LC_RPATH @executable_path/../lib in 
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib 
being ignored in restricted program because of @executable_path

I'll look into this further.

The dyld warning wasn't the issue. It turns out that the Xcode command line tools need to be installed for the sbhc.py script to work properly. You can issue the command xcode-select --install in a Terminal window to initiate installation of the command line tools. I've updated the README accordingly.

Please let me know if installation of the command line tools resolves this issue for you.

Yes, installing the cmd line tools did the trick. Thank you. However, I have to comment out three lines which lead to compiler errors:

Expected '>' to complete generic argument list
optional var currentAirPlayDevices: NSArray<iTunesAirPlayDevice { get set } // the currently selected AirPlay device(s)
optional func add(x: NSArray<NSURL!, to: SBObject!) -> iTunesTrack // add one or more files to a playlist
optional func convert(x: NSArray<SBObject!) -> iTunesTrack // convert one or more files or tracks

The according Obj-C code is:

@property (copy) NSArray<iTunesAirPlayDevice *> *currentAirPlayDevices;  // the currently selected AirPlay device(s)
- (iTunesTrack *) add:(NSArray<NSURL *> *)x to:(SBObject *)to;  // add one or more files to a playlist
- (iTunesTrack *) convert:(NSArray<SBObject *> *)x;  // convert one or more files or tracks

For me it's no issue as I do not use these expressions. Just to let you know.

This is an issue with the translation script not handling Objective-C lightweight generics. I plan to address this in a future update. As a near-term workaround, you could edit the header file to remove the type parameters from the declarations. For example, change NSArray<iTunesAirPlayDevice *> *currentAirPlayDevices to NSArray *currentAirPlayDevices and then re-run sbhc.py.

Thanks, I will give it a shot. While playing around I did not manage to set values (which worked with Obj-C and SB). Is this a limitation of Swift being more strict or do I miss something important? For example

var iTunes = SBApplication(bundleIdentifier: "com.apple.iTunes") as! iTunesApplication        
print(iTunes.soundVolume) // Shows current volume
iTunes.soundVolume = 50 // Compile error: Cannot assign to property: 'iTunes' is immutable

does not compile.

According to the generated iTunes.swift I would assume that setting a value should be possible:

optional var soundVolume: Int { get set } // the sound output volume (0 = minimum, 100 = maximum)

This is an unfortunate fact of life with the way the protocols are defined here. For optional properties, I'm not aware of a direct way to set them, even though they are marked as { get set }.

A workaround is to use key-value coding to set the value of the desired property. For example

(iTunes as! NSObject).setValue(50, forKey: "soundVolume")

Also, if you're interested, you can use the application function in ScriptingUtilities to instantiate an application based on it's name. This saves you the trouble of having to dig up the bundle identifier.

let iTunes = application(name: "iTunes") as! iTunesApplication

I've updated sbhc.py to handle Objective-C lightweight generic declarations in the header files generated by sdp. I've also modified the logic such that all properties are emitted as readonly and companion set accessors are added to the generated protocols where appropriate. So, instead of

optional var soundVolume: Int { get set } // the sound output volume (0 = minimum, 100 = maximum)

you'll now have

optional var soundVolume: Int { get } // the sound output volume (0 = minimum, 100 = maximum)

optional func setSoundVolume(soundVolume: Int) // the sound output volume (0 = minimum, 100 = maximum)

That's very nice and works like a charm. Leads to much nicer code compared to the key-value trick. Thank you.

You're welcome, and thank you for your interest in SwiftScripting. Please report other issues you encounter or any ideas you might have for improvement.

Hi,
I add target to do a framework of ScriptingUtilities. And I import ScriptingUtilities and also iTunes.h, iTunes.swift use follow the steps in the main of your github page

I use this line like the your comment above
let iTunes = application(name: "iTunes") as! iTunesApplication
But Xcode says " Missing argument for parameter 'delegateHandlesKey' in call "
what should i do with this error?

I also do like this
let iTunes = ScriptingUtilities.application(name: "iTunes") as! iTunesApplication
But I don't know, this not work at all. if I trying to print out iTunes.currentTrack.name is a empty string

Thanks for help

I do it again and it works, Thanks so much

Stan,

Thank you for your question. The ScriptingUtilies framework is intended to be built and installed into /Library/Frameworks. Once you’ve installed ScriptingUtilities, along with your iTunes framework, then you can have a script such as the following:

#!/usr/bin/xcrun swift -F /Library/Frameworks

import ScriptingUtilities
import iTunesScripting

let iTunes = application(name: "iTunes") as! iTunesApplication

print(iTunes.currentTrack!.name)

You should be able to run the above script and it will print out the name of the track currently playing in iTunes. If no track is playing, you’ll get an Optional empty string as the result.

I'm having the same problem, I built ScriptingUtilities and my own CalendarScripting and added them to /Library/Frameworks and restarted XCode, but the editor keeps reporting "Missing argument for parameter 'delegateHandlesKey' in call" on the application() call.

If I do the same using /usr/bin/xcrun swift -F /Library/Frameworks, like your example above, it does seem to work.

I checked Linking->Runpath Search Paths and Search Paths->Framework Search Paths and added /Library/Frameworks there explicitly, just to be sure. Note that xcode's autocomplete does see the alternate application() functions.

Any ideas?

Alexander,

Thank you for your interest in SwiftScripting.

With regard to your issue, I don't have a solution. I've not had great success using Xcode to build and run apps that leverage the scripting frameworks. The best option is really to stick with a plain text editor and run the code in interpreted mode using the #! convention.

Things change with each Xcode/swift release, so this situation may improve in the future.