On Mac the touch bar can automatically detect what app is playing audio, and lets you play/pause, skip, and even seek through the audio. This works for Spotify, Quicktime, and even websites in your browser, like a YouTube tab on Google Chrome. How can I get the same information (song name, thumbnail, app that's playing the song, duration, etc.) by using Swift?
macOS has a bunch more system integrations with media players. For example you can ask Siri for the current song or use the Now Playing Today widget:
All of these integrations use private APIs in the Media Remote private framework. Since it is a private framework Apple will reject your app if you try to submit it on the Mac App Store. You can still notarize it for distribution outside of the App Store.
To use that framework you can try this:
// Load framework
let bundle = CFBundleCreate(kCFAllocatorDefault, NSURL(fileURLWithPath: "/System/Library/PrivateFrameworks/MediaRemote.framework"))
// Get a Swift function for MRMediaRemoteGetNowPlayingInfo
guard let MRMediaRemoteGetNowPlayingInfoPointer = CFBundleGetFunctionPointerForName(bundle, "MRMediaRemoteGetNowPlayingInfo" as CFString) else { return }
typealias MRMediaRemoteGetNowPlayingInfoFunction = @convention(c) (DispatchQueue, @escaping ([String: Any]) -> Void) -> Void
let MRMediaRemoteGetNowPlayingInfo = unsafeBitCast(MRMediaRemoteGetNowPlayingInfoPointer, to: MRMediaRemoteGetNowPlayingInfoFunction.self)
// Get a Swift function for MRNowPlayingClientGetBundleIdentifier
guard let MRNowPlayingClientGetBundleIdentifierPointer = CFBundleGetFunctionPointerForName(bundle, "MRNowPlayingClientGetBundleIdentifier" as CFString) else { return }
typealias MRNowPlayingClientGetBundleIdentifierFunction = @convention(c) (AnyObject?) -> String
let MRNowPlayingClientGetBundleIdentifier = unsafeBitCast(MRNowPlayingClientGetBundleIdentifierPointer, to: MRNowPlayingClientGetBundleIdentifierFunction.self)
// Get song info
MRMediaRemoteGetNowPlayingInfo(DispatchQueue.main, { (information) in
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoArtist"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoTitle"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoAlbum"] as! String)
NSLog("%@", information["kMRMediaRemoteNowPlayingInfoDuration"] as! String)
let artwork = NSImage(data: information["kMRMediaRemoteNowPlayingInfoArtworkData"] as! Data)
// Get bundle identifier
let _MRNowPlayingClientProtobuf: AnyClass? = NSClassFromString("_MRNowPlayingClientProtobuf")
let handle : UnsafeMutableRawPointer! = dlopen("/usr/lib/libobjc.A.dylib", RTLD_NOW)
let object = unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyClass?,Selector?)->AnyObject).self)(_MRNowPlayingClientProtobuf,Selector("a"+"lloc"))
unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(AnyObject?,Selector?,Any?)->Void).self)(object,Selector("i"+"nitWithData:"),information["kMRMediaRemoteNowPlayingInfoClientPropertiesData"] as AnyObject?)
NSLog("%@", MRNowPlayingClientGetBundleIdentifier(object))
dlclose(handle)
})