Swift and scriptingbridge object initialization

六眼飞鱼酱① 提交于 2019-12-03 07:52:42

问题


I'm trying to write an application for swift control iTunes. But when initializing the application returns an object of type AnyObject, but must iTunesApplication.

This object does not respond to methods and variables iTunes. Who knows how to make it work?

var iTunes = SBApplication.applicationWithBundleIdentifier("com.apple.iTunes")

The iTunes.h header also holds classes that I need to access but cannot. These classes cause a compilation error as if they are not in a declared a iTunes.h.

Why is this happening to me is not yet clear.

The whole list of classes that are declared a iTunes.h in via @class:

@class iTunesPrintSettings, iTunesApplication, iTunesItem, iTunesAirPlayDevice, iTunesArtwork, iTunesEncoder, iTunesEQPreset, iTunesPlaylist, iTunesAudioCDPlaylist, iTunesLibraryPlaylist, iTunesRadioTunerPlaylist, iTunesSource, iTunesTrack, iTunesAudioCDTrack, iTunesFileTrack, iTunesSharedTrack, iTunesURLTrack, iTunesUserPlaylist, iTunesFolderPlaylist, iTunesVisual, iTunesWindow, iTunesBrowserWindow, iTunesEQWindow, iTunesPlaylistWindow;

For example in Objective - c you would use something like this to get the current track

iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
NSLog(@"Current song is %@", [[iTunes currentTrack] name]);

But I cannot get an equivalent in swift to work.


回答1:


In my swift project , I had issues with using the types defined in the generated iTunes.h file (linking errors and such).

The answer from markhunte explains that you can obtain a reference to the application object. But beyond that, I was getting compilation/linker errors when trying to obtain instances from that application object.

In my swift project, I ended up creating an objective C wrapper class that exposes the iTunes types as basic objective C types (arrays and dictionary), and adapts methods as well.

My swift classes use this wrapper instead of the iTunes types.

So, the objective C wrapper looks like this (redux):

#import "ITunesBridgex.h"
#import "iTunes.h"

@interface ITunesBridgex(){
    iTunesApplication *_iTunesApplication;
    iTunesSource* _iTunesLibrary;
}
@end
@implementation ITunesBridgex

-(id)init {
    self = [super init];
    if (self) {
        _iTunesApplication = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
        NSArray *sources = [_iTunesApplication sources];
        for (iTunesSource *source in sources) {
            if ([source kind] == iTunesESrcLibrary) {
                _iTunesLibrary = source;
                break;
            }
        }
    }
    return self;
}

- (NSDictionary*) currentTrack {
    iTunesTrack* track = _iTunesApplication.currentTrack;
    if (!track)
        return nil;
    NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: track.name, @"title", nil];
    return dict;
}

@end

and the calling swift code:

import Foundation
import Cocoa

class ITunesBridgeSimple {

    var iTunesBridgex: ITunesBridgex

    init(){
        iTunesBridgex = ITunesBridgex()
        self.updateFromCurrentTrack()
    }
    func updateFromCurrentTrack() {
        if let track = self.currentTrack {
            if let title : AnyObject = track.objectForKey("title"){
                println("Current track: \(title)")
            }
        }
    }
}



回答2:


I suspected that the problem was that the iTunes.h file was not being imported. Therefore it's methods where not being picked up.

So I created a -Bridging-Header.h file.

My Project is name swiftItunesTest. so the -Bridging-Header.h file is named:

swiftItunesTest-Bridging-Header.h

Inside of this I placed the #import "iTunes.h" line

And in the AppDelegate.swift file

import Cocoa
 import Appkit
 import ScriptingBridge





class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet var window: NSWindow


    func applicationDidFinishLaunching(aNotification: NSNotification?) {
        var iTunes : AnyObject = SBApplication.applicationWithBundleIdentifier("com.apple.iTunes")

        iTunes.playpause()


    }

    func applicationWillTerminate(aNotification: NSNotification?) {
        // Insert code here to tear down your application
    }


}

The iTunesApplication (iTunes.) now started to pick up the methods/functions


Here is a slightly updated example.

 func applicationDidFinishLaunching(aNotification: NSNotification) {
        let iTunes : AnyObject = SBApplication(bundleIdentifier: "com.apple.iTunes")!

        iTunes.playpause()


          guard let currentTrack: AnyObject =  iTunes.currentTrack!.name else {

             print("No Tracks Playing")
             return
       }
          print("\(currentTrack)")

    }

    func applicationWillTerminate(aNotification: NSNotification) {

    }



回答3:


I wrote a Python script to generate Scripting Bridge headers and then automatically make a native Swift version. That way you don't have to deal with writing full wrappers or even using a Bridging Header. Also, no worry of Linker Errors because it's completely in Swift. https://github.com/garrett-davidson/SwiftingBridge/




回答4:


An inelegant way to do work around the issue is to have iTunes declared as an SBApplication, then, when you call a function from iTunesApplication, downcast iTunes to an AnyObject:

(iTunes as AnyObject).play()

Note that there is no type-safety if you do this: you could call any function declared in any Objective-C header: it's just not guaranteed to be implemented in a specific class, and thus will crash the program.



来源:https://stackoverflow.com/questions/24082635/swift-and-scriptingbridge-object-initialization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!