Post

Replies

Boosts

Views

Activity

Reply to Where is the Web entry for app iCloud SDK
I have found where the Web entry is. I have to enable CloudKit service in project settings. Now I have another problem - I accidentally created several database containers (long time ago out of curiosity by clicking a button that says "Enable ClouldKit" or something similar). But I cannot find a way to delete the garbage items. The only way is to hide them in the CloudKit management Web UI.
Feb ’24
Reply to FileManager.enumerator and URL problem
Unfortunately, contentsOfDirectory(at:) suffers the same problem as the enumerator. But anyway, I devised a workaround that leads to a little more memory footprint. var fileList = [URL]() for case let fileURL as URL in it { fileList.append(fileURL) } // Sort by length so that the shortest comes first fileList.sort { $0.relativePath.count < $1.relativePath.count } // The shortest path without last component is the root path. let packageRoot = fileList[0].url.deletingLastPathComponent().path
Topic: App & System Services SubTopic: General Tags:
Jan ’24
Reply to Reading multi parts of a file concurrently
It seems my conclusion about a single channel cannot handle multiple reading operations is not correct. Here is a much improved sample to demonstrate the final solution: import Cocoa let OneMebi = 1024 * 1024 class IoJobState { let name: String let signature: UInt8 let label: NSTextField! var total: UInt64 = 0 var eof = false var offset: off_t = 0 var length = 0 var buffer = Data() var done = false var dispatchDone = false var dispatchData: DispatchData? var dispatchError: Int32 = 0 init(_ name: String, label: NSTextField, signature: UInt8) { self.name = name self.label = label self.signature = signature } } class DispatchIoVC: NSViewController { @IBOutlet weak var label: NSTextField! @IBOutlet weak var label1: NSTextField! @IBOutlet weak var label2: NSTextField! override func viewDidLoad() { super.viewDidLoad() } let opQ = OperationQueue() /// Should not be used! let globalDispatchQ = DispatchQueue.global() /// Note: the .concurrent attribute is a must, otherwise the created queue is in serial mode. let dispatchQ = DispatchQueue(label: "test", qos: .utility, attributes: .concurrent) @IBAction func test_click(_ sender: Any) { let filePath = ("~/tmp/bigfile" as NSString).expandingTildeInPath label.stringValue = filePath var fh: Int32 = -1 let signatures: [UInt8] = [0x11, 0xef] repeat { fh = open(filePath, O_RDONLY) if fh >= 0 { break } fh = open(filePath, O_WRONLY | O_CREAT) if (fh < 0) { perror("Opening") label1.stringValue = "Unable to create file" return } let a = [UInt8](repeating: signatures[0], count: OneMebi) a.withUnsafeBytes { for _ in 0..<100 { write(fh, $0.baseAddress, a.count) } } let a2 = [UInt8](repeating: signatures[1], count: OneMebi) a2.withUnsafeBytes { for _ in 0..<90 { write(fh, $0.baseAddress, a.count) } write(fh, $0.baseAddress, 13) } close(fh) filePath.utf8CString.withUnsafeBytes { if 0 != chmod($0.baseAddress, 0o644) { perror("chmod") } } } while true let fmt = ByteCountFormatter() fmt.countStyle = .binary let size = lseek(fh, 0, SEEK_END) print("size:", fmt.string(fromByteCount: size)) lseek(fh, 0, SEEK_SET) let jobs = [IoJobState("job1", label: label1, signature: signatures[0]), IoJobState("job2", label: label2, signature: signatures[1])] jobs[0].offset = 0 jobs[0].length = Int(size) / 2 jobs[1].offset = off_t(jobs[0].length) jobs[1].length = .max let channel = DispatchIO(type: .random, fileDescriptor: fh, queue: dispatchQ) { error in if error == 0 { print("Good cleanup") } else { print("Bad cleanup \(error)") } close(fh) if unlink(filePath) == 0 { print("File successfully deleted") } else { perror("unlink") } } jobs.forEach { job in channel.read(offset: job.offset, length: job.length, queue: self.dispatchQ) { done, data, error in job.dispatchDone = done job.dispatchData = data job.dispatchError = error job.eof = job.dispatchDone && job.dispatchError == 0 && (job.dispatchData == nil || job.dispatchData!.isEmpty) if let data = job.dispatchData { job.total += UInt64(data.count) } OperationQueue.main.addOperation { upateUI(with: job) } if let data { job.buffer.reserveCapacity(data.count) job.buffer.withUnsafeMutableBytes { _ = data.copyBytes(to: $0) } Thread.sleep(forTimeInterval: TimeInterval.random(in: 0.01..<0.05)) if !job.buffer.allSatisfy({ $0 == job.signature }) { print("\(job.name): bad reading \(job.total)") } } } } func upateUI(with job: IoJobState) { if job.dispatchDone && !job.done { job.done = true print("\(job.name) done") } if let data = job.dispatchData { let byteString = fmt.string(fromByteCount: Int64(data.count)) let totalString = fmt.string(fromByteCount: Int64(job.total)) job.label.stringValue = "\(job.name): got \(byteString) bytes, total \(totalString) \(job.total)" } if job.dispatchError != 0 { print("\(job.name): got error code:", job.dispatchError) perror("") } } } }
Topic: Programming Languages SubTopic: Swift Tags:
Jan ’24
Reply to Reading multi parts of a file concurrently
Here is my experiment with DispatchIO, hope it may be helpful for those who are looking for a quick start. Note that - The code still uses one channel (DispatchIO object) for reading. The result is that most of the cases 2nd part finishes and reaches EOF and thus causes the first part not having chance to execute cleanup handler. import Cocoa class IoJobState { let name: String! let label: NSTextField! var total: UInt64 = 0 var eof = false var offset: off_t = 0 var length = 0 var buffer = Data() var done = false var data: DispatchData? var error: Int32 = 0 init(_ name: String, label: NSTextField) { self.name = name self.label = label } } class DispatchIoVC: NSViewController { @IBOutlet weak var label1: NSTextField! @IBOutlet weak var label2: NSTextField! override func viewDidLoad() { super.viewDidLoad() } let opQ = OperationQueue() /// Should not be used! let globalDispatchQ = DispatchQueue.global() /// Note: the .concurrent attribute is a must, otherwise the created queue is in serial mode. let dispatchQ = DispatchQueue(label: "test", qos: .utility, attributes: .concurrent) @IBAction func test_click(_ sender: Any) { let filePath = ("~/tmp/bigfile" as NSString).expandingTildeInPath print(filePath) let fh = open(filePath, O_RDONLY) if fh < 0 { let proc = Process() proc.executableURL = URL(fileURLWithPath: "/usr/bin/truncate") proc.arguments = ["-s", "5G", filePath] do { try proc.run() label1.stringValue = "Created \(filePath) of size 5GB, try again" } catch { print(error) } return } let fmt = ByteCountFormatter() fmt.countStyle = .binary let size = lseek(fh, 0, SEEK_END) print("size:", fmt.string(fromByteCount: size)) lseek(fh, 0, SEEK_SET) let jobs = [IoJobState("job1", label: label1), IoJobState("job2", label: label2)] // TODO: A single channel cannot be used to issue multiple read operation? let d = DispatchIO(type: .random, fileDescriptor: fh, queue: dispatchQ) { error in if error == 0 { print("All good") } else { print("Got error code:", error) } if jobs[0].eof && jobs[1].eof { print("Closing file") close(fh) try? FileManager.default.removeItem(atPath: filePath) } } opQ.addOperation { jobs[0].offset = 0 jobs[0].length = Int(size) / 2 jobs[1].offset = off_t(jobs[0].length) jobs[1].length = .max jobs.forEach { job in d.read(offset: job.offset, length: job.length, queue: self.dispatchQ) { done, data, error in job.done = done job.data = data job.error = error job.eof = job.done && job.error == 0 && (job.data == nil || job.data!.isEmpty) if let data = job.data { job.total += UInt64(data.count) } OperationQueue.main.addOperation { upateUI(job: job) } if !job.eof, let data { job.buffer.reserveCapacity(data.count) job.buffer.withUnsafeMutableBytes { _ = data.copyBytes(to: $0) } } } } } func upateUI(job: IoJobState ) { if job.eof { print("\(job.name!) done") } if let data = job.data { let byteString = fmt.string(fromByteCount: Int64(data.count)) let totalString = fmt.string(fromByteCount: Int64(job.total)) job.label.stringValue = "\(job.name!): got \(byteString) bytes, total \(totalString)" } if job.error != 0 { print("\(job.name!): got error code:", job.error) perror("") } } } }
Topic: Programming Languages SubTopic: Swift Tags:
Jan ’24
Reply to How display shortened path in a label?
That can be done quite easily. But what I want is a more 'strict' way. I'd like to know if Cocoa/Appket provide some low-level mechnisms to measure if a long string fit in a constrained rectangle when it's displayed in a graphics context (here, an NSTextField). When it's not fit entirely, I can abbreviate some path components to make the path string shorter.
Topic: UI Frameworks SubTopic: AppKit Tags:
Dec ’23
Reply to GridView addRow height problem
In IB designer, I am not even able to resize an added image view in first cell. The width and height is fixed at 32; if I try to modify the value, it automatically reverts back to 32. It seems GridView is performing some kind of invisible magic, which I don't have any knowledge of.
Topic: UI Frameworks SubTopic: AppKit Tags:
Dec ’23
Reply to GridView addRow height problem
I found out why, but it's not expected: imageView.frame = NSMakeRect(0, 0, 80, 80) let row = gridView.addRow(with: [imageView, label]) print(gridView.column(at: 0).width, row.height) // output // 80.0 1.1754943508222875e-38 Why the initial height is 1.175 for a newly added row (note I already resized image view to 80x80)? Even later on I explicitly set row.height = 80, but it does not work; the image view is squeezed.
Topic: UI Frameworks SubTopic: AppKit Tags:
Dec ’23
Reply to Where is the Web entry for app iCloud SDK
I have found where the Web entry is. I have to enable CloudKit service in project settings. Now I have another problem - I accidentally created several database containers (long time ago out of curiosity by clicking a button that says "Enable ClouldKit" or something similar). But I cannot find a way to delete the garbage items. The only way is to hide them in the CloudKit management Web UI.
Replies
Boosts
Views
Activity
Feb ’24
Reply to "zero length data" error
Anyway, I found out the cause. The error is thrown when initializing XMLDocument with an empty string: let xd = try XMLDocument(xmlString: "")
Topic: Programming Languages SubTopic: General Tags:
Replies
Boosts
Views
Activity
Feb ’24
Reply to Can't get Xcode to parse the code in one file in editor
Xcode (starting from version 11, maybe) is buuuuuuuuuuuggy. It has big features, but fails miserably on small or tiny functionalities. You can try right-click the file and select "View As->Source Code". If this does not work either, then remove the file and re-add it back (at least this worked for me once or twice).
Replies
Boosts
Views
Activity
Jan ’24
Reply to String(localized:locale) ignores locale parameter
I believe you have to create localizable.strings resource file in FR locale. The String.init and runtime do not do the magic translation work.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jan ’24
Reply to FileManager.enumerator and URL problem
Unfortunately, contentsOfDirectory(at:) suffers the same problem as the enumerator. But anyway, I devised a workaround that leads to a little more memory footprint. var fileList = [URL]() for case let fileURL as URL in it { fileList.append(fileURL) } // Sort by length so that the shortest comes first fileList.sort { $0.relativePath.count < $1.relativePath.count } // The shortest path without last component is the root path. let packageRoot = fileList[0].url.deletingLastPathComponent().path
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jan ’24
Reply to Reading multi parts of a file concurrently
It seems my conclusion about a single channel cannot handle multiple reading operations is not correct. Here is a much improved sample to demonstrate the final solution: import Cocoa let OneMebi = 1024 * 1024 class IoJobState { let name: String let signature: UInt8 let label: NSTextField! var total: UInt64 = 0 var eof = false var offset: off_t = 0 var length = 0 var buffer = Data() var done = false var dispatchDone = false var dispatchData: DispatchData? var dispatchError: Int32 = 0 init(_ name: String, label: NSTextField, signature: UInt8) { self.name = name self.label = label self.signature = signature } } class DispatchIoVC: NSViewController { @IBOutlet weak var label: NSTextField! @IBOutlet weak var label1: NSTextField! @IBOutlet weak var label2: NSTextField! override func viewDidLoad() { super.viewDidLoad() } let opQ = OperationQueue() /// Should not be used! let globalDispatchQ = DispatchQueue.global() /// Note: the .concurrent attribute is a must, otherwise the created queue is in serial mode. let dispatchQ = DispatchQueue(label: "test", qos: .utility, attributes: .concurrent) @IBAction func test_click(_ sender: Any) { let filePath = ("~/tmp/bigfile" as NSString).expandingTildeInPath label.stringValue = filePath var fh: Int32 = -1 let signatures: [UInt8] = [0x11, 0xef] repeat { fh = open(filePath, O_RDONLY) if fh >= 0 { break } fh = open(filePath, O_WRONLY | O_CREAT) if (fh < 0) { perror("Opening") label1.stringValue = "Unable to create file" return } let a = [UInt8](repeating: signatures[0], count: OneMebi) a.withUnsafeBytes { for _ in 0..<100 { write(fh, $0.baseAddress, a.count) } } let a2 = [UInt8](repeating: signatures[1], count: OneMebi) a2.withUnsafeBytes { for _ in 0..<90 { write(fh, $0.baseAddress, a.count) } write(fh, $0.baseAddress, 13) } close(fh) filePath.utf8CString.withUnsafeBytes { if 0 != chmod($0.baseAddress, 0o644) { perror("chmod") } } } while true let fmt = ByteCountFormatter() fmt.countStyle = .binary let size = lseek(fh, 0, SEEK_END) print("size:", fmt.string(fromByteCount: size)) lseek(fh, 0, SEEK_SET) let jobs = [IoJobState("job1", label: label1, signature: signatures[0]), IoJobState("job2", label: label2, signature: signatures[1])] jobs[0].offset = 0 jobs[0].length = Int(size) / 2 jobs[1].offset = off_t(jobs[0].length) jobs[1].length = .max let channel = DispatchIO(type: .random, fileDescriptor: fh, queue: dispatchQ) { error in if error == 0 { print("Good cleanup") } else { print("Bad cleanup \(error)") } close(fh) if unlink(filePath) == 0 { print("File successfully deleted") } else { perror("unlink") } } jobs.forEach { job in channel.read(offset: job.offset, length: job.length, queue: self.dispatchQ) { done, data, error in job.dispatchDone = done job.dispatchData = data job.dispatchError = error job.eof = job.dispatchDone && job.dispatchError == 0 && (job.dispatchData == nil || job.dispatchData!.isEmpty) if let data = job.dispatchData { job.total += UInt64(data.count) } OperationQueue.main.addOperation { upateUI(with: job) } if let data { job.buffer.reserveCapacity(data.count) job.buffer.withUnsafeMutableBytes { _ = data.copyBytes(to: $0) } Thread.sleep(forTimeInterval: TimeInterval.random(in: 0.01..<0.05)) if !job.buffer.allSatisfy({ $0 == job.signature }) { print("\(job.name): bad reading \(job.total)") } } } } func upateUI(with job: IoJobState) { if job.dispatchDone && !job.done { job.done = true print("\(job.name) done") } if let data = job.dispatchData { let byteString = fmt.string(fromByteCount: Int64(data.count)) let totalString = fmt.string(fromByteCount: Int64(job.total)) job.label.stringValue = "\(job.name): got \(byteString) bytes, total \(totalString) \(job.total)" } if job.dispatchError != 0 { print("\(job.name): got error code:", job.dispatchError) perror("") } } } }
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Jan ’24
Reply to Reading multi parts of a file concurrently
Here is my experiment with DispatchIO, hope it may be helpful for those who are looking for a quick start. Note that - The code still uses one channel (DispatchIO object) for reading. The result is that most of the cases 2nd part finishes and reaches EOF and thus causes the first part not having chance to execute cleanup handler. import Cocoa class IoJobState { let name: String! let label: NSTextField! var total: UInt64 = 0 var eof = false var offset: off_t = 0 var length = 0 var buffer = Data() var done = false var data: DispatchData? var error: Int32 = 0 init(_ name: String, label: NSTextField) { self.name = name self.label = label } } class DispatchIoVC: NSViewController { @IBOutlet weak var label1: NSTextField! @IBOutlet weak var label2: NSTextField! override func viewDidLoad() { super.viewDidLoad() } let opQ = OperationQueue() /// Should not be used! let globalDispatchQ = DispatchQueue.global() /// Note: the .concurrent attribute is a must, otherwise the created queue is in serial mode. let dispatchQ = DispatchQueue(label: "test", qos: .utility, attributes: .concurrent) @IBAction func test_click(_ sender: Any) { let filePath = ("~/tmp/bigfile" as NSString).expandingTildeInPath print(filePath) let fh = open(filePath, O_RDONLY) if fh < 0 { let proc = Process() proc.executableURL = URL(fileURLWithPath: "/usr/bin/truncate") proc.arguments = ["-s", "5G", filePath] do { try proc.run() label1.stringValue = "Created \(filePath) of size 5GB, try again" } catch { print(error) } return } let fmt = ByteCountFormatter() fmt.countStyle = .binary let size = lseek(fh, 0, SEEK_END) print("size:", fmt.string(fromByteCount: size)) lseek(fh, 0, SEEK_SET) let jobs = [IoJobState("job1", label: label1), IoJobState("job2", label: label2)] // TODO: A single channel cannot be used to issue multiple read operation? let d = DispatchIO(type: .random, fileDescriptor: fh, queue: dispatchQ) { error in if error == 0 { print("All good") } else { print("Got error code:", error) } if jobs[0].eof && jobs[1].eof { print("Closing file") close(fh) try? FileManager.default.removeItem(atPath: filePath) } } opQ.addOperation { jobs[0].offset = 0 jobs[0].length = Int(size) / 2 jobs[1].offset = off_t(jobs[0].length) jobs[1].length = .max jobs.forEach { job in d.read(offset: job.offset, length: job.length, queue: self.dispatchQ) { done, data, error in job.done = done job.data = data job.error = error job.eof = job.done && job.error == 0 && (job.data == nil || job.data!.isEmpty) if let data = job.data { job.total += UInt64(data.count) } OperationQueue.main.addOperation { upateUI(job: job) } if !job.eof, let data { job.buffer.reserveCapacity(data.count) job.buffer.withUnsafeMutableBytes { _ = data.copyBytes(to: $0) } } } } } func upateUI(job: IoJobState ) { if job.eof { print("\(job.name!) done") } if let data = job.data { let byteString = fmt.string(fromByteCount: Int64(data.count)) let totalString = fmt.string(fromByteCount: Int64(job.total)) job.label.stringValue = "\(job.name!): got \(byteString) bytes, total \(totalString)" } if job.error != 0 { print("\(job.name!): got error code:", job.error) perror("") } } } }
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Jan ’24
Reply to Reading multi parts of a file concurrently
You just need multiple channels. This drives away the cloud in my mind. Based on what you describe, I believe a channel (wrapped as a DispathIO) is efficient and cheap to construct, which was what I worried about.
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Jan ’24
Reply to How detect cyclic symbolic links using NSFileManager?
BTW, I have latest Xcode 15.1 and Sonoma 14.2.1.
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jan ’24
Reply to Weird source code problem of a specific file
Yeah, I solved it myself. I removed this file from project and re-added it back. Now it works fine.
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Jan ’24
Reply to NSWorkspace.shared.isFilePackage
Okay, I found what I need. It's NSFileWrapper and specifically, I want matchesContents(of:) method.
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Dec ’23
Reply to How display shortened path in a label?
That can be done quite easily. But what I want is a more 'strict' way. I'd like to know if Cocoa/Appket provide some low-level mechnisms to measure if a long string fit in a constrained rectangle when it's displayed in a graphics context (here, an NSTextField). When it's not fit entirely, I can abbreviate some path components to make the path string shorter.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
Boosts
Views
Activity
Dec ’23
Reply to GridView addRow height problem
Finally, I solved my own question. It seems I have to set yPlacement to .fill.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
Boosts
Views
Activity
Dec ’23
Reply to GridView addRow height problem
In IB designer, I am not even able to resize an added image view in first cell. The width and height is fixed at 32; if I try to modify the value, it automatically reverts back to 32. It seems GridView is performing some kind of invisible magic, which I don't have any knowledge of.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
Boosts
Views
Activity
Dec ’23
Reply to GridView addRow height problem
I found out why, but it's not expected: imageView.frame = NSMakeRect(0, 0, 80, 80) let row = gridView.addRow(with: [imageView, label]) print(gridView.column(at: 0).width, row.height) // output // 80.0 1.1754943508222875e-38 Why the initial height is 1.175 for a newly added row (note I already resized image view to 80x80)? Even later on I explicitly set row.height = 80, but it does not work; the image view is squeezed.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
Boosts
Views
Activity
Dec ’23