Post

Replies

Boosts

Views

Activity

Reply to Why no artwork in iTunesLibrary Framework?
Maybe this can help someone in the future as I am sure you've already worked this out after 5 years. I use this to get album artwork that is embedded in the MP3 file (all my music in my library is MP3 music).       let playerItem = AVPlayerItem(url: mp3URL)       let mp3Details = playerItem.asset.metadata       for item in mp3Details {         guard let key = item.key?.description, let value = item.value else {           continue         }         /*          TPE1 = Album artist          TPE2 = Artist          USLT = Lyrics          COMM = Comments          TCON = Genre          TRCK = Track of tracks          TCOM = Composer          TSSE = Encoder          TPOS = Disk of Disks          TDRC = Year released          TALB = Album Name          TBPM = Beats per minute          */         switch key {         case "***2": mp3Info["title"] = value as? String ?? nil         case "TPE1": mp3Info["artist"] = value as? String ?? nil         case "APIC" where value is NSData : mp3Info["artwork"] = NSImage(data: (value as! NSData) as Data) ?? nil         case "TALB": mp3Info["album"] = value as? String ?? nil         case "USLT": mp3Info["lyrics"] = value as? String ?? nil         case "TBPM": mp3Info["bpm"] = value as? Int ?? nil         default: continue         }       } Of course, this will probably not work on downloaded artwork, since that is an Apple product/service. But if you have MP3 files and you add artwork manually, like I do for my music, then this should work for retrieval.
Mar ’22
Reply to ITLibrary musicFolderLocation wrong?
I have this problem as well using Xcode 13.2.1 and Swift 5.5.2. I think Apple broke something in the ITLibrary framework. When I call musicFolderLocation method on the ITLibrary, the URL path returned is completely fubar. It has “/Volumes/Music 1/iTunes/iTunes Media“. First of all, I don't even have a volume named “Music 1“ and I haven't used “iTunes“, or that naming convention, since Apple moonlighted it a long time ago. So I wanted to find out where the problem was, and it appears the ITLibrary is using a UserDefaults value from “iTunes-media-folder-url“ in “com.apple.AMPLibraryAgent.“ So I guess they still haven't gotten everything right with Music. The Music app is buggy as hell and I reported that to them since Catalina and it remains the same, if not worse with each new OS release. Anyway, I don't know if it would harm anything to manually rewrite that UserDefault or not, but until Apple fixes this, right now I don't see a way around it.
Mar ’22
Reply to progress bar copying to iOS / iPadOS
I don't know what the actual solution would be, but I imagine adding an optional notification name parameter to FileManager would be an easy solution i.e. FileManager.default.copyItem(_ at: URL, _ to: URL, _ notification: Notification.Name?). This way if someone wanted to observe that notification they can query the object or userInfo to get bytes written and time remaining and create any kind of progress status they wanted. I've been digging into this myself and would like to find some kind of solution whether it be notifications, or the ability to perform KVO of FileManager while it working.
Mar ’22
Reply to iOS Swift - Detecting external display
Swift 5.5.1 I know this is old and for iOS, but it may help someone who is trying to figure this out and comes across this question. I don't know if the same notification works in iOS, but there should be a similar way if not. You can use the notification NSApplication.didChangeScreenParametersNotification import Cocoa class ViewController: NSViewController { var externalDisplayCount:Int = 0 override func viewDidLoad() { super.viewDidLoad() externalDisplayCount = NSScreen.screens.count setupNotificationCenter() } func setupNotificationCenter() { NotificationCenter.default.addObserver( self, selector: #selector(handleDisplayConnection), name: NSApplication.didChangeScreenParametersNotification, object: nil) } @objc func handleDisplayConnection(notification: Notification) { if externalDisplayCount < NSScreen.screens.count { print("An external display was connected.") externalDisplayCount = NSScreen.screens.count } else if externalDisplayCount > NSScreen.screens.count { print("An external display was disconnected.") externalDisplayCount = NSScreen.screens.count } else { print("A display configuration change occurred.") } } } Notice that the final else is there and will be triggered whenever something changes with any of the connected displays configuration, such as toggling fullscreen mode on a display, or through system preferences.
Nov ’21
Reply to Temporarily switch from background thread to main thread (and back?)
The first thing that comes to mind would be using a notification. func downloadFiles(remoteFolder rf:String, localFolder lf:String, completion: @escaping (_ success:Bool, _ err: String) -> Void) { DispatchQueue.global(qos:.background).async { let fileName:String = (use whatever setter you have from your loop) NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateDownloadFileName", object: nil, userInfo: ["fileName":fileName])) ... DispatchQueue.main.async { completion(successBool, errorMsg) } } } In your ViewController: let myLabel = NSTextField(labelWithString: "") override func viewDidLoad() { setupNotificationCenter() } func updateDownloadFileName(notification: Notification) { if let data = notification.userInfo as? [String:String] { if let string = data["fileName"] { myDownloadLabel.stringValue = string } } } func setupNotificationCenter() { NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "updateDownloadFileName"), object: nil, queue: nil, using: updateDownloadFileName) } If you don't want to use Notifications then you could pass the Label to the method and update the stringValue through a dispatch to the main thread: func downloadFiles(remoteFolder rf:String, localFolder lf:String, myLabel:NSTextField, completion: @escaping (_ success:Bool, _ err: String) -> Void) { DispatchQueue.global(qos:.background).async { let fileName:String = (use whatever setter you have from your loop) DispatchQueue.main.async { myLabel.stringValue = fileName } ... DispatchQueue.main.async { completion(successBool, errorMsg) } } }
Nov ’21
Reply to Opening NSWindow on a connected display
As pointed out on another site, the problem was caused by me setting the styleMask as .borderless without .titled, which prevents the window from becoming key or main according to this. Here's my updated code that works in Swift 5.5: import Cocoa class ViewController: NSViewController {     var externalDisplay: NSScreen?     var fullScreenWindow: NSWindow?     var fullScreenView: NSView?     override func viewDidLoad() {         super.viewDidLoad()         // Do any additional setup after loading the view.     }     override var representedObject: Any? {         didSet {         // Update the view, if already loaded.         }     }     @IBAction func showExternalDisplayButtonClicked(sender: Any?) {         // Open the external display window if one is present         if NSScreen.screens.count > 1 {             externalDisplay = NSScreen.screens.last             let mask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable, .borderless]             fullScreenWindow = NSWindow(contentRect: externalDisplay!.frame, styleMask: mask, backing: .buffered, defer: true, screen: externalDisplay)             fullScreenWindow!.level = .normal             fullScreenWindow!.isOpaque = false             fullScreenWindow!.hidesOnDeactivate = false             fullScreenWindow!.backgroundColor = .red             let viewRect = NSRect(x: 0, y: 0, width: externalDisplay!.frame.width, height: externalDisplay!.frame.height)             fullScreenView = NSView(frame: viewRect)             fullScreenWindow!.contentView = fullScreenView             fullScreenView?.window?.toggleFullScreen(self)         }     } }
Sep ’21