I've looked for a while and maybe it's my search parameters but can't find doing this with Swift (5). An example I've looked at I need to translate somehow ... https://searchcode.com/file/101621017/OpenEmu/OEFileManager.m
So far I've only managed to copy a file,
let from_cchar = from.path.cString(using: .utf8)!
let to_cchar = to.path.cString(using: .utf8)!
let flags: copyfile_flags_t = UInt32(bitPattern: COPYFILE_DATA)
let success = copyfile(from_cchar, to_cchar, nil, flags)
I need to implement COPY_QUIT but no idea how to reference the file copy ? (will be async)
Once you have a copy state object, the specific state you need to set is COPYFILE_STATE_STATUS_CB. This takes a C function pointer (of type copyfile_callback_t) as a parameter.
Getting this all right is going to be tricky. It would be tricky even from a C-based language, and Swift doesn’t make it any easier.
The first thing I’d do is wrap the copyfile function so that it takes URL parameters directly:
Code Block private func copyfileQ(_ from: URL, _ to: URL, _ state: copyfile_state_t?, _ flags: copyfile_flags_t) -> Int32 { return from.withUnsafeFileSystemRepresentation { fromPtr -> Int32 in to.withUnsafeFileSystemRepresentation { toPtr -> Int32 in copyfile(fromPtr, toPtr, state, flags) } } }
You can then call it like so:
Code Block let success = copyfileQ(self.sourceURL, self.destinationURL, nil, copyfile_flags_t(COPYFILE_DATA)) >= 0 guard success else { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil) }
Now you have to deal with the copy state object. You can allocate this like so:
Code Block let state = copyfile_state_alloc() defer { copyfile_state_free(state) }
Note that this uses a defer statement to guarantee that it gets deallocated. Then pass it in to copyfile like so:
Code Block let success = copyfileQ(self.sourceURL, self.destinationURL, state, copyfile_flags_t(COPYFILE_DATA)) >= 0
Now you need to set up the callback. This is where things get tricky. The problem is that the type of the third parameter to copyfile_state_set depends on the selector you pass it. For example:
For COPYFILE_STATE_STATUS_CB, it is a C function pointer of type copyfile_callback_t.
For COPYFILE_STATE_STATUS_CTX, it is a C void * parameter.
Code Block let context = Unmanaged.passUnretained(self) copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CTX), context.toOpaque())
For COPYFILE_STATE_STATUS_CB, the desired parameter is a C function pointer, and that requires an ugly unsafeBitCast(…).
Code Block let callback: copyfile_callback_t = { what, stage, state, src, dst, context in copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), unsafeBitCast(callback, to: UnsafeRawPointer.self))
Putting this all together, you’ll find pasted in below the source for a command-line tool that copies from a source to a destination. If the source looks like this:
Code Block $ find /Users/quinn/Test/source /Users/quinn/Test/source /Users/quinn/Test/source/.DS_Store /Users/quinn/Test/source/f1.txt /Users/quinn/Test/source/nested /Users/quinn/Test/source/nested/.DS_Store /Users/quinn/Test/source/nested/f2.txt
it prints:
Code Block what: 2 stage: 1 state: 0x000000010183e680 src: /Users/quinn/Test/source dst: /Users/quinn/Test/destination/source what: 2 stage: 2 state: 0x000000010183e680 src: /Users/quinn/Test/source dst: /Users/quinn/Test/destination/source what: 1 stage: 1 state: 0x000000010050aa30 src: /Users/quinn/Test/source/.DS_Store dst: /Users/quinn/Test/destination/source/.DS_Store what: 4 stage: 4 state: 0x000000010050aa30 src: /Users/quinn/Test/source/.DS_Store dst: /Users/quinn/Test/destination/source/.DS_Store what: 1 stage: 2 state: 0x000000010050aa30 src: /Users/quinn/Test/source/.DS_Store dst: /Users/quinn/Test/destination/source/.DS_Store what: 1 stage: 1 state: 0x00000001006186e0 src: /Users/quinn/Test/source/f1.txt dst: /Users/quinn/Test/destination/source/f1.txt what: 4 stage: 4 state: 0x00000001006186e0 src: /Users/quinn/Test/source/f1.txt dst: /Users/quinn/Test/destination/source/f1.txt what: 1 stage: 2 state: 0x00000001006186e0 src: /Users/quinn/Test/source/f1.txt dst: /Users/quinn/Test/destination/source/f1.txt what: 2 stage: 1 state: 0x000000010183e680 src: /Users/quinn/Test/source/nested dst: /Users/quinn/Test/destination/source/nested what: 2 stage: 2 state: 0x000000010183e680 src: /Users/quinn/Test/source/nested dst: /Users/quinn/Test/destination/source/nested what: 1 stage: 1 state: 0x0000000101839d00 src: /Users/quinn/Test/source/nested/.DS_Store dst: /Users/quinn/Test/destination/source/nested/.DS_Store what: 4 stage: 4 state: 0x0000000101839d00 src: /Users/quinn/Test/source/nested/.DS_Store dst: /Users/quinn/Test/destination/source/nested/.DS_Store what: 1 stage: 2 state: 0x0000000101839d00 src: /Users/quinn/Test/source/nested/.DS_Store dst: /Users/quinn/Test/destination/source/nested/.DS_Store what: 1 stage: 1 state: 0x0000000101900540 src: /Users/quinn/Test/source/nested/f2.txt dst: /Users/quinn/Test/destination/source/nested/f2.txt what: 4 stage: 4 state: 0x0000000101900540 src: /Users/quinn/Test/source/nested/f2.txt dst: /Users/quinn/Test/destination/source/nested/f2.txt what: 1 stage: 2 state: 0x0000000101900540 src: /Users/quinn/Test/source/nested/f2.txt dst: /Users/quinn/Test/destination/source/nested/f2.txt what: 3 stage: 1 state: 0x0000000100736830 src: /Users/quinn/Test/source/nested dst: /Users/quinn/Test/destination/source/nested what: 3 stage: 2 state: 0x0000000100736830 src: /Users/quinn/Test/source/nested dst: /Users/quinn/Test/destination/source/nested what: 3 stage: 1 state: 0x0000000100736830 src: /Users/quinn/Test/source dst: /Users/quinn/Test/destination/source what: 3 stage: 2 state: 0x0000000100736830 src: /Users/quinn/Test/source dst: /Users/quinn/Test/destination/source callbackCount: 20
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Code Block import Foundation private func copyfileQ(_ from: URL, _ to: URL, _ state: copyfile_state_t?, _ flags: copyfile_flags_t) -> Int32 { return from.withUnsafeFileSystemRepresentation { fromPtr -> Int32 in to.withUnsafeFileSystemRepresentation { toPtr -> Int32 in copyfile(fromPtr, toPtr, state, flags) } } } class Copier { init(from sourceURL: URL, to destinationURL: URL) { self.sourceURL = sourceURL self.destinationURL = destinationURL } let sourceURL: URL let destinationURL: URL func run() throws { // Allocate our state, arranging to deallocate it when we return. let state = copyfile_state_alloc() defer { copyfile_state_free(state) } // Create a C function point that calls our `callback(…)` method. Note // that we can’t use `self` here because this is a C function pointer. // Rather, we have to recover `self` (here called `obj`) from the // `context` parameter. let callback: copyfile_callback_t = { what, stage, state, src, dst, context in let obj = Unmanaged<Copier>.fromOpaque(context!).takeUnretainedValue() return obj.callback(what: what, stage: stage, state: state!, src: src, dst: dst) } // Apply that to the state. Note the use of `unsafeBitCast(…)` here. // This is the best of a bad set of options for converting a C function // pointer to a C `void *`. copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), unsafeBitCast(callback, to: UnsafeRawPointer.self)) // We pass an unretained reference because the reference only needs to // persist as long as `state`, and we deallocate `state` before we return // from this function. let context = Unmanaged.passUnretained(self) copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CTX), context.toOpaque()) // We’re all set up, so let’s call `copyfile`. let success = copyfileQ(self.sourceURL, self.destinationURL, state, copyfile_flags_t(COPYFILE_DATA | COPYFILE_RECURSIVE)) >= 0 guard success else { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil) } print("callbackCount: \(self.callbackCount)") } var callbackCount = 0 private func callback(what: Int32, stage: Int32, state: copyfile_state_t, src: UnsafePointer<CChar>?, dst: UnsafePointer<CChar>?) -> Int32 { print("what: \(what)") print("stage: \(stage)") print("state: \(state)") print("src: \(src.flatMap { String(cString: $0) } ?? "nil")") print("dst: \(dst.flatMap { String(cString: $0) } ?? "nil")") print() // Increment the callback count to prove that `self` is functioning. self.callbackCount += 1 return COPYFILE_CONTINUE } } func main() { let src = URL(fileURLWithPath: "/Users/quinn/Test/source") let dst = URL(fileURLWithPath: "/Users/quinn/Test/destination") let copier = Copier(from: src, to: dst) try! copier.run() } main() exit(EXIT_SUCCESS)