Post

Replies

Boosts

Views

Created

filecopy function is slower than cp command
When copying a 7 GB file, the filecopy function is always about 20% slower than the cp command. I tested using my MacBook Pro 14" M1 and an external SSD connected via USB. Mac to Mac filecopy: 4.78s cp: 3.40s Mac to SSD filecopy: 20.22s cp: 15.93s SSD to Mac filecopy: 20.61s cp: 16.64s I use the following code: let openPanel = NSOpenPanel() openPanel.runModal() let source = openPanel.urls[0] openPanel.canChooseDirectories = true openPanel.canChooseFiles = false openPanel.runModal() let destination = openPanel.urls[0].appendingPathComponent(source.lastPathComponent) source.withUnsafeFileSystemRepresentation { sourcePath in destination.withUnsafeFileSystemRepresentation { destinationPath in let state = copyfile_state_alloc() defer { copyfile_state_free(state) } let date = Date() if copyfile(sourcePath, destinationPath, state, copyfile_flags_t(COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_EXCL)) != 0 { print(NSError(domain: NSPOSIXErrorDomain, code: Int(errno))) } print(-date.timeIntervalSinceNow) } } and the very basic cp command: time cp /path/to/source /path/to/destination Is there a faster way to copy files? Am I doing something wrong with filecopy?
0
0
595
May ’23
SecItemAdd creates keychain item with label "octagon-com.apple.security.keychain" instead of provided kSecAttrLabel
I use the following code to save a private key with a custom label, but the Keychain app shows an entry with name and account octagon-com.apple.security.keychain and type Octagon Account State (com.apple.security.keychain,defaultContext). (This entry, by the way, stays in the Keychain app even after trying to remove it from the Keychain app itself.) Can these values be customized, and what is kSecAttrLabel if it's not displayed in the Keychain app? The documentation only reads The corresponding value is of type CFString and contains the user-visible label for this item. class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { do { try storeKey("asdf") } catch { print(error) } } private func storeKey(_ key: String) throws { guard let data = Data(base64Encoded: key) else { fatalError() } let status = SecItemAdd([kSecClass as String: kSecClassKey, kSecAttrLabel as String: "Asdf", kSecAttrApplicationTag as String: "com.example.app2".data(using: .utf8)!, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecValueData as String: data, kSecAttrSynchronizable as String: true] as [String: Any] as CFDictionary, nil) if status != errSecSuccess { throw NSError(domain: NSOSStatusErrorDomain, code: Int(status)) } } }
1
0
1.8k
Apr ’23
Components of NSDatePicker inside NSTableView cannot be selected
In my table view, each row contains a popup button and a date picker. When clicking on the popup button, it behaves as expected: the menu appears and I can change the selected item. But when clicking on the date components of date picker, the table view row is selected, as if I'm clicking on a non-interactive control. Only when clicking on the stepper (the up/down arrows on the right) does the first date component get selected, which then allows me to click on the other ones as well. Can I change this behaviour so that clicking on the date picker behaves as if I'm directly interacting with it, instead of the table view?
Topic: UI Frameworks SubTopic: AppKit Tags:
1
0
472
Apr ’23
SecItemCopyMatching doesn't find key stored with SecItemAdd
I'm writing an app that uses the App Store Connect API and would like to store the private key contained in the .p8 file downloaded from the website in the keychain. The following code successfully stores a key in the keychain with SecItemAdd, then tries to read it immediately, but without success (the error code of SecItemCopyMatching is errSecItemNotFound and the console outputs nil). Running the code a second time causes SecItemAdd to fail with code errSecDuplicateItem, and SecItemCopyMatching again with code errSecItemNotFound. What am I doing wrong? class AppDelegate: NSObject, NSApplicationDelegate { private let secApplicationTag = "com.example.app".data(using: .utf8)! func applicationDidFinishLaunching(_ aNotification: Notification) { do { try storeKey("asdf") print(try readKey() as Any) } catch { print(error) } } private func storeKey(_ key: String) throws { guard let data = Data(base64Encoded: key) else { fatalError() } let status = SecItemAdd([kSecClass as String: kSecClassKey, kSecAttrLabel as String: "Asdf", kSecAttrApplicationTag as String: secApplicationTag, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecValueData as String: data, kSecAttrSynchronizable as String: true] as [String: Any] as CFDictionary, nil) if status != errSecSuccess { throw NSError(domain: NSOSStatusErrorDomain, code: Int(status)) } } private func readKey() throws -> String? { var item: CFTypeRef? let status = SecItemCopyMatching([kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: secApplicationTag, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecReturnData as String: true] as [String: Any] as CFDictionary, &item) switch status { case errSecSuccess: let data = item as! Data return (data as Data).base64EncodedString() case errSecItemNotFound: return nil default: throw NSError(domain: NSOSStatusErrorDomain, code: Int(status)) } } }
1
0
973
Apr ’23
Error when trying to set access permissions of file owned by user
I'm trying to copy the access permissions of a source directory to a destination directory. The Finder Info panel of the source directory shows the following permissions: Source directory: I: Read & write Everyone: No access Destination directory: I: Read & write Staff: Read only Everyone: Read only The following code succeeds in creating a file in the destination directory, but then fails when setting the URL.fileSecurityKey resource: let openPanel = NSOpenPanel() openPanel.canChooseDirectories = true openPanel.canChooseFiles = false openPanel.runModal() let source = openPanel.urls[0] openPanel.runModal() var destination = openPanel.urls[0] do { try Data().write(to: destination.appendingPathComponent("asd")) try destination.setResourceValues(source.resourceValues(forKeys: [.fileSecurityKey])) } catch { fatalError(error.localizedDescription) } The error message is: You don’t have permission to save the file “destination” in the folder “parent” I thought that an app run by a user has the same permissions as the user itself, and since I have read & write access to both the source and destination directories, the app should be able to copy the access permissions without issues. What is the problem?
1
0
407
Apr ’23
Setting URLResourceKey.fileSecurityKey raises error on some systems
A customer reported that when my app creates directories on their NAS, an error is shown. With their cooperation I boiled the source of the error down to setting URLResourceKey.fileSecurityKey on the directory URL, or setting any of FileAttributeKey.groupOwnerAccountID, .groupOwnerAccountName, .ownerAccountID or .ownerAccountName with FileManager. I thought that maybe URLResourceKey.volumeSupportsExtendedSecurityKey would allow me to determine in advance if setting any of these attributes works, but it seems that the result is false on one of my exFAT drives which doesn't yield any error when setting any of the attributes. I don't even know what "extended security" means in this case, and it doesn't seem to be documented. Should I rely on URLResourceKey.volumeSupportsExtendedSecurityKey? I tried running chown -vv myusername:admin on a file on that exFAT drive: even if the output includes the text 501:20 -> 501:80, which I assume means that the group should have been changed to admin, running the command again yields the exact same output and running stat shows that the group is still staff.
1
0
931
Apr ’23
Reading Finder Info ATTR_CMN_FNDRINFO with getattrlistbulk
After successfully implementing a scan with getattrlistbulk (thanks to https://developer.apple.com/forums/thread/656787/), I'm now trying for each file to get whether the extension is shown in the Finder or not. I think this could be read from the Finder Info (i.e.ATTR_CMN_FNDRINFO), although I'm not sure, since there doesn't seem do be any documentation about what this attribute actually contains. From the setattrlist documentation archive it seems that it is char[32], and I've tried different changes within readUnaligned() so that it would work with an array, but I couldn't get it to work. Below is my current implementation. The comment How to get Finder Info? shows where the implementation currently doesn't work. class AppDelegate: NSObject, NSApplicationDelegate { private static var attributeKeys: attrlist = { var attributeKeys = attrlist() attributeKeys.bitmapcount = u_short(ATTR_BIT_MAP_COUNT) attributeKeys.commonattr = attrgroup_t(ATTR_CMN_RETURNED_ATTRS) | attrgroup_t(bitPattern: ATTR_CMN_ERROR | ATTR_CMN_NAME | ATTR_CMN_OBJTYPE | ATTR_CMN_MODTIME | ATTR_CMN_FNDRINFO | ATTR_CMN_FILEID) attributeKeys.fileattr = attrgroup_t(bitPattern: ATTR_FILE_DATALENGTH) return attributeKeys }() private let bufferWithAlignment16 = UnsafeMutableRawBufferPointer.allocate(byteCount: 256, alignment: 16) func applicationDidFinishLaunching(_ notification: Notification) { let openPanel = NSOpenPanel() openPanel.canChooseDirectories = true openPanel.canChooseFiles = false openPanel.runModal() try! scan(directoryPath: openPanel.urls[0].path) } deinit { bufferWithAlignment16.deallocate() } func scan(directoryPath: String) throws { let fileDescriptor = open(directoryPath, O_RDONLY) if fileDescriptor < 0 { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno)) } defer { let result = close(fileDescriptor) assert(result == 0) } let attributeBuffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 2048, alignment: 16) defer { attributeBuffer.deallocate() } while true { let itemCount = Int(getattrlistbulk(fileDescriptor, &AppDelegate.attributeKeys, attributeBuffer.baseAddress!, attributeBuffer.count, 0)) if itemCount == 0 { return } else if itemCount > 0 { var entryOffset = attributeBuffer.baseAddress! for _ in 0..<itemCount { let length = Int(entryOffset.load(as: UInt32.self)) try unpackResources(at: entryOffset + MemoryLayout<UInt32>.size, parentDirectory: directoryPath) entryOffset += length } } else if errno != EINTR { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno)) } } } private func unpackResources(at attributeOffset: UnsafeMutableRawPointer, parentDirectory: String) throws { var attributeOffset = attributeOffset let returned = attributeOffset.load(as: attribute_set_t.self) attributeOffset += MemoryLayout<attribute_set_t>.size var error: Error? if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_ERROR)) != 0 { error = NSError(domain: NSPOSIXErrorDomain, code: Int(attributeOffset.load(as: UInt32.self))) attributeOffset += MemoryLayout<UInt32>.size } let name: String if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_NAME)) != 0 { let nameInfo = attributeOffset.load(as: attrreference_t.self) name = String(cString: (attributeOffset + Int(nameInfo.attr_dataoffset)).assumingMemoryBound(to: CChar.self)) attributeOffset += MemoryLayout<attrreference_t>.size } else { name = "" } let fileType: fsobj_type_t if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_OBJTYPE)) != 0 { fileType = attributeOffset.load(as: fsobj_type_t.self) attributeOffset += MemoryLayout<fsobj_type_t>.size } else { fileType = VNON.rawValue } var modificationDate: Date? if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_MODTIME)) != 0 { modificationDate = Date(timeIntervalSince1970: TimeInterval(readUnaligned(pointer: attributeOffset, as: timespec.self).tv_sec)) attributeOffset += MemoryLayout<timespec>.size } // How to get Finder Info? let finderInfo: [Int8] if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_FNDRINFO)) != 0 { finderInfo = readUnaligned(pointer: attributeOffset, as: [Int8].self) attributeOffset += MemoryLayout<Int8>.size } else { finderInfo = [] } let fileId: UInt64 if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_FILEID)) != 0 { fileId = readUnaligned(pointer: attributeOffset, as: UInt64.self) attributeOffset += MemoryLayout<UInt64>.size } else { fileId = 0 } let size: Int64 if (returned.fileattr & attrgroup_t(bitPattern: ATTR_FILE_DATALENGTH)) != 0 { size = Int64(readUnaligned(pointer: attributeOffset, as: off_t.self)) attributeOffset += MemoryLayout<off_t>.size } else { size = 0 } if let error = error { throw error } let isDirectory = fileType == VDIR.rawValue let isSymbolicLink = fileType == VLNK.rawValue print(name, isDirectory, isSymbolicLink, modificationDate as Any, finderInfo, fileId, size) } private func readUnaligned<Result>(pointer: UnsafeRawPointer, as: Result.Type) -> Result { bufferWithAlignment16.copyMemory(from: UnsafeRawBufferPointer(start: pointer, count: MemoryLayout<Result>.size)) return bufferWithAlignment16.baseAddress!.load(as: Result.self) } }
1
0
525
Apr ’23
Mismatching locale identifiers for default languages in Xcode and App Store Connect
The list of "main" languages that can be added in Xcode in the project's Info panel (i.e. the ones that can be added without diving into the long submenu) are basically the same that can be added in App Store Connect (except that Xcode also lists Chinese (Hong Kong) (zh-HK) and English (India) (en-IN) which don't exist in App Store Connect). But I noticed that the locale identifiers sometimes refer to different regions: e.g. Xcode has Spanish (es) and Spanish (Latin America) (es-419) while App Store Connect uses Spanish (Spain) and Spanish (Mexico) which when downloaded through the App Store Connect API have the identifiers en-ES and en-MX. Forgive my ignorance, but does that mean that the language used in Latin America and Mexico is the same? Or should I instead select the Mexico variant in Xcode so that the locale identifiers used in the app and on the App Store match? All the mismatching locales are (Xcode first, App Store Connect second): ar vs. ar-SA de vs de-DE en vs en-US es vs es-ES es-419 vs es-MX fr vs fr-FR nl vs nl-NL
0
0
670
Mar ’23
Localized name for default sounds in /System/Library/Sounds
In the Mail settings one can choose one of the default sounds located at /System/Library/Sounds. Playing them is easy, e.g. with NSSound(named: "Purr")?.play(), but how can I show a localized name for those sounds as Mail does? I couldn't find any way of getting a localized name. Would I have to manually translate each one to each supported language?
Topic: UI Frameworks SubTopic: AppKit Tags:
0
0
449
Mar ’23
Using custom shadow path on NSView
Unfortunately it seems that setting the shadow attributes on the layer directly doesn't work: let shadowView = NSView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) shadowView.wantsLayer = true shadowView.layer!.backgroundColor = .white shadowView.layer!.shadowRadius = 50 shadowView.layer!.shadowColor = .black shadowView.layer!.shadowOffset = CGSize(width: 0, height: -30) shadowView.layer!.shadowPath = CGPath(roundedRect: shadowView.bounds, cornerWidth: 25, cornerHeight: 25, transform: nil) Setting the NSView.shadow property instead works: let shadowView = NSView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))let shadow = NSShadow() shadow.shadowBlurRadius = 50 shadow.shadowColor = .black shadow.shadowOffset = CGSize(width: 0, height: -30) shadowView.shadow = shadow shadowView.wantsLayer = true shadowView.layer!.backgroundColor = .white But NSShadow doesn't support a custom path. What's the easiest way to set a shadow with a custom path?
Topic: UI Frameworks SubTopic: AppKit Tags:
0
0
500
Mar ’23
Override user default in UI test with key containing whitespaces
In my UI test I'm trying to force some user defaults. It seems that one can override them with code such as: var app = XCUIApplication() app.launchArguments += ["-myUserDefaultKey", "value"] app.launch() But I would like to replace the value of a default where the key contains whitespaces, such as the key created automatically when setting NSSplitView.autosaveName = "someSplitView", which is NSSplitView Subview Frames someSplitView. I tried escaping the whitespaces with NSSplitView\\ Subview\\ Frames\\ someSplitView and putting the key between single or double quotes, but nothing helped. Is this somehow possible? Also, what would be the preferred way of temporarily removing a user default instead of overwriting it?
1
0
1.5k
Mar ’23
Force app appearance in UI tests to be light or dark
In my UI test I'm trying to force the app's appearance (light or dark). When the system appearance is light, I can force the app to be dark with this code: var app = XCUIApplication() app.launchArguments += ["-AppleInterfaceStyle", "Dark"] app.launch() But replacing Dark with Light doesn't have any effect: if the system appearance is dark: the app remains dark. Is this not possible?
0
0
463
Mar ’23
Xcode UI testing right-to-left language but UI is left-to-right
In my UI test I'm trying to set different languages. I noticed that with right-to-left languages, such as Arabic, the UI is still aligned left-to-right. When I manually run the app with the scheme's language set to Arabic, the UI is correctly aligned right-to-left. Am I missing something? let app = XCUIApplication() app.launchArguments += ["-AppleLanguages", "(ar)"] app.launch()
1
0
1.2k
Mar ’23