Post

Replies

Boosts

Views

Activity

Handoff with iCloud document-based app doesn't work
When tapping the Handoff icon of my app on the Mac or on the iPad, Xcode logs this error: [default personal] [ERROR] +[NSURL(BRAdditions) br_documentURLFromBookmarkableString:error:]: (passed to caller) error: Error Domain=BRCloudDocsErrorDomain Code=8 "Not Entitled" UserInfo={NSDescription=Not Entitled} When setting a breakpoint at NSApplicationDelegate.application(_:continue:restorationHandler:), I can see that userInfo.activityType is the one specified with the key NSUbiquitousDocumentUserActivityType under the Info.plist Document Types, but userActivity.userInfo is empty, so the document url is apparently not sent. Is there something else I have to set up?
1
0
1.6k
Nov ’22
UITextView with custom NSUndoManager breaks auto-enabling of software keyboard undo button
I have a custom UITextView where entering text causes the undo button in the software keyboard to be enabled. When I override the undo manager like this: class TextView: UITextView { let _undoManager = UndoManager() override var undoManager: UndoManager? { return _undoManager } } the auto-enabling of the undo button doesn't work anymore. What is the correct way of providing my own undo manager?
Topic: UI Frameworks SubTopic: UIKit Tags:
1
1
769
Sep ’22
How to access other users' home folder
My app allows scanning any selected directory, but I noticed that even when I enable Full Disk Access in the System Preferences, when enumerating the contents of any of the subdirectories of another user's home folder (Documents, Downloads etc.) a permission error is returned. Even when running the app from the Terminal with sudo nothing changes. Using sudo du works. Is this a limitation of macOS apps?
1
0
846
Oct ’22
Hide app that was launched on login on macOS 13 Ventura
Up until macOS 12, there was a checkbox labelled "Hide" for each login item in the Users & Groups System Preferences. Now in macOS 13 the login items have been moved to the General System Preferences, but the "Hide" checkbox has disappeared. Is there a way to know from within the app that it was launched as a login item, without offering an in-app "Launch at login" option which would allow me to control the whole process, but would be quite some effort to just replace that one little option?
1
0
931
Oct ’22
SCNNode.position is sometimes not reflected on screen
The desired outcome of the code below is that both the red and yellow spheres should be at the same position at the center of the scene, (0, 0, 0), but instead the red sphere remains at its initial position (1, 0, 0). Commenting out any of the lines marked in the code, strangely, solves the issue. The code shows a simplified version of some other code, so it would be desirable to keep the order of the lines as they currently are. Am I doing something wrong? class GameViewController: NSViewController {          override func viewDidLoad() {         super.viewDidLoad()                  let scene = SCNScene(named: "art.scnassets/ship.scn")!                  let cameraNode = SCNNode()         cameraNode.camera = SCNCamera()         scene.rootNode.addChildNode(cameraNode)         cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)                  let lightNode = SCNNode()         lightNode.light = SCNLight()         lightNode.light!.type = .omni         lightNode.position = SCNVector3(x: 0, y: 10, z: 10)         scene.rootNode.addChildNode(lightNode)                  let scnView = self.view as! SCNView         scnView.scene = scene                  let parent0 = SCNNode()         scene.rootNode.addChildNode(parent0)         let parent1 = SCNNode()         scene.rootNode.addChildNode(parent1)         let sphere0 = SCNNode(geometry: SCNSphere(radius: 1))         sphere0.geometry!.firstMaterial!.diffuse.contents = NSColor.red                  let sphere1 = SCNNode(geometry: SCNSphere(radius: 1))         sphere1.geometry!.firstMaterial!.diffuse.contents = NSColor.yellow                  sphere0.position = SCNVector3(x: -1, y: 0, z: 0) // commenting out this line solves the issue         parent1.addChildNode(sphere0) // or commenting out this line solves the issue         sphere0.position = SCNVector3(x: 0, y: 0, z: 0)         parent0.addChildNode(sphere1) // or commenting out this line solves the issue                  DispatchQueue.main.asyncAfter(deadline: .now() + 1) {             print(sphere0.worldPosition, sphere1.worldPosition)         }     } }
1
0
791
Feb ’23
Passing nil to NEFilterDataProvider.apply(_:completionHandler:) causes error and doesn't apply default settings as documented
I'm trying to adapt the Filter Network Traffic sample code found at https://developer.apple.com/documentation/networkextension/filtering_network_traffic In FilterDataProvider.swift I've replaced the argument passed to NEFilterDataProvider.apply(_:completionHandler:) with nil, which according to the documentation should apply the default settings. Instead I'm getting the following error: Error Domain=NEFilterErrorDomain Code=1 "The settings parameter doesn’t correspond to a NEFilterSettings object" (translated from my local language) (When logging the error with os_log, the Console app just shows it as <private>. A NSLog call instead displays it correctly.) After that the internet doesn't seem to work at all anymore and I have to trash the app to make the internet work again. (If the bin is not completely empty before trashing the app from the Applications folder, I'm prompted that there is another operation in progress like moving a file.) If a nil parameter doesn't work, what is the correct way of not filtering anything, but just observing all the network traffic?
1
0
400
Mar ’23
Get executable path from audit token provided by NEFilterDataProvider
I'm using this code to get the path of an executable from the audit token provided in NEFilterDataProvider.handleNewFlow(_:): private func securePathFromAuditToken(_ auditToken: Data) throws -> String? { let secFlags = SecCSFlags() var secCode: SecCode? var status = SecCodeCopyGuestWithAttributes(nil, [kSecGuestAttributeAudit: auditToken] as CFDictionary, secFlags, &secCode) guard let secCode = secCode else { throw SecError(status) } var secStaticCode: SecStaticCode? status = SecCodeCopyStaticCode(secCode, secFlags, &secStaticCode) guard let secStaticCode = secStaticCode else { throw SecError(status) } var dict: CFDictionary? status = SecCodeCopySigningInformation(secStaticCode, secFlags, &dict) guard let dict = dict as NSDictionary? else { throw SecError(status) } if let identifier = dict[kSecCodeInfoIdentifier as String] as? String, let path = NSWorkspace.shared.urlForApplication(withBundleIdentifier: identifier)?.path { return path } else if let path = dict[kSecCodeInfoMainExecutable as String] as? String { return path } return nil } But it seems that only applications inside the /Applications folder have a non-nil path. For all other executables I have to resort to this code, which I have read is not as secure: private func insecurePathFromAuditToken(_ auditToken: Data) throws -> String? { if auditToken.count == MemoryLayout<audit_token_t>.size { let pid = auditToken.withUnsafeBytes { buffer in audit_token_to_pid(buffer.baseAddress!.assumingMemoryBound(to: audit_token_t.self).pointee) } let pathbuf = UnsafeMutablePointer<Int8>.allocate(capacity: Int(PROC_PIDPATHINFO_SIZE)) defer { pathbuf.deallocate() } let ret = proc_pidpath(pid, pathbuf, UInt32(PROC_PIDPATHINFO_SIZE)) if ret <= 0 { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno)) } return String(cString: pathbuf) } return nil } This seems to happen with both NEFilterFlow.sourceAppAuditToken and sourceProcessAuditToken. Is this expected? Can it really be that all executables that are not apps shipped with macOS are not signed?
1
0
575
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
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.4k
Mar ’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
521
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 -&gt; 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
920
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
402
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
960
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
466
Apr ’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