Done. FB11785400
Post
Replies
Boosts
Views
Activity
It's a swift answer, but you may be able to work it out for c++.
https://developer.apple.com/forums/thread/678914
I got an answer on SO. As it turns out I did not understand the use of isReleasedWhenClosed setting. By changing it from true to false, I no longer have crashes when I close the window and make it nil.
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.
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.
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.
Hmmm... Going back to that page and reading again I am now wondering if this is just a type-o?
Maybe the documentation should read "...and a URL for the appropriateFor parameter which determines the volume of the returned URL." ???
If that's the case, then I think I understand the purpose of this parameter.
After digging a little more on this, I found this old Apple SDKs for CMIOHardware.h over at github which shows "kCMIOHardwareUnknownPropertyError" means "The CMIOObject doesn't know about the property at the given address."
So, does this mean Apple is messing with something in the API/SDK that is now throwing this error?
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.
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)
}
}
}
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)
}
}
}
The window was not displaying on the external display, but yet I was not getting any kind of error. Someone else helped me on another site. I will post the answer.
Also, setting the view appearance to aqua in Storyboard does not change this either.
putting print("Appearance = (self.view.appearance!.name)") right after setting the appearance prints "Appearance = NSAppearanceName(_rawValue: NSAppearanceNameAqua)" to the console, so I know it is setting it. Do system preferences take precedence over setting this manually in code?
Had the same issue today. Went to the developer site and signed in. Turns out I needed to agree to the new developer agreement. Once I did that, everything worked as usual. Apple could have added an appropriate error text to tell me that...